基于行空板与二哈识图的智能番茄钟盆栽制作指南 1. 项目概述当桌面盆栽遇上番茄工作法最近在捣鼓行空板总想着用它做点既实用又有趣的小玩意儿。手边正好有个闲置的绿植盆栽灵光一闪为什么不把“番茄工作法”这个时间管理神器和这个有生命力的桌面摆件结合起来呢于是“桌面番茄钟盆栽”这个想法就诞生了。它的核心很简单一个能帮你专注工作25分钟、然后强制休息5分钟的智能计时器但它的载体不是冰冷的手机App或塑料闹钟而是一个会“呼吸”、能交互的盆栽。当你在专注时盆栽的灯光会柔和地变化提醒你时间在流逝休息时间到它会用更明显的方式“呼唤”你让你离开屏幕看看绿植放松眼睛和大脑。更有趣的是我加入了二哈识图HuskyLens这款AI视觉传感器让它能识别一些简单的物体或手势实现非接触式的控制比如挥挥手就能开始下一个番茄钟科技感和仪式感直接拉满。这个项目非常适合对物联网、MicroPython编程和AI应用感兴趣的创客朋友无论你是想学习传感器集成还是单纯想做一个提升幸福感的小工具都能从中找到乐趣。2. 核心思路与硬件选型解析2.1 为什么是“番茄钟盆栽”番茄工作法的精髓在于“专注”与“休息”的节奏化。然而手机或电脑上的计时器很容易被其他通知干扰或者因为太容易关闭而失去约束力。一个独立的物理设备能创造更强的仪式感和边界感。选择盆栽作为载体则引入了“自然”和“生长”的隐喻。绿植本身能舒缓压力将其与计时功能结合相当于给你的专注时间赋予了一个“生命体”见证者。从技术实现角度看盆栽内部有空间容纳电路土壤可以隐藏线缆植物本身也能与灯光、雾气等效果联动提供了丰富的感官反馈维度比一个单纯的盒子更有创意和温度。2.2 硬件核心三件套行空板、二哈识图与外围模块这个项目的硬件骨架清晰主要围绕三块核心搭建行空板我选择行空板作为主控而不是常见的Arduino或树莓派Pico主要看中它“开箱即用”的易用性。它集成了屏幕、Wi-Fi、蓝牙、多种传感器和丰富的扩展接口省去了大量硬件连接和驱动调试的麻烦。其内置的Python环境兼容MicroPython对初学者非常友好让我们可以更专注于功能逻辑而非底层硬件调试。对于这个需要处理UI显示、网络时间同步可选和复杂传感器交互的项目行空板的综合能力非常合适。二哈识图HuskyLens这是一款“傻瓜式”的AI视觉传感器。它最大的优点是将复杂的图像识别算法固化在硬件里我们通过简单的UART或I2C指令就能调用人脸识别、物体追踪、颜色识别、标签识别等功能。在这个项目里我主要用它实现两种交互手势控制识别“手掌”或特定的手势如OK手势作为开始/暂停番茄钟的触发信号。物体识别可以学习并识别一个特定的物体比如一个放在桌面的小摆件当把这个物体拿到摄像头前就触发休息模式。这增加了项目的可玩性和个性化。 选择二哈避免了在行空板上部署OpenCV等视觉库的复杂性和性能压力让AI交互变得轻量而稳定。外围反馈模块RGB LED灯带/灯环用于视觉反馈。我将灯带缠绕在盆栽内壁或花盆边缘。工作时灯光可以是缓慢变化的呼吸灯如从蓝到浅蓝表示时间平稳流逝休息时灯光可以变为舒缓的绿色并闪烁或呈现彩虹渐变营造放松氛围。微型水泵与雾化器可选进阶功能如果盆栽是真植物可以增加一个由继电器控制的小水泵在休息时间自动浇水几秒或者增加一个雾化片模块在长时间工作后比如完成4个番茄钟制造一点水雾增加趣味性。注意此部分涉及水电务必做好绝缘防水建议新手先从纯灯光版本开始。蜂鸣器或微型扬声器用于声音提示。工作开始、结束或休息时间到可以用不同的提示音效。行空板本身有扬声器可以直接利用。2.3 系统工作流程设计整个系统的运行逻辑是一个状态机清晰定义了不同状态下的设备行为待机状态盆栽灯光处于低亮度待机色如暖黄色。屏幕显示待机界面当前时间、植物表情等。启动识别用户做出预设手势如对着二哈摄像头比“OK”二哈识别后通过串口发送指令给行空板。工作状态25分钟行空板收到指令进入工作倒计时。灯光切换为“工作模式”如蓝色呼吸。屏幕显示大大的倒计时和“专注中”。开始播放一段白噪音或环境音可选。休息状态5分钟25分钟倒计时结束自动切换。灯光变为“休息模式”如绿色闪烁或彩虹渐变。屏幕显示“休息一下”并提示可以活动身体、看看远方。蜂鸣器播放愉悦的提示音。循环与重置休息时间结束后系统自动返回待机状态等待下一次手势启动。也可以通过识别特定物体如“休息令牌”提前结束工作或进入长休息。3. 硬件连接与软件环境搭建3.1 硬件接线图与要点硬件连接并不复杂关键在于给各个模块稳定供电和正确连接数据线。下图展示了核心的连接关系[行空板] | |-- (I2C 或 UART) -- [二哈识图传感器] // 用于发送识别指令和接收结果 | |-- (GPIO 或 专用接口) -- [RGB LED灯带] // 如WS2812B只需一个数据线 | |-- (内部) -- [扬声器/屏幕] // 行空板内置无需额外连接 | |-- (USB 5V) -- [供电] // 为整个系统供电注意电流需求具体接线步骤与注意事项二哈识图连接这是最关键的一步。二哈识图支持UART和I2C两种通信方式。我推荐使用I2C因为行空板的I2C接口更稳定且可以并联多个设备虽然本项目只有一个。将二哈的GND、VCC、SDA、SCL分别连接到行空板的GND、3.3V/5V注意二哈的电压要求、I2C的SDA和SCL引脚。务必在行空板设置中开启I2C功能。RGB LED灯带连接WS2812B灯带只需连接三根线VCC5V、GND、DIN数据输入。将DIN连接到行空板的一个GPIO口如P21并在代码中为该GPIO口设置一个上拉电阻软件设置即可以提高信号稳定性。如果灯带较长超过30颗灯珠建议在VCC和GND之间并联一个470μF以上的电容靠近灯带接入端以防止上电时的电流冲击导致行空板重启。供电考量行空板通过USB供电通常足够带动板子本身、二哈和一小段灯带。如果灯带很长灯珠多或者连接了水泵等大电流设备务必使用外部5V电源单独为灯带或电机供电并共地。避免因电流不足导致行空板运行不稳定或损坏。注意所有杜邦线连接处最好使用热熔胶或电工胶带进行固定防止因桌面震动导致接触不良。接线完成后务必再三检查VCC和GND是否接反接反必烧模块。3.2 行空板软件环境准备行空板默认搭载了基于Python的交互环境我们需要进行一些初始化设置和库安装。连接与初始化通过USB线连接行空板至电脑按照官方指南配置Wi-Fi使其和你的电脑处于同一局域网。通过浏览器访问行空板的IP地址进入Web编程界面。安装必要库行空板的Python环境已经内置了很多库但我们还需要管理WS2812B灯带的库。在行空板的“应用商店”或通过终端使用upip安装。通常neopixel库是控制WS2812B的标准选择。在行空板终端中可以尝试upip install neopixel进行安装。配置I2C在行空板的“设置”或“硬件”菜单中找到I2C选项并启用它。系统通常会分配好SDA和SCL的引脚记住这些引脚编号后续代码中会用到。二哈识图固件与配置确保二哈识图传感器固件是最新的可通过官方工具升级。首次使用时需要通过二哈上的按钮和屏幕将其通信协议设置为“I2C”模式并记下它的I2C设备地址默认是0x32。4. 核心代码实现与分步解析项目的软件逻辑是核心我们将分模块构建。这里假设你已经熟悉行空板的基本编程和文件管理。4.1 二哈识图驱动与手势识别首先我们需要一个与二哈识图通信的类。行空板没有专用的HuskyLens库但我们可以通过machine库的I2C功能直接与其通信协议是公开的。# huskylens_i2c.py import time from machine import I2C, Pin class HuskyLens: def __init__(self, i2c_bus1, i2c_addr0x32): # 初始化I2C行空板的硬件I2C(1)引脚是固定的通常是 SDA6, SCL7请根据实际板卡说明确认 self.i2c I2C(i2c_bus, sclPin(7), sdaPin(6), freq100000) self.addr i2c_addr self._algorithm 0 # 默认算法 def _write_to_husky(self, cmd, p10, p20, p30, p40): # 根据二哈协议封装命令 data bytearray([0x55, 0xAA, 0x11, cmd, p1, p2, p3, p4]) checksum (0x550xAA0x11cmdp1p2p3p4) 0xFF data.append(checksum) try: self.i2c.writeto(self.addr, data) time.sleep(0.1) except Exception as e: print(fWrite error: {e}) def _read_from_husky(self): # 读取二哈返回的数据 try: data self.i2c.readfrom(self.addr, 10) # 读取足够长度的数据 if len(data) 5 and data[0] 0x55 and data[1] 0xAA: # 简单的头部校验 return data except Exception as e: print(fRead error: {e}) return None def set_algorithm(self, algo): 设置识别算法: 0人脸, 1物体追踪, 2物体识别, 3颜色识别, 4标签识别, 5线条追踪, 6分类识别 self._algorithm algo self._write_to_husky(0x2D, algo, 0x00, 0x00, 0x00) time.sleep(0.5) # 给传感器切换算法的时间 def get_blocks(self): 获取识别到的结果块 self._write_to_husky(0x21, 0x00, 0x00, 0x00, 0x00) data self._read_from_husky() blocks [] if data and len(data) 7: num_blocks data[5] # 第6个字节是块数量 if num_blocks 0: # 简化解析实际应根据协议完整解析坐标、ID等信息 # 这里假设我们只关心有没有识别到东西 blocks.append({id: data[6], count: num_blocks}) return blocks # 手势识别逻辑封装 def check_start_gesture(husky): 检查是否为开始手势例如识别到特定ID的物体或人脸 husky.set_algorithm(1) # 切换到物体追踪算法 blocks husky.get_blocks() # 假设我们之前已经让二哈学习了一个“开始令牌”物体其ID为1 for block in blocks: if block.get(id) 1: # 识别到ID为1的物体 return True return False关键点解析协议对接二哈识图有固定的命令帧格式以0x55AA开头我们需要按照其文档封装读写函数。上述代码是一个极简化的示例实际应用中需要根据协议文档完整解析返回数据中的坐标、大小、ID等信息。算法切换在检查不同功能前必须用set_algorithm切换到对应的模式。比如手势识别可能用“物体追踪”或“分类识别”如果你训练了手势模型。学习功能二哈的“物体识别”模式需要先“学习”。在代码运行前你需要手动按下二哈上的学习键让它对着你的手势或特定物体学习并分配一个ID。这个ID就是代码中判断的依据。4.2 番茄钟状态机与灯光控制接下来是主程序它管理番茄钟的状态、计时和灯光效果。# main.py import time import _thread from huskylens_i2c import HuskyLens, check_start_gesture from neopixel import NeoPixel from machine import Pin # 常量定义 WORK_DURATION 25 * 60 # 25分钟单位秒 BREAK_DURATION 5 * 60 # 5分钟单位秒 LED_PIN 21 LED_NUM 16 # 灯珠数量 # 初始化 husky HuskyLens() np NeoPixel(Pin(LED_PIN, Pin.OUT), LED_NUM) state IDLE # 状态: IDLE, WORK, BREAK remaining_time 0 timer_thread_running False def led_effect_idle(): 待机效果暖黄色呼吸灯 for i in range(LED_NUM): np[i] (30, 15, 0) # 暖黄色 np.write() def led_effect_work(progress): 工作效果蓝色呼吸progress是0-1的进度 import math brightness int(50 50 * math.sin(progress * 2 * math.pi)) # 正弦波变化 color (0, 0, brightness) # 蓝色 for i in range(LED_NUM): np[i] color np.write() def led_effect_break(): 休息效果绿色渐变闪烁 for i in range(LED_NUM): np[i] (0, 50, 0) np.write() time.sleep(0.5) for i in range(LED_NUM): np[i] (0, 10, 0) np.write() time.sleep(0.5) def timer_countdown(): 倒计时线程函数 global state, remaining_time, timer_thread_running while timer_thread_running and remaining_time 0: time.sleep(1) remaining_time - 1 # 更新屏幕显示此处省略屏幕代码 print(fState: {state}, Time left: {remaining_time}s) if state WORK: progress 1 - (remaining_time / WORK_DURATION) led_effect_work(progress) elif state BREAK: # 休息时灯光效果独立循环 pass # 倒计时结束 if remaining_time 0: if state WORK: switch_to_break() elif state BREAK: switch_to_idle() def switch_to_work(): global state, remaining_time, timer_thread_running state WORK remaining_time WORK_DURATION print(切换到工作状态) # 启动倒计时线程 if not timer_thread_running: timer_thread_running True _thread.start_new_thread(timer_countdown, ()) def switch_to_break(): global state, remaining_time state BREAK remaining_time BREAK_DURATION print(切换到休息状态) # 休息状态灯光效果在主循环中控制 def switch_to_idle(): global state, timer_thread_running state IDLE timer_thread_running False print(返回待机状态。) led_effect_idle() # 主循环 led_effect_idle() # 启动时待机 print(番茄钟盆栽已启动等待手势...) while True: if state IDLE: # 检查手势开始工作 if check_start_gesture(husky): print(检测到开始手势) switch_to_work() time.sleep(2) # 防抖避免连续触发 elif state BREAK: # 休息状态灯光闪烁 led_effect_break() # 休息期间也可以检测手势提前结束休息可选 # if check_stop_gesture(husky): # switch_to_idle() time.sleep(0.1) # 主循环延迟降低CPU占用代码逻辑精讲状态机模式使用state变量清晰划分IDLE、WORK、BREAK三种状态每个状态有对应的入口、出口动作和循环行为。这是嵌入式系统常见的编程模式逻辑清晰易于维护和扩展。多线程计时倒计时timer_countdown在一个独立的线程中运行这样主循环可以继续响应传感器输入如手势而不被阻塞。行空板的_thread模块提供了基本的线程支持。注意多线程共享变量如remaining_time时在复杂场景下需要考虑锁机制本例中因为只有一个写线程暂时安全。灯光算法led_effect_work函数根据工作进度计算灯光亮度使用了正弦函数模拟呼吸效果。progress从0到1对应正弦波的一个周期使得灯光变化平滑自然。你可以调整函数内的参数来改变颜色和动态效果。4.3 行空板屏幕UI界面优化行空板的屏幕是其一大优势我们可以用unihiker库行空板自带GUI库来显示更友好的界面。# 在主程序中集成屏幕显示 from unihiker import GUI gui GUI() # 创建UI元素 time_label gui.draw_text(x120, y80, text25:00, font_size80, color#000000) state_label gui.draw_text(x120, y180, text准备就绪, font_size30, color#666666) hint_label gui.draw_text(x120, y230, text做出手势开始专注, font_size20, color#999999) def update_display(state, minutes, seconds): 更新屏幕显示 time_label.config(textf{minutes:02d}:{seconds:02d}) if state WORK: state_label.config(text专注工作中, color#0066CC) hint_label.config(text保持专注避免干扰) elif state BREAK: state_label.config(text休息一下, color#00AA66) hint_label.config(text活动身体看看远方) else: # IDLE state_label.config(text准备就绪, color#666666) hint_label.config(text做出手势开始专注) # 在 timer_countdown 线程和状态切换函数中调用 update_display # 例如在 timer_countdown 中 # mins, secs divmod(remaining_time, 60) # update_display(state, mins, secs)屏幕UI的加入立刻让项目变得直观和专业。你可以进一步添加进度条、可爱的植物动画等让界面更具吸引力。5. 组装、调试与效果优化5.1 盆栽内部组装与走线选择一个大小合适的陶瓷或塑料花盆。将行空板、二哈识图、扩展板如果有固定在花盆底部或侧面可以使用尼龙柱或蓝丁胶。RGB灯带沿着花盆内壁上部环绕固定确保光线能均匀透出。将二哈识图的摄像头镜头对准花盆外侧确保识别区域无障碍。所有线缆用扎带捆好从花盆底部的排水孔或专门开的小孔引出连接到行空板。最后放入植物真植物或仿真植物用土壤或装饰物遮盖电路部分。务必确保所有电路部分与土壤和水完全隔离。5.2 系统调试与问题排查二哈识图无反应检查供电首先用万用表测量二哈的VCC和GND之间电压是否为5V或3.3V取决于接线。检查I2C连接在行空板上运行一个简单的I2C扫描程序查看是否能扫描到地址0x32或你设置的地址。import machine i2c machine.I2C(1) print(i2c.scan())检查协议设置确认二哈识图屏幕上的通信协议是否为I2C。检查代码地址确认代码中的I2C地址与扫描到的地址一致。LED灯带不亮或颜色错乱检查数据线方向WS2812B灯带有方向性DIN数据输入必须接控制端DOUT数据输出接下一段灯带。第一颗灯珠的DIN接行空板GPIO。检查电源功率灯带全白时电流最大。如果灯珠很多行空板USB供电可能不足导致灯光暗淡或行空板重启。必须外接电源。检查接地外部电源的GND必须和行空板的GND连接在一起即“共地”否则信号无法正确传输。检查代码引脚确认NeoPixel初始化时使用的GPIO编号正确。番茄钟计时不准行空板的time.sleep(1)并非绝对精确的1秒尤其是当系统负载高时。对于长时间计时建议使用time.ticks_ms()或time.ticks_us()获取系统毫秒/微秒计时进行差值计算这样更准确。示例修正start_ticks time.ticks_ms() while timer_thread_running: time.sleep(0.1) # 短延迟降低CPU elapsed_seconds time.ticks_diff(time.ticks_ms(), start_ticks) // 1000 remaining_time total_duration - elapsed_seconds # ... 更新显示和灯光5.3 功能扩展与个性化基础版本完成后你可以尽情发挥创意网络时间同步利用行空板的Wi-Fi连接NTP服务器获取精确时间在待机状态下显示实时时钟。数据统计将每天完成的番茄钟数量记录到行空板本地文件或上传到简单的物联网平台如SIoT周末回顾一下自己的专注成果。多模式切换通过二哈识别不同的物体如不同颜色的卡片切换不同的计时模式例如“深度专注模式”50分钟、“短休息模式”3分钟。环境互动增加一个土壤湿度传感器在植物需要浇水时屏幕显示一个水滴图标提醒你把植物养护和休息提醒结合起来。音效升级替换行空板内置的简单蜂鸣提示播放存储在板载存储中的MP3文件作为提示音甚至可以在工作时段播放白噪音。这个项目从想法到实现最有趣的不是最终那个会发光、能识别的盆栽本身而是将硬件、软件、交互设计串联起来解决一个具体小问题的完整过程。每一个环节的调试每一次功能的添加都像在给这个电子盆栽“浇水施肥”看着它一点点“生长”出你想要的功能。过程中遇到的电源问题、信号干扰、代码bug都是宝贵的经验。我建议你先跑通最核心的“手势识别-计时-灯光反馈”循环获得正反馈后再一步步添加网络、统计、多模式等进阶功能。最重要的是让它真正服务于你的桌面成为你高效工作和健康休息的小助手。