1. 背景
ESP32 开发第一步,基本是先点灯吧?点灯,点灯,点灯,没想到我这个点灯,点了 2 周。一是工作比较忙,只有闲暇时间搞搞这个,再者也是需要查找学习各种资料,因为这个板载的灯没有那么好点,一时疏忽,找错了方向。
2. 状态灯的意义
我不清楚是不是有 ESP32 开发第一步要先点灯这个说法,但是设备的状态指示灯的作用还是非常大的,通过它可以向用户提供设备的工作状态信息。设备状态灯通常用不同颜色或闪烁频率来表示设备的不同工作状态。例如:
- 绿色:设备正常工作。
- 红色:设备出现故障。
- 黄色:设备正在进行某项操作,如升级。
- 闪烁:设备正在进行某项操作,如数据传输。
设备状态灯是一种非常简单易懂的人机交互手段,使用户能够快速了解设备的工作状态,并能够及时采取措施。
通常设备状态灯在设备外部或者是设备前面,方便用户查看,比如说一个路由器上面有个红绿灯,绿灯就是网络正常,红灯就是网络有问题。再比如一些简单的小家电,红灯表示正在充电,灯灭则表示充满。
3. 板载 LED
ESP32-S2-Pico 这款板载的 LED 硬件有过版本修改,通过简单的 GPIO 拉高并不能点亮。
查找原理图我们可以发现,LED 接的是 GPIO9 ,LED 这一块的电路是 WS2812B-0807,这一部分的原理图如下图所示,仅有一个灯珠:
4. WS2812
4.1 简介
WS2812 是一种高性能、低成本的全彩 LED 灯带。它是一种串联控制的灯,可以将多个 LED 灯连接在一起,通过串行信号控制各个 LED 灯的颜色和亮度。
WS2812 的主要特点是具有色彩丰富、高亮度、高动态范围、高精度的色温调节能力,可以实现 256 级灰度调节。
WS2812 的控制方式是通过一种称为单总线控制的方式。这种控制方式是通过单线控制所有 LED 灯,并且每个 LED 灯都有一个内置的控制器,可以接收单线信号并解码。这样可以使得控制器只需要通过单线发送信号,就可以控制所有 LED 灯的颜色和亮度。
WS2812 在很多领域都有广泛的应用,如:LED灯带、LED点阵屏幕、LED灯管、LED灯环等。
4.2 控制原理
WS2812 每个 LED 灯都有一个内置的控制器,它可以接收单线信号并解码。控制器通过接收单线信号来控制 LED 灯的颜色和亮度。
控制器解码信号的方式是通过高电平和低电平的时间来区分不同的数据位。
通常每个 LED 灯的颜色都由三个数据位(红、绿、蓝)组成,每个数据位可以表示 8 级亮度。
当控制器接收到单线信号时,首先会检测到一个高电平脉冲,这个脉冲用来同步数据。接下来是 24 位数据,其中 8 位红色数据、8 位绿色数据和 8 位蓝色数据。控制器按照这样的数据格式解码信号,并设置 LED 灯的颜色和亮度。
毕竟文章重点不同,在这里只是做了简单的介绍,知道了原理是可以自己写出驱动的,当然要查阅相关硬件的手册文档,更详细的了解相关数据发送的方式,不同灯珠通讯时序的具体时间要求会不大相同。感兴趣的同学可以自行查找资料,进行尝试。
原理介绍:https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf
这里我们只需要使用写好的驱动就可以了,比如 MicroPython 的 neopixel 类库。当然 .NET nanoFramework 也有相关的类库: Ws28xx.Esp32。
5. 板载 RGB 控制
这里我们采用 ESP32 WS2812 Driver ,代码仓库:https://github.com/nanoframework/nf-Community-Contributions/tree/master/drivers/ESP32-WS2812,提供了 PixelController
控制的帮助类,可以让我们轻松的实现 WS2812 的控制,你也可以通过源代码了解驱动的实现。
5.1 简单颜色测试
PixelController
构造函数提供三个参数,分别是 GPIO ,像素数,可选参数是否为 RGBW 灯带控制,默认 false
。
PixelController controller = new PixelController(9, 1);
// 简单测试颜色
controller.SetColor(0,255,0,0);//红
controller.UpdatePixels();
Thread.Sleep(1000);
controller.SetColor(0, 0, 255, 0);//绿
controller.UpdatePixels();
Thread.Sleep(1000);
controller.SetColor(0, 0, 0, 255);//蓝
controller.UpdatePixels();
Thread.Sleep(1000);
controller.SetColor(0, 255, 255, 255);//白
controller.UpdatePixels();
Thread.Sleep(1000);
效果:
5.2 HSV 颜色模型
上一节简单测试了灯光的红、绿、蓝、白几种颜色,使用的 SetColor
,这个方法是设置灯光的 RGB 颜色。另外一个设置颜色的方式是使用 SetHSVColor
,这个方式使用的 HSV 颜色模型。
HSV (Hue, Saturation, Value) 和 RGB (Red, Green, Blue) 是两种不同的颜色空间。在 HSV 颜色模型中:
- Hue(色调),表示颜色的类型,如红色、黄色、蓝色等。Hue 的值用角度度量,取值范围为0°~360°,从红色开始按逆时针方向计算,红色为0°,绿色为120°,蓝色为240°。它们的补色是:黄色为60°,青色为180°,紫色为300°;
- Saturation(饱和度),表示颜色接近光谱色的程度,值越大颜色越饱和,值越小颜色越灰暗。Saturation 的值范围为 0 到 1。
- Value(明度),表示颜色的明亮程度,值越大颜色越亮,值越小颜色越暗。Value 的值范围为 0 到 1。
与传统的 RGB 相比 HSV 可以非常方便的通过调整明度来平滑的显示颜色亮度变化。
5.3 呼吸灯效果
要想实现呼吸灯的效果,我们只需要通过 SetHSVColor
来设置不同明度的色彩即可平滑的实现呼吸灯的亮度变化效果。
// 呼吸灯效果
PixelController controller = new PixelController(9, 1);
var ts = 0;
for (; ; )
{
var add = true;
var v = 0f;
for (; ; )
{
controller.SetHSVColor(0, 240, 50, v);
controller.UpdatePixels();
if (add)
{
v += 0.05f;
}
else
{
v -= 0.05f;
}
if (v >= 1) add = false;
if (v <= 0) break;
Thread.Sleep(50);
}
if (ts > 5) break;
ts++;
}
效果:
5.4 整体颜色测试
色彩 RGB 我们可以通过随机三个 255 以内的数字来生成随机的颜色。了解了 HSV 后,我们也可以通过 Hue(色调)的取值,直接在圆环中均匀的截取颜色。
// 生成多少种颜色 或是 灯带的灯珠数量
uint ledCount = 25;
PixelController controller = new PixelController(9, ledCount);
// 添加开始时设置的 ledCount 种颜色
// step 是步长
int step = (int)(360 / ledCount);
var hue = 0;
for (uint i = 0; i < ledCount; i++)
{
// HSV
// 色调H 取值范围为0°~360°
// 饱和度S 取值范围为0%~100%,值越大,颜色越饱和
// 明度V 表示颜色明亮的程度,通常取值范围为0%(黑)到100%(白)
// V 这个取值挺好的,设置为1 差点亮瞎
controller.SetHSVColor((short)i, (short)hue, 1, 0.05f);
hue = hue + step;
controller.UpdatePixels();
}
// 循环变换颜色
for (; ; )
{
controller.MovePixelsByStep(1);
controller.UpdatePixels();
Thread.Sleep(500);
}
这段代码比较适合灯带,因为微雪的 ESP32-S2-Pico 只有一个灯珠,MovePixelsByStep
变换后位置后使用 UpdatePixels
也是可以看到颜色变化的。
许多玩家都是喜欢 RBG 光效这种“光污染”的,用这个控制灯带也是可以大有玩头的。
6. 官方驱动库 Ws28xx.Esp32
当然,最好是使用官方的 nanoFramework.Iot.Device.Ws28xx.Esp32
包,通过 Nuget 安装即可,需要注意的是,选对版本,其依赖的其他 nanoFramework
包需要和刷写的固件对应。
使用的方法也很简单,如下是简单的 RGB 颜色控制示例:
// 1 个灯珠,1像素
const int Count = 1;
// 微雪的 ESP32-S2-Pico 的 LED Pin
const int Pin = 9;
public static void Main()
{
Ws28xx neo = new Ws2812c(Pin, Count);
BitmapImage img = neo.Image;
for (; ; )
{
img.SetPixel(0, 0, Color.Red);
neo.Update();
Thread.Sleep(500);
img.SetPixel(0, 0, 0, 255, 0);
neo.Update();
Thread.Sleep(500);
img.SetPixel(0, 0, 0, 0, 255);
neo.Update();
Thread.Sleep(500);
}
}
但是,这里需要注意的是:Ws28xx
这里需要使用 Ws2812c
来实例化,不然颜色控制会不对。
7. 全彩 RGB
这一节演示通过 PWM 端口控制全彩红绿蓝三基色 LED 灯。
可以看到该模块有4 Pin,其电压为 5V,接 VBUS 引脚即可,其他三个引脚为 RBG 控制引脚接 GP2-4。
接好设备后面就是写代码了,首先编写初始化功能,定义 R_Pin、 B_Pin、 G_Pin,对应的 GPIO 使用 PWM 需要先进行设置,使用 Configuration.SetPinFunction
将对应 Pin 进行配置。然后使用 PwmChannel.CreateFromPin
从三个引脚创建 PWM 通道,接着启动控制。
/// <summary>
/// 定义 RBG 的 PWM 通道
/// </summary>
private static PwmChannel R_Pin, B_Pin, G_Pin;
/// <summary>
/// 初始化 配置PWM 信息
/// </summary>
public static void Init() {
// 先设置引脚 , RBG 分别接的 GP2-4
Configuration.SetPinFunction(2, DeviceFunction.PWM2);
Configuration.SetPinFunction(3, DeviceFunction.PWM3);
Configuration.SetPinFunction(4, DeviceFunction.PWM4);
// 从三个引脚创建 PWM 通道
R_Pin = PwmChannel.CreateFromPin(2, 40000, 0);
B_Pin = PwmChannel.CreateFromPin(3, 40000, 0);
G_Pin = PwmChannel.CreateFromPin(4, 40000, 0);
// 启动 PWM
R_Pin.Start();
B_Pin.Start();
G_Pin.Start();
}
核心的控制代码也很简单,这里随机生成 255 以内的数然后映射到百分比,PwmChannel
的占空比是个百分比,double 类型,取值 0-1 。
public static void Main()
{
Init();
Random random = new Random();
while (true)
{
R_Pin.DutyCycle = random.Next(255) / 255.0;
B_Pin.DutyCycle = random.Next(255) / 255.0;
G_Pin.DutyCycle = random.Next(255) / 255.0;
Thread.Sleep(500);
}
}
8. 最后
本文相关代码和之前相关系列文章的代码均已在 Github 开源,感兴趣的同学可以查阅:
https://github.com/sangyuxiaowu/ESP32_S2_PICO。
如有错漏之处,敬请指正。