B-5 写程序与烧录
板子做好了,该让它“活”起来了。这一页用 Arduino IDE 2 给 ESP32-C3 写三个小程序:彩虹渐变、按 BOOT 键切换模式、手机连 Wi-Fi 用网页调颜色。还没拿到板子也没关系,先在网页版模拟器 Wokwi 里玩。
B5.1 拿到板子,先做两个检查#
- 看:模组、彩灯、Type-C 有没有歪、有没有明显连锡;排针焊好了没有(不焊也能先用)。
- 插电:用 USB-C 线接到电脑。红色电源指示灯 LED4 应该亮。不亮就先别往下走,看本页最后的排错表。
- 摸一下稳压芯片 U2:常温或微温是正常的;烫手马上拔线。
B5.2 安装 Arduino IDE 2 和 ESP32 支持包#
- 打开 Arduino 官网 arduino.cc/en/software,下载 Arduino IDE 2.x:选 macOS 版(Apple 芯片的 Mac 选 Apple Silicon,老款 Intel Mac 选 Intel)选 Windows 版(安装包 .exe 或 .msi)。
- 打开下载的 .dmg,把 Arduino IDE 拖进「应用程序」文件夹。第一次打开时 macOS 会问“是否确定要打开”,点「打开」。
- 界面默认是英文,可以在「Arduino IDE」→「Settings…」(⌘,)里把 Language 改成中文,重启生效。
- 点左侧边栏的「开发板管理器」图标(或菜单「工具」→「开发板」→「开发板管理器…」),搜索
esp32。 - 找到「esp32 by Espressif Systems」(注意不是“Arduino ESP32 Boards”那一个),点「安装」,选最新的 3.x 版本。文件比较大,要等几分钟。
再装一个控制彩灯的库:
- 点左侧边栏的「库管理」图标(或「工具」→「管理库…」),搜索
Adafruit NeoPixel,点「安装」。
B5.3 连接板子,选开发板和端口#
- 用能传数据的 USB-C 线把板子接到电脑。
- 「工具」→「开发板」→「esp32」→ 选「ESP32C3 Dev Module」。
- 「工具」→「USB CDC On Boot」→ 选「Enabled」。
- 「工具」→「端口」→ 选名字像
/dev/cu.usbmodem…的那个。
「工具」菜单里要关注的几项
| 编号 | 选项 | 含义(大白话) | 本例 | 什么时候改 |
|---|---|---|---|---|
| 1 | 开发板 | 告诉 Arduino 你的板子用的是哪种芯片 | ESP32C3 Dev Module | 换别的芯片时 |
| 2 | USB CDC On Boot | 让程序里的 Serial(串口打印)走芯片自带的 USB。我们没有 USB 转串口芯片,必须开,否则串口监视器看不到任何输出 | Enabled | 一直开着 |
| 3 | Flash Size | 模组里 Flash 的大小。ESP32-C3-MINI-1-N4 的 N4 就是 4 MB | 4MB(默认) | — |
| 4 | 端口 | 板子在电脑上的“门牌号”。ESP32-C3 用自带 USB 时,在 Mac 上一般叫 /dev/cu.usbmodem 加一串数字在 Windows 上叫 COM3、COM5 这样的名字(可以在“设备管理器 → 端口(COM 和 LPT)”里看到) | 选它 | 插拔后号码可能变,重新选 |
| 5 | 其他(CPU Frequency、Upload Speed……) | 保持默认 | 默认 | — |
B5.4 程序 1:彩虹渐变#
「文件」→「新建项目」,把里面的内容全部删掉,粘贴下面的代码,点工具栏的「上传」(→ 箭头按钮,快捷键 ⌘U)。第一次编译比较慢,耐心等一下。下方输出窗口出现 “Hard resetting…”/“Done” 之类的字样就说明上传成功了。
// 程序 1:3 颗 RGB 灯彩虹渐变
#include <Adafruit_NeoPixel.h> // 引入 Adafruit NeoPixel 库(控制 WS2812B 灯)
#define LED_PIN 8 // 彩灯的数据线接在 GPIO8(原理图里的 RGB_DIN)
#define LED_COUNT 3 // 一共串了 3 颗灯
// 创建一个“灯带”对象:3 颗灯、接 GPIO8、颜色顺序 GRB、800 kHz 通信速度(WS2812B 的标准设置)
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
uint16_t hue = 0; // 当前色相:0~65535 转一圈(红→黄→绿→青→蓝→紫→红)
void setup() { // setup() 只在上电 / 复位时运行一次
Serial.begin(115200); // 打开串口,速度 115200(串口监视器里要选同样的速度)
strip.begin(); // 初始化灯带
strip.setBrightness(40); // 整体亮度 0~255。40 已经很亮,也更省电、不烫
strip.show(); // 把“全部熄灭”的状态发出去
Serial.println("彩虹程序启动"); // 在串口监视器里打印一句话,确认程序在跑
}
void loop() { // loop() 会一遍又一遍地循环运行
for (int i = 0; i < LED_COUNT; i++) { // 依次处理第 0、1、2 颗灯
uint16_t h = hue + i * 65536L / LED_COUNT; // 每颗灯的色相错开 1/3 圈
uint32_t color = strip.ColorHSV(h); // 把色相换算成 RGB 颜色(饱和度、亮度默认最大)
strip.setPixelColor(i, strip.gamma32(color)); // gamma32 让颜色过渡在人眼看来更均匀
}
strip.show(); // 把 3 颗灯的新颜色一次性发出去,灯才会变
hue += 256; // 色相往前走一小步(加到 65535 后自动回到 0)
delay(20); // 等 20 毫秒:每秒约 50 帧,转一圈约 5 秒
}
上传完成后三颗灯会开始彩虹渐变。点右上角的「串口监视器」(放大镜图标),波特率选 115200,能看到“彩虹程序启动”(按一下 RST 会重新打印一次)。
B5.5 程序 2:按 BOOT 键切换模式#
BOOT 键接在 GPIO9:松开是 HIGH、按下是 LOW(B2.5 的上拉电阻)。程序启动后再按它,它就是一个普通的用户按键。
// 程序 2:按 BOOT 键切换 4 种模式(彩虹 / 呼吸 / 单色 / 关灯),带按键消抖
#include <Adafruit_NeoPixel.h>
#define LED_PIN 8 // 彩灯数据线:GPIO8
#define LED_COUNT 3 // 3 颗灯
#define BTN_PIN 9 // BOOT 键接 GPIO9:松开 = HIGH,按下 = LOW
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
const int MODE_COUNT = 4; // 一共 4 种模式
int mode = 0; // 当前模式:0 彩虹、1 呼吸、2 单色、3 关灯
// ---- 消抖用的变量 ----
// 机械按键按下的一瞬间,触点会“弹”几下,电平在几毫秒内来回跳。
// 不处理的话按一次可能被当成按了好几次,所以要等电平稳定一段时间再认。
int lastReading = HIGH; // 上一次读到的原始电平
int stableState = HIGH; // 确认稳定后的电平
unsigned long lastChange = 0; // 原始电平最近一次变化的时刻(毫秒)
const unsigned long DEBOUNCE_MS = 30; // 稳定超过 30 毫秒才算数
uint16_t hue = 0; // 彩虹模式用的色相
void setup() {
Serial.begin(115200); // 打开串口
pinMode(BTN_PIN, INPUT_PULLUP); // GPIO9 设为输入,并打开芯片内部的上拉电阻
strip.begin(); // 初始化灯带
strip.setBrightness(40); // 亮度 40/255
strip.show(); // 先全部熄灭
}
// 每次调用检查一下按键:只有“确认按下”的那一刻返回 true,按住不放也只算一次
bool buttonPressed() {
int reading = digitalRead(BTN_PIN); // 读引脚当前的电平
if (reading != lastReading) { // 和上次不一样:可能在抖动,重新计时
lastChange = millis(); // millis() = 开机到现在过了多少毫秒
lastReading = reading;
}
if (millis() - lastChange > DEBOUNCE_MS && reading != stableState) {
stableState = reading; // 稳定了 30 毫秒,确认电平真的变了
if (stableState == LOW) return true; // 变成 LOW = 刚刚按下
}
return false; // 其余情况:没有新的按下动作
}
void showRainbow() { // 模式 0:彩虹(同程序 1)
for (int i = 0; i < LED_COUNT; i++) {
uint16_t h = hue + i * 65536L / LED_COUNT;
strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(h)));
}
hue += 256;
}
void showBreath() { // 模式 1:呼吸
float t = millis() / 1000.0; // 当前时间(秒)
uint8_t v = (sin(t * 2.0) + 1.0) * 0.5 * 255; // 正弦曲线:亮度在 0~255 之间慢慢起伏
uint32_t c = strip.ColorHSV(43690, 230, v); // 色相 43690 ≈ 蓝色,饱和度 230,亮度 v
strip.fill(strip.gamma32(c)); // 3 颗灯填同一个颜色
}
void showSolid() { // 模式 2:单色常亮
strip.fill(strip.Color(255, 80, 0)); // Color(红, 绿, 蓝),这里是橙色 🎨 换成你喜欢的
}
void showOff() { // 模式 3:关灯
strip.clear(); // 所有灯设为黑色(熄灭)
}
void loop() {
if (buttonPressed()) { // 检测到一次按下
mode = (mode + 1) % MODE_COUNT; // 切到下一个模式,3 之后回到 0
Serial.printf("切换到模式 %d\n", mode); // 串口打印当前模式
}
switch (mode) { // 按当前模式计算灯的颜色
case 0: showRainbow(); break;
case 1: showBreath(); break;
case 2: showSolid(); break;
default: showOff(); break;
}
strip.show(); // 发出去
delay(20); // 20 毫秒一帧(不影响按键:消抖靠 millis 计时)
}
B5.6 🎨 程序 3(选做):手机连 Wi-Fi,用网页调颜色#
这个程序让板子自己开一个 Wi-Fi 热点。用手机连上它,打开一个网页,拖动取色器,彩灯就跟着变色:
// 程序 3(选做):板子开一个 Wi-Fi 热点,手机连上后打开网页调颜色
#include <WiFi.h> // Wi-Fi 库(esp32 支持包自带)
#include <WebServer.h> // 简单网页服务器库(esp32 支持包自带)
#include <Adafruit_NeoPixel.h>
#define LED_PIN 8
#define LED_COUNT 3
const char* AP_SSID = "ESP32C3-LED"; // 热点名字 🎨 可以改,建议只用英文和数字
const char* AP_PASS = "12345678"; // 热点密码,至少 8 位
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
WebServer server(80); // 在 80 端口(网页默认端口)开一个服务器
// 手机上看到的网页:一个取色器,拖动时把颜色发给板子
const char PAGE[] = R"rawliteral(
<!DOCTYPE html><html><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>彩灯板调色</title>
<style>body{font-family:sans-serif;text-align:center;padding-top:40px}
input{width:220px;height:220px;border:none;background:none}</style></head>
<body><h2>ESP32-C3 彩灯板</h2>
<input type="color" id="c" value="#ff8000">
<p id="s">点上面的色块选颜色</p>
<script>
const c = document.getElementById('c');
c.addEventListener('input', () => {
fetch('/set?c=' + c.value.substring(1))
.then(r => r.text())
.then(t => document.getElementById('s').textContent = t);
});
</script></body></html>
)rawliteral";
void handleRoot() { // 有人打开 http://192.168.4.1/ 时运行
server.send(200, "text/html; charset=utf-8", PAGE); // 200 = 成功,把网页发回去
}
void handleSet() { // 网页请求 /set?c=ff8000 时运行
if (!server.hasArg("c")) { // 没带颜色参数
server.send(400, "text/plain", "missing c");
return;
}
String hex = server.arg("c"); // 取出 "ff8000" 这样的 6 位十六进制
long rgb = strtol(hex.c_str(), NULL, 16); // 十六进制字符串 → 数字
uint8_t r = (rgb >> 16) & 0xFF; // 拆出红
uint8_t g = (rgb >> 8) & 0xFF; // 拆出绿
uint8_t b = rgb & 0xFF; // 拆出蓝
strip.fill(strip.Color(r, g, b)); // 3 颗灯都设成这个颜色
strip.show();
server.send(200, "text/plain; charset=utf-8", "已设置 #" + hex); // 告诉网页“设好了”
}
void setup() {
Serial.begin(115200);
strip.begin();
strip.setBrightness(60);
strip.show();
WiFi.mode(WIFI_AP); // 让板子当“热点”(AP 模式)
WiFi.softAP(AP_SSID, AP_PASS); // 开启热点
Serial.print("热点已开启:");
Serial.println(AP_SSID);
Serial.print("手机连上后,用浏览器打开 http://");
Serial.println(WiFi.softAPIP()); // 热点模式下板子的地址,一般是 192.168.4.1
server.on("/", handleRoot); // 访问首页 → handleRoot
server.on("/set", handleSet); // 访问 /set → handleSet
server.begin(); // 启动网页服务器
}
void loop() {
server.handleClient(); // 不停地检查有没有手机发来请求
}
- 上传程序后,打开串口监视器(115200),按一下 RST,能看到热点名和地址。
- 手机打开 Wi-Fi 设置,连接
ESP32C3-LED,密码12345678。手机可能提示“此网络无法访问互联网”,选择保持连接。 - 手机浏览器打开
http://192.168.4.1(以串口监视器里打印的地址为准),点色块选颜色。
B5.7 下载模式:按住 BOOT 再按一下 RST 是在干什么#
大多数时候,Arduino 会通过 USB 自动让芯片进入下载状态,你什么都不用按。但如果遇到下面这些情况,就需要手动进入下载模式:
- 端口列表里找不到板子;
- 上传时报错,比如 “Failed to connect” 、“No serial data received”;
- 之前烧进去的程序有问题,导致 USB 一直不正常、板子反复重启。
- 按住 BOOT 键不放
- 按一下 RST 键(按下、松开)
- 松开 BOOT 键
- 重新在「工具」→「端口」里选一下端口(号码可能变了),再点上传
- 上传完成后,按一下 RST 让芯片正常启动、运行新程序
B5.8 💡 没拿到板子?先在 Wokwi 里玩#
Wokwi 是一个免费的网页版电子电路模拟器,支持 ESP32-C3 和 WS2812B 灯,打开浏览器就能跑 Arduino 代码,不用装任何东西:
- 打开 wokwi.com,新建一个项目,开发板选 ESP32-C3,语言选 Arduino。
- 点「+」添加元件,搜索 NeoPixel(WS2812),加 3 颗(或者一条 3 颗的灯带),把第一颗的 DIN 接到开发板的 GPIO8,电源和 GND 接好,灯与灯之间 DOUT → DIN 串起来。
- 在 Library Manager(库管理)里添加
Adafruit NeoPixel。 - 把程序 1 粘贴进代码区,点绿色的运行按钮。
- 想试程序 2:再加一个按键(pushbutton),一端接 GPIO9,另一端接 GND。