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

共2条 1/1 1 跳转至

DigiKey拾色播放器活动【成果贴】

菜鸟
2025-12-14 21:43:39   被打赏 50 分(兑奖)     打赏



系列目录


1. 开箱帖, 验证传感器和输出功能

2. 过程帖,动手时刻

3. 成果帖[本帖], 来吧展


效果展示



323afccf-11b1-4721-b030-8a60609c32e9.gif


项目功能设计


18dcd7f6-cddb-49f0-93f7-f05c1160e68c.png


        上面是 此次活动的基础任务, 其中 任务1 和 任务2 的细分任务以及在开箱完成, 其中基础任务 2 由于开发板被完全包裹, 无法f方便的查看到 neo-rgb 灯的情况。


基于上述基础任务,扩展出如下功能场景:


  1. 干簧管控制 彩色传感器识别和矫正的功能。

  2. TFT 屏幕输出颜色用于矫正传感器。

  3. TFT 图像形象显示被测试的物件的颜色色块。

  4. 存留识别到的色彩的记录并且可以支持切换显示。

  5. TFT 屏幕根据录制的乐曲周期切换不同颜色的图片后 让颜色传感器感应后,播放对应的音乐。


系统框图


c18400cf-d874-42a4-9bb2-bcd1c1d730e1.jpg


接线框图


因为需要用上所有购得的物件,因此干脆所有物件接上再说,从功能看,有干簧管 和 色彩传感器作为输入,那 ESP32 的 neo RGB 和 蜂鸣器 以及 TFT 显示屏作为输出,这完全足够开发丰富多样的交互场景。


ab64e1b0-374c-4877-9568-9d9d733441ba.jpg

功能原理


色彩传感器原理以及使用说明


        包含 8x8 阵列的光电二极管,每个像素是110um X 110um 的尺寸。按照 16个像素分成了四组,分别是 没有遮罩和有 蓝/绿/红 遮罩的像素,通过 两个 pin 脚 (S2,S3) 切换四组不同的组别使能切换。

其输出是使用一个 out 引脚,按照频率的形式输出对应通道的数值,这个频率也是 通过两个引脚了(S0, S1) 来做频率的缩放,具体对应关系参考:(注:S0/S1/S2/S3 都是有上拉电阻,不接的情况下默认是 H)


b6f37954-5b98-411e-ad7f-0c28672f94ce.png


        从规格说明书可以看到,这个芯片的工作电压是 2.7 ~ 5.5

输出色彩信号频率范围如下:(频率范围适合用 frequencyio 获取数值, 主控芯片频率是 240 MHz)
18477fce-fd0c-4dc2-9289-213011c38e94.png


        文档有给出使用建议:全速的情况下,每毫秒可以收集到1-2个数据。
    7be3cd94-75d4-40e1-81c9-10caed67f53d.png


