ESP32 Cơ Bản

Vi điều khiển WiFi & Bluetooth - Kết nối IoT

Mục Lục Bài Học

1. Giới Thiệu ESP32

ESP32 là vi điều khiển mạnh mẽ của Espressif Systems, tích hợp sẵn WiFi và Bluetooth, phù hợp cho các ứng dụng IoT.

Thông số kỹ thuật ESP32 DevKit V1

Thông Số Giá Trị
Vi xử lý Dual-core Xtensa 32-bit LX6
Tốc độ xung nhịp Lên đến 240MHz
RAM 520KB SRAM
Flash 4MB (thường)
Chân GPIO 34 chân (một số có chức năng đặc biệt)
ADC 18 kênh 12-bit
DAC 2 kênh 8-bit
PWM 16 kênh
WiFi 802.11 b/g/n (2.4GHz)
Bluetooth Classic & BLE 4.2
Điện áp 3.3V (quan trọng!)
⚠️ Cảnh báo: ESP32 hoạt động ở 3.3V, không phải 5V như Arduino Uno. Đừng cấp nguồn 5V trực tiếp vào các chân GPIO!

Sơ đồ chân ESP32 DevKit V1 (30 pin)

Chân cần lưu ý:
  • Input only: GPIO 34, 35, 36, 39 - chỉ đọc, không ghi được
  • Strapping pins: GPIO 0, 2, 5, 12, 15 - có chức năng đặc biệt khi boot
  • ADC2: Không dùng được khi WiFi hoạt động
  • Touch pins: GPIO 0, 2, 4, 12-15, 27, 32, 33 - cảm ứng điện dung

2. Cài Đặt ESP32 Trên Arduino IDE

Bước 1: Thêm Board Manager URL

  1. Mở Arduino IDE
  2. Vào File → Preferences
  3. Trong mục "Additional Board Manager URLs", thêm:
    https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
  4. Click OK

Bước 2: Cài đặt ESP32 Board

  1. Vào Tools → Board → Boards Manager
  2. Tìm "esp32" trong ô tìm kiếm
  3. Chọn "esp32 by Espressif Systems" và click Install
  4. Đợi cài đặt hoàn tất (có thể mất vài phút)

Bước 3: Cấu hình Board

  1. Kết nối ESP32 với máy tính qua USB
  2. Chọn Tools → Board → ESP32 Arduino → ESP32 Dev Module
  3. Chọn Tools → Port → COM... (cổng tương ứng)
  4. Cấu hình khác (để mặc định hoặc theo bảng dưới):
Tham số Giá trị khuyến nghị
Upload Speed 921600
CPU Frequency 240MHz
Flash Frequency 80MHz
Flash Mode QIO
Flash Size 4MB
Partition Scheme Default 4MB

Bước 4: Test Blink LED

ESP32 Blink Test
#define LED_PIN 2  // LED built-in trên ESP32 thường ở GPIO 2

void setup() {
  pinMode(LED_PIN, OUTPUT);
}

void loop() {
  digitalWrite(LED_PIN, HIGH);
  delay(1000);
  digitalWrite(LED_PIN, LOW);
  delay(1000);
}
💡 Lỗi upload thường gặp: Nếu gặp lỗi "A fatal error occurred: Failed to connect", nhấn giữ nút BOOT trên ESP32 khi bắt đầu upload.

3. GPIO - Digital & Analog

Digital Output - LED đơn giản

Điều khiển nhiều LED
const int LED1 = 23;
const int LED2 = 22;
const int LED3 = 21;

void setup() {
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);
}

void loop() {
  // Hiệu ứng chạy dọc
  digitalWrite(LED1, HIGH);
  delay(200);
  digitalWrite(LED1, LOW);
  
  digitalWrite(LED2, HIGH);
  delay(200);
  digitalWrite(LED2, LOW);
  
  digitalWrite(LED3, HIGH);
  delay(200);
  digitalWrite(LED3, LOW);
}

