电子网首页 > 开源与设计

DigiKey拾色播放器活动【开箱+元器件功能验证】

2025-12-08 01:09 | 来源:电子世界报

背景

"Let's do活动"是DigiKey联合EEPW发起的为期一年的"跟着E课堂学技术,完成任务返红包"活动,活动旨在带着电子爱好者一起学习实用的电子技术知识,一起积攒DIY经验,一起变成更好的自己 !


    这是 2025 年第三期活动, 活动主题是用一块带有240x135 液晶显示屏的 Adafruit开发板, 通过DFRobot 颜色传感器拾取现实物品的色彩来驱动蜂鸣器播放不同音阶的一场 DIY 活动。

笔者通 DigiKey 下单活动指定的元器件,按照 EEPW 提供的技术指导完成上述活动内容并在 EEPW 论坛发表活动博客以及成果视频。


    本人参与过去年同期的 墨水屏 DIY 活动,虽然没有完成全部任务(可以往前追溯下我已发的博文, 虽是拖延,但是最终败于自己搭建环境遇到的坑),但是依旧拿到了 活动的部分奖励。这次借此机会果断继续参与。


    我是 11月 8 日下单,17日完成通关后顺丰寄过来,19 日签收。收到货物后对比去年的快递,比较明显感觉到了 得捷(DigiKey) 更环保了,估计是收到了客户反馈(之前物料包装太精细,不环保),这次快递总共四个零件, 每个零件都采用小包装+原包装,相对    而言少了之前不必有的大量防静电大袋子(个人感觉挺好)。

image.png

    不过可能因为清关的原因, 依旧给我打印了两份材料清单以及器件原产地证明。


物件开箱

    订单项目如下

    到手实物如下


Adafruit开发板 1528-5691-ND 和包装一起 特写


开发板功能确认

    为了确认收到物件是否能正常工作,避免犯上一期的错误。首先需要先了解 开发板的大致功能, 通过开发板进一步验证其他元器件的功能。

    首先这款开发板商品名字:Adafruit ESP32-S3 Reverse TFT Feather, 简述了 它是基于 乐鑫 ESP32 开发的带有 TFT屏幕的轻便开发板。

    从得捷提供的规格书上可以看到:

            硬件上它使用双核 240 MHz Tensilica 处理器,有 4MB Flash, 2 MB PSRAM, 原生支持 usb 协议,使用 SPI 连接了一块 240x135 的 液晶彩色显示屏,拥有低功耗 BT 和 WIFI 能力。有四个物理按键,支持四针的 STEMMA QT 接口(Adafruit 定制的 I2C 传感器连接框架)...

           软件上它支持使用 ESP-IDF / CircuitPython / Arduion 等开发框架进行功能开发。



器件验证

    验证思路,基于 ESP32 开发板的简单编程功能,实现自身以及其他器件的简单验证。


ESP32 开发板更新 bootloader 以及 固件

    根据 EEPW 老师提供的 视频讲解了解到需要更新固件后才能使用最新版本的 CircuitPython 进行开发。
    通过 开发板官方的在线指导文档找到对应的链接:

https://learn.adafruit.com/esp32-s3-reverse-tft-feather/update-tinyuf2-bootloader-for-circuitpython-10-4mb-boards-only

里面介绍是因为 CircuitPython 10 的时候我们这款 4M 存储的开发板的 flash 布局做了调整,把两个 OTA 分区融合为了一个, 以便可以开发更复杂的功能。

    简单的说就是要更新板子的 bootloader 后才能用更新的 软件工具版本进行开发。

    注意点: type C 一定得确保是可以传递 数据的 usb 线,避免只能充电的数据线造成干扰(数据线用对了的话,设备管理器插拔数据线都会刷新)


更新 bootloader

开发板需要手动进入 ROM bootloader 模式

  • * 按住 BOOT 按键不放的情况下, 按一次 RESET 按键 后会进入黑屏状态, 这个时候就对了, 可以松开所有按键。(根据 pin out 说明可以看到 D0 按钮就是 BOOT).