编码任务实现工程骨架


        此处涉及多个业务环节,其实播放器播放过程中不能影响检测,因此需要 事件驱动的形式组合相关业务逻辑,通过串口调试信息输出需要的数据。

        事件驱动 包含:gpio 中断 、 timer 中断 两类,板子资源包括 按钮、输入输出、TFT 显示屏、neo-rgb。

        需要引入如下包:

    1. displayio:用于 TFT 显示绘制功能, 厂商参考 https://learn.adafruit.com/esp32-s3-reverse-tft-feather/displayio-example, 详细说明 https://learn.adafruit.com/circuitpython-display-support-using-displayiohttps://docs.circuitpython.org/en/latest/shared-bindings/displayio/index.html

    2. board:用于通过引脚别名获取 object, https://docs.circuitpython.org/en/latest/shared-bindings/board/index.html#module-board

    3. neopixel:用于板载 neo-rgb 灯驱动,厂商参考 https://learn.adafruit.com/esp32-s3-reverse-tft-feather/neopixelhttps://docs.circuitpython.org/projects/neopixel/en/latest/

    4. alarm :用于实现 timer 事件驱动,https://docs.circuitpython.org/en/latest/shared-bindings/alarm/index.html#module-alarm

    5. frequencyio: 用于获取 颜色传感器的频率,避免 python 虚拟机的延迟导致精度丢失。 https://docs.circuitpython.org/en/latest/shared-bindings/frequencyio/index.html


    下面是主要事件驱动的骨架代码

    通过时间和pin脚事件驱动方便实现多功能并行(主要是想实现音量控制, 和多按键的功能

import alarm

def runFunc(callback, *args):
    callback(*args)
class Interrupt():
    def __init__(self, desc, func, *args):
        self.callback = func
        self.args = args
        self.desc = desc
    def exec(self):
        #print(f"run interrupt:{self.desc} func:{self.callback} args:{self.args}")
        runFunc(self.callback, *self.args)

class Timer(Interrupt):
    def __init__(self, desc, delay, interval, func, *args):
        super().__init__(desc, func, *args) 
        ''' delay and interval with second union'''
        self.delay = delay
        self.interval = interval
        self.time_next = 0

    def __eq__(self, timer):
        if (self.desc == timer.desc and self.func == timer.func and self.interval == timer.interval):
            return True
        return False

class PinHandle(Interrupt):
    def __init__(self, desc, pin, pull_up, func,*args):
        super().__init__(desc, func, *args)
        self.pin = pin
        self.last_action_time = time.monotonic() #second
        self.value = 0
        self.pull_up = pull_up
        self.getValue()
        self.default_value = self.value
        print(f"create Pin handle:{desc}, pin:{pin} default_value:{self.default_value}")
    def getValue(self):
        with digitalio.DigitalInOut(self.pin) as gpio:   
            if self.pull_up:
                gpio.switch_to_input(pull=digitalio.Pull.UP)
            else:
                gpio.switch_to_input(pull=digitalio.Pull.DOWN)
            gpio.direction = digitalio.Direction.INPUT
            self.value = gpio.value
    def checkValueChange(self):
        old = self.value
        self.getValue()
        if old != self.value:
            self.exec()
            self.last_action_time = time.monotonic()
        return self.value

    def __eq__(self, other):
        return (self.pin == other.pin)

class EventLoop:
    def __init__(self):
        self.timers = []
        self.pins = []
        self.status_runing = 1
        self.status_stop = 0
        self.status = self.status_stop
        self.deadline = 0
        self.deadlineTimer = None
        self.start_time = 0
        self.pin_timer = None
    
    def updateTimerNextTime(self, timer):
        if timer.delay > 0:
            timer.time_next = time.monotonic_ns() + timer.delay * 1e9
            timer.delay = 0
            print(f'first run timer:{timer}')
        elif timer.interval > 0:
            timer.time_next = time.monotonic_ns() + timer.interval * 1e9
        else:
            self.rmTimer(timer)

    def addTimer(self, timer):
        if self.status == self.status_runing:
            self.updateTimerNextTime(timer)
        self.timers.append(timer)
        print(f'add timer:{timer}')
    
    def rmTimer(self, timer):
        if timer in self.timers:
            self.timers.remove(timer)
            print(f'remove timer:{timer}')
    
    def getDeadLine(self):
        self.deadline = self.timers[0]
        for timer in self.timers:
            if self.deadline.time_next > timer.time_next:
                self.deadline = timer
        #print(f'get deadline:{self.deadline}')
        return self.deadline
                    
    def addPin(self, pin):
        self.rmPin(pin)
        if len(self.pins) == 0 and self.pin_timer == None:
            self.pin_timer = Timer("pin scanner", 0, 0.005, self.triverPinStatus)
            self.addTimer(self.pin_timer)
        pin.checkValueChange()
        self.pins.append(pin)
    
    def rmPin(self, pin):
        if pin in self.pins:
            if len(self.pins) == 1 and self.pin_timer != None:
                self.rmTimer(self.pin_timer)
                self.pin_timer = None
            self.pins.remove(pin)

    def triverPinStatus(self):
        for p in self.pins:
            p.checkValueChange()
            #print(f'button:{p.desc} value:{p.value}')    
    
    def run(self):
        self.start_time = time.monotonic_ns()
        self.status = self.status_runing
        for i in self.timers:
            self.updateTimerNextTime(i)
        while True:
            # timer interrupt
            next = self.getDeadLine()
            now = time.monotonic_ns()
            if next.time_next <= now + 1e9:
                #print(f'time exec: {next.desc}')
                next.exec()
                self.updateTimerNextTime(next)
            else:
                next_timer = alarm.time.TimeAlarm(monotonic_time = next.time_next/1e9)
                alarm.light_sleep_until_alarms(next_timer)
mainloop = EventLoop()

g_volume_manager = VolumManager()
pin_volum_up = PinHandle("volum up", board.BUTTON, True, g_volume_manager.volumButtonUp)
pin_volum_down = PinHandle("volum down", board.D2, False, g_volume_manager.volumButtonDown)
g_volume_manager.pin_up = pin_volum_up
g_volume_manager.pin_down = pin_volum_down

mainloop.addPin(pin_volum_up)
mainloop.addPin(pin_volum_down)
mainloop.run()


蜂鸣器播放《两只老虎》

通过  简谱 编码蜂鸣器的音调变化, 以 0.5s 为节拍完成蜂鸣器的 频率切换。

two_tigger = [1,2,3,1,1,2,3,1, 3, 4,5,3,4,5,5,6,5,4,3,1,5,6,5,4,3,1,2,5,1,0,2,5,1,0]
two_tigger_len = len(two_tigger)
note_name = ['C4', 'D4', 'E4', 'F4', 'G4', 'A4', 'B4', 'C5']
note_frequencies = [0, 261, 293, 329, 349, 392, 440, 490, 523]
note_len = len(note_frequencies)

pin_pwm = pwm.PWMOut(board.D13, duty_cycle=0, frequency=440, variable_frequency=True)
g_volume = 1024

def playNote(note):
    global note_frequencies
    global note_len
    global pin_pwm
    global g_volume
    if note < note_len:
        freq = note_frequencies[note]
        if freq == 0:
            pin_pwm.duty_cycle = 0
        else:
            pin_pwm.frequency = freq
            pin_pwm.duty_cycle = g_volume
    else:
        print(f'Unknow note id:{note}')

def playNotify(sleep=0.5):
    playNote(3)
    time.sleep(float(sleep))
    playNote(0)


颜色传感器数据获取

        上述文档总结可以得知 通过 frequency 模块完全可以胜任颜色数据获取。在此对比 开箱时刻按照老师提供的文档编写的统计方法,可以看到 frequency 可以在 2-3 ms 内获取到一个通道的数据,而基于统计的方法需要采样多个周期时间是 几百ms 左右, 数值看起来,也是 frequency 模块统计的比较稳定, 但是这个模块有个要求,就是频率需要大于 1khz 才能监控, 所以弱光环境会直接变成 0.

        经过对比验证, 通过统计获取到的数据 最大范围是40khz, 在这个范围内,数值也很不稳定。是 frequency 获取到的两倍 (由于统计的是上升沿+下降沿), 而 frequency 的范围是 0 到 700k (red 和 clear 的确可以达到),但是它的精度有限只能 500 梯度 变化, 如果光照比较合适的情况下应该是可以正常使用。

(第二列是 frequency 获取到的颜色数值,第三列是其获取过程耗时)
3b14c4b5-f0bb-415b-a6d3-ab3ca05ca07b.png


色彩传感器的驱动关键代码


   def get_channel_value(self, channel):
       # 获取某个颜色值,通过 frequencyio 获取
        self.set_rgb_channel(channel)
        time.sleep(0.001)
        value = 0
        # for new api
        with frequencyio.FrequencyIn(self.out_pin) as frequency:
            frequency.capture_period = 1
            time.sleep(0.008)
            value = frequency.value
        return value

    def get_rgba_raw_value(self):
        start = time.monotonic_ns()
        red = self.get_channel_value("RED")
        blue = self.get_channel_value("BLUE")
        green = self.get_channel_value("GREEN")
        clear = self.get_channel_value("CLEAR")
        dt = (int)(time.monotonic_ns() - start)/1000000
        print(f"raw Red:{red} Blue:{blue} Green:{green} Clear:{clear} Time:{dt}ms")
        return [red, green, blue, clear]
    
    def calibration_max(self):
        # 标定最大值 获取每个梯度的颜色范围, 因为没有lut 所以只能大概颜色范围
        value = self.get_rgba_raw_value()
        for i in range(len(self.calibration)):
            self.calibration[i] = value[i] / 256.0
        print(f'calibration get scale:{self.calibration}')
    
    def get_rgb(self):
        # 根据标定的梯度值获取实际值
        value = self.get_rgba_raw_value()
        ret =[0, 0, 0]
        for i in range(3):
            scale = self.calibration[i]
            if ( scale == 0):
                print(f"Warning get rgb without calibration, just retrun raw value")
                scale = 1
            ret[i] = max(min(value[i] / scale,255),0)
            
        return ret


按键功能


音量控制 以及 补光控制 功能,使用 D0 / D2 短按来实现 音量增加和减少, 使用 D1 短按完成关闭补光

g_button_stable_s = 0.2

class VolumManager():
    def __init__(self):
        self.volume = 1024
        self.pin_down = None
        self.pin_up = None

    def volumStepChange(self, up):
        if up:
            self.volume = min(self.volume * 2, 2**16-1)
            print(f'volume up, curr:{self.volume}')
        else:
            self.volume = max(self.volume / 2, 2)
            print(f'volume down, curr:{self.volume}')
        playNotify(0.5)
    
    def volumButton(self, up):
        global g_button_stable_s
        pin = self.pin_down
        if up:
            pin = self.pin_up
        if pin.value == pin.default_value and time.monotonic() - pin.last_action_time > g_button_stable_s:
            # button release
            print(f'volum button released')
            self.volumStepChange(up)
    def volumButtonUp(self):
        self.volumButton(True)
    def volumButtonDown(self):
        self.volumButton(False)
...
g_volume_manager = VolumManager()
pin_volum_up = PinHandle("volum up", board.BUTTON, True, g_volume_manager.volumButtonUp)
pin_volum_down = PinHandle("volum down", board.D2, False, g_volume_manager.volumButtonDown)
g_volume_manager.pin_up = pin_volum_up
g_volume_manager.pin_down = pin_volum_down
...
    def onLEDButton(self):
        global g_button_stable_s
        if self.button_led.value == self.button_led.default_value and time.monotonic() - self.button_led.last_action_time > g_button_stable_s:
            # button release
            print(f'LED button released')
            self.set_led(not self.led.value)
...

pin_LED = PinHandle('LED control', board.D1, False, color.onLEDButton)
color.button_led = pin_LED
mainloop.addPin(pin_LED)


干簧管控制代码

def onMagicButton():
    global g_button_stable_s,pin_Magic,color
    if pin_Magic.value == pin_Magic.default_value:
       if (time.monotonic() - pin_Magic.last_action_time) > (4 * g_button_stable_s):
           # button long press
           print('Magic button long released, calibration...')
           color.calibration_max()
       elif (time.monotonic() - pin_Magic.last_action_time) >  g_button_stable_s:
            # button release
            print('Magic button released,record color:' '{:x}'.format(color.rgb888))
            g_color_record.append(color.get_rgb())

pin_Magic = PinHandle('Magic control', board.A0, True, onMagicButton)
mainloop.addPin(pin_Magic)


完整代码

import board
import digitalio
import time
import pwmio as pwm
import frequencyio
import alarm
import displayio
from adafruit_display_text import label
import terminalio
import neopixel


g_button_stable_s = 0.1 # short press time, long press time is x4

two_tigger = [1,2,3,1,1,2,3,1, 3, 4,5,3,4,5,5,6,5,4,3,1,5,6,5,4,3,1,2,5,1,0,2,5,1,0]
lubinghua = [1, 1, 6, 5, 5, 1, 1, 6, 5, 5, 6, 6, 6, 1, 6, 5, 5, 3, 5, 5, 3, 2, 3, 2, 2, 7, 6, 5, 7,
             6, 6, 6, 6, 3,6, 3, 2, 1, 3, 3, 1, 1, 2, 3, 5, 6, 3, 2, 6, 6, 1, 6, 5, 5, 3, 5, 3, 2,
             3, 2, 2, 7, 6, 5, 7, 6, 1, 6,5, 5, 1, 1, 6, 5, 5, 6, 6, 1, 6, 5, 5, 3, 5, 5, 3, 2]
two_tigger_len = len(two_tigger)
note_name = ['C4', 'D4', 'E4', 'F4', 'G4', 'A4', 'B4', 'C5']
note_frequencies = [0, 261, 293, 329, 349, 392, 440, 490, 523]
note_len = len(note_frequencies)

pin_pwm = pwm.PWMOut(board.D13, duty_cycle=0, frequency=440, variable_frequency=True)

def runFunc(callback, *args):
    callback(*args)
class Interrupt():
    def __init__(self, desc, func, *args):
        self.callback = func
        self.args = args
        self.desc = desc
    def exec(self):
        #print(f"run interrupt:{self.desc} func:{self.callback} args:{self.args}")
        runFunc(self.callback, *self.args)

class Timer(Interrupt):
    def __init__(self, desc, delay, interval, func, *args):
        super().__init__(desc, func, *args) 
        ''' delay and interval with second union'''
        self.delay = delay
        self.interval = interval
        self.time_next = 0

    def __eq__(self, timer):
        if (self.desc == timer.desc and self.func == timer.func and self.interval == timer.interval):
            return True
        return False

class PinHandle(Interrupt):
    def __init__(self, desc, pin, pull_up, func,*args):
        super().__init__(desc, func, *args)
        self.pin = pin
        self.last_action_time = time.monotonic() #second
        self.value = 0
        self.pull_up = pull_up
        self.getValue()
        self.default_value = self.value
        print(f"create Pin handle:{desc}, pin:{pin} default_value:{self.default_value}")
    def getValue(self):
        with digitalio.DigitalInOut(self.pin) as gpio:   
            if self.pull_up:
                gpio.switch_to_input(pull=digitalio.Pull.UP)
            else:
                gpio.switch_to_input(pull=digitalio.Pull.DOWN)
            gpio.direction = digitalio.Direction.INPUT
            self.value = gpio.value
    def checkValueChange(self):
        old = self.value
        self.getValue()
        if old != self.value:
            self.exec()
            self.last_action_time = time.monotonic()
        return self.value

    def __eq__(self, other):
        return (self.pin == other.pin)

class EventLoop:
    def __init__(self):
        self.timers = []
        self.pins = []
        self.status_runing = 1
        self.status_stop = 0
        self.status = self.status_stop
        self.deadline = 0
        self.deadlineTimer = None
        self.start_time = 0
        self.pin_timer = None
    
    def updateTimerNextTime(self, timer):
        if timer.delay > 0:
            timer.time_next = time.monotonic_ns() + timer.delay * 1e9
            timer.delay = 0
            print(f'first run timer:{timer}')
        elif timer.interval > 0:
            timer.time_next = time.monotonic_ns() + timer.interval * 1e9
        else:
            self.rmTimer(timer)

    def addTimer(self, timer):
        if self.status == self.status_runing:
            self.updateTimerNextTime(timer)
        self.timers.append(timer)
        print(f'add timer:"{timer.desc}" interval:{timer.interval}s')
    
    def rmTimer(self, timer):
        if timer in self.timers:
            self.timers.remove(timer)
            print(f'remove timer:{timer}')
    
    def getDeadLine(self):
        self.deadline = self.timers[0]
        for timer in self.timers:
            if self.deadline.time_next > timer.time_next:
                self.deadline = timer
        #print(f'get deadline:{self.deadline}')
        return self.deadline
                    
    def addPin(self, pin):
        self.rmPin(pin)
        if len(self.pins) == 0 and self.pin_timer == None:
            self.pin_timer = Timer("pin scanner", 0, 0.005, self.triverPinStatus)
            self.addTimer(self.pin_timer)
        pin.checkValueChange()
        self.pins.append(pin)
    
    def rmPin(self, pin):
        if pin in self.pins:
            if len(self.pins) == 1 and self.pin_timer != None:
                self.rmTimer(self.pin_timer)
                self.pin_timer = None
            self.pins.remove(pin)

    def triverPinStatus(self):
        for p in self.pins:
            p.checkValueChange()
            #print(f'button:{p.desc} value:{p.value}')    
    
    def run(self):
        self.start_time = time.monotonic_ns()
        self.status = self.status_runing
        for i in self.timers:
            self.updateTimerNextTime(i)
        while True:
            # timer interrupt
            next = self.getDeadLine()
            now = time.monotonic_ns()
            if next.time_next <= now + 1e9:
                #print(f'time exec: {next.desc}')
                next.exec()
                self.updateTimerNextTime(next)
            else:
                next_timer = alarm.time.TimeAlarm(monotonic_time = next.time_next/1e9)
                alarm.light_sleep_until_alarms(next_timer)

class VolumManager():
    def __init__(self):
        self.volume = 2**12
        self.pin_down = None
        self.pin_up = None

    def volumStepChange(self, up):
        if up:
            self.volume = min(self.volume * 2, 2**16-1)
            print(f'volume up, curr:{self.volume}')
        else:
            self.volume = max(self.volume / 2, 2)
            print(f'volume down, curr:{self.volume}')
        playNotify(0.5)
    
    def volumButton(self, up):
        global g_button_stable_s
        pin = self.pin_down
        if up:
            pin = self.pin_up
        if pin.value == pin.default_value and time.monotonic() - pin.last_action_time > g_button_stable_s:
            # button release
            print(f'volum button released')
            self.volumStepChange(up)
    def volumButtonUp(self):
        self.volumButton(True)
    def volumButtonDown(self):
        self.volumButton(False)

def playNote(note):
    global note_frequencies
    global note_len
    global pin_pwm
    global g_volume_manager
    if note < note_len:
        freq = note_frequencies[note]
        if freq == 0:
            pin_pwm.duty_cycle = 0
        else:
            pin_pwm.frequency = freq
            pin_pwm.duty_cycle = int(g_volume_manager.volume)
    else:
        print(f'Unknow note id:{note}')



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_pin = 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
        self.set_frequency(0)
        self.calibration = [0, 0, 0, 0]
        self.button_led = None
        self.last_rgb = [0, 0, 0]
        self.rgb888 = 0

    def set_frequency(self, mode):
        print(f"frequency not support for product")
        return
        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 onLEDButton(self):
        global g_button_stable_s
        if self.button_led.value == self.button_led.default_value:
           if (time.monotonic() - self.button_led.last_action_time) > (4 * g_button_stable_s):
               # button long press
               print('LED button long released, calibration...')
               self.calibration_max()
           elif (time.monotonic() - self.button_led.last_action_time) >  g_button_stable_s:
                # button release
                print('LED button released')
                self.set_led(not self.led.value)

    def get_channel_value(self, channel):
        self.set_rgb_channel(channel)
        time.sleep(0.001)
        value = 0
        # for new api
        with frequencyio.FrequencyIn(self.out_pin) as frequency:
            frequency.capture_period = 1
            time.sleep(0.008)
            value = frequency.value
        return value

    def get_rgba_raw_value(self):
        start = time.monotonic_ns()
        red = self.get_channel_value("RED")
        blue = self.get_channel_value("BLUE")
        green = self.get_channel_value("GREEN")
        clear = self.get_channel_value("CLEAR")
        dt = (int)(time.monotonic_ns() - start)/1000000
        #print(f"raw Red:{red} Blue:{blue} Green:{green} Clear:{clear} Time:{dt}ms")
        return [red, green, blue, clear]
    
    def calibration_max(self):
        value = self.get_rgba_raw_value()
        for i in range(4):
            self.calibration[i] = value[i] / 256.0
        print(f'calibration get scale:{self.calibration}')
    
    def get_rgb(self):
        value = self.get_rgba_raw_value()
        for i in range(3):
            scale = self.calibration[i]
            if (scale == 0):
                #print(f"Warning get rgb without calibration, just retrun raw value")
                scale = 1
            self.last_rgb[i] = max(min(int(value[i] / scale),255),0)
        new = self.last_rgb
        value = (int(new[0]) << 16) + int(new[2])  + (int(new[1]) << 8)
        value = min(value, 0xFFFFFF)
        #if value != self.rgb888:
            #print(f"[Color update] Red:{new[0]} Blue:{new[1]} Green:{new[2]}")
        self.rgb888 = value
        return self.rgb888
        
class TFTDisplay:
    def __init__(self):
        self.MODE_CALIBRATION = 1
        self.mode = self.MODE_CALIBRATION
        self.curr_color = 0x0

    def showColor(self, color):
        global pixel
        if color == self.curr_color:
            return
        self.curr_color = color
        red = self.curr_color >>16
        green = (self.curr_color & 0xFF00) >> 8
        blue = self.curr_color & 0xFF
        
        pixel.fill((red, green, blue))
        root =  displayio.Group()
        screen = board.DISPLAY
        screen.root_group = root
        bitmap = displayio.Bitmap(screen.width, screen.height, 1)
        palette = displayio.Palette(1)
        tileGrid = displayio.TileGrid(bitmap, pixel_shader=palette)
        root.append(tileGrid)
        palette[0] = color
        bitmap.fill(0)
        txt = f"R:{red} G:{green} B:{blue}"
        textView = label.Label(terminalio.FONT, text=txt, color=0x123456, scale=2)
        textView.x = 10
        textView.y = 10
        root.append(textView)
        screen.refresh()

def showColor(color):
    # without calibration
    value = color.get_rgb()
    print(f"Red:{value[0]} Blue:{value[1]} Green:{value[2]}")

def playLuBinHua():
    index = 0
    global two_tigger, two_tigger_len
    while index < two_tigger_len:
        playNote(two_tigger[index])
        index=index + 1
        time.sleep(0.5)

def playNotify(sleep=0.5):
    playNote(3)
    time.sleep(float(sleep))
    playNote(0)


mainloop = EventLoop()
g_volume_manager = VolumManager()
pin_volum_up = PinHandle("volum up", board.BUTTON, True, g_volume_manager.volumButtonUp)
pin_volum_down = PinHandle("volum down", board.D2, False, g_volume_manager.volumButtonDown)
g_volume_manager.pin_up = pin_volum_up
g_volume_manager.pin_down = pin_volum_down
mainloop.addPin(pin_volum_up)
mainloop.addPin(pin_volum_down)
g_color_record = []
color = ColorSensor(digitalio.DigitalInOut(board.D6), digitalio.DigitalInOut(board.D9),
                    digitalio.DigitalInOut(board.D10), digitalio.DigitalInOut(board.D11),
                    digitalio.DigitalInOut(board.D12), board.D5)
color.set_frequency(3)
color.set_led(True) 

pin_LED = PinHandle('LED control', board.D1, False, color.onLEDButton)
color.button_led = pin_LED
mainloop.addPin(pin_LED)

def onMagicButton():
    global g_button_stable_s,pin_Magic,color
    if pin_Magic.value == pin_Magic.default_value:
       if (time.monotonic() - pin_Magic.last_action_time) > (4 * g_button_stable_s):
           # button long press
           print('Magic button long released, calibration...')
           color.calibration_max()
       elif (time.monotonic() - pin_Magic.last_action_time) >  g_button_stable_s:
            # button release
            print('Magic button released,record color:' '{:x}'.format(color.rgb888))
            #g_color_record.append(color.rgb888)

pin_Magic = PinHandle('Magic control', board.A0, True, onMagicButton)
mainloop.addPin(pin_Magic)

color_timer = Timer("color get", 0, 0.1, color.get_rgb)
mainloop.addTimer(color_timer)

time.sleep(1)
playNotify()

# for task 1 and 2 only
'''
g_display = TFTDisplay()
pixel = neopixel.NeoPixel(board.NEOPIXEL, 1)
def showColorOnScreen():
    global g_display, color
    g_display.showColor(color.rgb888)
task1_timer = Timer("show color", 0, 0.2, showColorOnScreen)
mainloop.addTimer(task1_timer)
'''
#for task 3 only
index = 0
g_display = TFTDisplay()
pixel = neopixel.NeoPixel(board.NEOPIXEL, 1)
def showMusicColorOnScreen():
    global two_tigger, two_tigger_len,index,g_display
    note = two_tigger[index]
    step = 256 / 3
    value = int(((note%3)*step)*(2**8)**(int(note / 3)))
    index = (index+1)%two_tigger_len
    print(f"show index:{index} note:{note} color:{value}")
    g_display.showColor(value)
    #playNote(note)
def playMusicWithColor():
    global color
    max = 0
    index = 0
    for i in range(len(color.last_rgb)):
        if max < color.last_rgb[i]:
            index = i
            max = color.last_rgb[i]
    step = 256 / 3
    index = index * 3 + int(max / step)
    
    print(f"play color:{color.rgb888}, index:{index}")
    playNote(index)
    

task2_timer = Timer("show color", 0, 2, showMusicColorOnScreen)
mainloop.addTimer(task2_timer)

task2_play_timer = Timer("play color", 0, 1, playMusicWithColor)
mainloop.addTimer(task2_play_timer)
mainloop.run()
参考





关键词: DigiKey     拾色     播放器     活动     成果     time    

菜鸟
2025-12-15 09:26:30     打赏
2楼

最后 review 代码的时候发现 这里面 mainloop 的 deadline 执行的判定 归并的范围值为1s,明显偏大,会导致忙碌执行,可以优化下。


共2条 1/1 1 跳转至

回复

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