Đèn giao thông ngã tư với ESP32: dự án STEM chủ đề giao thông cho học sinh
Làm đèn giao thông ngã tư thu nhỏ với ESP32: 6 đèn LED cho hai hướng, bảng 6 pha, millis không dùng delay dài, dây cảm ứng làm nút xin qua đường và mô hình bìa cứng.
Đèn giao thông là "chương trình máy tính" mà trẻ gặp mỗi ngày trên đường đi học. Dự án này cho học sinh tự làm một ngã tư thu nhỏ: 6 đèn LED cho hai hướng đường, ESP32 điều khiển theo một bảng pha, và một dây cảm ứng làm nút xin qua đường cho người đi bộ. Qua dự án, trẻ vừa ôn luật giao thông, vừa học cách mô tả một quy trình lặp lại bằng bảng trạng thái, một ý tưởng rất quan trọng trong lập trình.
1. Chuẩn bị
- Một mạch ESP32 DevKit V1 và dây USB nạp code.
- Một breadboard MB-102 và dây cắm test board.
- 6 đèn LED 5 mm (2 đỏ, 2 vàng, 2 xanh lá) và 6 điện trở 220 Ω.
- Bìa cứng, ống hút, bút màu để làm mô hình ngã tư; vài xe đồ chơi nhỏ.
2. Ba màu đèn và luật đi đường
- Đèn xanh: được đi.
- Đèn vàng: phải dừng lại trước vạch dừng, trừ khi đã đi quá vạch dừng thì được đi tiếp. Đèn vàng không phải là "đi nhanh lên".
- Đèn đỏ: dừng lại trước vạch dừng.
Một điều trẻ ít để ý: sau đèn vàng của hướng này, có một khoảng ngắn cả hai hướng cùng đỏ, để xe cuối cùng kịp ra khỏi ngã tư rồi hướng kia mới được đi. Chương trình trong bài cũng làm như vậy.
3. Nối dây

| Hướng | Đỏ | Vàng | Xanh |
|---|---|---|---|
| A (đường dọc) | D25 | D26 | D27 |
| B (đường ngang) | D23 | D22 | D21 |
- LED có chiều: chân dài (dương) quay về phía chân ESP32, chân ngắn về GND. Cắm ngược thì đèn không sáng nhưng không hỏng.
- Điện trở 220 Ω giới hạn dòng qua LED. Chân ESP32 cho ra 3,3 V, LED đỏ cần khoảng 2 V, nên dòng điện khoảng (3,3 − 2) : 220 ≈ 6 mA: đủ sáng và an toàn cho chân ESP32.
- Một dây cắm ở D4, đầu kia để trống hoặc dán vào miếng giấy bạc: đó là nút cảm ứng "xin qua đường".
4. Bảng pha: mô tả đèn giao thông bằng dữ liệu

Thay vì viết hàng chục lệnh bật, tắt rời rạc, chương trình mô tả đèn giao thông bằng một bảng 6 pha. Mỗi pha ghi: tên, thời gian, đèn nào của hướng A sáng, đèn nào của hướng B sáng. Chương trình chỉ việc chạy lần lượt từng dòng của bảng, hết dòng cuối thì quay lại dòng đầu.
// PYWORLD.VN - ESP32 DevKit V1: đèn giao thông ngã tư cho hai hướng A và B
// Hướng A: đỏ D25, vàng D26, xanh D27; hướng B: đỏ D23, vàng D22, xanh D21 (mỗi LED nối tiếp điện trở 220 ohm xuống GND)
// Dây cảm ứng ở D4 là "nút xin qua đường": chạm vào thì đèn xanh đang chạy được rút ngắn
const int A_DO = 25, A_VANG = 26, A_XANH = 27;
const int B_DO = 23, B_VANG = 22, B_XANH = 21;
const int CHAN_CHAM = 4;
const int DEN[6] = {A_DO, A_VANG, A_XANH, B_DO, B_VANG, B_XANH};
const unsigned long XANH_MS = 8000; // thời gian đèn xanh
const unsigned long VANG_MS = 3000; // thời gian đèn vàng
const unsigned long DO_CHUNG_MS = 1000; // hai hướng cùng đỏ để xe cuối kịp ra khỏi ngã tư
const unsigned long XANH_CON_LAI_MS = 3000; // có người xin qua đường: đèn xanh chỉ còn tối đa chừng này
struct Pha {
const char* ten;
unsigned long thoiGian;
bool a[3]; // hướng A: đỏ, vàng, xanh
bool b[3]; // hướng B: đỏ, vàng, xanh
};
const Pha PHA[] = {
{"A xanh, B do", XANH_MS, {0, 0, 1}, {1, 0, 0}},
{"A vang, B do", VANG_MS, {0, 1, 0}, {1, 0, 0}},
{"Hai huong do", DO_CHUNG_MS, {1, 0, 0}, {1, 0, 0}},
{"A do, B xanh", XANH_MS, {1, 0, 0}, {0, 0, 1}},
{"A do, B vang", VANG_MS, {1, 0, 0}, {0, 1, 0}},
{"Hai huong do", DO_CHUNG_MS, {1, 0, 0}, {1, 0, 0}},
};
const int SO_PHA = sizeof(PHA) / sizeof(PHA[0]);
int pha = 0;
unsigned long batDauPha = 0, thoiGianPha = 0;
uint32_t giaTriNen = 1;
bool dangCham = false;
void batPha(int p) {
pha = p;
batDauPha = millis();
thoiGianPha = PHA[p].thoiGian;
for (int i = 0; i < 3; i++) {
digitalWrite(DEN[i], PHA[p].a[i] ? HIGH : LOW);
digitalWrite(DEN[3 + i], PHA[p].b[i] ? HIGH : LOW);
}
Serial.printf("[%lu s] %s (%lu giay)\n", millis() / 1000, PHA[p].ten, thoiGianPha / 1000);
}
bool dangXanh() {
return PHA[pha].a[2] || PHA[pha].b[2];
}
// Chạm vào dây D4: độ lệch so với giá trị nền vượt 30 %
bool vuaCham() {
uint32_t v = touchRead(CHAN_CHAM);
float lech = ((float)v - (float)giaTriNen) / giaTriNen;
if (lech < 0) lech = -lech;
bool cham = lech > 0.30;
bool moi = cham && !dangCham;
dangCham = cham;
return moi;
}
void setup() {
Serial.begin(115200);
for (int i = 0; i < 6; i++) pinMode(DEN[i], OUTPUT);
delay(500);
uint32_t tong = 0;
for (int i = 0; i < 30; i++) { // lúc khởi động không chạm dây D4
tong += touchRead(CHAN_CHAM);
delay(2);
}
giaTriNen = tong / 30;
if (giaTriNen == 0) giaTriNen = 1;
batPha(0);
}
void loop() {
unsigned long daChay = millis() - batDauPha;
if (vuaCham() && dangXanh() && daChay + XANH_CON_LAI_MS < thoiGianPha) {
thoiGianPha = daChay + XANH_CON_LAI_MS; // rút ngắn đèn xanh đang chạy
Serial.println("Co nguoi xin qua duong: den xanh con 3 giay");
}
if (daChay >= thoiGianPha) {
batPha((pha + 1) % SO_PHA);
}
delay(20);
}
struct Phavà mảngPHA[]: mỗi phần tử là một dòng của bảng. Muốn đổi thời gian đèn xanh, chỉ sửaXANH_MS; muốn thêm pha, chỉ thêm một dòng.- Không dùng
delay()dài: chương trình ghi thời điểm bắt đầu pha (batDauPha) và so vớimillis(). Nhờ vậy, trong lúc đèn đang sáng, ESP32 vẫn kiểm tra được nút cảm ứng. - Nút xin qua đường: chạm vào dây D4 khi một hướng đang xanh và còn hơn 3 giây, đèn xanh được rút ngắn chỉ còn 3 giây. Cảm ứng dùng độ lệch so với giá trị nền như bài chạm tay bật đèn, nên lúc cấp điện đừng chạm vào dây.
- Mở Serial Monitor (115200 baud) để xem mỗi lần đổi pha.
5. Làm mô hình ngã tư

