Arduino 红绿灯项目:三颗 LED 做出会自动循环的交通灯(RBT 学校项目适用)

Projek lampu isyarat Arduino di atas breadboard putih dengan LED merah kuning hijau, rintangan biru dan Arduino Uno biru

Arduino 红绿灯项目(traffic light Arduino)就是把红、黄、绿三颗 LED 接到 Arduino Uno 的三个 digital pin,每颗都经过一个 220Ω 串联电阻,所有短脚汇集到 breadboard 上的一条共用接地行。代码按 红 → 绿 → 黄 的循环,一次只点亮一颗 LED。一个下午就能做完。

Arduino 红绿灯项目是怎么运作的?

Arduino 红绿灯其实是三个独立的 LED 电路,由同一个程序轮流点亮。Arduino Uno 并不”知道”什么是红绿灯 —— 它只是往一个 pin 输出 5V,等几秒,熄灭,再点亮下一个 pin。

马来西亚的红绿灯循环很简单:红灯停,绿灯行,黄灯是转回红灯前的警告 —— 没有英国那种红+黄同亮的阶段。这也是它一直稳坐 RBT(Reka Bentuk dan Teknologi,马来西亚学校的”设计与工艺”科目)经典项目位置的原因:概念一句话就能讲清楚,却同时教会 digital output、元件极性、欧姆定律和程序结构。

需要哪些元件?

这个 Arduino 红绿灯项目的元件清单又短又便宜。全部都插在 breadboard 上,完全不用焊接,所以就算是中一学生做也很安全。

我们也有现成的红绿灯模块 —— 一块小小的黑色 PCB,三颗 5mm LED 竖排,带 4 pin header(G、Y、R、GND)。模块更整洁,可以直接在产品页下单。不过做 RBT 作业集(folio),我们还是建议用上面的独立 LED 接法 —— 老师要看的,正是学生一根线一根线搞懂的 wiring。

为什么每颗 LED 都要串联电阻?

Arduino Uno 的 digital pin 在 HIGH 时会输出完整的 5V,但 LED 不是普通灯泡 —— 它是 diode(二极管)。电压一旦超过 LED 的正向电压(forward voltage,Vf),它的内阻就几乎归零,电流会毫无限制地飙升,把 LED 和 Arduino 的 pin 一起烧掉。

官方数字是这样的。Arduino 在 Uno Rev3 的规格里写明 DC Current per I/O Pin = 20 mA,这就是你设计时该守住的安全上限。ATmega328P 的 datasheet 则标明每个 I/O pin 40.0 mA、整颗芯片的 VCC 和 GND 合计 200.0 mA —— 但 “absolute maximum rating” 指的是损坏点,不是设计目标。计算直接套欧姆定律:

I = (5V − Vf) / R

Vf 因颜色而异。3mm 红色 LED 通常是 1.8–2.2V,而黄色绿色更高,在 2.0–2.4V。

3mm LED 颜色 典型 Vf 220Ω 时的电流 330Ω 时的电流
红色 1.8–2.2V ≈12.7–14.5 mA ≈8.5–9.7 mA
黄色 2.0–2.4V ≈11.8–13.6 mA ≈7.9–9.1 mA
绿色 2.0–2.4V ≈11.8–13.6 mA ≈7.9–9.1 mA

我们的选择:220Ω。它让三颗 LED 都落在 12–14.5 mA 左右 —— 课堂演示够亮,又离 20 mA 上限留有舒服的余量。就算三颗同时点亮,总电流也只有 42 mA 左右。

蓝色金属膜(metal-film)电阻用的是 5 道色环220Ω = 红-红-黑-黑-棕330Ω = 橙-橙-黑-黑-棕。色环看得头晕的话,直接用数字万用表量。180Ω 到 470Ω 之间的任何值三种颜色都能用,阻值越大只是越暗。150Ω 留给绿色专用 —— 配红色 LED 它可能达到 ≈21 mA,稍微超出 20 mA 上限。

白色迷你 breadboard 上带色环的蓝色轴向电阻与一颗 3mm 红色 LED 并排
蓝色金属膜电阻与 3mm LED 串联 —— 每种颜色都必须配上的一对。

怎么分辨 LED 的长脚和短脚?

3mm LED 的两只脚长短不一,这不是工厂瑕疵 —— 是极性标记。把 LED 按进 breadboard 之前先确认好,因为插反的 LED 完全不亮,也不会给你任何提示。

