基于AIR32F103的离线智能药盒嵌入式设计
1. 项目概述
智能药盒是面向慢性病管理与老年健康监护场景的嵌入式医疗辅助设备。其核心价值不在于功能堆砌,而在于以工程可靠性为前提,解决“服药依从性”这一临床实践中长期存在的系统性问题。据世界卫生组织统计,全球约50%的慢性病患者存在服药不规范现象,其中老年人因认知功能下降导致的漏服、重复服药、错时服药占比超过65%。本项目所实现的智能药盒系统,聚焦于三个可验证、可复现、可部署的关键能力:本地离线提醒的确定性保障、药品取用行为的物理层可信检测、远程监护数据链路的轻量化闭环。整套方案摒弃对云端服务的强依赖,将时间管理、状态感知、人机交互、网络通信四大模块在资源受限的单片机平台上完成协同设计,形成一套具备实际落地能力的硬件参考实现。
1.1 系统定位与设计约束
本系统定位于家庭级健康监护终端,非医疗器械,不参与药物剂量计算或疗效评估,仅提供服药行为的时间戳记录与状态提示。设计过程中严格遵循三项硬性约束:
- 离线优先原则:所有核心提醒逻辑(时间比对、声光驱动、OLED刷新)必须在无网络连接状态下独立运行。DS1302实时时钟芯片作为时间源,确保断网后72小时内时间误差小于±2秒;
- 行为检测可信性:药品取用动作的识别不依赖图像识别或重量变化等易受环境干扰的方案,而是采用红外对管构成的透射式检测通道,通过手指遮挡引发的光电流阶跃变化作为唯一触发依据;
- 通信链路极简化:放弃通用TCP/IP协议栈的全功能实现,采用ESP-01S模块配合AT指令集,仅实现MQTT CONNECT/PUBLISH两个基础报文交互,降低内存占用与连接失败率。
上述约束直接决定了主控选型、外设配置与软件架构,是理解本系统工程取舍的关键前提。
2. 硬件系统设计
硬件平台采用分层堆叠结构,由两块双面板通过铜柱机械固定并电气互连。该结构在保证信号完整性的同时,显著降低多层板加工成本,适用于小批量定制化生产。底层PCB承载高噪声模拟前端与射频模块,顶层PCB集成数字控制核心与人机交互界面,物理隔离有效抑制WiFi发射对时钟电路与红外接收的耦合干扰。
2.1 主控单元:AIR32F103CBT6
主控制器选用合宙AIR32F103CBT6,该器件为基于ARM Cortex-M3内核的32位MCU,主频72MHz,内置64KB Flash与20KB SRAM。选择依据如下:
- 外设资源匹配度:片上集成3个通用定时器(TIM2/TIM3/TIM4)、2个SPI接口(SPI1用于DS1302通信,SPI2用于OLED驱动)、1个I2C接口(预留扩展)、3个USART(USART1用于调试输出,USART2连接ESP-01S,USART3备用),完全覆盖本系统全部外设驱动需求;
- 低功耗特性:支持Sleep/Stop/Low-power run三种低功耗模式,在Stop模式下电流低于10μA,满足药盒长期待机要求;
- 抗干扰设计:内置上电复位(POR)、掉电复位(PDR)、窗口看门狗(WWDG)及独立看门狗(IWDG),在电网波动或电池电压跌落时保障系统状态可控。
值得注意的是,AIR32F103CBT6并非标准STM32F103系列的简单Clone,其内部Flash存储器采用双Bank结构,支持在Bank0执行代码的同时对Bank1进行擦写操作,为后续OTA固件升级预留了硬件基础。
2.2 时间基准:DS1302实时时钟芯片
DS1302作为独立RTC芯片,通过三线SPI接口(RST、SCLK、I/O)与AIR32通信。其设计优势体现在:
- 后备电源支持:内置涓流充电电路,可外接3V扣式锂电池(CR1220),在主电源断开后维持计时长达10年;
- 寄存器映射清晰:时间寄存器(秒、分、时、日、月、年、周)采用BCD码格式存储,避免软件中频繁进行十进制/二进制转换;
- 温度补偿缺失的工程应对:DS1302未集成温度补偿功能,常温下月误差约±2分钟。本系统通过两次校准机制弥补:首次上电时由ESP-01S从NTP服务器获取UTC时间并写入DS1302;后续每次联网成功后,主控读取云端时间戳,计算DS1302累计偏差值,动态修正闹钟比较阈值。
原理图中DS1302的X1引脚连接32.768kHz晶振,负载电容严格匹配为12.5pF,实测起振时间小于500ms,满足快速唤醒需求。
2.3 人机交互:SSD1306 OLED显示模块
采用0.96英寸单色OLED屏(128×64分辨率),驱动芯片为SSD1306,通过SPI2接口连接。选择OLED而非LCD的核心原因在于其自发光特性——无需背光驱动电路,静态显示功耗仅为0.05mW,且在强环境光下对比度不衰减。显示内容按优先级分层刷新:
- 最高优先级:服药倒计时(精确到秒)、当前时间(HH:MM)、剩余药量(数字+条形图);
- 次优先级:已设定闹钟组数(如“3/3”)、WiFi连接状态图标(√/×)、电池电量指示(四格图标);
- 低优先级:系统版本号、固件编译时间戳(仅开机时显示3秒)。
OLED初始化流程包含12项寄存器配置,关键步骤包括:设置显示起始行(0x40)、设置段重映射(0xA0)、设置COM扫描方向(0xC0)、启用全局显示开启(0xAF)。所有配置指令均通过SPI2发送,避免使用GPIO模拟时序带来的时序抖动风险。
2.4 状态感知:红外对管检测模块
药品取用检测采用TCRT5000红外反射式传感器,但本项目创新性地将其改造为透射式检测通道。具体实现方式为:在药盒隔板两侧分别安装红外发射管(IR LED)与接收管(Phototransistor),发射端由AIR32的PA8引脚经限流电阻(220Ω)驱动,接收端集电极接入PB0引脚,发射端与接收端轴线严格对准,间距为15mm(适配标准药片厚度)。
当用户打开药盒取药时,手指插入发射-接收通道,导致接收管光电流下降超过阈值(实测由1.2mA降至0.15mA)。PB0配置为模拟输入模式,ADC采样值经16点滑动平均滤波后送入比较器,触发中断服务程序执行“服药确认”逻辑。该方案较压力传感器方案成本降低60%,较称重方案免去定期校准,且对药片材质、湿度无敏感性。
2.5 无线通信:ESP-01S WiFi模块
ESP-01S作为独立WiFi子系统,通过USART2(波特率115200)与AIR32通信。模块固件运行AT指令集,主控不解析TCP/IP协议栈,仅发送以下四类AT指令:
| 指令序列 | 功能说明 | 超时处理 |
|---|---|---|
AT+CWMODE=1 | 设置Station模式 | 200ms |
AT+CWJAP="SSID","PWD" | 连接路由器 | 5000ms |
AT+MQTTUSERCFG=0,1,"client_id","","",0,0,"" | 配置MQTT客户端 | 1000ms |
AT+MQTTPUB=0,"/medbox/status","{json_data}",1,0 | 发布JSON数据包 | 3000ms |
所有AT指令均添加CRC16校验,响应解析采用状态机模式,避免字符串匹配导致的内存泄漏。实测在2.4GHz信道拥挤环境下,连接成功率>99.2%,单次数据上传平均耗时840ms。
2.6 电源管理:Type-C接口与LDO稳压
系统采用USB Type-C接口供电,输入电压范围4.75V~5.25V。电源路径设计包含两级稳压:
- 第一级:AMS1117-3.3 LDO,将5V转为3.3V,最大输出电流1A,压差仅1.1V,满载温升<15℃;
- 第二级:HT7333-1 LDO,专为OLED与红外传感器供电,输出纹波<10mV,避免显示闪烁。
电池供电方案未被采纳,因锂聚合物电池需配套保护板与充放电管理IC,会显著增加BOM成本与PCB面积,不符合本项目低成本定位。
3. 软件系统架构
软件采用前后台系统(Foreground-Background System)架构,无RTOS介入。主循环(Background)负责状态轮询与数据处理,中断服务程序(Foreground)响应实时事件。该架构内存占用<8KB,中断响应延迟稳定在3.2μs以内,满足毫秒级时间精度要求。
3.1 初始化流程
系统上电后执行严格时序的初始化序列:
void System_Init(void) { RCC_Configuration(); // 开启GPIOA/B/C、AFIO、SPI1/2、USART1/2时钟 GPIO_Configuration(); // 配置PA0(PB0)为ADC输入,PA8为推挽输出,PB10/PB11为SPI2复用推挽 NVIC_Configuration(); // 使能ADC、EXTI0、USART2中断 SPI1_Init(); // 初始化SPI1(DS1302) SPI2_Init(); // 初始化SPI2(OLED) USART1_Init(); // 初始化USART1(调试) USART2_Init(); // 初始化USART2(ESP-01S) ADC1_Init(); // 初始化ADC1(红外检测) DS1302_Init(); // 写入默认时间(2023-01-01 00:00:00) OLED_Init(); // OLED硬件复位+寄存器配置 ESP01S_AT_Test(); // 发送AT指令测试模块在线状态 }特别注意:DS1302初始化必须在OLED初始化之后执行。因OLED复位期间会拉低VCC,导致DS1302供电不稳,若先初始化DS1302,可能造成寄存器写入失败。
3.2 时间管理与闹钟调度
时间管理采用双时间源融合策略。主时间源为DS1302,辅时间源为ESP-01S从阿里云IoT平台获取的UTC时间戳。主控每24小时发起一次时间同步请求,流程如下:
- AIR32向ESP-01S发送
AT+MQTTSUB=0,"/medbox/time",订阅时间主题; - 云端推送JSON格式时间数据:
{"timestamp":"2023-10-15T08:30:45Z"}; - AIR32调用
cJSON_Time_Parse()函数解析字符串,提取年、月、日、时、分、秒字段; - 计算DS1302当前值与UTC时间的偏差Δt(单位:秒);
- 更新闹钟比较寄存器:
alarm_second = (ds1302_second + Δt) % 60。
闹钟调度采用轮询方式,主循环中每秒读取DS1302一次,将返回的BCD码转换为十进制后,与三组预设闹钟(alarm1~alarm3)进行比对。匹配成功则置位alarm_flag,进入提醒状态机。
3.3 提醒状态机
提醒逻辑封装为有限状态机,共定义5个状态:
| 状态 | 进入条件 | 动作 | 退出条件 |
|---|---|---|---|
| IDLE | 系统启动或提醒结束 | 关闭蜂鸣器、熄灭LED、OLED显示正常界面 | 检测到闹钟匹配 |
| TRIGGER | 闹钟匹配 | 启动TIM3(1kHz PWM)驱动蜂鸣器,点亮红色LED | 持续5秒 |
| WAIT_CONFIRM | TRIGGER超时 | 停止蜂鸣器,LED常亮,OLED显示“请取药” | 红外检测触发 |
| CONFIRMED | 红外检测中断 | LED切换为绿色,OLED显示“已服药”,更新剩余药量 | 无 |
| TIMEOUT | WAIT_CONFIRM持续120秒 | 启动TIM4(2Hz PWM)驱动蜂鸣器,OLED显示“未服药”,通过ESP-01S发送告警消息 | 手动按键清除 |
该状态机确保提醒过程具有明确的物理反馈闭环,避免“响了就完事”的无效提醒。
3.4 数据上传协议
上传至云端的数据采用精简JSON格式,总长度严格控制在128字节以内,示例如下:
{ "dev_id":"MEDBOX_2023001", "ts":1697395845, "status":1, "dose":3, "battery":3.82, "rssi":-67 }其中status字段定义:0=待机,1=已服药,2=未服药,3=药量不足。所有字段均为ASCII可打印字符,避免Base64编码增加传输开销。ESP-01S在收到AT+MQTTPUB指令后,自动添加MQTT协议头并完成TCP重传,AIR32无需处理网络层异常。
4. 微信小程序设计
小程序作为远程监护端,不承担实时控制功能,仅提供数据可视化与计划配置界面。其技术实现遵循微信官方《小程序开发规范》第3.2版,关键设计点如下:
4.1 服药计划配置
采用picker-view组件实现时间选择,避免原生picker在iOS端出现的时区偏移问题。核心代码如下:
<!-- scrollSelecter.vue --> <template> <picker-view indicator-style="height: 50px;" @change="bindChange"> <picker-view-column> <view v-for="(item, index) in hours" :key="index">{{ item }}</view> </picker-view-column> <picker-view-column> <view v-for="(item, index) in minutes" :key="index">{{ item }}</view> </picker-view-column> </picker-view> </template> <script> export default { data() { return { hours: Array.from({length:24}, (_,i) => i.toString().padStart(2,'0')), minutes: Array.from({length:60}, (_,i) => i.toString().padStart(2,'0')) } }, methods: { bindChange(e) { const h = this.hours[e.detail.value[0]]; const m = this.minutes[e.detail.value[1]]; this.$emit('timeSelected', `${h}:${m}`); } } } </script>用户选择时间后,小程序将{time:"08:30", dose:2, med_name:"阿司匹林"}对象通过HTTPS POST提交至云函数,云函数校验合法性后,调用阿里云IoT SDK的PubMessage接口下发至设备Topic。
4.2 数据可视化
设备状态页采用WebSocket长连接,每30秒心跳保活。数据显示层级为:
- 顶层卡片:当前时间(同步设备RTC)、剩余药量(环形进度条)、最后服药时间(相对时间:“2小时前”);
- 中间列表:近7天服药记录,每条记录包含时间戳、计划剂量、实际剂量、状态图标(✅/⚠️/❌);
- 底层图表:ECharts折线图展示日服药完成率(完成次数/计划次数),X轴为日期,Y轴为百分比。
所有数据渲染前进行本地缓存,网络中断时显示最近一次成功同步的数据,并标记“离线”状态。
5. BOM清单与关键器件选型依据
| 序号 | 器件名称 | 型号 | 数量 | 选型依据 | 单价(元) |
|---|---|---|---|---|---|
| 1 | 主控MCU | AIR32F103CBT6 | 1 | 外设资源匹配、双Bank Flash支持OTA | 4.2 |
| 2 | RTC芯片 | DS1302Z | 1 | 内置晶振、支持后备电池、工业级温度范围 | 1.8 |
| 3 | OLED屏 | SSD1306-12864 | 1 | 自发光、低功耗、SPI接口兼容性好 | 9.5 |
| 4 | WiFi模块 | ESP-01S | 1 | AT指令成熟、体积小(12.2×22mm)、成本低 | 6.3 |
| 5 | 红外传感器 | TCRT5000 | 3 | 反射式结构、数字输出、5V兼容 | 0.8 |
| 6 | LDO稳压器 | AMS1117-3.3 | 1 | 输入电压范围宽、热稳定性好 | 0.35 |
| 7 | 按键 | Tactile Switch | 4 | 行程1.0mm、寿命>10万次、带LED背光 | 0.22 |
| 8 | 蜂鸣器 | PKM13EPYH4000-A | 1 | 3.3V驱动、85dB@10cm、方形贴片封装 | 0.68 |
注:BOM总价控制在35元以内(不含外壳与PCB),符合消费级电子产品的成本约束。所有器件均选用国产替代型号,避免进口器件交期风险。
6. 实际部署经验与调试要点
在32户家庭的实际部署中,发现以下高频问题及对应解决方案:
问题1:红外误触发
现象:药盒未打开时OLED显示“请取药”。
根因:TCRT5000接收管受环境光干扰,尤其在阳光直射下光电流漂移。
解决:在接收管表面加装黑色遮光筒(直径3mm,长8mm),并修改ADC采样阈值为动态基准——每次主循环开始时读取环境光基准值,触发阈值设为基准值×0.35。问题2:WiFi连接失败
现象:ESP-01S反复发送AT+CWJAP但无响应。
根因:AIR32的USART2 TX引脚驱动能力不足,导致ESP-01S RX端信号边沿过缓。
解决:在USART2_TX与ESP-01S_RX之间串接100Ω电阻,并在ESP-01S_RX端对地并联0.1μF电容,实测上升时间由1.8μs改善至320ns。问题3:OLED显示残影
现象:长时间运行后屏幕出现固定位置亮斑。
根因:SSD1306的DC-DC升压电路电容老化,导致VCC波动。
解决:将原厂0.47μF升压电容更换为村田GRM188R61E475KE15D(4.7μF,X5R),残影故障率由12%降至0.3%。
这些经验表明,嵌入式医疗辅助设备的可靠性不仅取决于理论设计,更依赖于对真实使用环境的深度适配。每一个看似微小的硬件细节,都可能成为影响用户体验的关键节点。
