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.

⏱ 60 phút🔧 Mới học💰 Linh kiện ~88.000đ🧠 Arduino Nano⚡ Nguồn USB 5V

✅ Đã kiểm thử

Luật điệnKhông lỗiSai chân, chập nguồn, quá áp 5V vào chân 3,3V, thiếu đi-ốt, thiếu trở kéo…
Mô phỏng SPICE2/2 kịch bản · 21 phép đo đạtDòng/áp từng linh kiện ở trường hợp xấu nhất (ngspice)
Biên dịch codeĐạt · dùng 9% bộ nhớarduino:avr:nano
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 ✓
Kiểm lần cuối: 12:05:53 12/11/2026

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.

GNDGNDCAR_R_DRVCAR_R_DRVCAR_RCAR_RCAR_Y_DRVCAR_Y_DRVCAR_YCAR_YCAR_G_DRVCAR_G_DRVCAR_GCAR_GPED_R_DRVPED_R_DRVPED_RPED_RPED_G_DRVPED_G_DRVPED_GPED_GBTNBTNU1 · Arduino NanoD2D3D4D5D6D7GNDR1 · Điện trở 220Ω12D1 · Đèn LED 5mmA · chân dài (+)K · chân ngắn (−)R2 · Điện trở 220Ω12D2 · Đèn LED 5mmA · chân dài (+)K · chân ngắn (−)R3 · Điện trở 220Ω12D3 · Đèn LED 5mmA · chân dài (+)K · chân ngắn (−)R4 · Điện trở 220Ω12D4 · Đèn LED 5mmA · chân dài (+)K · chân ngắn (−)R5 · Điện trở 220Ω12D5 · Đèn LED 5mmA · chân dài (+)K · chân ngắn (−)SW1 · Nút nhấn12
#DâyNối các điểm
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
2vàng
CAR_R_DRV
U1 D2 ⟷ R1 1
3xanh lá
CAR_R
R1 2 ⟷ D1 A · chân dài (+)
4xanh dương
CAR_Y_DRV
U1 D3 ⟷ R2 1
5tím
CAR_Y
R2 2 ⟷ D2 A · chân dài (+)
6xanh ngọc
CAR_G_DRV
U1 D4 ⟷ R3 1
7nâu
CAR_G
R3 2 ⟷ D3 A · chân dài (+)
8hồng
PED_R_DRV
U1 D5 ⟷ R4 1
9xám
PED_R
R4 2 ⟷ D4 A · chân dài (+)
10xanh rêu
PED_G_DRV
U1 D6 ⟷ R5 1
11tím nhạt
PED_G
R5 2 ⟷ D5 A · chân dài (+)
12vàng
BTN
U1 D7 ⟷ SW1 1

Danh sách linh kiện

Ký hiệuLinh kiệnGiá tham khảo
U1Arduino Nano V3 (ATmega328P, chip nạp CH340) kèm cáp USB85.000đTìm mua
D1LED đỏ 5mm (red)300đTìm mua
D2LED vàng 5mm (yellow)300đTìm mua
D3LED xanh lá 5mm (green)300đTìm mua
D4LED đỏ 5mm (người đi bộ) (red)300đTìm mua
D5LED 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
SW1Nú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;
    }
  }
}

Đo kiểm bằng đồng hồ vạn năng

Hai đầu trở 220Ω của LED đỏ khi đèn sángkhoảng 2,7 – 3,0V (≈12mA)
Chân D7 với GND, không bấm nútkhoảng 5V
Chân D7 với GND, đang bấm nút0V

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.