
Arduino IDE nâng cao: Debug và Serial Monitor hiệu quả
Kỹ thuật debug firmware ESP32 bằng Serial Monitor và Serial Plotter: đặt log đúng chỗ, đọc lỗi biên dịch, theo dõi giá trị cảm biến DHT22 theo thời gian thực, và các mẹo debug thường bị bỏ qua trong Arduino IDE.
Phần lớn lỗi khi làm IoT không nằm ở phần cứng mà ở chỗ ta không nhìn thấy chương trình đang làm gì. Bài này hướng dẫn dùng Serial Monitor và Serial Plotter trong Arduino IDE để debug hiệu quả, minh hoạ bằng chính firmware đọc nhiệt độ - độ ẩm DHT22 trên ESP32: từ log trạng thái khởi động, phát hiện cảm biến mất kết nối, đến vẽ đồ thị giá trị theo thời gian thực.
Hướng dẫn chi tiết
Kỹ thuật debug firmware ESP32 bằng Serial Monitor và Serial Plotter: log có cấu trúc, phát hiện cảm biến mất kết nối, vẽ đồ thị DHT22 theo thời gian thực.
1. Giới thiệu
Phần lớn lỗi khi làm IoT không nằm ở phần cứng mà ở chỗ ta không nhìn thấy chương trình đang làm gì. ESP32 không có màn hình, không có breakpoint như lập trình desktop - công cụ debug chính là Serial Monitor (đọc log dạng text) và Serial Plotter (vẽ đồ thị giá trị số theo thời gian) có sẵn trong Arduino IDE. Bài này hướng dẫn dùng cả hai công cụ này để debug hiệu quả, minh hoạ bằng chính firmware đọc nhiệt độ - độ ẩm DHT22 trên ESP32.
2. Linh kiện cần có
| Linh kiện | Số lượng | Giá tham khảo |
|---|---|---|
| ESP32 DevKit V4 (ESP32-WROOM-32) | 1 | ~75.000 - 120.000đ |
| Cảm biến DHT22 (AM2302) | 1 | ~45.000 - 70.000đ |
| Dây jumper cái - đực | 3 | ~5.000đ |
3. Sơ đồ kết nối
| DHT22 | ESP32 DevKit V4 |
|---|---|
| VCC | 3V3 |
| GND | GND |
| DATA | GPIO4 |
Cấp nguồn DHT22 bằng 3.3V, không dùng 5V - chân DATA giao tiếp trực tiếp với GPIO 3.3V của ESP32.
4. Ba nguyên tắc debug qua Serial
- Luôn có "boot banner": dòng log đầu tiên sau
Serial.begin()cho biết chương trình đã thực sự khởi động lại, không phải "treo im lặng". - Log có cấu trúc: gắn nhãn
[INFO]/[WARN]/[ERROR]thay vì in giá trị trần trụi - khi log dài hàng trăm dòng, bạn cần lọc nhanh bằng mắt. - Tách dữ liệu số khỏi log chữ: Serial Plotter chỉ vẽ được dòng có định dạng
nhãn:giá_trịtách nhau bằng dấu phẩy - không được lẫn text mô tả vào cùng dòng.
Mở Serial Monitor: Tools → Serial Monitor (hoặc Ctrl+Shift+M), chỉnh đúng tốc độ baud khớp với Serial.begin() trong code (ở đây là 115200). Mở Serial Plotter: Tools → Serial Plotter (hoặc Ctrl+Shift+L) - Plotter tự nhận diện các cặp label:value trong dòng in ra và vẽ thành đường riêng biệt.
5. Code ví dụ #1 — log có cấu trúc [INFO]/[WARN]/[ERROR]
enum LogLevel { LOG_INFO, LOG_WARN, LOG_ERROR };
void logMsg(LogLevel level, const String &msg) {
switch (level) {
case LOG_INFO: Serial.print(F("[INFO] ")); break;
case LOG_WARN: Serial.print(F("[WARN] ")); break;
case LOG_ERROR: Serial.print(F("[ERROR] ")); break;
}
Serial.println(msg);
}
Một hàm log nhỏ như trên giúp mọi dòng debug trong project đều có định dạng nhất quán - khi copy log gửi cho người khác xem giúp, họ lọc được ngay lỗi nghiêm trọng bằng Ctrl+F "[ERROR]".
6. Code ví dụ #2 — dòng dữ liệu cho Serial Plotter
// Dinh dang danh cho Serial Plotter: "ten:gia_tri,ten:gia_tri"
Serial.print(F("Temp_C:"));
Serial.print(tempC);
Serial.print(F(",Humidity_pct:"));
Serial.println(humidity);
In đúng định dạng này, Serial Plotter sẽ tự vẽ 2 đường Temp_C và Humidity_pct theo thời gian thực trên cùng một đồ thị - rất hữu ích để phát hiện DHT22 đọc giá trị nhảy vọt bất thường (nhiễu tín hiệu) hay đứng yên một chỗ (cảm biến hỏng/lỏng dây) mà chỉ nhìn cột số thì khó nhận ra.
7. Code ví dụ #3 — firmware debug đầy đủ, phát hiện mất kết nối
// proj2_debug.ino - Debug hieu qua bang Serial Monitor + Serial Plotter (Arduino IDE)
// Board: ESP32 DevKit V4 | Cam bien: DHT22 tren GPIO4
#include <DHT.h>
#define DHT_PIN 4
#define DHT_TYPE DHT22
DHT dht(DHT_PIN, DHT_TYPE);
enum LogLevel { LOG_INFO, LOG_WARN, LOG_ERROR };
void logMsg(LogLevel level, const String &msg) {
switch (level) {
case LOG_INFO: Serial.print(F("[INFO] ")); break;
case LOG_WARN: Serial.print(F("[WARN] ")); break;
case LOG_ERROR: Serial.print(F("[ERROR] ")); break;
}
Serial.println(msg);
}
unsigned long lastReadAt = 0;
const unsigned long READ_INTERVAL_MS = 2000;
uint8_t consecutiveFailures = 0;
void setup() {
Serial.begin(115200);
delay(300);
Serial.println();
Serial.println(F("======================================"));
Serial.println(F(" ESP32 + DHT22 - Debug Demo"));
Serial.println(F(" Serial Monitor: xem log [INFO]/[WARN]/[ERROR]"));
Serial.println(F(" Serial Plotter: xem 2 duong Temp_C, Humidity_pct"));
Serial.println(F("======================================"));
dht.begin();
logMsg(LOG_INFO, F("dht.begin() done, cho cam bien on dinh..."));
}
void loop() {
unsigned long now = millis();
if (now - lastReadAt < READ_INTERVAL_MS) {
return;
}
lastReadAt = now;
float humidity = dht.readHumidity();
float tempC = dht.readTemperature();
if (isnan(humidity) || isnan(tempC)) {
consecutiveFailures++;
logMsg(LOG_WARN, "Doc DHT22 that bai (lan thu " + String(consecutiveFailures) + ")");
if (consecutiveFailures >= 5) {
logMsg(LOG_ERROR, F("5 lan doc lien tiep that bai - kiem tra day noi VCC/GND/DATA"));
}
return;
}
consecutiveFailures = 0;
Serial.print(F("Temp_C:"));
Serial.print(tempC);
Serial.print(F(",Humidity_pct:"));
Serial.println(humidity);
logMsg(LOG_INFO, "Doc thanh cong -> " + String(tempC, 1) + " C / " + String(humidity, 1) + " %");
}
Chú ý biến đếm consecutiveFailures: nếu chỉ log 1 lần đọc thất bại rồi thôi, bạn dễ bỏ sót; nhưng nếu 5 lần liên tiếp đều thất bại, đó gần như chắc chắn là dây DATA lỏng hoặc cảm biến hỏng - lúc đó log nên chuyển mức từ [WARN] sang [ERROR] để dễ nhận ra khi cuộn qua hàng trăm dòng log.
8. Lỗi thường gặp
| Lỗi | Nguyên nhân | Cách sửa |
|---|---|---|
| Serial Monitor hiện toàn ký tự lạ (mojibake) | Tốc độ baud trong Serial Monitor không khớp Serial.begin() | Chỉnh baud rate ở góc dưới Serial Monitor về đúng giá trị trong code (115200) |
| Serial Plotter không vẽ được đường nào | Dòng in ra lẫn text mô tả cùng với cặp label:value, hoặc thiếu dấu phẩy giữa các cặp | Tách hẳn dòng dữ liệu Plotter khỏi dòng log mô tả, đúng định dạng ten:so,ten:so |
| Không mở được Serial Monitor - báo "port đang bận" | Một chương trình khác (ví dụ script Python, hoặc IDE khác) đang giữ cổng COM | Đóng hết chương trình khác đang mở cùng cổng COM trước khi mở lại Serial Monitor |
Không thấy log nào xuất hiện dù code có Serial.print | Thiếu Serial.begin() trong setup(), hoặc board vừa reset nhưng chưa kịp in do thiếu delay() ngắn sau begin() | Đảm bảo gọi Serial.begin(115200) đầu tiên trong setup(), thêm delay(300) trước khi in log |
9. Tổng kết
Serial Monitor và Serial Plotter là hai công cụ debug miễn phí, có sẵn, nhưng thường bị dùng hời hợt (chỉ Serial.print giá trị thô, không log trạng thái). Thêm boot banner, gắn nhãn mức độ log, và tách riêng dòng dữ liệu cho Plotter là ba thói quen nhỏ giúp bạn tiết kiệm hàng giờ đoán mò khi firmware "không chạy đúng" mà không rõ vì sao.