更新 bootloader 有三种方式

  • 1. 通过 circuitpython.org 的 downloads 里面找到对应的板子后, 点击 open installer 按钮完成傻瓜式安装(我首先果断选择它, 但是奈何因为网络原因最终尝试失败)

  • 2. 通过 Adafruit WebSerial ESPTool 手动安装 (最终尝试失败的方案)

    • 2.1 需要下载   TinyUF2 bootloader 下载地址: https://adafruit-circuit-python.s3.amazonaws.com/bootloaders/esp32/adafruit_feather_esp32s3_reverse_tft/tinyuf2-adafruit_feather_esp32s3_reverse_tft-0.33.0-combined.bin

    • 2.2 访问: https://adafruit.github.io/Adafruit_WebSerial_ESPTool/

    • 2.3 点击连接设备

    • 2.4 点击 erase 按钮 (点击后 所有数据会被清除, 这个时候不能断开设备,后续动作需要一气呵成,否则应该会变砖头)(耗时:16s)

    • 2.5 点击第一个扇区( offset:0) 选择上面下载的 TinyUF2 bootloader 文件。

    • 2.6 本来预期是可以直接刷进去的, 但是奈何,依旧是网络问题,导致部分模块无法加载,最终 image 校验失败,没能刷成功,目前变砖了。(我只想知道 这些 JS 的 CDN 都这么不受待见吗?)

    网页报错


  • 3. 通过 esptool.py 命令行安装(最后的尝试)

  • 3.1 先安装 python3 的环境(建议借助 window 应用商店安装)

  • 3.2 打开 windows powershell 后执行: pip3 install esptool (如果提示 pip3找不到,请排查 python3 的环境问题)

  • 3.3 确认 esptool 环境安装成功: 执行 python3 -m esptool 输出如下:

  • 3.4 通过 设备管理器找到 当前设备的 COM 口(网页也可以看到)我是COM3

  • 3.5 确认设备连接状态:python3 -m esptool --port COM3 chip_id

  • 3.6 借助第二个方案,我完成了 flash 的 erase, 剩下刷入 bootloader了(没有 erase 的 需要使用这个工具清理下:python3 -m esptool --port COM3 erase_flash)

  • 3.7 烧入 bootloader 分区:python3 -m esptool --port COM3 write_flash 0x0 bootloaderbin 文件

   (开心)刷机后 reset 起来的界面

  • 注意: 如果你直接刷入 circuitpython 的固件的话你会 进入不了 bootloader 而无法刷入其他 固件, 只能使用 python 编程, 最终不管你按多次/多久 reset 都无法重新进入 bootloader。每次都是进入下面的界面。(说实话这翻译的拼音真心不行, 一开始就脏话, 不建议用拼音版本), 这个时候你需要重新刷 tinyuf2 这个 bootloader

  • 3.8 至此 bootloader 不顺利的更新完成了, 这个时候还没刷入固件,默认就是 弹出磁盘 让你拖入固件如下

  • 3.9 当你 拖入 uf2 结尾的固件后, 按 reset 即可进入新固件。当你要重新刷入固件 只需要长按 reset一会后,松开等背面 neo-rgb 彩色灯亮起粉色后 里面再按下 reset 即可。

刷入老师提供的验证固件

  • 1. 刷入 blinky.uf2 可以看到 neo-rgb 和电源指示灯闪烁。

  • 2. 刷入 adafruit-circuitpython-adafruit_feather_esp32s3_reverse_tft-en_US-10.0.3.uf2 后能进入 circuitpython, 并且对应的代码分区, 后续的传感器都基于这个版本的固件进行代码开发验证。


验证蜂鸣器 + 干簧管模块

    通过干簧管切换 蜂鸣器音调, 这里面做了防抖 0.3s 处理, 用磁铁靠近干簧管后移除来替换按钮来切换音符。

实现代码如下:

import pwmio as pwm
import time
import board
import digitalio
pin_pwm = pwm.PWMOut(board.D13, duty_cycle=0,frequency=440, variable_frequency=True)
pin_switch = digitalio.DigitalInOut(board.D12)

note_name= ['C4', 'D4', 'E4','F4', 'G4', 'A4', 'B4', 'C5']
frequencies = [261, 293, 329, 349, 392, 440, 490, 523]
index = 0
def play_note(note):
    global note_name
    global pin_pwm
    if note in range(len(note_name)):
        names = note_name[note] 
        freq = frequencies[note]
        print(f'Playing {names} at {freq} Hz')
        pin_pwm.frequency = int(freq)
        pin_pwm.duty_cycle = 2 ** 10
    else:
        print(f'Unknow note id:{note}')

def stop_note():
    global pin_pwm
    pin_pwm.duty_cycle = 0

def switch_next_note():
    global index
    global note_name
    stop_note()
    play_note(index)
    index=(index+1)%len(note_name)

action_time = 0
is_down = False

def main_loop():
    global pin_switch
    global is_down
    global action_time
    if pin_switch.value != is_down:
        last_time = time.monotonic()
        if is_down:
            if last_time - action_time > 0.3:
                print (f"switch to {pin_switch.value} dt{last_time - action_time}")
                switch_next_note()
        action_time = time.monotonic()
        is_down = pin_switch.value

while True:
    main_loop()

    

    因为基于指导老师提供的音阶频率,这次验证下来基本上比较轻松,这里面因为用了干簧管做交互,需要注意抖动处理,ESP32 的 pwm 设置的确是比较方便的,实际体验下来能准确的通过蜂鸣器还原各个音阶。


验证彩色传感器

实现代码如下

import board
import digitalio
import time

