1. 项目概述:当传统药箱遇上物联网
作为一名嵌入式开发工程师,我最近完成了一个基于STM32的智能药箱系统项目。这个看似简单的药箱,通过加入温湿度传感器、重量检测、WiFi模块和提醒功能,彻底改变了传统药箱的使用体验。想象一下,当老人忘记服药时,药箱不仅能发出声光提醒,还能通过手机APP通知家人;当药品存储环境不达标时,系统会自动报警并记录异常数据。这正是物联网技术在医疗健康领域的典型应用场景。
这个项目的核心在于STM32F103C8T6主控芯片与多种传感器的协同工作。通过精心设计的硬件电路和嵌入式程序,我们实现了药品存量监测、服药提醒、环境监控等实用功能。整个系统成本控制在200元以内,却解决了家庭用药管理中的多个痛点问题。下面我将从硬件设计到软件实现,详细拆解这个项目的技术细节。
2. 硬件系统设计解析
2.1 主控芯片选型与电路设计
选择STM32F103C8T6作为主控主要基于三点考虑:首先,它的72MHz主频足够处理多传感器数据;其次,内置的多个定时器和ADC通道完美适配本项目需求;最后,其丰富的外设接口(包括USART、SPI、I2C)为扩展功能提供了便利。实际电路设计中,我特别注意了以下几点:
- 电源部分采用AMS1117-3.3V稳压芯片,配合100μF电解电容和0.1μF陶瓷电容滤波
- 所有IO口串联220Ω电阻作为保护,防止传感器短路损坏MCU
- 保留SWD调试接口,方便程序下载和调试
- 为WiFi模块单独设计3.3V电源电路,避免大电流导致系统复位
重要提示:STM32的NRST复位引脚必须接10kΩ上拉电阻,我在初期测试时曾因忽略这点导致系统频繁异常复位。
2.2 传感器模块选型与集成
传感器是系统的"感官",本项目使用了以下关键传感器:
-
HX711称重模块:用于检测药品余量
- 量程5kg,精度±1g
- 通过24位ADC实现高精度测量
- 需特别注意安装时的机械结构设计,避免侧向力影响测量
-
DHT22温湿度传感器:
- 温度测量范围-40~80℃,精度±0.5℃
- 湿度测量范围0~100%RH,精度±2%RH
- 单总线协议,需严格遵循时序要求
-
DS1302实时时钟:
- 提供精确的定时基准
- 内置电池可保证断电后继续计时
- 通过三线接口与MCU通信
这些传感器通过不同的接口与STM32连接:
c复制// 传感器接口定义
#define HX711_DT_PIN GPIO_PIN_0
#define HX711_SCK_PIN GPIO_PIN_1
#define DHT22_PIN GPIO_PIN_2
#define DS1302_RST GPIO_PIN_3
#define DS1302_IO GPIO_PIN_4
#define DS1302_SCLK GPIO_PIN_5
2.3 人机交互设计
良好的人机交互是产品易用性的关键。本系统包含以下交互组件:
-
OLED显示屏:0.96寸SSD1306驱动,I2C接口
- 显示当前时间、药品信息、温湿度等
- 采用自定义UI布局,重点信息突出显示
-
蜂鸣器报警电路:
- 采用有源蜂鸣器,驱动电流约30mA
- 通过NPN三极管放大STM32的IO驱动能力
-
按键输入:
- 三个轻触按键实现功能选择/确认/取消
- 硬件消抖电路(100nF电容并联10kΩ电阻)
-
ESP8266 WiFi模块:
- 通过AT指令与STM32通信
- 实现远程报警和数据上传
- 需特别注意电源稳定性,实测工作电流峰值可达200mA
3. 软件系统架构与实现
3.1 系统任务划分与调度
基于FreeRTOS实时操作系统,我将系统功能划分为多个独立任务:
-
传感器采集任务(优先级3)
- 周期性读取各类传感器数据
- 采用信号量保护共享数据
-
用户界面任务(优先级2)
- 刷新OLED显示内容
- 处理按键输入事件
-
报警检测任务(优先级4)
- 检查服药时间、药品余量、环境参数
- 触发本地和远程报警
-
网络通信任务(优先级1)
- 维护WiFi连接
- 上传数据到云平台
任务间通信通过消息队列实现,关键代码如下:
c复制// 创建传感器数据消息队列
xQueueSensorData = xQueueCreate(10, sizeof(SensorData_t));
// 报警任务示例
void vTaskAlarm(void *pvParameters) {
AlarmCheck_t alarmCheck;
while(1) {
xQueueReceive(xQueueAlarmCheck, &alarmCheck, portMAX_DELAY);
if(alarmCheck.medTime || alarmCheck.lowWeight ||
alarmCheck.badEnvironment) {
triggerAlarm(alarmCheck);
}
vTaskDelay(pdMS_TO_TICKS(100));
}
}
3.2 关键算法实现
3.2.1 药品重量动态检测算法
由于药箱在使用过程中会产生震动,直接读取HX711原始数据会有较大波动。我采用移动平均滤波结合动态阈值的方法:
- 采集100个样本,去除最大最小值后取平均
- 当连续5次检测到重量变化超过阈值(如5g),才确认状态改变
- 根据药品密度换算剩余药片数量
c复制#define SAMPLE_NUM 100
#define CHANGE_THRESHOLD 5
float getStableWeight() {
float samples[SAMPLE_NUM];
for(int i=0; i<SAMPLE_NUM; i++) {
samples[i] = HX711_Read();
delay(10);
}
// 排序并去除异常值
bubbleSort(samples, SAMPLE_NUM);
float sum = 0;
for(int i=10; i<SAMPLE_NUM-10; i++) {
sum += samples[i];
}
return sum / (SAMPLE_NUM-20);
}
3.2.2 服药时间管理算法
考虑到不同药品的服用频率不同(如每日1次、每日3次、隔日1次等),我设计了一个灵活的时间表系统:
-
每个药品条目包含:
- 药品ID
- 每日服用次数
- 每次服用时间点数组
- 每次服用剂量
-
系统每分钟检查一次RTC时间
-
当到达预设时间且药品未服用时触发提醒
-
提供15分钟宽限期,超时未服用则升级报警级别
c复制typedef struct {
uint8_t medID;
uint8_t timesPerDay;
TimeStruct takeTime[MAX_TAKE_TIMES];
uint8_t dose[MAX_TAKE_TIMES];
bool taken[MAX_TAKE_TIMES];
} MedicationSchedule;
3.3 低功耗优化策略
虽然本项目对功耗要求不高,但我仍做了以下优化:
-
传感器采样间隔动态调整:
- 活跃时段(如8:00-22:00):每分钟采集一次
- 非活跃时段:每10分钟采集一次
-
OLED屏幕超时关闭:
- 无操作30秒后降低亮度
- 无操作2分钟后关闭显示
- 按键唤醒恢复显示
-
WiFi连接策略:
- 平时保持断开状态
- 需要上传数据时建立连接
- 传输完成后立即断开
4. 云平台对接与数据可视化
4.1 网络通信协议设计
选用MQTT协议与云平台通信,主要考虑其轻量级和发布/订阅模式的优势。通信帧设计如下:
-
主题设计:
- /medbox/[deviceID]/status → 上传状态数据
- /medbox/[deviceID]/alert → 上传报警信息
- /medbox/[deviceID]/command → 接收控制命令
-
数据格式(JSON):
json复制{
"device": "MB_001",
"timestamp": "2023-07-20T14:30:00",
"weight": 125.6,
"temp": 25.3,
"humi": 45.2,
"batt": 85
}
4.2 微信小程序开发要点
配套开发的微信小程序主要实现以下功能:
- 实时查看药箱状态
- 接收服药提醒
- 查看历史服药记录
- 远程设置服药计划
关键实现技巧:
- 使用WebSocket保持长连接,及时接收报警
- 采用ECharts实现服药历史可视化
- 利用微信云开发降低后端复杂度
5. 常见问题与解决方案
5.1 硬件调试问题
问题1:HX711读数不稳定
- 可能原因:电源噪声、机械振动、接线过长
- 解决方案:
- 在HX711电源引脚并联100μF电解电容
- 增加橡胶减震垫
- 缩短传感器与MCU距离,或使用屏蔽线
问题2:ESP8266频繁断线
- 可能原因:电源不足、天线干扰、AT指令超时
- 解决方案:
- 确保电源能提供至少300mA电流
- 将模块天线部分伸出外壳
- 增加AT指令重试机制
5.2 软件调试技巧
实时监控变量值
c复制// 在调试版本中添加以下代码
void monitorVariables() {
printf("Weight: %.1fg, Temp: %.1fC, Humi: %.1f%%\r\n",
currentWeight, currentTemp, currentHumi);
// 通过串口工具可实时观察变量变化
}
内存泄漏检测
c复制// 在FreeRTOSConfig.h中启用堆检查
#define configUSE_TRACE_FACILITY 1
#define configUSE_STATS_FORMATTING_FUNCTIONS 1
// 定期打印任务状态
void checkMemory() {
char buffer[512];
vTaskList(buffer);
printf("Task List:\r\n%s\r\n", buffer);
}
6. 项目优化方向
在实际使用中,我发现还可以从以下几个方向进一步提升系统:
- 增加语音交互:集成SYN6288语音模块,方便视力不佳的用户
- 药品识别功能:加入摄像头和简单图像识别,自动记录药品信息
- 电池供电方案:设计低功耗模式,支持移动使用场景
- 多用户支持:区分不同家庭成员的服药计划
这个项目最让我有成就感的是看到它真正帮助到了家中的老人。技术最终要服务于人,而作为工程师,我们需要用专业能力解决生活中的实际问题。在开发过程中,保持与最终用户的沟通非常重要,他们的反馈往往能指出我们技术思维中的盲点。