这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » Let'sdo-2025年第3期-DIY拾色播放器-环境搭建和任务实现

共2条 1/1 1 跳转至

Let'sdo-2025年第3期-DIY拾色播放器-环境搭建和任务实现

助工
2025-12-08 12:57:49     打赏

一、环境搭建。

这次活动开发板 Adafruit开发板 1528-5691-ND 主控为ESP32-S3。可以支持esp-idf、Arduino、microPython、CircuitPython。这次完成任务,我使用的是CircuitPython。

从https://learn.adafruit.com/esp32-s3-reverse-tft-feather/update-tinyuf2-bootloader-for-circuitpython-10-4mb-boards-only下载固件“tinyuf2-adafruit_feather_esp32s3_reverse_tft-0.33.0-combined.bin”,和CircuitPython的固件文件。使用flash tool先将固件烧入开发板。然后通过电脑将CircuitPython拖入U盘即可完成CircuitPython环境的搭建。开发工具我选择用thonny。

image.png

image.png

tinyuf2-adafruit_feather_esp32s3_reverse_tft-0.33.0-combined.zip

adafruit-circuitpython-adafruit_feather_esp32s3_reverse_tft-en_US-10.0.3.zip



二、驱动板载的全彩LED灯。

Adafruit-ESP32-S3-TFT-Feather板子上集成了一颗WS2812全彩LED灯。在CircuitPython下使用neopixel库来驱动。

import time
import board
import neopixel

pixel = neopixel.NeoPixel(board.NEOPIXEL, 1)

pixel.brightness = 0.3

while True:
    pixel.fill((255, 0, 0))
    time.sleep(0.5)
    pixel.fill((0, 255, 0))
    time.sleep(0.5)
    pixel.fill((0, 0, 255))
    time.sleep(0.5)

image.png


三、接近传感器的测试。

购买的器件中有一个接近传感器Littelfuse 接近传感器 18-59001-1-T-02-A-ND。测试了一下,是一个霍尔传感器。当有磁铁靠近探头时,就会短路,用来无接触地感知物体的接近。

import board
import digitalio
import time

# 初始化按键(假设按键连接在 GPIO0 引脚)
button = digitalio.DigitalInOut(board.D5)
button.direction = digitalio.Direction.INPUT
button.pull = digitalio.Pull.UP  # 上拉电阻

while True:
    if not button.value:  # 按键按下时读取值为 False
        print("磁铁靠近!")
        time.sleep(0.2)  # 简单防抖延时
    time.sleep(0.01)

image.png


四、基础任务1、实现颜色传感器驱动,按键获取当前颜色数据并可以通过串口或屏幕进行打印。2、实现板载RGB的全彩控制,实时显示当前获取的颜色

给Adafruit-ESP32-S3-TFT-Feather板子焊上了排针,使用面包板来连接DFRobot 颜色传感器。这里遇到一个问题。开发板的背面有电池接口,焊上排针后,排针没有比电池接口高多少。导致插入面包板时插不紧,时不时有接触不良情况出现。

TCS3200颜色传感器是一款全彩的颜色检测器,包括了一块TAOS TCS3200RGB感应芯片和4个白色LED灯,TCS3200能在一定的范围内检测和测量几乎所有的可见光。TCS3200有大量的光检测器,每个都有红绿蓝和清除4种滤光器。每6种颜色滤光器均匀地按数组分布来清除颜色中偏移位置的颜色分量。内置的振荡器能输出方波,其频率与所选择的光的强度成比例关系。

c0df957e68f9edef837052a067734f8a.jpge790ac56e4a3c16abcee5cf032842efb.jpg

这里在使用TCS3200颜色传感器时,我使用一块TFT屏幕作为颜色的来源,所以就没有打开TCS3200颜色传感器上的LED灯。颜色只是识别红、绿、蓝三种颜色。在TCS3200颜色传感器初始化时进行一次使用白光的校准。然后每次按下按键就开始测量当前颜色,并在串口和屏幕打印识别信息。最后驱动LED灯显示对应的颜色。

image.png

TCS3200类的实现。

# tcs3200.py: CircuitPython driver for the TCS3200 color sensor
# 
# Copyright (c) U. Raich
# Adapted for CircuitPython
# Released under MIT license