PWM - Điều chỉnh độ sáng (LEDC)

ESP32 có 16 kênh PWM độc lập (gọi là LEDC - LED Control).

PWM trên ESP32
const int LED_PIN = 23;
const int FREQ = 5000;      // Tần số PWM 5kHz
const int LED_CHANNEL = 0;  // Kênh PWM 0-15
const int RESOLUTION = 8;   // Độ phân giải 8-bit (0-255)

void setup() {
  // Cấu hình kênh PWM
  ledcSetup(LED_CHANNEL, FREQ, RESOLUTION);
  
  // Gắn chân với kênh
  ledcAttachPin(LED_PIN, LED_CHANNEL);
}

void loop() {
  // Tăng độ sáng
  for (int brightness = 0; brightness <= 255; brightness++) {
    ledcWrite(LED_CHANNEL, brightness);
    delay(10);
  }
  
  // Giảm độ sáng
  for (int brightness = 255; brightness >= 0; brightness--) {
    ledcWrite(LED_CHANNEL, brightness);
    delay(10);
  }
}

Analog Input (ADC)

Đọc giá trị Analog
const int POT_PIN = 34;  // GPIO 34 là ADC1_CH6

void setup() {
  Serial.begin(115200);
  
  // Cấu hình độ phân giải ADC (9-12 bit)
  analogReadResolution(12);  // 0-4095
  
  // Cấu hình attenuation (phạm vi điện áp)
  analogSetAttenuation(ADC_11db);  // 0-3.3V
}

void loop() {
  int value = analogRead(POT_PIN);
  
  // Chuyển đổi sang điện áp
  float voltage = value * (3.3 / 4095.0);
  
  Serial.print("Giá trị: ");
  Serial.print(value);
  Serial.print(" - Điện áp: ");
  Serial.print(voltage);
  Serial.println("V");
  
  delay(500);
}
⚠️ Lưu ý ADC:
  • ADC2 (GPIO 0, 2, 4, 12-15, 25-27) không hoạt động khi WiFi bật
  • Chỉ dùng ADC1 (GPIO 32-39) cho độ tin cậy cao

4. Kết Nối WiFi

ESP32 có thể hoạt động ở 3 chế độ WiFi: Station (STA), Access Point (AP), hoặc cả hai (AP+STA).

Chế độ Station - Kết nối WiFi có sẵn

Kết nối WiFi cơ bản
#include <WiFi.h>

const char* ssid = "TenWiFi";      // Thay bằng tên WiFi của bạn
const char* password = "MatKhau";  // Thay bằng mật khẩu WiFi

void setup() {
  Serial.begin(115200);
  Serial.println();
  Serial.print("Đang kết nối WiFi");
  
  // Bắt đầu kết nối
  WiFi.begin(ssid, password);
  
  // Chờ kết nối
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  
  Serial.println("\nĐã kết nối WiFi!");
  Serial.print("Địa chỉ IP: ");
  Serial.println(WiFi.localIP());
  Serial.print("Cường độ tín hiệu: ");
  Serial.print(WiFi.RSSI());
  Serial.println(" dBm");
}

void loop() {
  // Kiểm tra kết nối
  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("WiFi vẫn kết nối");
  } else {
    Serial.println("Mất kết nối WiFi!");
  }
  
  delay(5000);
}

Chế độ Access Point - Tạo WiFi riêng

ESP32 làm Access Point
#include <WiFi.h>

const char* ap_ssid = "ESP32-AP";
const char* ap_password = "12345678";  // Tối thiểu 8 ký tự

void setup() {
  Serial.begin(115200);
  
  // Tạo Access Point
  WiFi.softAP(ap_ssid, ap_password);
  
  // Lấy IP của AP (mặc định: 192.168.4.1)
  IPAddress IP = WiFi.softAPIP();
  Serial.print("AP đã khởi động!");
  Serial.print("Địa chỉ IP: ");
  Serial.println(IP);
  Serial.println("Kết nối WiFi: " + String(ap_ssid));
}

