Mô hình đèn giao thông có nút nhấn cho người đi bộ (Arduino Nano)
Mô hình ngã tư cho học sinh: đèn xanh – vàng – đỏ cho xe và đèn đỏ/xanh cho người đi bộ chạy đúng chu kỳ. Người đi bộ bấm nút thì đèn xe rút ngắn thời gian xanh để cho sang đường. Code dạng máy trạng thái, không dùng delay chặn — bài học chuẩn về lập trình nhúng.
✅ Đã kiểm thử
Xem chi tiết 21 phép đo
Tất cả 5 LED cùng sáng (kiểm tra đèn lúc khởi động) + đang bấm nút
- D1: dòng 12.8mA (cho phép 20.0mA).
- D2: dòng 12.1mA (cho phép 20.0mA).
- D3: dòng 11.7mA (cho phép 20.0mA).
- D4: dòng 12.8mA (cho phép 20.0mA).
- D5: dòng 11.7mA (cho phép 20.0mA).
- Chân D2 cấp 12.8mA (an toàn ≤ 20.0mA).
- Chân D3 cấp 12.1mA (an toàn ≤ 20.0mA).
- Chân D4 cấp 11.7mA (an toàn ≤ 20.0mA).
- Chân D5 cấp 12.8mA (an toàn ≤ 20.0mA).
- Chân D6 cấp 11.7mA (an toàn ≤ 20.0mA).
- Tổng dòng đỉnh từ USB 91.1mA (đủ với cục sạc 5V 1A).
- Dòng LED đỏ: 12.8mA ✓
- Dòng LED xanh: 11.7mA ✓
- Dòng chân D4 phải cấp: 11.7mA ✓
- Áp chân D7 khi bấm nút (mức thấp): 0.00V ✓
Nút thả ra
- D1: dòng 12.8mA (cho phép 20.0mA).
- D5: dòng 11.7mA (cho phép 20.0mA).
- Chân D2 cấp 12.8mA (an toàn ≤ 20.0mA).
- Chân D6 cấp 11.7mA (an toàn ≤ 20.0mA).
- Tổng dòng đỉnh từ USB 54.5mA (đủ với cục sạc 5V 1A).
- Áp chân D7 khi thả nút (mức cao nhờ trở kéo trong): 4.98V ✓
Nguyên lý hoạt động
Mỗi LED nối một chân của Arduino qua trở 220Ω: chân xuất 5V, LED đỏ rơi ~1,9V nên dòng chỉ khoảng 12mA — đủ sáng mà an toàn cho chân (giới hạn 20mA). Nút nhấn nối chân D7 với GND; trong code bật trở kéo lên bên trong chip (INPUT_PULLUP) nên bình thường D7 đọc mức CAO, bấm nút thì đọc mức THẤP. Chương trình là một máy trạng thái: XANH → VÀNG → ĐỎ (người đi bộ được đi, cuối pha nháy cảnh báo) → XANH. Thời gian được đếm bằng millis() nên chip luôn sẵn sàng đọc nút, không bị "đứng" như khi dùng delay().
Sơ đồ đấu dây
Mỗi màu là một dây. Chấm tròn là điểm hàn. Chạm vào tên dây trong bảng để làm nổi bật.
Đây là mô phỏng một chiều (DC) ở trạng thái tĩnh: dùng để kiểm tra mức áp, dòng và công suất. Hiệu ứng nhấp nháy, PWM hay giao tiếp số do code quyết định nên không chạy ở đây.
| # | Dây | Nối các điểm | Áp mô phỏng |
|---|---|---|---|
| 1 | đen GND | U1 GND ⟷ D1 K · chân ngắn (−) ⟷ D2 K · chân ngắn (−) ⟷ D3 K · chân ngắn (−) ⟷ D4 K · chân ngắn (−) ⟷ D5 K · chân ngắn (−) ⟷ SW1 2 | — |
| 2 | vàng CAR_R_DRV | U1 D2 ⟷ R1 1 | — |
| 3 | xanh lá CAR_R | R1 2 ⟷ D1 A · chân dài (+) | — |
| 4 | xanh dương CAR_Y_DRV | U1 D3 ⟷ R2 1 | — |
| 5 | tím CAR_Y | R2 2 ⟷ D2 A · chân dài (+) | — |
| 6 | xanh ngọc CAR_G_DRV | U1 D4 ⟷ R3 1 | — |
| 7 | nâu CAR_G | R3 2 ⟷ D3 A · chân dài (+) | — |
| 8 | hồng PED_R_DRV | U1 D5 ⟷ R4 1 | — |
| 9 | xám PED_R | R4 2 ⟷ D4 A · chân dài (+) | — |
| 10 | xanh rêu PED_G_DRV | U1 D6 ⟷ R5 1 | — |
| 11 | tím nhạt PED_G | R5 2 ⟷ D5 A · chân dài (+) | — |
| 12 | vàng BTN | U1 D7 ⟷ SW1 1 | — |
Danh sách linh kiện
| Ký hiệu | Linh kiện | Giá tham khảo | |
|---|---|---|---|
| U1 | Arduino Nano V3 (ATmega328P, chip nạp CH340) kèm cáp USB | 85.000đ | Tìm mua |
| D1 | LED đỏ 5mm (red) | 300đ | Tìm mua |
| D2 | LED vàng 5mm (yellow) | 300đ | Tìm mua |
| D3 | LED xanh lá 5mm (green) | 300đ | Tìm mua |
| D4 | LED đỏ 5mm (người đi bộ) (red) | 300đ | Tìm mua |
| D5 | LED xanh lá 5mm (người đi bộ) (green) | 300đ | Tìm mua |
| R1 | Điện trở 220Ω 1/4W (đỏ-đỏ-nâu) | 100đ | Tìm mua |
| R2 | Điện trở 220Ω 1/4W (đỏ-đỏ-nâu) | 100đ | Tìm mua |
| R3 | Điện trở 220Ω 1/4W (đỏ-đỏ-nâu) | 100đ | Tìm mua |
| R4 | Điện trở 220Ω 1/4W (đỏ-đỏ-nâu) | 100đ | Tìm mua |
| R5 | Điện trở 220Ω 1/4W (đỏ-đỏ-nâu) | 100đ | Tìm mua |
| SW1 | Nút nhấn 4 chân 6×6mm (nút bấm nhả) | 1.000đ | Tìm mua |
| Tổng (chưa gồm board hàn, dây, thiếc) | 88.000đ |
Các bước làm
Code nạp cho mạch
Chọn board Arduino Nano.
/*
Mạch Gọn — Mô hình đèn giao thông có nút nhấn cho người đi bộ
Board: Arduino Nano (ATmega328P)
Chân : D2 đỏ xe, D3 vàng xe, D4 xanh xe, D5 đỏ người đi bộ, D6 xanh người đi bộ
D7 nút nhấn (nối GND, dùng trở kéo lên bên trong)
Code dạng máy trạng thái, không dùng delay() chặn.
*/
// ---- Cấu hình thời gian (mili giây) ----
const unsigned long T_GREEN = 10000; // xanh cho xe
const unsigned long T_GREEN_MIN = 3000; // xanh tối thiểu khi có người bấm nút
const unsigned long T_YELLOW = 3000; // vàng
const unsigned long T_RED = 10000; // đỏ cho xe = xanh cho người đi bộ
const unsigned long T_PED_BLINK = 3000; // 3 giây cuối pha đỏ: đèn người đi bộ nháy
const unsigned long BLINK_PERIOD = 500;
const unsigned long DEBOUNCE_MS = 30;
const uint8_t PIN_CAR_R = 2, PIN_CAR_Y = 3, PIN_CAR_G = 4;
const uint8_t PIN_PED_R = 5, PIN_PED_G = 6;
const uint8_t PIN_BTN = 7;
enum State { CAR_GREEN, CAR_YELLOW, CAR_RED };
State state = CAR_GREEN;
unsigned long stateStart = 0;
bool pedRequest = false;
// chống dội nút
bool btnStable = HIGH, btnLast = HIGH;
unsigned long btnChanged = 0;
void setLights(bool cr, bool cy, bool cg, bool pr, bool pg) {
digitalWrite(PIN_CAR_R, cr); digitalWrite(PIN_CAR_Y, cy); digitalWrite(PIN_CAR_G, cg);
digitalWrite(PIN_PED_R, pr); digitalWrite(PIN_PED_G, pg);
}
void enter(State s) {
state = s;
stateStart = millis();
const char* names[] = {"XANH (xe di)", "VANG", "DO (nguoi di bo di)"};
Serial.print(F("Trang thai: "));
Serial.println(names[s]);
}
// trả về true đúng 1 lần mỗi khi nút vừa được bấm xuống
bool buttonPressed() {
bool r = digitalRead(PIN_BTN);
if (r != btnLast) { btnLast = r; btnChanged = millis(); }
if (millis() - btnChanged > DEBOUNCE_MS && r != btnStable) {
btnStable = r;
if (btnStable == LOW) return true;
}
return false;
}
void setup() {
Serial.begin(115200);
const uint8_t outs[] = {PIN_CAR_R, PIN_CAR_Y, PIN_CAR_G, PIN_PED_R, PIN_PED_G};
for (uint8_t p : outs) pinMode(p, OUTPUT);
pinMode(PIN_BTN, INPUT_PULLUP);
// kiểm tra đèn: bật cả 5 đèn 1 giây (lúc khởi động, delay ở đây không ảnh hưởng)
setLights(HIGH, HIGH, HIGH, HIGH, HIGH);
delay(1000);
Serial.println(F("Den giao thong san sang"));
enter(CAR_GREEN);
}
void loop() {
unsigned long now = millis();
unsigned long elapsed = now - stateStart;
if (buttonPressed() && state == CAR_GREEN && !pedRequest) {
pedRequest = true;
Serial.println(F("Nguoi di bo bam nut"));
}
switch (state) {
case CAR_GREEN:
setLights(LOW, LOW, HIGH, HIGH, LOW);
if (elapsed >= T_GREEN || (pedRequest && elapsed >= T_GREEN_MIN)) enter(CAR_YELLOW);
break;
case CAR_YELLOW:
setLights(LOW, HIGH, LOW, HIGH, LOW);
if (elapsed >= T_YELLOW) { pedRequest = false; enter(CAR_RED); }
break;
case CAR_RED: {
bool pedGreen = HIGH;
if (elapsed >= T_RED - T_PED_BLINK) pedGreen = ((elapsed / BLINK_PERIOD) % 2) == 0; // nháy cảnh báo
setLights(HIGH, LOW, LOW, LOW, pedGreen);
if (elapsed >= T_RED) enter(CAR_GREEN);
break;
}
}
}
🔌 Cắm thử trên breadboard
Cắm nháp trên board cắm trước cho chắc, chạy đúng rồi hãy làm mạch in. Sơ đồ này do máy tự xếp từ chính netlist ở trên và đã đối chiếu lại: hai chân cùng một cột (cùng nửa board) là thông nhau, không có net nào bị chập.
- Mỗi cột 5 lỗ trong cùng một nửa board thông nhau; hai nửa cách nhau bởi rãnh giữa.
- Hai đường kẻ dọc mép board là đường nguồn: hàng + (vạch đỏ) và hàng − (vạch xanh). Nhớ bắc dây nối đường nguồn trên với đường nguồn dưới nếu dùng cả hai nửa.
- Màu dây trên hình trùng với màu trong bảng nối dây ở mục Sơ đồ, cắm theo màu cho dễ dò.
🧲 Mạch in để ủi (PCB 1 mặt)
Bản mạch in một mặt đồng, tự xếp linh kiện và tự đi dây, đã kiểm quy tắc khoảng cách và đối chiếu đúng 100% với sơ đồ nguyên lý ở trên. In tỉ lệ 1:1 rồi ủi lên tấm đồng.
Hướng dẫn ủi – ăn mòn – khoan – hàn (đầy đủ)
Hướng dẫn làm mạch in bằng phương pháp ủi
Mạch: Mô hình đèn giao thông có nút nhấn cho người đi bộ (Arduino Nano)
Kích thước board: 86,4×68,6 mm · 54 lỗ khoan (mũi 0,8, 0,9 và 1 mm) · 21 đoạn mạch, tổng 525,7 mm · 0 cầu nhảy.
Quy tắc đã kiểm: đường mạch rộng 0,8mm (nguồn và GND 1mm), khe cách tối thiểu 0,5mm. Mạch in đã được máy đối chiếu đúng 100% với sơ đồ nguyên lý.
1. In đúng tỉ lệ 1:1
- In trang "MẶT ĐỒNG" bằng máy in LASER, đặt tỉ lệ 100%, KHÔNG chọn "Fit to page" hay "Scale to fit".
- Dùng thước trên bản in đo lại đoạn 100mm: phải đúng 100mm mới dùng được.
- In trên giấy in ảnh bóng, giấy decal bóc, hoặc giấy tạp chí láng. Mực càng dày càng tốt.
- Trang "MẶT ĐỒNG" đã được LẬT GƯƠNG sẵn: úp mặt có mực vào tấm đồng là đúng chiều.
2. Chuẩn bị tấm đồng
- Cắt phíp đồng (board đồng một mặt) lớn hơn kích thước mạch mỗi bề khoảng 5mm.
- Chà mặt đồng bằng giấy nhám mịn (P800–P1200) rồi rửa sạch, lau khô. Mặt đồng phải sáng, không vết dầu mỡ, không chạm tay vào.
3. Ủi mực sang đồng
- Úp mặt có mực vào tấm đồng, dùng băng keo giấy dán cố định một cạnh.
- Bàn ủi để mức nóng nhất (cotton/linen), KHÔNG dùng chế độ phun hơi nước.
- Ủi đè mạnh và đều 3–5 phút, tập trung từng vùng nhỏ, chú ý các góc và mép.
- Để nguội hẳn rồi ngâm cả tấm vào nước ấm 5–10 phút, bóc giấy thật nhẹ. Giấy decal thì bóc khi còn ấm.
- Soi kỹ: chỗ nào mực bị thiếu hoặc đứt đường thì tô lại bằng bút lông dầu (bút CD) rồi để khô.
4. Ăn mòn (etching)
- Pha dung dịch sắt(III) clorua FeCl3 theo hướng dẫn trên bao bì, hoặc dung dịch oxy già + muối ăn + a-xít; dùng hộp nhựa, KHÔNG dùng đồ kim loại.
- Nhiệt độ dung dịch khoảng 40–50°C thì ăn nhanh hơn. Lắc nhẹ hộp cho đều, thường 10–25 phút là xong.
- An toàn: đeo găng tay cao su và kính bảo hộ, làm ở nơi thoáng. Dung dịch ăn mòn làm hỏng quần áo, bồn rửa và gây bỏng rát da.
- Xong thì rửa board bằng nước sạch, lau khô, dùng xăng thơm hoặc axeton lau sạch lớp mực.
- Dung dịch đã dùng KHÔNG đổ xuống bồn rửa hay cống; đựng vào can kín và bỏ đúng nơi thu gom chất thải.
5. Khoan lỗ
- Đối chiếu trang "BẢN VẼ LỖ KHOAN" để biết lỗ nào dùng mũi nào.
- Dùng mũi khoan 0,8mm, 0,9mm và 1mm, khoan từ MẶT ĐỒNG vào để mũi tự định tâm giữa pad.
- Khoan tốc độ cao, đẩy nhẹ; mũi nhỏ rất dễ gãy. Khoan xong chà nhẹ bavia hai mặt.
6. Hàn linh kiện
- Linh kiện cắm ở MẶT KHÔNG CÓ ĐỒNG, hàn ở mặt đồng. Theo trang "MẶT LINH KIỆN" để đặt đúng vị trí và đúng chiều.
- Hàn linh kiện thấp trước (điện trở, đi-ốt), cao sau (tụ, hàng chân, board vi điều khiển).
- Chú ý chiều: LED chân dài là (+); đi-ốt có vạch trắng là K; tụ hoá có vạch là (−); transistor theo mặt chữ như nét vẽ đỏ.
- Trước khi cấp điện: đo thông mạch giữa chân nguồn và GND — KHÔNG được kêu (không được chập).
Lưu ý với board vi điều khiển
- Board nhái có thể khác thứ tự chân. Đối chiếu tên chân in cạnh từng lỗ trên trang "MẶT LINH KIỆN" với chữ in trên board thật TRƯỚC KHI ăn mòn. Nếu khác, đừng hàn mà báo lại để sửa bản vẽ.
Bản vẽ lỗ khoan
Đo kiểm bằng đồng hồ vạn năng
| Hai đầu trở 220Ω của LED đỏ khi đèn sáng | khoảng 2,7 – 3,0V (≈12mA) |
| Chân D7 với GND, không bấm nút | khoảng 5V |
| Chân D7 với GND, đang bấm nút | 0V |
Sửa lỗi thường gặp
Một LED không sáng
LED cắm ngược chiều hoặc trở chưa cắm chặt. Lúc khởi động cả 5 LED sáng 1 giây — LED nào không sáng thì kiểm tra LED đó.
Bấm nút không có tác dụng
Nút chỉ có tác dụng khi đèn xe đang XANH. Kiểm tra 2 chân nút đang dùng là 2 chân chéo nhau; xem Serial Monitor có dòng "Nguoi di bo bam nut" không.
Nạp code báo lỗi avrdude: not in sync
Đổi Processor sang ATmega328P (Old Bootloader), chọn lại cổng COM, thử cáp USB khác.
⚠️ An toàn
- Chỉ dùng điện 5V qua cổng USB, an toàn cho học sinh.