基于 ESP32-C3 的网页控制 RGB 彩灯实现教程


ChangeCode
原创
发布时间: 2025-11-22 16:44:50 | 阅读数 0收藏数 0评论数 0
封面
在物联网开发中,网页远程控制硬件是最基础也最实用的场景之一。本文将以 ESP32-C3 开发板为核心,实现 “网页拖动选色→实时控制 RGB 彩灯变色” 的完整项目,从硬件连接、环境搭建、代码编写到网页设计,进行分步详解,即使是电子创客新手也能轻松上手。
1

项目概述

1. 实现功能

  1. 搭建 ESP32-C3 WiFi 服务器,局域网内通过浏览器访问开发板 IP;
  2. 网页端提供可拖动的颜色选择面板,面板内的小圆圈支持点击 / 拖动选色;
  3. 选色后网页自动将 RGB 值通过 HTTP 请求发送给 ESP32-C3;
  4. ESP32-C3 通过 PWM 信号控制 RGB 彩灯,实时显示选中的颜色。

2. 核心技术

  1. 硬件层:ESP32-C3 的 PWM 输出(analogWrite封装)、GPIO 口控制;
  2. 网络层:ESP32-C3 的 WiFi STA 模式、简易 HTTP 服务器搭建;
  3. 网页层:HTML/CSS 实现界面、JavaScript 实现拖动选色与 AJAX 通信。

3. 物料清单


组件

数量

说明

ESP32-C3 开发板

1

核心控制单元

共阴 RGB 彩灯(4 引脚)

1

GND/R/G/B 引脚,共阴类型

杜邦线(公对公)

4

连接开发板与彩灯

Micro-USB 数据线

1

供电与程序上传


2

硬件连接(关键:共阴彩灯的接线逻辑)

本次使用的是共阴 RGB 彩灯(4 引脚:GND、R、G、B),其核心逻辑是:引脚输入的 PWM 值越大,对应颜色越亮(若为共阳彩灯,逻辑相反,需反转 PWM 值)。

1. 接线表

将 RGB 彩灯的引脚与 ESP32-C3 的 GPIO 口一一对应,本文选用以下引脚(可根据开发板实际引脚调整):


RGB 彩灯引脚

ESP32-C3 引脚

功能说明

GND

GND

共地,必须连接

R

GPIO5

红色通道 PWM 输出

G

GPIO6

绿色通道 PWM 输出

B

GPIO7

蓝色通道 PWM 输出

2. 接线示意图

ESP32-C3 共阴RGB彩灯
GND <-----> GND
GPIO5 <-----> R
GPIO6 <-----> G
GPIO7 <-----> B


注意:接线时需断电操作,避免引脚短路损坏开发板;若彩灯亮度不足,可在 RGB 通道串接 220Ω 限流电阻(可选)。


3

软件环境搭建

1. Arduino IDE 安装

前往Arduino 官网下载对应系统的 Arduino IDE(推荐 2.0 以上版本),安装后打开。

2. ESP32 开发板包安装

ESP32-C3 并非 Arduino 原生支持的开发板,需手动添加开发板包:

  1. 打开 Arduino IDE,点击「文件」→「首选项」;
  2. 在「附加开发板管理器网址」中输入:https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json,点击「确定」;
  3. 点击「工具」→「开发板」→「开发板管理器」,在搜索框输入ESP32,选择「Espressif Systems ESP32 Boards」,安装最新版本(推荐 2.0.14 及以上);
  4. 安装完成后,重启 Arduino IDE,在「工具」→「开发板」中选择「ESP32 Arduino → ESP32-C3 Dev Module」。

3. 端口选择

将 ESP32-C3 通过 Micro-USB 连接电脑,在 Arduino IDE 的「工具」→「端口」中选择对应的串口(如 Windows 的 COM3、Mac 的 /dev/cu.usbserial-xxx)。

4

完整代码与模块解析

本文使用的代码已解决ledc函数编译报错、颜色通道反转等问题,采用analogWrite实现 PWM 输出,兼容性更强。以下是完整代码及逐模块解析

1. 完整代码