void loop() {
  // Hiển thị số thiết bị đang kết nối
  Serial.print("Số thiết bị kết nối: ");
  Serial.println(WiFi.softAPgetStationNum());
  delay(5000);
}

WiFi Scanner - Quét mạng WiFi

Quét các WiFi xung quanh
#include <WiFi.h>

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  WiFi.disconnect();
  delay(100);
}

void loop() {
  Serial.println("Bắt đầu quét WiFi...");
  
  int n = WiFi.scanNetworks();
  
  if (n == 0) {
    Serial.println("Không tìm thấy mạng WiFi nào");
  } else {
    Serial.print(n);
    Serial.println(" mạng WiFi được tìm thấy:");
    
    for (int i = 0; i < n; i++) {
      // In thông tin mỗi mạng
      Serial.print(i + 1);
      Serial.print(": ");
      Serial.print(WiFi.SSID(i));
      Serial.print(" (");
      Serial.print(WiFi.RSSI(i));
      Serial.print(" dBm) ");
      Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? "Mở" : "Có bảo mật");
    }
  }
  
  Serial.println();
  delay(10000);  // Quét lại sau 10 giây
}

5. Web Server Cơ Bản

Tạo web server trên ESP32 để điều khiển thiết bị qua trình duyệt.

Web Server điều khiển LED
#include <WiFi.h>
#include <WebServer.h>

const char* ssid = "TenWiFi";
const char* password = "MatKhau";

const int LED_PIN = 2;
bool ledState = false;

WebServer server(80);  // Server trên cổng 80

// Hàm xử lý trang chủ
void handleRoot() {
  String html = "<!DOCTYPE html><html>";
  html += "<head><meta charset='UTF-8'>";
  html += "<title>ESP32 Web Server</title>";
  html += "<style>body{font-family:Arial;text-align:center;margin-top:50px;}";
  html += "button{padding:20px 40px;font-size:20px;margin:10px;cursor:pointer;}</style>";
  html += "</head><body>";
  html += "<h1>ESP32 Web Server</h1>";
  html += "<p>Trạng thái LED: <b>" + String(ledState ? "BẬT" : "TẮT") + "</b></p>";
  html += "<button onclick=\"location.href='/on'\">BẬT LED</button>";
  html += "<button onclick=\"location.href='/off'\">TẮT LED</button>";
  html += "</body></html>";
  
  server.send(200, "text/html", html);
}

// Hàm bật LED
void handleLedOn() {
  ledState = true;
  digitalWrite(LED_PIN, HIGH);
  server.sendHeader("Location", "/");
  server.send(303);
}

// Hàm tắt LED
void handleLedOff() {
  ledState = false;
  digitalWrite(LED_PIN, LOW);
  server.sendHeader("Location", "/");
  server.send(303);
}

void setup() {
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT);
  
  // Kết nối WiFi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  
  Serial.println("\nĐã kết nối WiFi!");
  Serial.print("IP: ");
  Serial.println(WiFi.localIP());
  
  // Cấu hình các route
  server.on("/", handleRoot);
  server.on("/on", handleLedOn);
  server.on("/off", handleLedOff);
  
  // Khởi động server
  server.begin();
  Serial.println("Web server đã khởi động!");
}

void loop() {
  server.handleClient();
}
✅ Cách sử dụng: Sau khi upload code, mở Serial Monitor để xem địa chỉ IP. Truy cập IP đó trên trình duyệt (ví dụ: 192.168.1.100) để điều khiển LED.

6. HTTP Client - Gọi API

ESP32 có thể gửi request HTTP để lấy dữ liệu từ API hoặc gửi dữ liệu lên server.

GET Request - Lấy dữ liệu thời tiết

