ESP32 Smart Home tự động với Web Server

Trong dự án IoT, chủ đề ESP32 Smart Home này, chúng ta hãy cùng làm một sản phẩm nhà thông minh với Web Server. Bạn có thể dùng trang Web trên điện thoại hoặc laptop, máy tính bảng để điều khiển căn nhà của mình (thông qua 4 relay).

Sau khi bật thiết bị, ESP32 sẽ kết nối với mạng WiFi của bạn và tạo Website. Khi dùng địa chỉ IP của mạch ESP32 đó, bạn có thể truy cập vào Website và điều khiển Smart Home bằng ESP32. Bằng cách bật / tắt trên Website, chúng ta có thể thay đổi trạng thái của 4 Relay.

Giới thiệu dự án ESP32 Smart Home

Để nâng cao tính di động và sự tùy chỉnh cho Smart Home, chúng ta sẽ dùng PCB tùy chỉnh trong dự án này. Trong bài viết dưới có kèm sẵn sơ đồ kết nối, bố cục PCB, tập tin Gerbet, vật liệu cần dùng và chương trình lập trình đầy đủ để bạn làm một ESP32 Smart Home.

Dự án ESP32 Smart Home này có thể hoạt động độc lập, không phụ thuộc vào bất kỳ ứng dụng IoT nào bên ngoài. Nhờ đó, chúng ta có thể tích hợp chúng vào hệ thống tự động hóa mini trong nhà.

Chuẩn bị

Dưới đây là danh sách vật liệu cần dùng trong dự án

  • 8 Tụ điện 100nf
  • 3 tụ điện 10uf
  • 1 tụ điện 220uf, 25V
  • 4 đi ốt
  • 1 SP4322-01ETG
  • 4 Bộ ghép quang PC817C
  • 1 ESP32 WROOM-32
  • 1 Khối đầu cuối 2 chân
  • 4 Khối đầu cuối 3 chân
  • 4 Relay SRD05VDC-SLC
  • 5 đèn LED đỏ
  • 1 đầu nam 6 pin
  • 1 HLK 10M05
  • 6 Transistor
  • 8 điện trở 220R
  • 4 DNP
  • 3 điện trở 12K
  • 4 điện trở 1K
  • 1 điện trở 470R
  • 4 điện trở 10K
  • 4 nút nhấn Swift
  • 4 Swift thủ công
  • 1 Voltage Regulator

Sơ đồ kết nối

Dưới đây là sơ đồ kết nối các thiết bị, được vẽ bằng Altium Designer:

Sơ đồ kết nối ESP32 Smart Home

Chúng ta sẽ dùng tụ điện, điện trở và đèn LED. Để đổi điện áp 220V thành 5 V DC, bạn có thể dùng bộ chuyển đổi AC to DC.

Để cung cấp điện cho ESP32 và các thiết bị ngoại vi khác, chúng ta dùng IC LDO HT7333 công suất thấp.

Chúng ta dùng thêm bộ ghép quang PC817 để tách dây công suất cao ra khỏi mạch 3.3V.

Chúng ta dùng LED5 được dùng để biểu thị nguồn điện.

Tương tự, các LED từ 1 đến 4 được dùng lần lượt cho các Relay từ 1 đến 4. LED số 5 dùng để báo nguồn.

Các Relay từ 1 dến 4 được kết nối lần lượt với ESP32 qua các chân GPIO 12, 14, 27 và 26. Các nút nhấn được dùng để kết nối và điều khiển Relay bằng tay, chúng được kết nối với chân GPIO 5, 17, 13 và 16 trên ESP32. Bạn có thể gắn 1 công tắc thủ công tại đó và lập trình bộ điều khiển để điều khiển.

Nếu muốn điều khiển thiết bị gia dụng trong nhà, bạn kết nối thiết bị gia dụng bằng kết nối Relay tại 3 chân đầu cuối là J2, J3, J4 và J5. Trong đó, đầu nối 2 chân J1 được dùng để cấp nguồn AC trực tiếp với mạch. Ta kết nối tụ điện C12 220uf, 25V với các dao động điện áp dừng PCB.

Tải PCB Gerber File

PCB được dùng trong dự án ESP32 SMart Home này cũng được thiết kế bằng phần mềm thiết kế Altium, chúng trông như hình dưới:

Sơ đồ PCB cho dự án ESP32 Smart Home

Dưới đây là chế độ 3D khi xem PCB:

Bạn có thể tải xuống tập tin Gerber qua link bên dưới và đặt mua PCB từ nước ngoài qua link PCBGOGO.

Sau đó, bạn có thể upload tập tin Gerbet bằng cách click vào Quote now. Tại đó, bạn có thể chọn loại vật liệu, kích cỡ, độ dày, số lượng, màu sắc và một số thông số quan trọng khác:

Upload file và chọn thông số vật liệu cho dự án ESP32 Smart Home