/*
ESP32-C3 网页RGB颜色选择器(终极稳定版)
功能:网页拖动/点击选色 → 实时控制共阴RGB灯
接线:RGB灯GND接ESP32 GND,R接GPIO5、G接GPIO6、B接GPIO7
特点:去掉所有ledc相关函数(彻底解决编译报错)、优化WiFi连接逻辑、网页选色更精准
*/
#include <WiFi.h>

// ---------------------- 配置区(只改这里!)----------------------
const char* WIFI_SSID = " "; // 你的WiFi名称
const char* WIFI_PASSWORD = " "; // 你的WiFi密码
const int RGB_PIN_R = 5; // 红色通道引脚
const int RGB_PIN_G = 6; // 绿色通道引脚
const int RGB_PIN_B = 7; // 蓝色通道引脚
// ---------------------- 配置区结束 ----------------------

WiFiServer webServer(80); // Web服务器(80端口)

// 当前RGB颜色值(初始白色)
int currentR = 255;
int currentG = 255;
int currentB = 255;


void setup() {
Serial.begin(115200);
delay(500); // 给硬件启动留时间

// 初始化RGB灯引脚(直接用输出模式,analogWrite自动支持PWM)
pinMode(RGB_PIN_R, OUTPUT);
pinMode(RGB_PIN_G, OUTPUT);
pinMode(RGB_PIN_B, OUTPUT);
updateRGBLight(); // 初始点亮白色


// ---------------------- 连接WiFi(优化逻辑:失败会重试)----------------------
Serial.print("\n正在连接WiFi:");
Serial.println(WIFI_SSID);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

// 最多重试20次,每次等500ms
int wifiRetryCount = 0;
while (WiFi.status() != WL_CONNECTED && wifiRetryCount < 20) {
delay(500);
Serial.print(".");
wifiRetryCount++;
}

// 连接结果判断
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nWiFi连接成功!");
Serial.print("设备IP地址:");
Serial.println(WiFi.localIP()); // 网页访问用这个IP
webServer.begin(); // 启动Web服务器
} else {
Serial.println("\nWiFi连接失败!请检查SSID/密码");
// 连接失败时,RGB灯闪红色提醒
while (1) {
digitalWrite(RGB_PIN_R, HIGH);
delay(500);
digitalWrite(RGB_PIN_R, LOW);
delay(500);
}
}
}


void loop() {
// 处理Web客户端请求
handleWebClient();
}


// ---------------------- 核心功能函数 ----------------------
// 更新RGB灯颜色(共阴:值越大越亮)
void updateRGBLight() {
analogWrite(RGB_PIN_R, currentR);
analogWrite(RGB_PIN_G, currentG);
analogWrite(RGB_PIN_B, currentB);
Serial.printf("当前RGB:R=%d, G=%d, B=%d\n", currentR, currentG, currentB);
}


// 处理Web浏览器的请求
void handleWebClient() {
WiFiClient client = webServer.available();
if (!client) return; // 没有客户端连接,直接返回

Serial.println("\n新客户端连接");
String request = client.readStringUntil('\n'); // 读取请求内容
client.flush(); // 清空缓存


// ---------------------- 解析RGB控制请求(格式:/setRGB?r=xx&g=xx&b=xx)----------------------
if (request.indexOf("/setRGB?") != -1) {
// 提取R/G/B的值
currentR = extractParam(request, "r");
currentG = extractParam(request, "g");
currentB = extractParam(request, "b");
// 限制值在0-255之间(避免异常值)
currentR = constrain(currentR, 0, 255);
currentG = constrain(currentG, 0, 255);
currentB = constrain(currentB, 0, 255);
// 更新RGB灯
updateRGBLight();

// 给客户端返回成功响应
client.println("HTTP/1.1 200 OK");
client.println("Content-Length: 0");
client.println();
client.stop();
return;
}


// ---------------------- 返回颜色选择网页 ----------------------
sendColorWebPage(client);
client.stop();
Serial.println("客户端已断开");
}


// 从请求中提取参数值(比如从/setRGB?r=255提取r的值)
int extractParam(String request, String paramName) {
int start = request.indexOf(paramName + "=") + paramName.length() + 1;
int end = request.indexOf("&", start);
if (end == -1) end = request.indexOf(" ", start);
return request.substring(start, end).toInt();
}


