💡 划重点
- 核心痛点:久坐伏案导致腰椎劳损与下肢循环不畅,传统单一时长提醒难以改变行为惯性。
- 方案核心:基于 RP2040 的番茄钟与站姿提醒二合一工位终端,25 分钟专注 + 5 分钟强制离座缓冲期,震动电机物理打断「沉浸式久坐」。
- 硬件关键:0.96 寸 SPI OLED 实时显示倒计时/站立进度,GPIO 15 驱动震动电机形成「温柔而坚决」的体感提醒,按键 6/7 完成开始/跳过/重置。
- 工程要点:独立按键扫描逻辑避免阻塞延时;OLED 采用帧刷新机制降低刷新噪声;震动电机采用 PWM 分级强度,根据用户响应动态调节震动时长。
- 成本控制:核心物料总计 52.7 元,预留 2.3 元为线材与焊料损耗空间,严格控制在 55 元目标以内。
需求洞察
目标用户:每天连续伏案 6 小时以上的程序员、设计师、新媒体编辑与远程办公人群,年龄段集中在 22-40 岁,具备基础电子动手能力或愿意购买成品。
真实痛点与隐含需求:
- 时间失真:当人高度专注进入「心流」状态时,主观时间流速变慢,45 分钟后才察觉腰背僵硬。普通手机闹钟在锁屏界面容易被忽略,且与工作无关的通知混杂,降低警惕性。
- 提醒的仪式感缺失:电脑弹窗提醒极易被一键关闭,没有任何物理性的后果;而纯声音提醒在佩戴耳机时无效。
- 「站立」不等于「离开」:许多久坐提醒只是弹出「请站起来」,但用户可能站起来拿个文件又坐下,并未真正活动。本方案设计「双阶段确认」:震动后若未按下站立确认键,系统不启动下一轮番茄钟,以行为锚点强制驱动微运动。
为什么值得做:从健康经济学角度,腰椎间盘突出单次治疗成本远超 300 元;一支 55 元的工位硬件可连续使用 3 年以上,日均成本不到 5 分钱。RP2040 双核架构提供了后续扩展空间——未来可叠加低功耗蓝牙上报健康数据,不会因算力不足导致二次设计。
方案设计
感知层
- 输入:按键 6(开始/确认站立)、按键 7(跳过/重置)。
- 物理感知扩展预留:通过 I2C 接口预留 MPU6050 加速度计焊盘,用于检测坐姿倾斜角度(本方案主版本不焊接该模块,但底层驱动已预留接口)。
控制层
- RP2040 运行状态机逻辑:
IDLE → FOCUS(25min)→ VIBRATE_STAND(震动 10s)→ STANDING(5min 缓冲)→ FOCUS。 - 采用双核分工:
Core0处理 UI 刷新与按键扫描;Core1通过硬件定时器管理毫秒级番茄钟计时,保证即使 OLED 刷新阻塞也不丢失计时精度。
执行层
- OLED(SSD1315)实时渲染三种界面:番茄图标 + 环形剩余进度、站立缓冲倒计时、累计完成轮数。
- 震动电机采用 PWM 驱动(占空比 80%,频率 100Hz),分为「提示震动」1s 短震和「催促震动」3s 强震(若用户 30s 内未按键确认站立)。
日志/联网层
- 板载 2MB Flash 划分为日志扇区,记录每次站立的时刻与持续时间,可通过 REPL 读取。本版本不设计 WiFi 联网,保留 UART 透传接口以便外接 ESP-01S 进行数据上云(成本增加 9.9 元,可选项)。
物料清单
| 物料 | 数量 | 用途 | 估算单价 | 小计 |
|---|---|---|---|---|
| Raspberry Pi Pico 开发板(RP2040) | 1 | 主控 | 12.5 元 | 12.5 元 |
| 0.96 寸 OLED SPI 模块(SSD1315) | 1 | 计时显示与交互界面 | 16.8 元 | 16.8 元 |
| 微型扁平震动电机(1027 型) | 1 | 体感提醒输出 | 3.5 元 | 3.5 元 |
| S8050 三极管 + 1kΩ 电阻 | 1 套 | 震动电机驱动放大 | 0.6 元 | 0.6 元 |
| 6×6×5mm 轻触按键 | 2 | 开始确认/跳过重置 | 0.25 元 ×2 | 0.5 元 |
| 1kΩ/10kΩ 电阻排 | 若干 | OLED 上拉/分压 | 0.4 元 | 0.4 元 |
| 103450 聚合物锂电池(1200mAh) | 1 | 便携供电 | 9.5 元 | 9.5 元 |
| TP4056 充电模块 | 1 | 锂电池充放电管理 | 2.4 元 | 2.4 元 |
| 自锁电源开关 | 1 | 整机电源控制 | 0.4 元 | 0.4 元 |
| TYPE-C 线 | 1 | 充电与程序烧录(兼用) | 1.2 元 | 1.2 元 |
| 亚克力/3D 打印外壳散件 | 1 | 工位固定与保护 | 4.5 元 | 4.5 元 |
| 杜邦线/飞线 | 若干 | 内部接线 | 0.4 元 | 0.4 元 |
| 合计 | 52.7 元 |
接线与结构
接线表(颜色示例仅作示意)
| RP2040 GPIO | 外设引脚 | 说明 |
|---|---|---|
| GPIO 14 | OLED DC(数据/命令) | 拉高为数据,拉低为命令 |
| GPIO 13 | OLED CS(片选) | 低电平有效 |
| GPIO 10 (SPI0 SCK) | OLED SCK | 复用 SPI 时钟线 |
| GPIO 11(SPI0 MOSI) | OLED SDA | 复用 SPI 数据线 |
| GPIO 15 | 三极管基极(经 1kΩ 电阻) | 震动电机 PWM 驱动 |
| GPIO 6 | 按键 1 输入(启用内部下拉) | 按下拉高,读取到高电平为确认 |
| GPIO 7 | 按键 2 输入(启用内部下拉) | 按下拉高,读取到高电平为跳过 |
| 3V3 | OLED VCC,按键左端 | 供电 |
| VSYS(经二极管) | TP4056 OUT | 锂电池供电入口 |
| GND | 公共地 | 所有模块共地 |
结构构思
采用 94×56×22mm 迷你桌面盒体,OLED 以 30° 仰角嵌入前壳便于视读,两枚按键置于侧面以免误触。震动电机使用 3M 双面胶贴附在底壳内侧下方,使其紧贴桌面——利用桌面作为共振腔增强震感传播,且避免电机旋转直接接触外壳产生异响。
核心代码
以下为 MicroPython 骨架代码(RP2040 官方 MicroPython 固件),展示状态机与定时器核心逻辑:
import time, ssd1315
# --- Pin 初始化 ---
oled_cs = Pin(13, Pin.OUT, value=1)
oled_dc = Pin(14, Pin.OUT)
spi = SPI(0, baudrate=20_000_000, polarity=0, phase=0, firstbit=SPI.MSB)
oled = ssd1315.SSD1315_SPI(128, 64, spi, oled_dc, oled_cs)
KEY_START = Pin(6, Pin.IN, Pin.PULL_DOWN)
KEY_SKIP = Pin(7, Pin.IN, Pin.PULL_DOWN)
motor_pwm = PWM(Pin(15, Pin.OUT))
motor_pwm.freq(100)
# --- 全局状态 ---
FOCUS_TIME = 25 * 60
STAND_TIME = 5 * 60
state = 'IDLE'
state_start_ms = 0
# --- 计时回调(基于单调时钟) ---
def show_ui():
global state, state_start_ms
now_ms = time.ticks_ms()
elapsed = time.ticks_diff(now_ms, state_start_ms) // 1000
remain = 0
if state == 'FOCUS':
remain = FOCUS_TIME - elapsed
elif state == 'STANDING':
remain = STAND_TIME - elapsed
elif state == 'VIBRATE':
remain = 0
oled.fill(0)
oled.text(state, 0, 0)
mins = remain // 60
secs = remain % 60
oled.text(f"{mins:02d}:{secs:02d}", 20, 24)
oled.show()
def check_state_transition():
global state, state_start_ms
now_ms = time.ticks_ms()
elapsed = time.ticks_diff(now_ms, state_start_ms) // 1000
if state == 'FOCUS' and elapsed >= FOCUS_TIME:
state = 'VIBRATE'
motor_pwm.duty_u16(52000) # 开启强震
state_start_ms = time.ticks_ms()
elif state == 'VIBRATE':
# 若按键站立确认
if KEY_START.value() == 1 and elapsed > 1:
motor_pwm.duty_u16(0)
state = 'STANDING'
state_start_ms = time.ticks_ms()
# 若 30s 无响应则持续震动
if elapsed > 30:
motor_pwm.duty_u16(52000)
elif state == 'STANDING' and elapsed >= STAND_TIME:
state = 'IDLE'
state_start_ms = time.ticks_ms()
def key_skip_handler(pin):
global state, state_start_ms
if state == 'VIBRATE':
motor_pwm.duty_u16(0)
state = 'IDLE'
elif state == 'IDLE':
state = 'FOCUS'
else:
state = 'FOCUS'
state_start_ms = time.ticks_ms()
KEY_SKIP.irq(trigger=Pin.IRQ_RISING, handler=key_skip_handler)
# --- 主循环 ---
state_start_ms = time.ticks_ms()
while True:
check_state_transition()
show_ui()
time.sleep_ms(100)
调试步骤
- 单元测试 OLED 显示:单独运行 I2C/SPI 扫描,确认 Screen 显示工程名与时间静态字符串。
- 振动通路验证:用
motor_pwm.duty_u16(32000)测试是否振动,若无动作检查三极管基极电压是否 >0.7V。 - 按键电平测试:用
while True: print(KEY_START.value())确认按下时读到 1,排除悬空干扰。 - 状态机联调:将
FOCUS_TIME临时改为 10s 进行加速循环测试,观察 UI 跳变与震动/停止时序。 - 整机功耗测流:正常显示时 26mA,震动 15s 时 120mA,使用 1200mAh 电池续行约 8 个番茄周期——若低于预期,检查 OLED 是否未进入睡眠而全亮。
- 人手实测反馈:第 25 分钟震动是否被感知——若感觉微弱,调高 PWM 占空比至 90%,或在电机下方垫高密度海绵增强贴合力。
设计图
番茄钟工位站姿提醒结构flow
1
感知输入
→
2
控制处理
→
3
执行反馈
→
4
数据上报