HTTP GET Request
#include <WiFi.h>
#include <HTTPClient.h>

const char* ssid = "TenWiFi";
const char* password = "MatKhau";

void setup() {
  Serial.begin(115200);
  
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nĐã kết nối WiFi!");
}

void loop() {
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    
    // URL API (ví dụ: JSONPlaceholder)
    http.begin("https://jsonplaceholder.typicode.com/posts/1");
    
    // Gửi GET request
    int httpCode = http.GET();
    
    if (httpCode > 0) {
      Serial.printf("HTTP Code: %d\n", httpCode);
      
      if (httpCode == HTTP_CODE_OK) {
        String payload = http.getString();
        Serial.println("Response:");
        Serial.println(payload);
      }
    } else {
      Serial.printf("Lỗi: %s\n", http.errorToString(httpCode).c_str());
    }
    
    http.end();
  }
  
  delay(10000);  // Gọi API mỗi 10 giây
}

POST Request - Gửi dữ liệu cảm biến

HTTP POST Request với JSON
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>

const char* ssid = "TenWiFi";
const char* password = "MatKhau";
const char* serverUrl = "http://your-server.com/api/data";

void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nĐã kết nối!");
}

void loop() {
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    http.begin(serverUrl);
    http.addHeader("Content-Type", "application/json");
    
    // Tạo JSON data
    StaticJsonDocument<200> doc;
    doc["device"] = "ESP32";
    doc["temperature"] = 25.5;
    doc["humidity"] = 60.2;
    
    String jsonData;
    serializeJson(doc, jsonData);
    
    // Gửi POST request
    int httpCode = http.POST(jsonData);
    
    if (httpCode > 0) {
      Serial.printf("HTTP Code: %d\n", httpCode);
      String response = http.getString();
      Serial.println(response);
    } else {
      Serial.println("Lỗi gửi dữ liệu");
    }
    
    http.end();
  }
  
  delay(30000);  // Gửi mỗi 30 giây
}

7. Bluetooth Classic - Serial

ESP32 hỗ trợ Bluetooth Classic (SPP - Serial Port Profile) để giao tiếp với điện thoại/máy tính.

Bluetooth Serial cơ bản
#include <BluetoothSerial.h>

BluetoothSerial SerialBT;
const int LED_PIN = 2;

void setup() {
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT);
  
  // Khởi động Bluetooth với tên "ESP32-BT"
  SerialBT.begin("ESP32-BT");
  Serial.println("Bluetooth đã sẵn sàng!");
  Serial.println("Kết nối từ điện thoại: ESP32-BT");
}

void loop() {
  // Nhận dữ liệu từ Bluetooth
  if (SerialBT.available()) {
    char command = SerialBT.read();
    Serial.print("Nhận lệnh: ");
    Serial.println(command);
    
    if (command == '1') {
      digitalWrite(LED_PIN, HIGH);
      SerialBT.println("LED đã BẬT");
    }
    else if (command == '0') {
      digitalWrite(LED_PIN, LOW);
      SerialBT.println("LED đã TẮT");
    }
  }
  
  // Gửi dữ liệu từ Serial lên Bluetooth
  if (Serial.available()) {
    SerialBT.write(Serial.read());
  }
}
💡 Cách test: Tải app "Serial Bluetooth Terminal" trên Android hoặc "LightBlue" trên iOS để kết nối và gửi/nhận dữ liệu.

Bài Tập Thực Hành

  1. Điều khiển đèn qua Web: Tạo web server điều khiển nhiều LED với slider điều chỉnh độ sáng.
  2. Trạm thời tiết IoT: Đọc DHT11 và gửi dữ liệu lên ThingSpeak/Blynk.
  3. Notification Telegram: Gửi thông báo lên Telegram khi có sự kiện (nút nhấn, cảm biến chuyển động).
  4. RC Car qua Bluetooth: Điều khiển robot xe qua Bluetooth bằng app điện thoại.