跳到主要内容

趣味灯条

重要提示:关于开发板的兼容性

本教程的核心逻辑适用于所有 ESP32 开发板,但所有操作步骤均以 微雪 ESP32-S3-Zero 迷你开发板 为例进行讲解。如果您使用其他型号的开发板,请根据实际情况修改相应设置。

项目介绍

这个项目展示了一个通过电位器控制 WS2812 可编程 LED 灯条的程序。通过旋转电位器,可以实时改变灯条的显示效果:灯条会依次经历三个阶段的颜色变化(黄色 → 绿色 → 红色),亮起的灯珠数量也会随着电位器的旋转而逐渐增加,形成流畅的视觉渐变效果。

硬件连接

需要使用的器件有:

  • WS2812 灯条 * 1
  • 电位器 * 1
  • 面包板 * 1
  • 导线
  • ESP32 开发板

按照下面接线图连接电路:

ESP32-S3-Zero 引脚图

ESP32-S3-Zero-Pinout

接线图

代码实现

import time
from machine import Pin, ADC
import neopixel

# --- 配置参数 ---
POT_PIN_NUM = 7 # 电位器引脚
NEO_PIN_NUM = 8 # WS2812 引脚
NUM_LEDS = 8 # 灯珠数量

# --- 颜色定义 (R, G, B) ---
COLOR_YELLOW = (255, 255, 0)
COLOR_GREEN = (0, 255, 0)
COLOR_RED = (255, 0, 0)
COLOR_OFF = (0, 0, 0)

# 初始化 WS2812 灯带
np = neopixel.NeoPixel(Pin(NEO_PIN_NUM), NUM_LEDS)

# 初始化电位器 (ADC)
pot = ADC(Pin(POT_PIN_NUM))

def update_leds(adc_val):
"""
根据 ADC 值更新 LED 状态
adc_val: 0 - 65535
"""
# 将 0-65535 映射到 0-24 (3 个阶段 * 8 个灯)
total_steps = 3 * NUM_LEDS
position = int((adc_val / 65535) * total_steps)

# 限制最大值,防止溢出
if position > total_steps:
position = total_steps

for i in range(NUM_LEDS):
# 逻辑判断:优先级从高到低 (红 -> 绿 -> 黄)

# 第三部分:红色覆盖 (当进度超过 16 + 灯的索引)
if position > (2 * NUM_LEDS + i):
np[i] = COLOR_RED

# 第二部分:绿色覆盖 (当进度超过 8 + 灯的索引)
elif position > (1 * NUM_LEDS + i):
np[i] = COLOR_GREEN

# 第一部分:黄色亮起 (当进度超过 灯的索引)
elif position > i:
np[i] = COLOR_YELLOW

# 其他情况:熄灭
else:
np[i] = COLOR_OFF

# 将数据写入灯带
np.write()

# --- 主程序 ---
print("系统启动:电位器控制 WS2812")

while True:
try:
# 读取电位器模拟值 (16 位无符号整数:0 - 65535)
val = pot.read_u16()

# 更新灯光
update_leds(val)

# 简单的延时,防止刷新过快
time.sleep_ms(50)

except KeyboardInterrupt:
# 按下 Ctrl+C 时熄灭所有灯
for i in range(NUM_LEDS):
np[i] = COLOR_OFF
np.write()
print("程序停止")
break

代码解释

  • 导入库machine 库用于控制硬件(ADC 和 GPIO),neopixel 库用于控制 WS2812 灯带,time 库用于实现延时。ESP32 MicroPython 固件默认包含 neopixel 库,无需手动安装。

  • 配置参数:程序开头定义了电位器引脚号、WS2812 引脚号和灯珠数量。将这些参数集中管理,便于后续根据实际需求快速调整。

    # --- 配置参数 ---
    POT_PIN_NUM = 7 # 电位器引脚
    NEO_PIN_NUM = 8 # WS2812 引脚
    NUM_LEDS = 8 # 灯珠数量
  • 颜色定义:程序以 RGB 三元组格式定义了四种颜色(黄、绿、红、关闭),每个值的范围是 0-255。这种预定义方式使代码更清晰易读。

    # --- 颜色定义 (R, G, B) ---
    COLOR_YELLOW = (255, 255, 0)
    COLOR_GREEN = (0, 255, 0)
    COLOR_RED = (255, 0, 0)
    COLOR_OFF = (0, 0, 0)
  • 硬件初始化

    • 使用 neopixel.NeoPixel() 初始化 WS2812 灯带,第一个参数是 GPIO 引脚对象,第二个参数是灯珠数量。
    • 使用 machine.ADC() 初始化电位器模拟输入。
    # 初始化 WS2812 灯带
    np = neopixel.NeoPixel(Pin(NEO_PIN_NUM), NUM_LEDS)

    # 初始化电位器 (ADC)
    pot = ADC(Pin(POT_PIN_NUM))
  • 核心函数 update_leds():根据电位器的 ADC 值更新 LED 灯带的显示状态。

    • 参数映射:将 ADC 的 0-65535 范围映射到 0-24(3 个阶段 × 8 个灯珠),代表整个显示进度。

      # 将 0-65535 映射到 0-24 (3 个阶段 * 8 个灯)
      total_steps = 3 * NUM_LEDS
      position = int((adc_val / 65535) * total_steps)
    • 逐灯判断:遍历每个灯珠,根据当前 position 值判断该灯应该显示的颜色。判断逻辑采用从高优先级到低优先级的顺序(红 → 绿 → 黄),确保颜色正确覆盖:

      for i in range(NUM_LEDS):
      # 第三部分:红色覆盖 (当进度超过 16 + 灯的索引)
      if position > (2 * NUM_LEDS + i):
      np[i] = COLOR_RED
      # 第二部分:绿色覆盖 (当进度超过 8 + 灯的索引)
      elif position > (1 * NUM_LEDS + i):
      np[i] = COLOR_GREEN
      # 第一部分:黄色亮起 (当进度超过 灯的索引)
      elif position > i:
      np[i] = COLOR_YELLOW
      # 其他情况:熄灭
      else:
      np[i] = COLOR_OFF
    • 数据写入:调用 np.write() 将颜色数据发送到 WS2812 灯带。WS2812 采用单线串行通信协议,必须调用此方法才能让设置生效。

  • 主循环逻辑:程序在一个无限循环 while True 中执行以下操作:

    • 读取电位器:使用 pot.read_u16() 读取 16 位无符号整数(范围 0-65535)。
    • 更新灯带:调用 update_leds() 函数根据读取的值更新灯带显示。
    • 延时控制:使用 time.sleep_ms(50) 实现 50 毫秒延时,防止刷新过快导致闪烁或资源浪费。
  • 异常处理:使用 try...except 结构增强程序的稳定性。

    • except KeyboardInterrupt:捕获用户中断信号(如 Ctrl+C),在退出前将所有灯珠熄灭,确保硬件处于安全状态。

参考链接