Sau khi chọn các thông số trên, bạn chọn quốc gia và phương thức vận chuyển rồi chọn đặt hàng.

Hàn PCB

Sau khi nhận PCB về, bạn tiến hành hàn các thành phần như điện trở, tụ điện, relay, đèn LED,… lên mạch. Quan trọng là bạn cần chú ý về cực của đèn LED SMD:

Hàn PCB - Hoàn tất phần cứng ESP32 Smart Home

Sau khi hàn tất cả linh kiện trên, bạn tiến hành hàn chip thô ESP32. Sau khi hàn tất cả thiết bị, chúng ta đã có một mạch hoàn hảo để dùng cho dự án ESP32 Smart Home.

Hàn PCB - Hoàn tất phần cứng ESP32 Smart Home

Code cho EPS32 Smart Home

Dưới đây là đoạn code đơn giản để làm dự án Smart Home, giúp điều khiển 4 thiết bị được nối với các chân GPIO thông qua 1 Website.

Bạn hãy thay đổi thông tin mạng WiFi (tên mạng WiFi và mật khẩu WiFi), sau đó sử dụng bình thường nhé:

#include <WiFi.h>
 
// Replace with your network credentials
const char* ssid     = "***************";  // Network SSID (name)
const char* password = "***************";  // Network password
 
// Set web server port number to 80 (HTTP default)
WiFiServer server(80);
 
// Variable to store the HTTP request from the client
String header;
 
// Variables to store the current state of each device (ON/OFF)
String Device1State = "off";
String Device2State = "off";
String Device3State = "off";
String Device4State = "off";
 
// Assign each device to a GPIO pin
const int Device1 = 12;
const int Device2 = 14;
const int Device3 = 27;
const int Device4 = 26;
 
// Variables to track the current time and last time a client made a request
unsigned long currentTime = millis();
unsigned long previousTime = 0;
// Define timeout time in milliseconds (2000ms = 2 seconds)
const long timeoutTime = 2000;
 
void setup() {
  Serial.begin(115200);
  // Initialize the GPIO pins for the devices as outputs and set them to LOW
  pinMode(Device1, OUTPUT);
  pinMode(Device2, OUTPUT);
  pinMode(Device3, OUTPUT);
  pinMode(Device4, OUTPUT);
  digitalWrite(Device1, LOW);
  digitalWrite(Device2, LOW);
  digitalWrite(Device3, LOW);
  digitalWrite(Device4, LOW);
 
  // Connect to Wi-Fi network with SSID and password
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  // Print the local IP address once connected
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  server.begin();
}
 
