回到首页 返回首页
回到顶部 回到顶部
返回上一页 返回上一页

M10控制RGB灯环的炫酷灯效 简单

头像 rzyzzxw 2025.07.22 9 0

7.22

 

【写在前面】

上一个作业中写了用小智AI和炫酷灯环互动的事,有AI的加持,项目就有趣多了。然后改写了一个不用小智版本,直接用屏幕按钮来控制灯环的灯效,不用联网,可以从行空板上启动,也很方便。

48a2c0b30b3ba25b79a57419a2d36af.jpg

 

材料清单

  • M10 X1
  • M10扩展板 X1
  • RGB灯环 X1

灯环接P23。

 

这个帖子主要就是放代码。

 

功能说明
UI界面设计​​:
采用两列按钮布局(10个灯效按钮 + 2个速度控制)
顶部状态栏显示当前灯效状态和速度级别
背景图片支持(需准备back.png)
​​灯效控制​​:
彩虹旋转 🌈
单色闪烁(红色默认)🔴
呼吸效果(蓝色默认)💙
跑马灯(绿色默认)💚
火焰效果 🔥
流星效果(橙色默认)☄️
彩虹呼吸 🌈💨
音乐同步(模拟)🎵
灯效序列(随机组合)🔄
熄灭灯环 ⚫
​​速度控制​​:
速度+/-按钮调节效果速度(1-10级)
实时显示当前速度级别

​​使用说明​​:
将代码保存为local_light_control.py
准备背景图片back.png(可选)
在行空板M10上直接运行
点击按钮切换不同灯效
速度+/-按钮实时调节效果速度
 

代码
# local_light_control.py
from unihiker import GUI
import threading
import time
import random
import sys
from pinpong.board import Board, Pin, NeoPixel
import math

# 初始化硬件
Board().begin()
np = NeoPixel(Pin(Pin.P23), 24)
np.brightness(100)  # 固定亮度设置

# 全局变量
current_effect_thread = None
stop_event = threading.Event()
effect_speed = 1.0  # 效果速度系数 (1.0为正常速度)
speed_level = 5  # 速度级别 (1-10)

# 灯效函数 (同步版)
def rainbow_rotate():
    """彩虹色循环旋转效果"""
    speed = 0.1 / effect_speed
    np.rainbow(0, np.num - 1, 1, 360)
    while not stop_event.is_set():
        np.rotate(1)
        time.sleep(speed)

def blink(color):
    """闪烁效果"""
    interval = 0.5 / effect_speed
    while not stop_event.is_set():
        np.range_color(0, np.num, color)
        time.sleep(interval)
        np.clear()
        time.sleep(interval)

def breathe(color):
    """呼吸灯效果"""
    interval = 0.03 / effect_speed
    min_brightness = 30
    max_brightness = 100
    
    try:
        np.range_color(0, np.num, color)
        current_bright = min_brightness
        direction = 1
        
        while not stop_event.is_set():
            np.brightness(current_bright)
            time.sleep(interval)
            
            current_bright += direction * 2
            if current_bright >= max_brightness:
                current_bright = max_brightness
                direction = -1
            elif current_bright <= min_brightness:
                current_bright = min_brightness
                direction = 1
    except Exception as e:
        print(f"呼吸灯错误: {e}")

def running_lights(color):
    """跑马灯效果"""
    interval = 0.1 / effect_speed
    try:
        np.clear()
        while not stop_event.is_set():
            for i in range(np.num):
                if stop_event.is_set(): break
                np[i] = color
                if i > 0:
                    np[i-1] = (color >> 1) & 0x7F7F7F  # 半亮
                if i > 1:
                    np[i-2] = 0  # 熄灭
                time.sleep(interval)
    except Exception as e:
        print(f"跑马灯错误: {e}")