物理特征 端子 接到
Anode(正极 +) 电阻 → Arduino digital pin
脚,灯罩边缘有一处平面 Cathode(负极 −) breadboard 上的共用接地行

如果两只脚已经被剪成一样长,就找塑料灯罩边缘的平面(flat spot)—— 有平面的那一侧永远是 cathode。第三个线索:往透明灯罩里看 —— 通常里面较大那片金属是 cathode,但有例外,所以还是以短脚和平面为准。

3mm 绿色 LED 特写,显示长脚 anode、短脚 cathode 以及灯罩边缘的平面
长脚 = anode 接电阻;短脚和边缘平面 = cathode 接 GND。

breadboard 上的线要怎么接?

只要排布得当,170 孔迷你 breadboard 对这个 Arduino 红绿灯项目来说完全够用。有一点很关键:这种 170 孔迷你 breadboard 没有大 breadboard 侧边那种 +/− 电源轨 —— 所以要挑一个普通的 5 孔行(比如最边上那一行),把它设为共用接地行。所有短脚都必须集中到那一行,再用一根 jumper 线把同一行接到 Arduino 的 GND pin,一根就够。

元件 引脚 / 端 接到
红色 LED 长脚(anode) 220Ω 电阻 → pin 10
短脚(cathode) breadboard 上的共用接地行
黄色 LED 长脚(anode) 220Ω 电阻 → pin 9
短脚(cathode) breadboard 上的共用接地行
绿色 LED 长脚(anode) 220Ω 电阻 → pin 8
短脚(cathode) breadboard 上的共用接地行
GND jumper 一根线 共用接地行 → Arduino 的 GND pin

注意整个电路完全没有用到 5V pin。当代码把 digital pin 设为 HIGH 时,供电的正是那个 pin 本身 —— 这就是整个项目的核心概念。我们也刻意避开 pin 13,因为它和 Arduino 板载 LED 共用,那颗 LED 会跟着一起闪,debug 时会把你搞糊涂。

关于 breadboard 再补一句:孔是按行连通的,中间由一条凹槽隔开。电阻要横跨那条凹槽放 —— 如果两端插进同一行,它就被短路(short)了。

想看真实的手怎么把 LED 和电阻按进 breadboard?这个视频从零开始演示的正是同一个搭建过程:

Arduino 红绿灯完整接线全景:白色 breadboard、红黄绿 LED、蓝色电阻和蓝色 Arduino Uno
完整布局 —— 三颗 LED、三颗电阻,以及一条接到 Arduino GND pin 的共用接地行。

红绿灯循环的完整 Arduino 代码是什么?

下面的 Arduino 代码是完整的,不需要任何额外 library,直接上传到 Uno 就能跑。注意每个阶段的时长都存在顶部的命名常量里 —— 改一个数字,整个循环就跟着变,完全不用碰 loop()

// Arduino 红绿灯项目 - Arduino Uno 上的 3 颗 LED
// 马来西亚式循环:红 -> 绿 -> 黄 -> 红(不断重复)

const int PIN_RED    = 10;   // 红色 LED 的 anode,经 220 ohm 电阻
const int PIN_YELLOW = 9;    // 黄色 LED 的 anode,经 220 ohm 电阻
const int PIN_GREEN  = 8;    // 绿色 LED 的 anode,经 220 ohm 电阻

// 每个阶段的时长(毫秒)- 想调节奏就改这里的数值
const unsigned long TIME_RED    = 5000;   // 5 秒:车辆停下
const unsigned long TIME_GREEN  = 4000;   // 4 秒:车辆通行
const unsigned long TIME_YELLOW = 2000;   // 2 秒:转红前的警告

void allOff() {
  digitalWrite(PIN_RED,    LOW);
  digitalWrite(PIN_YELLOW, LOW);
  digitalWrite(PIN_GREEN,  LOW);
}

void setup() {
  pinMode(PIN_RED,    OUTPUT);
  pinMode(PIN_YELLOW, OUTPUT);
  pinMode(PIN_GREEN,  OUTPUT);
  allOff();                   // 开始时全部熄灭
}

void loop() {
  // 阶段 1 - 红灯
  allOff();
  digitalWrite(PIN_RED, HIGH);
  delay(TIME_RED);

  // 阶段 2 - 绿灯
  allOff();
  digitalWrite(PIN_GREEN, HIGH);
  delay(TIME_GREEN);

  // 阶段 3 - 黄灯
  allOff();
  digitalWrite(PIN_YELLOW, HIGH);
  delay(TIME_YELLOW);

  // loop() 会自动重复 - 回到阶段 1
}