- Vẽ hai con đường cắt nhau trên bìa cứng, kẻ vạch qua đường cho người đi bộ.
- Cắm LED vào ống hút làm cột đèn, luồn dây xuống dưới bìa. Ở ngã tư thật, mỗi hướng có đèn ở cả hai phía đường; trong mô hình, có thể nối thêm LED song song cùng chân (mỗi LED một điện trở riêng).
- Dán miếng giấy bạc nối với dây D4 ở góc đường làm nút xin qua đường.
- Trẻ đóng vai người lái xe đồ chơi: đèn đỏ thì dừng trước vạch, đèn vàng thì sao?
6. Xử lý lỗi thường gặp
| Hiện tượng | Nguyên nhân thường gặp | Cách xử lý |
|---|---|---|
| Một LED không bao giờ sáng | Cắm ngược chiều LED; lỏng dây | Xoay ngược LED: chân dài về phía chân ESP32 |
| Đèn sáng sai màu, sai hướng | Cắm nhầm chân | So lại với bảng chân nối ở mục 3 |
| Đèn xanh tự rút ngắn dù không ai chạm | Lúc cấp điện đang chạm dây D4, giá trị nền sai | Bỏ tay ra, bấm nút EN để khởi động lại |
| Chạm dây D4 không có tác dụng | Đèn đang vàng hoặc đỏ, hoặc đèn xanh chỉ còn dưới 3 giây | Chạm khi một hướng vừa chuyển xanh để thấy rõ |
7. Thử thách mở rộng
- Đường chính, đường phụ: cho hướng A xanh 12 giây, hướng B xanh 6 giây. Cần sửa bảng như thế nào?
- Chế độ ban đêm: thêm một pha đặc biệt: cả hai hướng đèn vàng nhấp nháy.
- Đèn cho người đi bộ: thêm một LED đỏ, một LED xanh hình người đi bộ; xanh khi hướng xe cắt qua vạch đang đỏ.
- Đếm ngược: in số giây còn lại của đèn xanh mỗi giây trên Serial Monitor, hoặc hiện lên màn hình OLED.
8. Gợi ý cho phụ huynh và giáo viên
- Bắt đầu từ quan sát thật: trước buổi học, cho trẻ đứng ở một ngã tư (an toàn, có người lớn) đếm số giây của đèn xanh, vàng, đỏ. Kết quả đó chính là dữ liệu để điền vào bảng pha.
- Liên môn: giáo dục an toàn giao thông, Toán (thời gian, biểu đồ), Tin học (bảng, vòng lặp, trạng thái).
- Độ tuổi: lớp 4–7. Học sinh nhỏ làm mô hình và chơi đóng vai; học sinh lớn đọc và sửa bảng pha.
- Thời lượng: 2–3 buổi 45 phút: nối LED và chạy chương trình; làm mô hình; thử thách.
9. Xem thêm
- Bài đầu tiên với ESP32 cho học sinh: đèn nhấp nháy và mã Morse
- Chạm tay bật đèn: nút cảm ứng trên ESP32 (giải thích kỹ giá trị nền và độ lệch)
- Quy trình thiết kế kỹ thuật trong dự án STEM
- Trong cùng loạt còn có dự án STEM cho trẻ mầm non theo chủ đề giao thông (xe chạy bằng bóng bay).
Xem thêm