def fire_effect():
    """火焰效果"""
    interval = 0.05 / effect_speed
    heat = [0] * np.num
    while not stop_event.is_set():
        # 冷却
        for i in range(np.num):
            heat[i] = max(0, heat[i] - random.randint(0, 5))
        
        # 传播热量
        for i in range(np.num - 3, 0, -1):
            heat[i] = (heat[i-1] + heat[i-2] + heat[i-2]) // 3
        
        # 添加火花
        if random.randint(0, 255) < 120:
            y = random.randint(0, 7)
            heat[y] = min(255, heat[y] + random.randint(160, 255))
        
        # 转换为颜色
        for i in range(np.num):
            # 热色映射 (黑->红->黄->白)
            t = heat[i]
            if t < 85:
                r = t * 3
                g = 0
                b = 0
            elif t < 170:
                r = 255
                g = (t - 85) * 3
                b = 0
            else:
                r = 255
                g = 255
                b = (t - 170) * 3
            
            np[i] = (min(255, r) << 16) | (min(255, g) << 8) | min(255, b)
        
        time.sleep(interval)

def meteor_effect(color=0xFFA500):
    """流星效果"""
    interval = 0.05 / effect_speed
    tail_length = 10
    decay = 0.7
    
    while not stop_event.is_set():
        # 创建新流星
        meteor_pos = 0
        meteor_brightness = 1.0
        meteor_pixels = [0] * np.num
        
        while meteor_pos < np.num + tail_length and not stop_event.is_set():
            # 清除上一帧
            np.clear()
            
            # 绘制流星
            for i in range(tail_length):
                pos = meteor_pos - i
                if 0 <= pos < np.num:
                    # 计算亮度衰减
                    brightness = meteor_brightness * (decay ** i)
                    r = int(((color >> 16) & 0xFF) * brightness)
                    g = int(((color >> 8) & 0xFF) * brightness)
                    b = int((color & 0xFF) * brightness)
                    meteor_pixels[pos] = (r << 16) | (g << 8) | b
                    np[pos] = meteor_pixels[pos]
            
            meteor_pos += 1
            time.sleep(interval)
        
        # 流星消失后添加一些星星
        for _ in range(5):
            if stop_event.is_set(): break
            for i in range(5):
                pos = random.randint(0, np.num-1)
                np[pos] = 0xFFFFFF
            time.sleep(0.2)
            np.clear()
            time.sleep(0.1)

def rainbow_breathe():
    """彩虹呼吸效果"""
    interval = 0.03 / effect_speed
    min_brightness = 30
    max_brightness = 100
    hue = 0
    
    try:
        current_bright = min_brightness
        direction = 1
        
        while not stop_event.is_set():
            # 计算当前颜色
            r, g, b = hsv_to_rgb(hue, 1.0, 1.0)
            color = (int(r*255) << 16) | (int(g*255) << 8) | int(b*255)
            
            # 设置所有灯珠
            np.range_color(0, np.num, color)
            np.brightness(current_bright)
            
            # 更新亮度和色相
            current_bright += direction * 2
            if current_bright >= max_brightness:
                current_bright = max_brightness
                direction = -1
            elif current_bright <= min_brightness:
                current_bright = min_brightness
                direction = 1
                hue = (hue + 30) % 360  # 每次呼吸周期改变颜色
            
            time.sleep(interval)
    except Exception as e:
        print(f"彩虹呼吸错误: {e}")

def music_sync_effect():
    """音乐同步效果 (模拟)"""
    try:
        # 创建频谱数据 (模拟音乐频谱)
        spectrum = [0.1] * 8
        decay = 0.9
        
        while not stop_event.is_set():
            # 随机添加"音乐节拍"
            if random.random() < 0.3:
                band = random.randint(0, 7)
                spectrum[band] = min(1.0, spectrum[band] + random.uniform(0.2, 0.8))
            
            # 将频谱映射到灯环
            for i in range(np.num):
                # 将灯珠分组对应频谱带
                band_idx = i % 8
                value = spectrum[band_idx]
                
                # 计算颜色 (从蓝到红)
                r = int(255 * value)
                g = int(100 * value)
                b = int(255 * (1 - value))
                np[i] = (r << 16) | (g << 8) | b
            
            # 频谱衰减
            for i in range(8):
                spectrum[i] = max(0.1, spectrum[i] * decay)
            
            time.sleep(0.05 / effect_speed)
    except Exception as e:
        print(f"音乐同步错误: {e}")