// 发送颜色选择网页(优化了颜色面板+交互)
void sendColorWebPage(WiFiClient client) {
// HTTP响应头(指定UTF-8编码,避免乱码)
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html; charset=utf-8");
client.println("Connection: close");
client.println();

// 网页HTML(优化了颜色面板为“红→绿→蓝+亮度渐变”,选色更丰富)
client.print(R"(
<!DOCTYPE html>
<html>
<head>
<title>RGB彩灯控制器</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: "微软雅黑", Arial; text-align: center; padding: 20px; background: #f5f5f5; }
h1 { color: #333; margin-bottom: 20px; }
#colorPanel {
width: 320px;
height: 320px;
margin: 0 auto 20px;
background: linear-gradient(to right, red, green, blue), linear-gradient(to bottom, rgba(0,0,0,0), rgba(0,0,0,1));
background-blend-mode: multiply;
border: 3px solid #333;
border-radius: 8px;
position: relative;
cursor: crosshair;
}
#colorPicker {
width: 22px;
height: 22px;
border-radius: 50%;
background: #fff;
border: 2px solid #000;
position: absolute;
top: 150px;
left: 150px;
pointer-events: none;
}
#colorInfo {
font-size: 18px;
color: #333;
padding: 10px;
background: #fff;
border-radius: 8px;
width: 320px;
margin: 0 auto;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
</style>
</head>
<body>
<h1>RGB彩灯颜色选择器</h1>
<div id="colorPanel">
<div id="colorPicker"></div>
</div>
<div id="colorInfo">当前颜色:R=255, G=255, B=255</div>

<script>
const panel = document.getElementById('colorPanel');
const picker = document.getElementById('colorPicker');
const info = document.getElementById('colorInfo');
const panelWidth = panel.offsetWidth;
const panelHeight = panel.offsetHeight;

// 点击/拖动选色
panel.addEventListener('mousedown', (e) => {
updateColor(e.offsetX, e.offsetY);
panel.addEventListener('mousemove', onMove);
});
document.addEventListener('mouseup', () => {
panel.removeEventListener('mousemove', onMove);
});
panel.addEventListener('click', (e) => {
updateColor(e.offsetX, e.offsetY);
});

// 移动时更新颜色
function onMove(e) {
updateColor(e.offsetX, e.offsetY);
}

// 更新颜色(获取面板坐标对应的RGB值)
function updateColor(x, y) {
// 限制选择器在面板内
x = Math.max(0, Math.min(x, panelWidth));
y = Math.max(0, Math.min(y, panelHeight));
picker.style.left = (x - 11) + 'px';
picker.style.top = (y - 11) + 'px';

// 水平方向(x):左→右 = 红→绿→蓝;垂直方向(y):上→下 = 亮度从亮到暗
let r = 0, g = 0, b = 0;
const halfWidth = panelWidth / 2;
if (x <= halfWidth) {
// 左半部分:红→绿(r从255降0,g从0升255)
r = Math.floor(255 * (1 - x/halfWidth));
g = Math.floor(255 * (x/halfWidth));
b = 0;
} else {
// 右半部分:绿→蓝(g从255降0,b从0升255)
r = 0;
g = Math.floor(255 * (1 - (x - halfWidth)/halfWidth));
b = Math.floor(255 * ((x - halfWidth)/halfWidth));
}
// 垂直方向控制亮度(上亮下暗)
const brightness = 1 - y/panelHeight;
const finalR = Math.floor(constrain(r, 0, 255) * brightness);
const finalG = Math.floor(constrain(g, 0, 255) * brightness);
const finalB = Math.floor(constrain(b, 0, 255) * brightness);

// 显示颜色信息
info.textContent = `当前颜色:R=${finalR}, G=${finalG}, B=${finalB}`;

// 发送给ESP32
fetch(`/setRGB?r=${finalR}&g=${finalG}&b=${finalB}`);
}

// 限制数值范围
function constrain(val, min, max) {
return Math.max(min, Math.min(val, max));
}
</script>
</body>
</html>
)");
}

2. 代码模块解析

(1)配置区:极简修改,新手友好

代码最上方的配置区仅包含 WiFi 名称、密码和 RGB 引脚,无需修改其他部分,降低新手操作难度。