import time
import board
import digitalio
import pulseio
class TCS3200:  
    NUM_CYCLES = 44  # 测试多少个周期  
  
    def __init__(self, s0_pin, s1_pin, s2_pin, s3_pin, out_pin,led_pin=None):  
        # 初始化控制引脚  
        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  
        # 设置频率缩放比为20% (最佳精度)  
        self.set_frequency_scaling(20)  
        # 白平衡校准系数 (初始值为1,需要实际校准)  
        self.r_scal = 1.0  
        self.g_scal = 1.0  
        self.b_scal = 1.0
        if led_pin:
            self._led = digitalio.DigitalInOut(led_pin)
            self._led.direction = digitalio.Direction.OUTPUT
            self.led = True
    @property
    def led(self):
        return self._led.value if self._led else None
    
    @led.setter
    def led(self, state):
        if self._led:
            self._led.value = state

  
    def set_frequency_scaling(self, scaling):  
        """  
        设置传感器的频率缩放比例  
        # s0  s1  
        # L   L   关闭  
        # L   H   2%  
        # H   L   20%  
        # H   H   100%  
        """  
        if scaling == 2:       # 2%  
            self.s0.value = False  
            self.s1.value = True  
            # print("TCS3200传感器设置频率缩放比为:%2 -> s0,s1[0,1]")  
        elif scaling == 20:    # 20%  
            self.s0.value = True  
            self.s1.value = False  
            # print("TCS3200传感器设置频率缩放比为:%20 -> s0,s1[1,0]")  
        elif scaling == 100:   # 100%  
            self.s0.value = True  
            self.s1.value = True  
            # print("TCS3200传感器设置频率缩放比为:%100 -> s0,s1[1,1]")  
        else:                  # 关闭  
            self.s0.value = False  
            self.s1.value = False  
            # print("TCS3200传感器设置频率缩放比为:%0 -> s0,s1[0,0]")  
  
        time.sleep(0.01)       # 短暂延时稳定频率缩放比例  
  
    def set_color_filter(self, filter_type):  
        """  
        设置传感器的颜色滤波器  
        # s2  s3  
        # L   L   Red  
        # H   H   Green  
        # L   H   Blue  
        # H   L   Clear(no filter)  
        """  
        if filter_type == "Red":  
            self.s2.value = False  
            self.s3.value = False  
            # print("TCS3200传感器设置颜色滤波器为:Red -> s2,s3[0,0]")  
        elif filter_type == "Green":  
            self.s2.value = True  
            self.s3.value = True  
            # print("TCS3200传感器设置颜色滤波器为:Green -> s2,s3[1,1]")  
        elif filter_type == "Blue":  
            self.s2.value = False  
            self.s3.value = True  
            # print("TCS3200传感器设置颜色滤波器为:Blue -> s2,s3[0,1]")  
        else:  # "Clear"  
            self.s2.value = True  
            self.s3.value = False  
            # print("TCS3200传感器设置颜色滤波器为:Clear -> s2,s3[1,0]")  
  
        time.sleep(0.01)       # 短暂延时稳定滤波器  
  
    def _bubble_sort(self, arr, n):  
        # 简单的冒泡排序实现从小到大排列  
        for i in range(n):  
            for j in range(0, n - i - 1):  
                if arr[j] > arr[j + 1]:  
                    arr[j], arr[j + 1] = arr[j + 1], arr[j]  
  
    def measure_frequency(self):  
        # 测量频率,并转换单位为Hz  
        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)  
  
        # 数据从小到大排列  
        self._bubble_sort(periods, len(periods))  
        # print("原始数据系列:", len(periods), periods)  
          
        # 修剪数据,计算总和  
        totals = 0  
        start_index = 5  
        end_index = len(periods) - start_index  
        # print(f"数据取值编号:{start_index:.d}->{end_index-1:.d}")  
        for i in range(start_index, end_index, 1):  
            totals += periods[i]  
            # print(f"修剪数据系列 -> {i:.d}, {periods[i]:.d}")  
          
        # avg_period_ns = sum(periods) / len(periods)  
        avg_period_ns = totals / (end_index - start_index)  
        # print(f"原始数据总和 -> {totals:.d}, {(end_index - start_index):.d}")  
        # print(f"原始数据均值 -> {avg_period_ns:.3f}")  
        frequency = 1_000_000_000 / avg_period_ns  # 转换为 Hz  
        # print(f"原始频率值 -> Frequency:{frequency:.3f}")  
  
        return frequency  
  
    def read_rgb_freq(self):  
        # 读取RGB三个通道的频率值  
        red_freq = 0  
        green_freq = 0  
        blue_freq = 0  
  
        # 读取红色分量 (S2=0, S3=0)  
        self.set_color_filter("Red")  
        red_freq = self.measure_frequency()  
        # 读取绿色分量 (S2=1, S3=1)  
        self.set_color_filter("Green")  
        green_freq = self.measure_frequency()  
        # 读取蓝色分量 (S2=0, S3=1)  
        self.set_color_filter("Blue")  
        blue_freq = self.measure_frequency()  
  
        # 清除绿色分量 (S2=1, S3=0)  
        # self.set_color_filter("Clear")  
  
        # print(f"原始频率值 -> R:{red_freq:.3f}, G:{green_freq:.3f}, B:{blue_freq:.3f}")  
  
        return red_freq, green_freq, blue_freq  
  
    def calibrate_white_balance(self):  
        # 白平衡校准 - 将传感器对准白色参考物后调用此方法  
        print("正在进行白平衡校准...")  
        print("请将传感器对准白色参考物")  
  
        # 读取白色参考物的原始频率  
        red, green, blue = self.read_rgb_freq()  
  
        # 换算为RGB值 (18000/255 = 70)  
        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  
  
        # 计算校准系数 (假设我们希望白色时RGB值接近255)  
        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:.3f}, G:{self.g_scal:.3f}, B:{self.b_scal:.3f}")  
  
    def read_rgb(self):  
        # 读取RGB三个通道的频率值,应用白平衡,并转换为RGB  
        r = 0  
        g = 0  
        b = 0  
  
        # 读取RGB三个通道的频率值  
        red_freq, green_freq, blue_freq = self.read_rgb_freq()  
  
        # 应用白平衡校准 (18000/255 = 70)  
        r = int(red_freq / 70 * self.r_scal)  
        g = int(green_freq / 70 * self.g_scal)  
        b = int(blue_freq / 70 * self.b_scal)  
  
        # 限制在0-255范围  
        r = max(0, min(255, r))  
        g = max(0, min(255, g))  
        b = max(0, min(255, b))  
  
        return r, g, b