def effect_sequence():
    """灯效组合序列"""
    try:
        # 序列1: 派对模式
        sequences = {
            "派对模式": [
                (rainbow_rotate, 5.0),
                (lambda: blink(0xFF0000), 2.0),
                (lambda: blink(0x00FF00), 2.0),
                (lambda: blink(0x0000FF), 2.0),
                (meteor_effect, 8.0)
            ],
            "浪漫氛围": [
                (lambda: breathe(0xFF69B4), 4.0),  # 粉色
                (lambda: breathe(0x800080), 4.0),  # 紫色
                (rainbow_breathe, 8.0)
            ],
            "火焰派对": [
                (fire_effect, 6.0),
                (lambda: meteor_effect(0xFF4500), 6.0),  # 橙红色
                (fire_effect, 6.0),
                (lambda: meteor_effect(0xFFD700), 6.0)   # 金色
            ],
            "彩虹狂欢": [
                (rainbow_rotate, 8.0),
                (rainbow_breathe, 8.0),
                (lambda: running_lights(0x00FF00), 4.0),
                (lambda: running_lights(0xFF0000), 4.0),
                (lambda: running_lights(0x0000FF), 4.0)
            ]
        }
        
        # 随机选择一个序列
        seq_name, seq = random.choice(list(sequences.items()))
        update_status(f"序列: {seq_name}")
        
        # 执行序列
        for effect_func, duration in seq:
            if stop_event.is_set(): return
            stop_current_effect()
            threading.Thread(target=effect_func, daemon=True).start()
            time.sleep(duration)
        
        # 序列结束后恢复彩虹旋转
        stop_current_effect()
        threading.Thread(target=rainbow_rotate, daemon=True).start()
        update_status("彩虹旋转")
        
    except Exception as e:
        print(f"灯效序列错误: {e}")

def hsv_to_rgb(h, s, v):
    """HSV转RGB颜色"""
    h = h % 360
    c = v * s
    x = c * (1 - abs((h / 60) % 2 - 1))
    m = v - c
    
    if 0 <= h < 60:
        r, g, b = c, x, 0
    elif 60 <= h < 120:
        r, g, b = x, c, 0
    elif 120 <= h < 180:
        r, g, b = 0, c, x
    elif 180 <= h < 240:
        r, g, b = 0, x, c
    elif 240 <= h < 300:
        r, g, b = x, 0, c
    else:  # 300 <= h < 360
        r, g, b = c, 0, x
    
    return r + m, g + m, b + m

def stop_current_effect():
    """停止当前效果"""
    global stop_event
    stop_event.set()
    time.sleep(0.1)  # 给线程一点时间退出
    stop_event.clear()
    np.clear()

def get_color_by_name(color_str):
    """根据名称获取颜色值"""
    color_map = {
        "红色": 0xFF0000, "绿色": 0x00FF00, "蓝色": 0x0000FF,
        "黄色": 0xFFFF00, "紫色": 0x800080, "青色": 0x00FFFF, 
        "白色": 0xFFFFFF, "橙色": 0xFFA500, "粉色": 0xFFC0CB,
        "金色": 0xFFD700, "品红": 0xFF00FF
    }
    return color_map.get(color_str, 0xFF0000)  # 默认为红色

def set_speed(level):
    """设置速度 (1-10)"""
    global effect_speed, speed_level
    speed_level = level
    # 1-10 映射到 0.5-2.0
    effect_speed = 0.5 + (speed_level - 1) * 0.15
    update_status(f"速度: {speed_level}级")

# 按钮事件处理函数
def start_rainbow():
    stop_current_effect()
    threading.Thread(target=rainbow_rotate, daemon=True).start()
    update_status("彩虹旋转")

def start_blink():
    stop_current_effect()
    threading.Thread(target=blink, args=(get_color_by_name("红色"),), daemon=True).start()
    update_status("红色闪烁")

def start_breathe():
    stop_current_effect()
    threading.Thread(target=breathe, args=(get_color_by_name("蓝色"),), daemon=True).start()
    update_status("蓝色呼吸")

def start_running_lights():
    stop_current_effect()
    threading.Thread(target=running_lights, args=(get_color_by_name("绿色"),), daemon=True).start()
    update_status("绿色跑马灯")

def start_fire():
    stop_current_effect()
    threading.Thread(target=fire_effect, daemon=True).start()
    update_status("火焰效果")

def start_meteor():
    stop_current_effect()
    threading.Thread(target=meteor_effect, args=(get_color_by_name("橙色"),), daemon=True).start()
    update_status("橙色流星")

def start_rainbow_breathe():
    stop_current_effect()
    threading.Thread(target=rainbow_breathe, daemon=True).start()
    update_status("彩虹呼吸")

def start_music_sync():
    stop_current_effect()
    threading.Thread(target=music_sync_effect, daemon=True).start()
    update_status("音乐同步")

def start_sequence():
    stop_current_effect()
    threading.Thread(target=effect_sequence, daemon=True).start()
    update_status("灯效序列")

def turn_off():
    stop_current_effect()
    update_status("灯环已熄灭")

def speed_up():
    set_speed(min(10, speed_level + 1))

def speed_down():
    set_speed(max(1, speed_level - 1))

def update_status(text):
    """更新状态显示"""
    global status_label
    if status_label:
        status_label.config(text=f"状态: {text}")
    else:
        print(f"状态: {text}")

# 初始化GUI
u_gui = GUI()
u_gui.draw_image(image="back.png", x=0, y=0)
u_gui.draw_text(text="M10灯效控制器", x=10, y=5, font_size=20, color="#FFFFFF")

# 添加状态显示标签
status_label = u_gui.draw_text(text="状态: 就绪", x=10, y=30, font_size=12, color="#FFFFFF")

# 创建灯效控制按钮 (两列布局)
# 第一列
rainbow_btn = u_gui.add_button(text="彩虹", x=20, y=60, w=80, h=30, onclick=start_rainbow)
blink_btn = u_gui.add_button(text="闪烁", x=20, y=100, w=80, h=30, onclick=start_blink)
breathe_btn = u_gui.add_button(text="呼吸", x=20, y=140, w=80, h=30, onclick=start_breathe)
running_btn = u_gui.add_button(text="跑马灯", x=20, y=180, w=80, h=30, onclick=start_running_lights)
fire_btn = u_gui.add_button(text="火焰", x=20, y=220, w=80, h=30, onclick=start_fire)

# 第二列
meteor_btn = u_gui.add_button(text="流星", x=120, y=60, w=80, h=30, onclick=start_meteor)
rainbow_b_btn = u_gui.add_button(text="彩虹呼吸", x=120, y=100, w=80, h=30, onclick=start_rainbow_breathe)
music_btn = u_gui.add_button(text="音乐同步", x=120, y=140, w=80, h=30, onclick=start_music_sync)
sequence_btn = u_gui.add_button(text="灯效序列", x=120, y=180, w=80, h=30, onclick=start_sequence)
off_btn = u_gui.add_button(text="熄灭", x=120, y=220, w=80, h=30, onclick=turn_off)

# 速度控制按钮
speed_up_btn = u_gui.add_button(text="速度+", x=20, y=270, w=80, h=30, onclick=speed_up)
speed_down_btn = u_gui.add_button(text="速度-", x=120, y=270, w=80, h=30, onclick=speed_down)

# 设置初始速度
set_speed(speed_level)
update_status("就绪")

# 保持程序运行
try:
    while True:
        time.sleep(1)
except KeyboardInterrupt:
    stop_current_effect()
    print("\n程序退出")

本项目代码由DeepSeek协助完成,致谢。

 

社区的帖子一是记笔记,二是做备份,以后想玩这个项目了来找代码,不会丢。

评论

user-avatar