(2)初始化函数setup()

  1. 串口初始化Serial.begin(115200)用于在串口监视器查看调试信息(如 WiFi 连接状态、开发板 IP);
  2. 引脚初始化pinMode(...)将 RGB 引脚设为输出模式,ESP32 的analogWrite会自动为引脚配置 PWM,无需手动调用ledc函数(解决编译报错);
  3. WiFi 连接:添加 20 次重试机制,避免因网络波动导致连接失败;连接失败时红灯闪烁,直观提示问题。

(3)核心功能函数

  1. updateRGBLight():通过analogWrite输出 PWM 值控制彩灯,共阴彩灯的逻辑是值越大越亮
  2. handleWebClient():检测网页客户端的连接,解析 RGB 控制请求并更新彩灯,无控制请求时返回颜色选择网页;
  3. extractParam():从 HTTP 请求中提取 R/G/B 参数值,是网页与硬件通信的 “桥梁”;
  4. sendColorWebPage():将网页的 HTML/CSS/JS 代码发送给浏览器,实现可视化选色界面。

(4)主循环loop()

仅调用handleWebClient(),循环检测网页请求,保证硬件实时响应网页操作。

5

网页设计与交互逻辑解析

网页是用户与硬件交互的 “入口”,本文的网页采用纯前端技术(无后端框架),轻量化且适配手机 / 电脑。

1. 网页结构(HTML)

  1. 标题:RGB彩灯颜色选择器,清晰说明功能;
  2. 颜色面板:#colorPanel是核心选色区域,宽高 320px,适配移动端;
  3. 选色器:#colorPicker是面板内的白色小圆圈,用于直观显示选色位置;
  4. 颜色信息:#colorInfo实时显示当前选中的 RGB 值。

2. 样式设计(CSS)

  1. 采用linear-gradient实现颜色面板的红→绿→蓝水平渐变亮→暗垂直渐变
  2. 选色器设为圆形,添加黑色边框,保证在任何颜色背景下都清晰可见;
  3. 适配移动端:添加<meta name="viewport" content="width=device-width, initial-scale=1.0">,手机访问时界面不缩放。

3. 交互逻辑(JavaScript)

  1. 拖动 / 点击选色:监听mousedown/mousemove/mouseup事件,实现选色器的拖动;
  2. RGB 值计算:修正后的颜色逻辑保证面板左→右为红→绿→蓝,垂直方向控制亮度,解决之前 “绿蓝反色” 的问题;
  3. AJAX 通信:通过fetch()发送 HTTP 请求,将 RGB 值传递给 ESP32-C3,实现 “选色即变色” 的实时交互。


6

测试与调试

程序上传与启动

  1. 将代码复制到 Arduino IDE,修改配置区的 WiFi 名称和密码;
  2. 点击 IDE 右上角的「上传」按钮,等待程序编译并上传到 ESP32-C3;
  3. 上传完成后,打开「工具」→「串口监视器」(波特率选 115200),等待 WiFi 连接成功,记录开发板的 IP 地址(如192.168.1.100)。

2. 网页控制彩灯

  1. 将手机 / 电脑连接到与 ESP32-C3 相同的 WiFi;
  2. 打开浏览器,输入串口监视器中显示的 IP 地址,进入颜色选择网页;
  3. 拖动 / 点击面板内的白色小圆圈,彩灯会实时显示选中的颜色,网页下方会显示当前 RGB 值。

3. 常见问题排查


问题现象

解决方法

串口监视器无 IP 地址

检查 WiFi 名称 / 密码是否正确;开发板是否连接到正确的 WiFi

彩灯颜色反了

确认是共阴彩灯;若仍反色,交换代码中 RGB 引脚的定义

网页无法访问

电脑 / 手机与 ESP32-C3 是否在同一 WiFi;关闭电脑防火墙

彩灯不亮

检查接线是否正确(尤其是 GND);引脚是否与代码配置一致


阅读记录0
点赞0
收藏0
禁止 本文未经作者允许授权,禁止转载
猜你喜欢
评论/提问(已发布 0 条)
评论 评论
收藏 收藏
分享 分享
pdf下载 下载
pdf下载 举报