1. 项目概述:基于STM32的智能药盒开发全解析
作为一名嵌入式开发工程师,我最近完成了一个针对老年人和慢性病患者的智能药盒项目。这个药盒不仅能定时提醒服药,还能通过短信通知亲属,确保用药安全。核心功能包括:
- 多时段药物分装管理(最多支持8种药物)
- 精确到分钟级的服药提醒
- 双重提醒机制(本地蜂鸣器+远程短信)
- 用药记录存储与查询
选择STM32F103C8T6作为主控是经过充分考虑的:72MHz主频足够处理复杂逻辑,丰富的外设接口(5个USART、2个SPI、2个I2C)方便扩展功能,最重要的是其低功耗特性非常适合这种需要长期运行的设备。
2. 硬件设计详解
2.1 核心器件选型
主控芯片:
STM32F103C8T6(Cortex-M3内核)的选型基于三点考量:
- 成本控制:零售价约12元,远低于同性能ARM芯片
- 开发生态:STM32CubeMX工具链成熟,资料丰富
- 外设资源:内置RTC(配合外部32.768kHz晶振),满足定时需求
时钟模块:
DS1302虽然精度一般(±2ppm,即每天误差约0.17秒),但相比DS3231(±2ppm)成本降低60%。实测证明,对于服药提醒应用,这个精度完全够用。
GSM模块:
SIM800L的选型经历了三次迭代:
- 初版尝试ESP8266 WiFi方案,但很多老年人家中没有稳定WiFi
- 第二版改用NB-IoT,发现部分地区网络覆盖不足
- 最终选定2G网络的SIM800L,全国覆盖率达99%
2.2 机械结构设计
药盒采用旋转式六格设计,每个格子容积5ml,通过28BYJ-48步进电机(减速比1:64)驱动。关键设计参数:
c复制// 步进电机控制参数
#define STEPS_PER_REV 2048 // 360°对应步数
#define CELL_DEGREE 60 // 每格间隔角度
#define STEPS_PER_CELL (STEPS_PER_REV*CELL_DEGREE/360) // 341步
实际装配时发现两个问题:
- 原设计直接用电机轴连接药盒导致扭矩不足
- 塑料齿轮存在回差问题
解决方案:
- 增加1:3的齿轮组放大扭矩
- 采用消隙齿轮设计,将旋转定位误差控制在±0.5°以内
3. 软件架构与实现
3.1 系统框架设计
采用前后台系统架构:
code复制┌──────────────┐ ┌──────────────┐
│ 定时中断 │ │ │
│ (1kHz时钟源) ├─→│ 前台系统 │
└──────────────┘ │ (时间关键任务)│
└──────┬───────┘
↓
┌──────────────┐ ┌──────┴───────┐
│ GSM短信处理 │←─┤ │
├──────────────┤ │ 后台主循环 │
│ 用户界面更新 │ │ (非实时任务) │
├──────────────┤ │ │
│ 数据存储管理 │ └──────────────┘
└──────────────┘
3.2 关键功能实现
定时提醒逻辑:
c复制void check_medication_time()
{
static uint8_t last_hour = 0xFF;
RTC_TimeTypeDef current_time;
HAL_RTC_GetTime(&hrtc, ¤t_time, RTC_FORMAT_BIN);
if(last_hour != current_time.Hours) {
last_hour = current_time.Hours;
for(int i=0; i<MAX_ALARMS; i++) {
if(alarms[i].hour == current_time.Hours &&
alarms[i].minute == current_time.Minutes) {
trigger_alarm(i);
break;
}
}
}
}
短信协议优化:
原始AT指令发送存在30%失败率,通过以下改进提升到99%:
- 增加指令重试机制(最多3次)
- 在"AT+CMGS"后添加500ms延时
- 采用UTF-8编码避免中文乱码
改进后的发送函数:
c复制HAL_StatusTypeDef robust_send_sms(const char *num, const char *msg)
{
uint8_t retry = 0;
while(retry++ < 3) {
if(HAL_UART_Transmit(&huart1, "AT+CMGS=\"", 8, 100) != HAL_OK)
continue;
// 省略号码和内容发送部分...
if(wait_for_response("+CMGS:", 5000) == HAL_OK)
return HAL_OK;
}
return HAL_ERROR;
}
4. 低功耗设计与优化
4.1 电源架构
系统采用3.7V/2000mAh锂电池供电,通过TPS63020降压到3.3V。实测各模块电流消耗:
| 工作模式 | 主控电流 | GSM电流 | 总电流 |
|---|---|---|---|
| 正常运行 | 8.2mA | 1.5mA | 9.7mA |
| 待机(STOP模式) | 12μA | 0.5mA | 512μA |
| 短信发送瞬时 | 15mA | 450mA | 465mA |
4.2 低功耗策略
- 主控休眠:在无操作时进入STOP模式,仅保留RTC运行,通过外部中断唤醒
c复制void enter_stop_mode(void)
{
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
// 唤醒后需要重新配置时钟
SystemClock_Config();
}
- GSM模块智能调度:
- 平常处于睡眠模式(AT+CSCLK=1)
- 每天只在预设提醒时间前5分钟唤醒
- 发送完成立即返回睡眠
- 外围设备管理:
- OLED屏幕仅在按键操作时点亮,30秒无操作自动关闭
- 步进电机驱动后立即切断供电
- 所有未使用IO口设置为模拟输入模式
5. 用户交互设计
5.1 界面布局方案
0.96寸OLED(128x64)显示优化历程:
code复制初始方案:
┌───────────────────────┐
│ 当前时间:14:25 │
│ 下次服药:降压药 17:00│
│ 剩余:2小时35分钟 │
└───────────────────────┘
改进方案:
┌───────────────────────┐
│ 14:25 ☀︎ │
├───────────────────────┤
│ 下一剂 │
│ 降压药 17:00 │
│ │
│ [OK]确认服用 │
└───────────────────────┘
关键改进点:
- 增加图标化显示(太阳表示白天)
- 突出关键信息(药品名称和时间)
- 固定按钮提示位置
5.2 提醒策略
三级提醒机制确保服药:
- 主提醒:蜂鸣器响铃+OLED闪烁(持续2分钟)
- 二级提醒:若5分钟内未确认,蜂鸣器改为急促报警
- 远程通知:10分钟未响应则发送短信给预设联系人
实际测试发现,单纯声音提醒对听力障碍者效果差,最终增加:
- 振动马达(型号:ERM-1450)
- RGB LED红灯快闪
6. 扩展功能实现
6.1 服药记录系统
采用AT24C02 EEPROM存储最近30天的服药记录,数据结构如下:
c复制#pragma pack(push, 1)
typedef struct {
uint8_t year; // 年份-2000
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t med_id; // 药品ID
uint8_t taken; // 是否服用
} MedRecord;
#pragma pack(pop)
存储优化技巧:
- 采用环形缓冲区设计
- 每次写入前检查是否需要擦除
- 重要数据双备份存储
6.2 环境监测扩展
通过预留的I2C接口连接SHT30温湿度传感器:
c复制void read_sht30(float *temp, float *hum)
{
uint8_t cmd[2] = {0x2C, 0x06};
uint8_t data[6];
HAL_I2C_Master_Transmit(&hi2c1, 0x44<<1, cmd, 2, 100);
HAL_Delay(15);
HAL_I2C_Master_Receive(&hi2c1, 0x44<<1, data, 6, 100);
*temp = -45 + 175*(float)((data[0]<<8)|data[1])/65535;
*hum = 100*(float)((data[3]<<8)|data[4])/65535;
}
当检测到温度>30℃或��度>70%时,会在界面显示药品保存警告。
7. 开发中的经验教训
- GSM天线问题:
初版将天线放在金属药盒内导致信号强度只有5-10dBm,改进方案:
- 改用外置弹簧天线
- 在天线下方保留至少15x15mm的净空区
- 添加SAW滤波器减少干扰
- 电机驱动教训:
直接使用IO口驱动电机导致STM32复位,原因是:
- 步进电机反电动势损坏IO口
- 电机启动电流导致电源电压跌落
最终解决方案:
- 增加ULN2003驱动芯片
- 电源端添加1000μF电解电容
- 电机供电与主控完全隔离
- RTC校准技巧:
发现DS1302每月快约25秒,通过软件补偿:
c复制void apply_rtc_compensation(void)
{
if(++compensation_counter >= 2592000) { // 30天
compensation_counter = 0;
RTC_TimeTypeDef time;
HAL_RTC_GetTime(&hrtc, &time, RTC_FORMAT_BIN);
time.Seconds -= 25; // 回拨25秒
HAL_RTC_SetTime(&hrtc, &time, RTC_FORMAT_BIN);
}
}
8. 量产优化建议
经过小批量试产(50台),总结以下改进点:
- 成本控制:
- 将STM32F103C8T6换成STM32F030F4P6(节省3.5元)
- 用国产RX8025T替换DS1302(节省1.2元)
- 塑料齿轮改为模注成型(单件成本从2.8元降至0.5元)
- 可靠性提升:
- 增加TVS二极管防护GSM模块SIM卡接口
- 改用防水型蜂鸣器(IP67等级)
- 药格添加硅胶密封圈防潮
- 用户体验改进:
- 增加NFC功能,支持手机快速配置
- 药格改为透明材质方便查看余量
- 添加语音提示功能(WT588D芯片)
这个项目从原型到量产历时6个月,最深的体会是:嵌入式开发不能只关注代码实现,必须同步考虑机械结构、生产工艺和最终用户体验。特别是在医疗相关设备开发中,可靠性必须放在首位。