#1、实现颜色传感器驱动,按键获取当前颜色数据并可以通过串口或屏幕进行打印;
import board  
import digitalio  
import time  
import neopixel  
import displayio  
import terminalio  
from adafruit_display_text import label  
from tcs3200 import TCS3200
import neopixel

pixel = neopixel.NeoPixel(board.NEOPIXEL, 1)

def detect_color(r, g, b):
    """
    智能颜色识别算法
    返回: 颜色名称字符串
    """
    total = r + g + b
    if total < 30: return "黑色"
    if total > 600: return "白色"
    
    # 计算各通道占比
    r_ratio, g_ratio, b_ratio = r/total, g/total, b/total
    
    # 基于比例和绝对值的综合判断
    if r_ratio > 0.45 and r > 80: return "RED"
    if g_ratio > 0.42 and g > 80: return "GREEN"
    if b_ratio > 0.42 and b > 80: return "BLUE"
    return "UNKNOWN"


# 初始化板载按键D0/BOOT0,按下接地  
button0 = digitalio.DigitalInOut(board.BUTTON)  
button0.switch_to_input(pull=digitalio.Pull.UP)

# 创建TCS3200对象  
color_sensor = TCS3200(s0_pin=board.A0,s1_pin=board.A1,s2_pin=board.A2,
                       s3_pin=board.A3,out_pin=board.A5 )

#首先做白平衡校准
color_sensor.calibrate_white_balance()  
# 主循环  
while True:  
    time.sleep(0.05)    
    # 读取按键0  
    if not button0.value:  
        print("button0按下")
        if color_sensor is not None:  
            try:  
                # 调用read_rgb方法读取颜色值  
                R_Val, G_Val, B_Val = color_sensor.read_rgb()    
                print("RGB: {:.3f},{:.3f},{:.3f}".format(R_Val, G_Val, B_Val),detect_color(R_Val, G_Val, B_Val))
                if detect_color(R_Val, G_Val, B_Val)=="RED":
                    pixel.fill((255, 0, 0))
                elif detect_color(R_Val, G_Val, B_Val)=="GREEN":
                    pixel.fill((0, 255, 0))
                elif detect_color(R_Val, G_Val, B_Val)=="BLUE":
                    pixel.fill((0, 0, 255))
                else:
                    pixel.fill((0, 0, 0))
                    
                
            except Exception as e:  
                print(f"读取颜色传感器错误: {e}")

校正

image.png识别到红色。板载的全彩LED灯,在板子的背面,只能通过边上漏出的光看见显示的颜色。

image.png

image.pngimage.png


基础任务3、实现蜂鸣器驱动,并播放八阶音符

手头有一个蜂鸣器模块。使用的是手机上的蜂鸣器。效果要比圆柱形蜂鸣器好一些。ESP32S3使用pwm方波,通过变换频率来变换音阶。

image.png

import board
import pwmio
import time  
pwm = pwmio.PWMOut(board.D13, duty_cycle=0, frequency=440, variable_frequency=True)  
  
  
# 8 音阶频率(C4, D4, E4, F4, G4, A4, B4, C5)  
note_names = ['C4', 'D4', 'E4', 'F4', 'G4', 'A4', 'B4', 'C5']  
frequencies = [261, 293, 329, 349, 392, 440, 493, 523]  
  
# 音符播放控制函数  
def play_note(note):  
    if note in range(len(note_names)):  
        names = note_names[note]  
        freq = frequencies[note]  
        print(f"Playing {names} at {freq} Hz")  
        pwm.frequency = int(freq)  
        pwm.duty_cycle = 2 ** 10  
    else:  
        print(f"Note {names} is not valid in the scale")  

# 停止播放音符  
def stop_playing():  
    pwm.duty_cycle = 0
    
note_index = 0  # 当前音符的索引  
 
while True:  
    stop_playing()  # 停止当前音符(如果正在播放)  
    play_note(note_index)  # 播放下一个音符    
    note_index = (note_index + 1) % len(note_names)  # 切换到下一个音符,若达到最后一个音符则从头开始                   
    time.sleep(1)  # 延时1秒

image.png


院士
2025-12-08 23:17:36     打赏
2楼

使用Python编程,看着代码量好少啊


共2条 1/1 1 跳转至

回复

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