void loop() {
  WiFiClient client = server.available();   // Listen for incoming clients
 
  if (client) {                             // If a new client connects,
    Serial.println("New Client.");          // print a message out in the serial port
    String currentLine = "";                // make a String to hold incoming data from the client
    currentTime = millis();
    previousTime = currentTime;
    while (client.connected() && currentTime - previousTime <= timeoutTime) { // loop while the client's connected
      currentTime = millis();
      if (client.available()) {             // if there's bytes to read from the client,
        char c = client.read();             // read a byte, then
        Serial.write(c);                    // print it out the serial monitor
        header += c;
        if (c == '\n') {                    // if the byte is a newline character
          // if the current line is blank, you got two newline characters in a row.
          // that's the end of the client HTTP request, so send a response:
          if (currentLine.length() == 0) {
            // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
            // and a content-type so the client knows what's coming, then a blank line:
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println("Connection: close");
            client.println();
 
            // turns the Relays on and off
            if (header.indexOf("GET /1/on") >= 0) {
              Serial.println("Device 1 on");
              Device1State = "on";
              digitalWrite(Device1, HIGH);
            } else if (header.indexOf("GET /1/off") >= 0) {
              Serial.println("Device 1 off");
              Device1State = "off";
              digitalWrite(Device1, LOW);
            } else if (header.indexOf("GET /2/on") >= 0) {
              Serial.println("Device 2 on");
              Device2State = "on";
              digitalWrite(Device2, HIGH);
            } else if (header.indexOf("GET /2/off") >= 0) {
              Serial.println("Device 2 off");
              Device2State = "off";
              digitalWrite(Device2, LOW);
            }
            else if (header.indexOf("GET /3/on") >= 0) {
              Serial.println("Device 3 off");
              Device3State = "on";
              digitalWrite(Device3, HIGH);
            }
            else if (header.indexOf("GET /3/off") >= 0) {
              Serial.println("Device 3 off");
              Device3State = "off";
              digitalWrite(Device3, LOW);
            }
            else if (header.indexOf("GET /4/on") >= 0) {
              Serial.println("Device 4 off");
              Device4State = "on";
              digitalWrite(Device4, HIGH);
            }
            else if (header.indexOf("GET /4/off") >= 0) {
              Serial.println("Device 4 off");
              Device4State = "off";
              digitalWrite(Device4, LOW);
            }
 
            // Display the HTML web page
            client.println("<!DOCTYPE html><html>");
            client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
            client.println("<link rel=\"icon\" href=\"data:,\">");
            // Enhanced CSS for a compact look
            client.println("<style>");
            client.println("html { font-family: 'Roboto', sans-serif; background: #f5f5f5; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; }");
            client.println("body { max-width: 600px; background: #fff; padding: 20px; margin: 20px; border-radius: 5px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); }");
            client.println("h1 { color: #333; text-align: center; }");
            client.println(".device { background: linear-gradient(to right, #dae2f8, #d6a4a4); padding: 15px; margin: 10px 0; border-radius: 10px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); text-align: center; }"); // Reduced padding and margin
            client.println("p { font-size: 18px; color: #555; margin: 10px 0; }");
            client.println(".button { border: none; padding: 15px 35px; text-align: center;"); // Slightly reduced padding
            client.println("text-decoration: none; display: inline-block; font-size: 22px; margin: 10px; cursor: pointer; border-radius: 5px; transition: all 0.3s ease; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); }");
            client.println(".button:hover { box-shadow: 0 4px 8px rgba(0, 0, 0, 0.4); }");
            client.println(".button-on { background-color: #4CAF50; color: white; }");
            client.println(".button-off { background-color: #f44336; color: white; }");
            client.println("</style></head>");
 
            // Web Page Heading
            client.println("<body><h1>Smart Home Automation</h1>");
 
            // Display current state, and ON/OFF buttons for Relay 1
            client.println("<div class=\"device\"><p>Device 1 - State " + Device1State + "</p>");
            if (Device1State == "off") {
              client.println("<a href=\"/1/on\"><button class=\"button button-on\">ON</button></a>");
            } else {
              client.println("<a href=\"/1/off\"><button class=\"button button-off\">OFF</button></a>");
            }
            client.println("</div>");
 
            // Display current state, and ON/OFF buttons for Relay 2
            client.println("<div class=\"device\"><p>Device 2 - State " + Device2State + "</p>");
            if (Device2State == "off") {
              client.println("<a href=\"/2/on\"><button class=\"button button-on\">ON</button></a>");
            } else {
              client.println("<a href=\"/2/off\"><button class=\"button button-off\">OFF</button></a>");
            }
            client.println("</div>");
 
            // Display current state, and ON/OFF buttons for Relay 3
            client.println("<div class=\"device\"><p>Device 3 - State " + Device3State + "</p>");
            if (Device3State == "off") {
              client.println("<a href=\"/3/on\"><button class=\"button button-on\">ON</button></a>");
            } else {
              client.println("<a href=\"/3/off\"><button class=\"button button-off\">OFF</button></a>");
            }
            client.println("</div>");
 
            // Display current state, and ON/OFF buttons for Relay 4
            client.println("<div class=\"device\"><p>Device 4 - State " + Device4State + "</p>");
            if (Device4State == "off") {
              client.println("<a href=\"/4/on\"><button class=\"button button-on\">ON</button></a>");
            } else {
              client.println("<a href=\"/4/off\"><button class=\"button button-off\">OFF</button></a>");
            }
            client.println("</div>");
 
            client.println("</body></html>");
 
            // The HTTP response ends with another blank line
            client.println();
            // Break out of the while loop
            break;
          } else { // if you got a newline, then clear currentLine
            currentLine = "";
          }
        } else if (c != '\r') {  // if you got anything else but a carriage return character,
          currentLine += c;      // add it to the end of the currentLine
        }
      }
    }
    // Clear the header variable
    header = "";
    // Close the connection
    client.stop();
    Serial.println("Client disconnected.");
    Serial.println("");
  }
}

Lưu ý rằng bạn hãy kết nối module FTDI với chân FTDI của PCB nhé:

Kết nối chân FTDT - Hoàn tất phần cứng ESP32 Smart Home

Sau đó, bạn hãy upload code vào mạch. Khi quá trình hoàn tất, bạn mở Serial Monitor.

Trên màn hình hiển thị thông tin ESP32 kết nối với mạng WiFI, sau đó chúng hiển thị địa chỉ IP của mạch ESP32.

Khi đó, bạn có thể truy cập vào địa chỉ IP ESP32 này trên trình duyệt Internet của điện thoại / laptop để truy cập vào Website và điều khiển các thiết bị. Chúc bạn thành công!

Lời kết

Trên đây là các hướng dẫn chi tiết về dự án ESP32 Smart Home, chúng là dự án ở mức độ nâng cao vì bạn cần hàn PCB và chuẩn bị nhiều công đoạn khác. Trên Website IoTZone hiện nay đã có sẵn các bài viết hướng dẫn đơn giản hơn, bạn có thể tham khảo:

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *