这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【Let'sdo第3期-拾色播放器DIY】--成果贴

共1条 1/1 1 跳转至

【Let'sdo第3期-拾色播放器DIY】--成果贴

工程师
2025-12-17 21:07:36     打赏

简介

本章节将对本次的Let‘s do 第三期拾色播放器的任务进行汇总。


任务列表

一、【Let'sdo第3期-拾色播放器DIY】--拾色播放器DIY活动开箱帖和环境搭建

二、【Let'sdo第3期-拾色播放器DIY】--驱动颜色传感器

三、【Let'sdo第3期-拾色播放器DIY】--实现板载RGB的全彩控制,实时显示当前获取的颜色

四、【Let'sdo第3期-拾色播放器DIY】--实现蜂鸣器驱动,并播放八阶音符


视频效果


系统框图
image.png

项目的主要目标为使用ESP32S3开发板通过CPY在短时间内读取颜色传感器内的不同颜色通道的PWM然后来计算出同一时间内的(近似同一时间)RGB强度数据,然后颜色数据显示在屏幕上。从而使其屏幕的颜色可以根据颜色传感器输入的数据进行变化。


程序流程图

image.png

这段程序主要是构建了一个颜色的采集和显示系统:上电后首先初始化所有硬件资源,包括板载 NeoPixel RGB LED、4 个功能按键(白平衡、模式切换、颜色/特效切换、亮度调节)、状态指示灯、补光灯以及显示屏,并通过 displayio 创建包含背景、标题、颜色块、RGB 数值和 HEX 值的可视化界面;然后来初始化 TCS3200传感器,通过控制 S0–S3 引脚切换滤波通道并测量 OUT 引脚输出频率来获取红、绿、蓝三色强度,然后供其屏幕的显示同时提供白平衡校准机制,用于修正环境光和器件差异对测量结果的影响。

程序核心运行在一个无限循环中,以状态机的方式响应按键输入并驱动系统行为:在传感器的模式下,系统周期性读取 TCS3200 的 RGB 频率数据,换算为 0–255 的颜色值后,同步更新 RGB LED 颜色和屏幕背景、数值显示。在预设效果模式下下,用户可通过按键在多种预定义颜色之间循环切换,并独立调节 LED 亮度,屏幕实时显示对应的 RGB 与 HEX 信息;在屏幕特效下,系统根据子状态生成彩虹渐变、呼吸亮度变化或颜色循环等动画效果。


完整的任务二代码如下

import time
import board
import neopixel
import digitalio
import displayio
import terminalio
from adafruit_display_text import label
import math

# 初始化板载RGB LED
pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.5, auto_write=False)
pixel.fill((0, 0, 0))  # 初始关闭
pixel.show()

# 按键变量
Button0_Value = 0
Button1_Value = 0
Button2_Value = 0
Button3_Value = 0

class TCS3200:
    NUM_CYCLES = 10

    def __init__(self, s0_pin, s1_pin, s2_pin, s3_pin, out_pin):
        # 初始化引脚
        self.s0 = digitalio.DigitalInOut(s0_pin)
        self.s1 = digitalio.DigitalInOut(s1_pin)
        self.s2 = digitalio.DigitalInOut(s2_pin)
        self.s3 = digitalio.DigitalInOut(s3_pin)
        self.out = digitalio.DigitalInOut(out_pin)
        
        # 配置引脚方向
        self.s0.direction = digitalio.Direction.OUTPUT
        self.s1.direction = digitalio.Direction.OUTPUT
        self.s2.direction = digitalio.Direction.OUTPUT
        self.s3.direction = digitalio.Direction.OUTPUT
        self.out.direction = digitalio.Direction.INPUT
        
        # 设置频率缩放
        self.set_frequency_scaling(100)
        
        # 白平衡系数
        self.r_scal = 1.0
        self.g_scal = 1.0
        self.b_scal = 1.0

    def set_frequency_scaling(self, scaling):
        if scaling == 2:      # 2%
            self.s0.value = False
            self.s1.value = True
        elif scaling == 20:   # 20%
            self.s0.value = True
            self.s1.value = False
        elif scaling == 100:  # 100%
            self.s0.value = True
            self.s1.value = True
        else:                 # 关闭
            self.s0.value = False
            self.s1.value = False
        time.sleep(0.01)

    def set_color_filter(self, filter_type):
        if filter_type == "Red":
            self.s2.value = False
            self.s3.value = False
        elif filter_type == "Green":
            self.s2.value = True
            self.s3.value = True
        elif filter_type == "Blue":
            self.s2.value = False
            self.s3.value = True
        else:  # "Clear"
            self.s2.value = True
            self.s3.value = False
        time.sleep(0.01)

    def measure_frequency(self):
        timestamps = []
        last_state = self.out.value

        while len(timestamps) < self.NUM_CYCLES:
            current_state = self.out.value
            if current_state != last_state:
                timestamps.append(time.monotonic_ns())
                last_state = current_state

        periods = []
        for i in range(2, len(timestamps), 2):
            period_ns = timestamps[i] - timestamps[i - 2]
            periods.append(period_ns)

        if periods:
            avg_period_ns = sum(periods) / len(periods)
            frequency = 1000000000 / avg_period_ns
        else:
            frequency = 0
            
        return frequency

    def read_rgb_freq(self):
        self.set_color_filter("Red")
        red_freq = self.measure_frequency()
        
        self.set_color_filter("Green")
        green_freq = self.measure_frequency()
        
        self.set_color_filter("Blue")
        blue_freq = self.measure_frequency()

        return red_freq, green_freq, blue_freq

    def calibrate_white_balance(self):
        print("白平衡校准: 对准白色物体")
        time.sleep(1)
        
        red, green, blue = self.read_rgb_freq()
        
        red = red / 70 if red > 0 else 1.0
        green = green / 70 if green > 0 else 1.0
        blue = blue / 70 if blue > 0 else 1.0
        
        self.r_scal = 255.0 / red if red > 0 else 1.0
        self.g_scal = 255.0 / green if green > 0 else 1.0
        self.b_scal = 255.0 / blue if blue > 0 else 1.0
        
        print(f"校准完成: R={self.r_scal:.2f}, G={self.g_scal:.2f}, B={self.b_scal:.2f}")

    def read_rgb(self):
        red_freq, green_freq, blue_freq = self.read_rgb_freq()
        
        r = int(red_freq / 70 * self.r_scal)
        g = int(green_freq / 70 * self.g_scal)
        b = int(blue_freq / 70 * self.b_scal)
        
        r = max(0, min(255, r))
        g = max(0, min(255, g))
        b = max(0, min(255, b))

        return r, g, b

# 初始化按键
button0 = digitalio.DigitalInOut(board.BUTTON)  # 白平衡校准
button0.switch_to_input(pull=digitalio.Pull.UP)
button1 = digitalio.DigitalInOut(board.D1)      # 开关LED/切换模式
button1.switch_to_input(pull=digitalio.Pull.DOWN)
button2 = digitalio.DigitalInOut(board.D2)      # 切换颜色/切换效果
button2.switch_to_input(pull=digitalio.Pull.DOWN)
button3 = digitalio.DigitalInOut(board.D3)      # 亮度调节
button3.switch_to_input(pull=digitalio.Pull.DOWN)

# 初始化状态LED
led0 = digitalio.DigitalInOut(board.LED)
led0.direction = digitalio.Direction.OUTPUT
led0.value = True

# 初始化补光灯
led1 = digitalio.DigitalInOut(board.D5)
led1.direction = digitalio.Direction.OUTPUT
led1.value = True  # 默认开启补光

# 初始化显示屏
display = board.DISPLAY
splash = displayio.Group()

# 创建全屏背景
screen_width = display.width
screen_height = display.height
background = displayio.Bitmap(screen_width, screen_height, 1)
bg_palette = displayio.Palette(1)
bg_palette[0] = 0x000000  # 初始黑色
bg_tilegrid = displayio.TileGrid(background, pixel_shader=bg_palette, x=0, y=0)
splash.append(bg_tilegrid)

# 创建标题
main_title_label = label.Label(
    terminalio.FONT, 
    text="EEPW DIY", 
    color=0x00FFFF,  # 青色
    x=20, 
    y=30, 
    scale=3
)

sub_title_label = label.Label(
    terminalio.FONT, 
    text="Let's do", 
    color=0xFFA500,  # 橙色
    x=20, 
    y=60, 
    scale=2
)

project_label = label.Label(
    terminalio.FONT, 
    text="颜色传感器RGB显示器", 
    color=0xFFFFFF,  # 白色
    x=20, 
    y=90, 
    scale=2
)

# 创建当前LED颜色显示区域
color_display = displayio.Bitmap(200, 60, 1)
color_palette = displayio.Palette(1)
color_palette[0] = 0x000000
color_tilegrid = displayio.TileGrid(color_display, pixel_shader=color_palette, x=20, y=120)
splash.append(color_tilegrid)

# 创建颜色数值显示
rgb_value_label = label.Label(
    terminalio.FONT,
    text="RGB: 0, 0, 0",
    color=0xFFFFFF,
    x=20,
    y=190,
    scale=2
)

hex_label = label.Label(
    terminalio.FONT,
    text="#000000",
    color=0xFFFFFF,
    x=20,
    y=220,
    scale=3
)

# 添加所有标签到显示组
splash.append(main_title_label)
splash.append(sub_title_label)
splash.append(project_label)
splash.append(rgb_value_label)
splash.append(hex_label)

# 设置显示组
display.root_group = splash

# 创建颜色传感器
try:
    color_sensor = TCS3200(
        s0_pin=board.D12,
        s1_pin=board.D11,
        s2_pin=board.D10,
        s3_pin=board.D9,
        out_pin=board.D6
    )
    print("颜色传感器初始化成功")
    sensor_available = True
except Exception as e:
    print(f"颜色传感器初始化失败: {e}")
    sensor_available = False

# LED和屏幕控制变量
led_enabled = True
led_brightness = 0.5
current_led_color = (0, 0, 0)
mode = 1  # 1:传感器模式, 2:预设颜色模式, 3:屏幕特效模式
screen_effect = 0  # 0:关闭, 1:彩虹渐变, 2:呼吸效果, 3:颜色循环

# 预设颜色列表
preset_colors = [
    ("红", (255, 0, 0)),
    ("绿", (0, 255, 0)),
    ("蓝", (0, 0, 255)),
    ("黄", (255, 255, 0)),
    ("紫", (255, 0, 255)),
    ("青", (0, 255, 255)),
    ("白", (255, 255, 255)),
    ("橙", (255, 165, 0)),
    ("粉", (255, 192, 203)),
    ("关", (0, 0, 0))
]
current_preset_index = 9  # 默认"关"

# 屏幕特效变量
rainbow_hue = 0
breath_value = 0.1
breath_direction = 1
color_cycle_index = 0
effect_speed = 0.05  # 特效速度
last_effect_time = 0

def update_background_color(r, g, b):
    """更新屏幕背景颜色"""
    hex_color = (r << 16) | (g << 8) | b
    bg_palette[0] = hex_color
    
    # 根据背景亮度调整文本颜色
    brightness = (r + g + b) / 3
    if brightness > 128:
        text_color = 0x000000
    else:
        text_color = 0xFFFFFF
    
    rgb_value_label.color = text_color
    hex_label.color = text_color
    # 标题保持固定颜色
    main_title_label.color = 0x00FFFF
    sub_title_label.color = 0xFFA500
    project_label.color = 0xFFFFFF

def update_display(r, g, b):
    """更新屏幕显示"""
    # 更新颜色显示区域
    hex_color = (r << 16) | (g << 8) | b
    color_palette[0] = hex_color
    
    # 更新数值显示
    rgb_value_label.text = f"R:{r:3d} G:{g:3d} B:{b:3d}"
    hex_label.text = f"#{r:02X}{g:02X}{b:02X}"
    
    # 如果不在屏幕特效模式,更新背景颜色
    if screen_effect == 0:
        update_background_color(r, g, b)
    
    # 强制刷新显示
    display.refresh()

def update_led(r, g, b):
    """更新RGB LED"""
    global current_led_color
    
    if not led_enabled:
        pixel.fill((0, 0, 0))
        pixel.show()
        current_led_color = (0, 0, 0)
    else:
        # 应用亮度调节
        r_adj = int(r * led_brightness)
        g_adj = int(g * led_brightness)
        b_adj = int(b * led_brightness)
        
        pixel.fill((r_adj, g_adj, b_adj))
        pixel.show()
        current_led_color = (r, g, b)  # 存储原始颜色

def set_preset_color(index):
    """设置预设颜色"""
    global current_preset_index, mode, screen_effect
    current_preset_index = index
    mode = 2
    screen_effect = 0  # 关闭屏幕特效
    color_name, color = preset_colors[index]
    print(f"预设颜色: {color_name}")
    
    # 更新LED
    update_led(*color)
    
    # 更新显示
    update_display(*color)
    
    return color

def rainbow_color(hue):
    """生成彩虹色"""
    hue = hue % 256
    if hue < 85:
        r = hue * 3
        g = 255 - hue * 3
        b = 0
    elif hue < 170:
        hue -= 85
        r = 255 - hue * 3
        g = 0
        b = hue * 3
    else:
        hue -= 170
        r = 0
        g = hue * 3
        b = 255 - hue * 3
    
    return (r, g, b)

def update_screen_effect():
    """更新屏幕特效"""
    global rainbow_hue, breath_value, breath_direction, color_cycle_index, last_effect_time
    
    current_time = time.monotonic()
    if current_time - last_effect_time < effect_speed:
        return
    
    last_effect_time = current_time
    
    if screen_effect == 1:  # 彩虹渐变
        rainbow_hue = (rainbow_hue + 1) % 256
        r, g, b = rainbow_color(rainbow_hue)
        update_background_color(r, g, b)
        # 也更新LED显示彩虹色
        if led_enabled and mode == 2 and current_preset_index == 9:  # 只有在"关"模式下才显示特效
            update_led(r, g, b)
            update_display(r, g, b)
        
    elif screen_effect == 2:  # 呼吸效果
        if breath_direction == 1:
            breath_value += 0.02
            if breath_value >= 0.8:
                breath_value = 0.8
                breath_direction = -1
        else:
            breath_value -= 0.02
            if breath_value <= 0.1:
                breath_value = 0.1
                breath_direction = 1
        
        r = int(255 * breath_value)
        g = int(255 * breath_value)
        b = int(255 * breath_value)
        update_background_color(r, g, b)
        
    elif screen_effect == 3:  # 颜色循环
        cycle_colors = [
            (255, 0, 0),    # 红
            (255, 165, 0),  # 橙
            (255, 255, 0),  # 黄
            (0, 255, 0),    # 绿
            (0, 255, 255),  # 青
            (0, 0, 255),    # 蓝
            (255, 0, 255),  # 紫
        ]
        
        color_cycle_index = (color_cycle_index + 1) % len(cycle_colors)
        r, g, b = cycle_colors[color_cycle_index]
        update_background_color(r, g, b)
        # 也更新LED显示
        if led_enabled and mode == 2 and current_preset_index == 9:  # 只有在"关"模式下才显示特效
            update_led(r, g, b)
            update_display(r, g, b)

# 主循环
print("="*40)
print("EEPW Let's do DIY - 颜色传感器RGB显示器")
print("="*40)
print("按键功能:")
print("  BOOT键: 白平衡校准")
print("  按键1: 开关LED/切换模式")
print("  按键2: 切换颜色/切换特效")
print("  按键3: 调节亮度")
print("="*40)
print("屏幕特效模式:")
print("  1. 彩虹渐变")
print("  2. 呼吸效果")
print("  3. 颜色循环")
print("  0. 关闭特效")
print("="*40)

# 初始显示
update_display(0, 0, 0)
set_preset_color(9)  # 初始为关闭

while True:
    # 状态指示灯闪烁
    led0.value = not led0.value
    
    # 读取按键
    if not button0.value:  # 白平衡校准
        if sensor_available:
            color_sensor.calibrate_white_balance()
        time.sleep(0.3)
        
    if button1.value:  # 开关LED/切换模式
        if mode == 2:  # 如果在预设模式,切换到传感器模式
            mode = 1
            screen_effect = 0
            print("模式: 传感器")
        elif mode == 1:  # 如果在传感器模式,切换到屏幕特效模式
            mode = 3
            screen_effect = 1
            print("模式: 屏幕特效 (彩虹)")
        else:  # 如果在屏幕特效模式,切换回预设模式
            mode = 2
            screen_effect = 0
            set_preset_color(current_preset_index)
            print("模式: 预设颜色")
        time.sleep(0.3)
        
    if button2.value:  # 切换颜色/切换特效
        if mode == 2:  # 预设颜色模式
            current_preset_index = (current_preset_index + 1) % len(preset_colors)
            set_preset_color(current_preset_index)
        elif mode == 3:  # 屏幕特效模式
            screen_effect = (screen_effect + 1) % 4
            effect_names = ["关闭", "彩虹渐变", "呼吸效果", "颜色循环"]
            print(f"屏幕特效: {effect_names[screen_effect]}")
        time.sleep(0.3)
        
    if button3.value:  # 调节亮度
        led_brightness += 0.2
        if led_brightness > 1.0:
            led_brightness = 0.1
        
        pixel.brightness = led_brightness
        
        if led_enabled and current_led_color != (0, 0, 0):
            update_led(*current_led_color)
        
        print(f"亮度: {int(led_brightness * 100)}%")
        time.sleep(0.3)
    
    # 传感器模式:实时读取并显示颜色
    if led_enabled and mode == 1 and sensor_available:
        try:
            r, g, b = color_sensor.read_rgb()
            update_led(r, g, b)
            update_display(r, g, b)
        except Exception as e:
            print(f"读取错误: {e}")
    
    # 屏幕特效模式
    if mode == 3:
        update_screen_effect()
        # 刷新显示
        display.refresh()
    
    time.sleep(0.05)  # 更快的刷新率以获得平滑的动画效果


总结

本次项目对我而言最大的收获是学会了如何使用TCS3200传感器,这是我第一次接触到颜色的传感器。之前觉得颜色传感器可以单词直接输出RGB的颜色来供MCU进行读取。但是在TCS3200上我才发现原来竟然可以通过在短期的通过开关不同的颜色的通道来读取到对应的数据,然后再把数据组合在一起从而变成了RGB。这次DIY活动使我受益良多。最后感谢EEPW和Digikey对本次活动的大力支持,让我体验和学习到了新的知识。非常感谢!




关键词: 拾色播放器     颜色传感器     TCS3200     成果贴    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]