1. 项目概述
这个防睡眠窒息手环项目源于一个真实的健康需求。作为一名长期熬夜加班的嵌入式开发者,我曾亲眼目睹同事在工位上因睡眠呼吸暂停突发昏厥。传统医用多导睡眠监测仪价格昂贵且使用不便,而消费级手环的血氧监测功能又过于简单。于是决定用STM32开发一套开源、可定制的专业级监测方案。
核心功能是通过MAX30102传感器实时采集使用者的血氧饱和度(SpO2)和心率数据,当检测到血氧低于阈值或呼吸暂停时,通过振动马达和手机APP双重报警。系统采用模块化设计,包含STM32F103C8T6主控、OLED显示屏、HC-05蓝牙模块和18650锂电池供电,整体成本控制在200元以内。
关键设计指标:血氧检测精度±2%,采样率100Hz,待机时间>72小时,报警响应延迟<3秒
1.1 硬件架构解析
主控选用STM32F103C8T6主要基于三点考量:
- 72MHz主频足够处理传感器数据流
- 内置12位ADC满足信号采集需求
- 丰富的外设接口(I2C/SPI/USART)简化硬件连接
传感器选型对比了MAX30102和MAX30100:
- MAX30102集成环境光抑制电路,适合动态佩戴场景
- 自带FIFO缓冲,降低MCU中断频率
- 支持高达1.6kHz的采样率(实际使用100Hz)
显示模块采用0.96寸OLED(SSD1306驱动):
- 128x64分辨率足够显示实时波形
- 自发光特性在夜间更醒目
- 仅0.1W的超低功耗
1.2 软件设计要点
嵌入式端采用分层架构:
code复制应用层:报警逻辑/显示更新/蓝牙通信
算法层:IIR滤波/SpO2计算/异常检测
驱动层:传感器/I2C/定时器/中断
血氧算法实现关键步骤:
- 原始信号经过带通滤波(0.5-5Hz)去除运动伪影
- 用AC/DC分量法计算R值:R=(AC_red/DC_red)/(AC_ir/DC_ir)
- 通过经验公式转换:SpO2=110-25×R
实测发现环境温度会影响传感器精度,需在算法中加入温度补偿:
c复制float temp_compensation = 0.05 * (current_temp - 25); SpO2_corrected = raw_SpO2 + temp_compensation;
2. 硬件实现细节
2.1 电路设计注意事项
电源管理部分容易踩坑:
- MAX30102需要3.3V稳压供电,纹波<50mV
- 振动马达需单独MOS管驱动(STM32 GPIO驱动能力不足)
- 蓝牙模块天线周围要留出15mm净空区
PCB布局经验:
- 传感器接口放置板边便于贴合皮肤
- 模拟与数字地通过0Ω电阻单点连接
- 关键信号线走线等长(如I2C的SCL/SDA)
2.2 组装调试技巧
传感器校准方法:
- 将手指静置在传感器上30秒
- 通过串口输出原始波形,检查红光/红外信号幅度
- 若DC分量不在15000-30000范围内,调整LED电流:
c复制MAX30102_SetPulseAmplitudeRed(0x1F); // 31mA
MAX30102_SetPulseAmplitudeIR(0x1F);
常见硬件故障排查:
- 蓝牙无法连接:检查AT指令设置的波特率(默认9600)
- 显示花屏:重刷OLED的初始化序列
- 数据跳变:在I2C线上加4.7kΩ上拉电阻
3. 嵌入式软件开发
3.1 关键代码实现
中断服务例程设计:
c复制void TIM3_IRQHandler() {
static uint32_t sample_count = 0;
if(MAX30102_Check() == STATUS_OK) {
raw_red[sample_count] = MAX30102_ReadFIFO_Red();
raw_ir[sample_count] = MAX30102_ReadFIFO_IR();
if(++sample_count >= WINDOW_SIZE) {
osSignalSet(process_tid, PROCESS_SIGNAL);
sample_count = 0;
}
}
}
多任务调度方案:
- 创建3个FreeRTOS任务:
- 数据采集任务(优先级3)
- 算法处理任务(优先级2)
- 人机交互任务(优先级1)
3.2 低功耗优化
实测功耗分布:
- 传感器工作:4.2mA
- 蓝牙广播:8.7mA
- 待机模式:0.5mA
优化措施:
- 采用事件驱动架构,CPU90%时间处于STOP模式
- 蓝牙间隔广播从100ms改为1s
- 显示刷新率从60Hz降至10Hz
4. 上位机开发要点
4.1 Qt跨平台实现
蓝牙通信核心代码:
cpp复制void MainWindow::connectDevice() {
discoveryAgent->start();
connect(discovery[Agent](https://taotoken.net?utm_source=hardware), &QBluetoothDeviceDiscoveryAgent::deviceDiscovered,
[=](const QBluetoothDeviceInfo &device){
if(device.name().contains("OXYGEN")) {
socket->connectToService(device.address(),
QBluetoothUuid(QString("00001101-0000-1000-8000-00805F9B34FB")));
}
});
}
数据可视化方案:
- 使用QCustomPlot绘制实时曲线
- 采用双缓冲机制避免界面卡顿
- 关键参数:
cpp复制plot->xAxis->setRange(0, 60); // 60秒时间窗口
plot->yAxis->setRange(85, 100); // SpO2显示范围
4.2 数据库设计
SQLite表结构:
sql复制CREATE TABLE records (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
spo2 INTEGER CHECK(spo2 BETWEEN 0 AND 100),
heart_rate INTEGER CHECK(heart_rate BETWEEN 30 AND 200),
alarm_flag BOOLEAN
);
性能优化技巧:
- 采用事务批量插入(每秒1条→每分钟60条合并插入)
- 建立时间戳索引加速查询
- 定期执行VACUUM减少存储空间
5. 实测效果与改进
5.1 临床对比测试
与医用血氧仪对比数据:
| 指标 | 本设备 | 医用设备 | 误差 |
|---|---|---|---|
| SpO2基础值 | 98% | 97% | +1% |
| 最低值 | 89% | 87% | +2% |
| 响应延迟 | 2.8s | 1.5s | +1.3s |
5.2 用户反馈改进
收集的典型问题:
- 夜间误报(翻身导致传感器位移)
- 解决方案:增加加速度计检测体位变化
- 腕带不适
- 改用医用级硅胶材料
- 数据同步失败
- 增加蓝牙断线重连机制
5.3 后续升级方向
正在开发的增强功能:
- 加入机器学习算法识别呼吸模式
- 开发微信小程序替代原生APP
- 增加家庭多设备组网功能
这个项目从原型到稳定版本迭代了7个硬件版本,最大的体会是生物信号处理必须兼顾算法精度和实时性。比如最初采用8阶IIR滤波器导致计算延迟过高,后来改用3阶Butterworth滤波配合移动平均,在保证波形质量的同时将处理时间从15ms降到了3ms。