allOff() 在每个阶段开始前都会被调用,确保同一时刻只亮一盏灯。把这个调用删掉,上一颗 LED 就会一直亮着,最后三颗全部同时亮。

关于 delay():对这个项目来说它是正确的选择 —— 简洁、易读,而且红绿灯本来就该等。但 delay() 是阻塞的:那段时间里 Arduino 真的停在原地,读不了按钮也读不了 sensor —— 等你开始加行人按钮,就是学 millis() 写法的时候了。

怎么把它变成能拿分的学校项目?

这个基础版 Arduino 红绿灯是能跑的,但三颗 LED 的版本老师已经看过几十遍。分数来自一个你能在全班面前自己讲清楚的加分项 —— 只挑一个,把它做扎实。

  • 行人按钮。用 push button 强制循环提早进入红灯阶段 —— 这是学 millis() 最自然的理由,因为 delay() 会”漏掉”按键。
  • 第二个路口。再加三颗 LED 给交叉的马路,代码写成一边绿灯就意味着另一边红灯。Uno 有 14 个 digital pin —— 够用还有剩。
  • 给 OKU(残障人士)的 buzzer。安全过马路阶段发出哔哔声,模仿真实路口的声音提示。无障碍加分很能吸引评审的目光。
  • 3D 打印的红绿灯外壳。打印出来的灯柱和灯罩,把光秃秃的 breadboard 变成一个真正的路口模型。

想在同一块板子上做第二个学校项目?寻线小车(line follower robot)在同一块 Uno 上再加上 sensor 和马达。

我们在真实客户身上常见的错误

LED 插反了

这是第一名,而且特别迷惑,因为什么都没坏 —— 那颗 LED 只是不出声。Diode 只让电流往一个方向走。如果只有一颗 LED 不亮而另外两颗正常,先拔出来转 180° 再插回去,然后才轮到怪代码。

忘了接电阻

有人想不接电阻先试一下,说就一下子而已。LED 可能会非常亮地撑几秒,然后永久变暗或直接报废 —— 那个 Arduino pin 也可能跟着陪葬。三颗电阻都装好再插 USB 线,不是插了再装。

Cathode 没有共用同一个 GND

如果某只短脚插错行,没接到共用接地行,就只有那一颗 LED 不亮、其余正常 —— 这种现象很容易被误判成 LED 坏了。

用了 5V pin 而不是 digital pin

把 anode 接到 5V pin 确实能点亮 LED,但它会永远亮着,你的代码毫无作用。5V pin 是常开的;只有 0–13 这些 digital pin(以及 A0–A5,它们也能当 digital pin 用)才受 digitalWrite() 控制。

抄来的代码 pin 号不对

网上的代码常常用 pin 13、12、11,而你的接线是 10、9、8 —— 电路明明完好,LED 却全部看起来”死了”。先检查三行 const int 是否和实际接线一致,再去怪板子。

FAQ

可以用 5mm LED 代替 3mm LED 吗?

可以,直接换就行。同一种颜色的 Vf 和工作电流几乎一样,所以 220Ω 电阻照用。5mm LED 只是体积大一点、脚粗一点。

为什么绿色 LED 看起来比红色暗?

因为绿色的 Vf 更高(2.0–2.4V,红色是 1.8–2.2V),同样的电阻下它分到的电流少一些。这是正常的。想要亮度均衡,只给绿色换 150Ω。

这个项目需要外接电源吗?

不需要。三颗 LED 每颗大约抽 13 mA,laptop 的 USB 线就绰绰有余。Arduino Uno 上那个黑色 barrel jack 是留给马达之类更重的项目,或是展示时板子需要脱离 laptop 独立运行的场合。

每盏灯应该亮几秒?

没有标准答案 —— 真实路口按车流各不相同。课堂演示的话,红灯 5 秒、绿灯 4 秒、黄灯 2 秒的循环节奏刚好,观众不用等太久。黄灯阶段应该保持最短,因为它是警告,不是等待状态。

用现成的红绿灯模块更好吗?

模块确实更整洁,但它把老师真正要检查的部分都藏起来了:LED 极性、电阻计算和共用接地行。做 RBT folio,独立 LED 的搭法在问答环节更容易讲得住。

想让作品固定下来、不只停在 breadboard 阶段?LED 和电阻可以直接焊到洞洞板上 —— 基本手法和如何焊接排针教程里的一样。

最后更新:2026年8月。有问题?WhatsApp 联系我们

Leave a Reply

Your email address will not be published. Required fields are marked *