Ở Phần 1, bạn đã:
- Làm quen với ESP32 DevKit 38 pin.
- Cài đặt Arduino IDE + ESP32 core.
- Nạp thử chương trình Hello from ESP32 và Blink LED.
Trước khi bước sang NRF24L01, cảm biến, relay, MQTT, bạn bắt buộc phải nắm vững:
- GPIO là gì?
- Cách cấu hình INPUT / OUTPUT / INPUT_PULLUP.
- Cách nối nút nhấn + LED sao cho an toàn và ổn định.
Đây là “bài tập nhập môn” để bạn hiểu cách ESP32 tương tác với thế giới bên ngoài.
Trong Phần 2, chúng ta sẽ xây dựng một ví dụ rất gần gũi:
Nhấn nút → bật/tắt LED (kiểu công tắc điện tử đơn giản).
1. GPIO là gì? – Nhìn lại khái niệm cơ bản
GPIO (General Purpose Input/Output) là các chân trên vi điều khiển có thể được cấu hình để:
- INPUT: đọc trạng thái từ bên ngoài (nút nhấn, cảm biến digital…).
- OUTPUT: xuất tín hiệu điều khiển ra ngoài (LED, relay, buzzer…).
Trên ESP32, mỗi GPIO có thể có nhiều chức năng (ADC, PWM, I2C, SPI, UART…). Trong bài này, ta tạm coi chúng chỉ là chân digital cơ bản:
- OUTPUT:
digitalWrite(pin, HIGH/LOW). - INPUT:
digitalRead(pin).
Một số điểm quan trọng:
- Mức HIGH thường tương ứng gần 3.3V.
- Mức LOW là gần 0V (GND).
- Khi chân INPUT không được kéo lên/kéo xuống, nó sẽ bị trôi (floating) → đọc lúc HIGH lúc LOW, rất nhiễu.
Để tránh floating, ta phải:
- Dùng điện trở kéo lên (pull-up) hoặc kéo xuống (pull-down).
- Hoặc dùng chức năng INPUT_PULLUP / INPUT_PULLDOWN tích hợp sẵn trong ESP32.
2. Thiết kế bài tập: Nhấn nút – bật/tắt LED
Mục tiêu:
- Khi nhấn nút, LED đổi trạng thái (nếu đang tắt → bật, đang bật → tắt).
- Khi không nhấn, hệ thống ổn định, không bị nhấp nháy do nhiễu.
2.1. Chọn chân GPIO cho nút & LED
Ví dụ cho ESP32 DevKit 38 pin, ta chọn:
GPIO2→ điều khiển LED (LED on-board nhiều board cũng dùng chân này).GPIO14→ đọc nút nhấn.
Bạn có thể đổi sang chân khác nếu cần, nhưng tránh các chân quá “nhạy cảm” khi boot.
2.2. Sơ đồ nối nút nhấn với INPUT_PULLUP
Để giảm linh kiện rời, ta dùng INPUT_PULLUP:
- Cấu hình chân nút là
INPUT_PULLUP. - Khi không nhấn, chân đọc sẽ là HIGH (do được kéo lên 3.3V nội bộ).
- Khi nhấn, chân được nối xuống GND → đọc LOW.
Sơ đồ logic:
3V3
| (pull-up nội bộ bên trong ESP32)
GPIO14 ---- Nút nhấn ---- GND
LED nối đơn giản:
GPIO2 ---- Điện trở 220–1kΩ ---- Anode LED
Cathode LED ---- GND
Nếu board có sẵn LED on-board nối với GPIO2, bạn có thể dùng luôn LED on-board, không cần LED ngoài.
3. Code cơ bản: đọc nút & in trạng thái lên Serial
Trước khi điều khiển LED, ta kiểm tra xem nút nhấn hoạt động đúng hay chưa.
3.1. Khai báo chân & setup
#define BUTTON_PIN 14 // Nút nhấn
void setup() {
Serial.begin(115200);
pinMode(BUTTON_PIN, INPUT_PULLUP); // Kich hoat tro keo len noi
}
void loop() {
int buttonState = digitalRead(BUTTON_PIN); // HIGH hoac LOW
Serial.print("Button: ");
Serial.println(buttonState == LOW ? "PRESSED" : "RELEASED");
delay(200);
}
- Mở Serial Monitor (115200).
- Khi không nhấn nút → thấy
Button: RELEASED. - Khi nhấn nút → thấy
Button: PRESSED.
Nếu trạng thái không ổn định:
- Kiểm tra nối GND chung.
- Đảm bảo nút nối đúng: 1 chân vào GPIO14, 1 chân xuống GND.
4. Code hoàn chỉnh: Nhấn nút → bật/tắt LED (TOGGLE)
Bây giờ ta thêm LED vào.
4.1. Sơ đồ logic cho TOGGLE
- Ta lưu trạng thái hiện tại của LED trong một biến, ví dụ
bool ledState. - Mỗi lần phát hiện sườn xuống của nút (từ HIGH → LOW), ta đảo trạng thái
ledStatevàdigitalWrite()lại LED.
4.2. Code ví dụ đầy đủ
#define BUTTON_PIN 14 // Nút nhấn
#define LED_PIN 2 // LED (on-board hoac LED ngoai)
bool ledState = false; // Trang thai hien tai cua LED
int lastButtonState = HIGH; // Gia tri nut o lan doc truoc (INPUT_PULLUP -> mac dinh HIGH)
void setup() {
Serial.begin(115200);
pinMode(BUTTON_PIN, INPUT_PULLUP); // Nhan thap
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, ledState ? HIGH : LOW);
Serial.println("=== Demo: Nhan nut -> Toggle LED ===");
}
void loop() {
int buttonState = digitalRead(BUTTON_PIN);
// Phat hien suon xuong: HIGH -> LOW
if (lastButtonState == HIGH && buttonState == LOW) {
// Dao trang thai LED
ledState = !ledState;
digitalWrite(LED_PIN, ledState ? HIGH : LOW);
Serial.print("Button pressed. LED is now: ");
Serial.println(ledState ? "ON" : "OFF");
// Chong doi don gian: doi nut nha ra
delay(50);
while (digitalRead(BUTTON_PIN) == LOW) {
delay(10);
}
}
lastButtonState = buttonState;
delay(10);
}
Giải thích nhanh:
lastButtonStatedùng để phát hiện lúc vừa nhấn xuống (chuyển từ HIGH → LOW).- Sau khi xử lý, ta chờ nút nhả ra (
while (digitalRead(BUTTON_PIN) == LOW)) để tránh việc người dùng giữ nút làm LED nháy liên tục. delay(10)ở cuối giúp giảm tải CPU, đủ nhanh cho thao tác nút.
5. Lưu ý chống dội (debounce) & nhiễu cho nút nhấn
Thực tế, khi nhấn nút:
- Tiếp điểm cơ khí không đóng/mở “ngọt” 100%.
- Trong vài ms đầu có thể tạo nhiều xung lên xuống → LED có thể bị nháy ngoài ý muốn.
Các cách xử lý:
5.1. Chống dội bằng delay đơn giản
- Sau khi phát hiện nhấn,
delay(20–50ms)rồi mới đọc trạng thái. - Phù hợp cho project đơn giản, ít yêu cầu thời gian thực.
5.2. Chống dội bằng phần mềm (state machine)
- Dùng biến lưu thời điểm nhấn, chỉ chấp nhận nhấn mới sau một khoảng thời gian tối thiểu.
- Tách riêng logic đọc nút & xử lý.
5.3. Chống dội bằng phần cứng
- Dùng tụ nhỏ (0.1µF) + điện trở tạo mạch RC filter.
- Áp dụng cho hệ thống cần độ tin cậy cao.
Trong phạm vi series này, chúng ta chấp nhận dùng giải pháp đơn giản bằng delay, đủ dùng cho các ví dụ NRF & IoTLabs sau này.
6. Kết nối với các phần tiếp theo của series
Đến đây, bạn đã:
- Hiểu được cách cấu hình GPIO của ESP32 làm INPUT/OUTPUT.
- Biết sử dụng INPUT_PULLUP để giảm linh kiện.
- Biết đọc trạng thái nút nhấn và điều khiển LED theo kiểu TOGGLE.
Những kỹ năng này sẽ được sử dụng rất nhiều ở các phần sau:
- Với NRF24L01, bạn sẽ dùng nút để gửi lệnh remote điều khiển (Phần 9).
- Với cảm biến, bạn sẽ dùng GPIO để đọc digital input (cảm biến cửa từ, PIR, công tắc hành trình…).
- Với relay, bạn thay LED bằng relay để điều khiển tải thực.
Ở Phần 3, chúng ta sẽ bắt đầu bước vào thế giới NRF24L01:
Phần 3 – Kết nối ESP32 38 Pin với NRF24L01: sơ đồ nối chân SPI & kiểm tra module hoạt động.
Bạn có thể lưu series lại trên iotlabs.vn để theo dõi trọn bộ và áp dụng cho các project IoT của riêng mình.