class ColorSensor:
    def __init__(self, s0, s1, s2, s3, led, out):
        self.s0 = s0
        self.s1 = s1
        self.s2 = s2
        self.s3 = s3
        self.led = led
        self.out = out
        s0.direction = digitalio.Direction.OUTPUT
        s1.direction = digitalio.Direction.OUTPUT
        s2.direction = digitalio.Direction.OUTPUT
        s3.direction = digitalio.Direction.OUTPUT
        led.direction = digitalio.Direction.OUTPUT
        out.direction = digitalio.Direction.INPUT
        self.set_frequency(0)
        
    def set_frequency(self, mode):
        frequency = [[False,False], [False,True],[True, False],[True, True]]
        if mode in range(len(frequency)):
            self.s0.value = frequency[mode][0]
            self.s1.value = frequency[mode][1]
        else:
            print(f'wrong frequency mode:{mode}, it need in range:0-{len(frequency)}')

    def set_rgb_channel(self, color):
        colors = {
            "RED": [False,False],
            "BLUE": [False, True],
            "CLEAR": [True, False],
            "GREEN": [True, True]
        }
        if color in colors:
            self.s2.value = colors[color][0]
            self.s3.value = colors[color][1]
        else:
            print(f'wrong color: {color}, you need use {colors} only')

    def set_led(self, value):
        self.led.value = value
        print(f'set led:{value}')

    def get_rgb_value(self):
        ret = 0
        times = []
        # measure out pin freqence
        now_value = self.out.value
        count = 0
        while len(times) < 10:
            new_value = self.out.value
            if now_value != new_value:
                times.append(time.monotonic_ns())
                now_value = new_value
        
        periods = []
        for i in range(2, len(times), 2):
            ns = times[i] - times[i-1]
            periods.append(ns)
        avg = sum(periods) / len(periods)
        ret = 1000000000 / avg
        return ret

color = ColorSensor(digitalio.DigitalInOut(board.D6), digitalio.DigitalInOut(board.D9), digitalio.DigitalInOut(board.D10), digitalio.DigitalInOut(board.D11), digitalio.DigitalInOut(board.D12), digitalio.DigitalInOut(board.D5))

color.set_frequency(3)
color.set_led(True)
while True:
    # without calibration
    start = time.monotonic_ns()
    color.set_rgb_channel("RED")
    time.sleep(0.1)
    red = color.get_rgb_value()
    color.set_rgb_channel("BLUE")
    time.sleep(0.1)
    blue = color.get_rgb_value()
    color.set_rgb_channel("GREEN")
    time.sleep(0.2)
    green = color.get_rgb_value()
    color.set_rgb_channel("CLEAR")
    time.sleep(0.2)
    clear = color.get_rgb_value()
    dt = (int)(time.monotonic_ns() - start)/1000000
    print(f"Red:{red}, Blue:{blue}, Green:{green}, Clear:{clear} Time:{dt}ms")


最终效果:


    这里面的编程内容依据是基于 指导老师提供的代码片段拼接出来,因为没有做白光的标定,所以实验下来的数值不是具体颜色数值,而是原始数值。数值范围和周围亮度以及是否补光强相关。


    在不补光的情况下:用传感器的黑色包装盒对比数值

频率模式1:

黑色数值 Red:40.9984, Blue:15.2587, Green:18.941, Clear:79.1976
白色数值 Red:147.604, Blue:56.2541, Green:70.2046, Clear:274.784

频率模式2:
黑色数值 Red:466.448, Blue:180.292, Green:222.156, Clear:910.221
白色数值 Red:1524.09, Blue:564.965, Green:712.35, Clear:2730.65

频率模式3:
黑色数值 Red:2383.15, Blue:923.044, Green:1129.93, Clear:4369.01
白色数值 Red:13107.0, Blue:3744.96, Green:5242.9, Clear:16383.8

从数值看,不同模式只是频率不同,范围有相关性, 唯一区别在于输出频率高的话可以提高采用速度,正常基于其中一种即可。测量过程中,数值也是抖动的厉害,具体原因还需要进一步对比看下。



开箱总结

    从报名到开箱已经过去了整整一个月,拖延症始终困扰着自己,不知道大家是如何克服的,可以教教我吗?
欢迎大家发表评论,以及借鉴参考。

    我在编辑开箱前看到有人已经很好的完成了任务,自己学习了一把后才决定动手,算是受到了激励。
suncat0504https://forum.eepw.com.cn/thread/398012/1

    这次拿到元器件后, 对比上次参与的活动,可以感受到更加容易入手,可以直接使用 micropython 编程,直插即用的元器件,借助指导老师和同学的参考资料可以比较容易的完成这次 DIY.


这次感谢 EEPW 和 DigiKey 一起举办的 DIY 活动,让我能够动手薅羊毛的同时提升动手能力和克服拖延症。


参考

  1. Let’s do 2025年第3期——DigiKey陪你渡过春夏秋冬

  2. Adafruit STEMMA & STEMMA QT

  3. Adafruit ESP32-S3 Reverse TFT Feather

  4. factory reset and bootloader repair

  5. Adafruit ESP32-S3 Reverse TFT Feather Pinouts

  6. circuit python api documentation


推荐技术

返回顶部