1. MQTT Protocol - Giao Thức IoT
MQTT (Message Queuing Telemetry Transport) là giao thức nhẹ, hiệu quả cho IoT. Cần cài thư viện: PubSubClient
MQTT Client - Kết nối Broker
#include <WiFi.h> #include <PubSubClient.h> const char* ssid = "TenWiFi"; const char* password = "MatKhau"; const char* mqtt_server = "broker.hivemq.com"; // Public broker WiFiClient espClient; PubSubClient client(espClient); void callback(char* topic, byte* payload, unsigned int length) { Serial.print("Nhận tin: ["); Serial.print(topic); Serial.print("] "); for (int i = 0; i < length; i++) { Serial.print((char)payload[i]); } Serial.println(); } void reconnect() { while (!client.connected()) { Serial.print("Đang kết nối MQTT..."); if (client.connect("ESP32Client")) { Serial.println("Đã kết nối!"); client.subscribe("esp32/led"); // Subscribe topic } else { Serial.print("Lỗi, rc="); Serial.print(client.state()); delay(5000); } } } void setup() { Serial.begin(115200); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } client.setServer(mqtt_server, 1883); client.setCallback(callback); } void loop() { if (!client.connected()) { reconnect(); } client.loop(); // Publish dữ liệu mỗi 5 giây static unsigned long lastMsg = 0; unsigned long now = millis(); if (now - lastMsg > 5000) { lastMsg = now; float temp = random(20, 30); char msg[50]; snprintf(msg, 50, "Nhiệt độ: %.1f°C", temp); client.publish("esp32/temp", msg); Serial.println(msg); } }
2. FreeRTOS - Đa Nhiệm
FreeRTOS cho phép chạy nhiều task đồng thời trên ESP32.
FreeRTOS - Hai Task độc lập
void Task1(void *pvParameters) { while(1) { Serial.println("Task 1: Đang chạy..."); vTaskDelay(1000 / portTICK_PERIOD_MS); } } void Task2(void *pvParameters) { while(1) { Serial.println("Task 2: Đang chạy..."); vTaskDelay(500 / portTICK_PERIOD_MS); } } void setup() { Serial.begin(115200); // Tạo Task 1 trên Core 0 xTaskCreatePinnedToCore( Task1, // Hàm task "Task1", // Tên task 10000, // Stack size NULL, // Tham số 1, // Priority NULL, // Task handle 0); // Core // Tạo Task 2 trên Core 1 xTaskCreatePinnedToCore(Task2, "Task2", 10000, NULL, 1, NULL, 1); } void loop() { // Loop để trống vì các task độc lập }
3. Deep Sleep - Tiết Kiệm Pin
Deep Sleep giúp ESP32 tiêu thụ chỉ vài microampere, rất quan trọng cho thiết bị pin.
Deep Sleep với Timer
#define uS_TO_S_FACTOR 1000000 #define TIME_TO_SLEEP 10 // Ngủ 10 giây void setup() { Serial.begin(115200); delay(1000); Serial.println("ESP32 đã thức dậy!"); // Đọc cảm biến, gửi dữ liệu... Serial.println("Đang gửi dữ liệu..."); delay(2000); // Cấu hình wakeup sau 10 giây esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR); Serial.println("Đi ngủ 10 giây..."); esp_deep_sleep_start(); } void loop() { // Không chạy vào đây }
Deep Sleep với External Wakeup (nút nhấn)
Wakeup bằng nút nhấn
#define BUTTON_PIN 33 void setup() { Serial.begin(115200); // Kiểm tra nguyên nhân thức dậy print_wakeup_reason(); // Cấu hình wakeup bằng nút nhấn esp_sleep_enable_ext0_wakeup(GPIO_NUM_33, 0); // LOW = nhấn nút Serial.println("Đi ngủ. Nhấn nút để đánh thức..."); delay(1000); esp_deep_sleep_start(); } void print_wakeup_reason() { esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause(); switch(wakeup_reason) { case ESP_SLEEP_WAKEUP_EXT0: Serial.println("Thức dậy bởi nút nhấn"); break; case ESP_SLEEP_WAKEUP_TIMER: Serial.println("Thức dậy bởi timer"); break; default: Serial.println("Khởi động lần đầu"); break; } } void loop() {}
4. OTA Update - Cập Nhật Từ Xa
OTA (Over-The-Air) cho phép cập nhật firmware ESP32 qua WiFi không cần cáp USB.
OTA Update cơ bản
#include <WiFi.h> #include <ArduinoOTA.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!"); Serial.print("IP: "); Serial.println(WiFi.localIP()); // Cấu hình OTA ArduinoOTA.setHostname("ESP32-OTA"); ArduinoOTA.setPassword("admin"); // Optional ArduinoOTA.onStart([]() { Serial.println("Bắt đầu OTA..."); }); ArduinoOTA.onEnd([]() { Serial.println("\nOTA hoàn tất!"); }); ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { Serial.printf("Tiến độ: %u%%\r", (progress / (total / 100))); }); ArduinoOTA.onError([](ota_error_t error) { Serial.printf("Lỗi[%u]: ", error); }); ArduinoOTA.begin(); Serial.println("OTA đã sẵn sàng!"); } void loop() { ArduinoOTA.handle(); // Code khác... }
💡 Cách upload OTA: Trong Arduino IDE, chọn Tools → Port → ESP32-OTA at [IP] thay vì cổng COM.
5. NVS - Lưu Dữ Liệu Vĩnh Viễn
NVS (Non-Volatile Storage) cho phép lưu dữ liệu không bị mất khi tắt nguồn.
Lưu và đọc dữ liệu với Preferences
#include <Preferences.h> Preferences preferences; void setup() { Serial.begin(115200); // Mở namespace "myapp" preferences.begin("myapp", false); // Đọc counter (mặc định = 0) unsigned int counter = preferences.getUInt("counter", 0); Serial.printf("Lần khởi động thứ: %u\n", counter); // Tăng counter và lưu counter++; preferences.putUInt("counter", counter); // Lưu các kiểu dữ liệu khác preferences.putString("name", "ESP32"); preferences.putFloat("temp", 25.5); preferences.putBool("state", true); // Đóng preferences.end(); } void loop() {}
6. Tích Hợp IoT Platform
Blynk - App điều khiển IoT
Bước 1: Tải app Blynk trên điện thoại
Bước 2: Tạo project mới, lấy Auth Token
Bước 3: Cài thư viện Blynk trong Arduino IDE
Bước 2: Tạo project mới, lấy Auth Token
Bước 3: Cài thư viện Blynk trong Arduino IDE
Kết nối Blynk
#include <WiFi.h> #include <BlynkSimpleEsp32.h> char auth[] = "YourAuthToken"; char ssid[] = "TenWiFi"; char pass[] = "MatKhau"; void setup() { Serial.begin(115200); Blynk.begin(auth, ssid, pass); } void loop() { Blynk.run(); }
ThingSpeak - Lưu dữ liệu đám mây
Gửi dữ liệu lên ThingSpeak
#include <WiFi.h> #include <HTTPClient.h> String apiKey = "YourAPIKey"; const char* server = "http://api.thingspeak.com/update"; void sendToThingSpeak(float temp, float humid) { if (WiFi.status() == WL_CONNECTED) { HTTPClient http; String url = String(server) + "?api_key=" + apiKey + "&field1=" + String(temp) + "&field2=" + String(humid); http.begin(url); int httpCode = http.GET(); if (httpCode > 0) { Serial.println("Đã gửi dữ liệu!"); } http.end(); } }
Bài Tập Nâng Cao
- Nhà thông minh: Hệ thống điều khiển đèn, quạt qua MQTT + Dashboard
- Trạm thời tiết IoT: Đọc nhiều cảm biến, gửi ThingSpeak, Deep Sleep
- Camera giám sát: ESP32-CAM stream video qua web + phát hiện chuyển động
- Robot tự hành: Điều khiển qua Blynk + tránh vật cản + line following