去年参与社区智慧养老项目时,我们遇到一个棘手问题:如何实时监测独居老人的生命体征并在异常时快速响应?传统方案要么成本高昂,要么依赖WiFi网络。最终我们选择STM32F103ZET6作为主控,配合SIM800C模块和OneNET平台,搭建了一套不足500元的远程监测系统。这个方案最让我惊喜的是,在郊区信号较弱区域仍能保持稳定数据传输,这正是今天要分享的这套系统的核心价值。
这套系统本质上是个微型物联网终端,通过STM32采集心率、血氧、体温等体征数据,经SIM800C的GPRS功能上传至OneNET物联网平台。医护人员通过网页或APP即可查看实时数据,当检测到异常时,系统会通过短信和平台告警双重提醒。定位功能则利用基站定位技术,精度约200-500米,虽不及GPS但功耗更低,特别适合室内场景使用。
STM32F103ZET6属于Cortex-M3内核的增强型单片机,选择它主要基于三点考量:
实际开发中发现其DMA控制器特别实用,在采集MAX30102血氧传感器数据时,通过DMA传输可降低30%的CPU占用率。硬件连接时需注意:
SIM800C的GPRS功能是本项目联网核心,这些经验值得注意:
我们优化后的通信流程如下:
c复制// SIM800C初始化示例
void SIM800_Init(void) {
Send_AT_Command("AT", 500); // 测试模块响应
Send_AT_Command("AT+CPIN?", 1000); // 检查SIM卡
Send_AT_Command("AT+CSQ", 500); // 信号质量查询
Send_AT_Command("AT+CGATT=1", 30000); // 附着GPRS网络
Send_AT_Command("AT+CIPSHUT", 10000); // 关闭移动场景
}
生命体征监测选用这些传感器:
硬件设计时踩过的坑:
采用状态机模式管理多传感器采集:
c复制typedef enum {
SENSOR_IDLE,
SENSOR_HR_READING, // 心率采集
SENSOR_SPO2_READING, // 血氧采集
SENSOR_TEMP_READING // 体温采集
} SensorState;
// 通过定时器触发状态转换
void TIM3_IRQHandler(void) {
static uint8_t counter = 0;
if(TIM_GetITStatus(TIM3, TIM_IT_Update)) {
counter = (counter + 1) % 3;
switch(counter) {
case 0: currentState = SENSOR_HR_READING; break;
case 1: currentState = SENSOR_SPO2_READING; break;
case 2: currentState = SENSOR_TEMP_READING; break;
}
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
}
}
OneNET平台支持HTTP和MQTT两种协议,我们选择更轻量级的MQTT:
json复制{
"datastreams": [
{
"id": "heart_rate",
"datapoints": [{"value": 72}]
},
{
"id": "temperature",
"datapoints": [{"value": 36.5}]
}
]
}
关键实现代码:
c复制void MQTT_PublishData(void) {
char payload[256];
sprintf(payload, "{\"datastreams\":[{\"id\":\"heart_rate\",\"datapoints\":[{\"value\":%d}]}]}", heartRate);
Send_AT_Command("AT+CIPSTART=\"TCP\",\"183.230.40.39\",6002", 5000);
Send_AT_Command("AT+CIPSEND", 500);
SIM800_Send(payload);
Send_AT_Command("AT+CIPCLOSE", 500);
}
虽然STM32F103不是低功耗系列,但通过以下措施使系统平均电流降至25mA:
现象:模块响应AT指令但无法注册网络
排查步骤:
常见原因及解决方案:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 连接超时 | 网络信号弱 | 检查AT+CSQ(应>10) |
| 数据被拒 | API Key错误 | 重新生成OneNET密钥 |
| 数据丢失 | 缓冲区不足 | 增加AT+CIPSEND前的延时 |
MAX30102数据漂移的解决方法:
c复制#define FILTER_DEPTH 5
int32_t filterBuffer[FILTER_DEPTH];
int32_t MovingAverage(int32_t newVal) {
static uint8_t index = 0;
filterBuffer[index] = newVal;
index = (index + 1) % FILTER_DEPTH;
int32_t sum = 0;
for(uint8_t i=0; i<FILTER_DEPTH; i++) {
sum += filterBuffer[i];
}
return sum / FILTER_DEPTH;
}
虽然基站定位成本低,但可以通过这些方法改进:
实测数据对比:
| 定位方式 | 功耗(mA) | 精度(m) | 成本(元) |
|---|---|---|---|
| 纯基站 | 18 | 200-500 | 0 |
| 基站+WiFi | 45 | 50-100 | 35 |
| 基站+蓝牙 | 22 | 5-10 | 20 |
在STM32上实现简单异常检测算法:
c复制void CheckEmergency(void) {
static uint8_t alertCount = 0;
if(heartRate < 50 || heartRate > 120) {
alertCount++;
if(alertCount > 3) {
Send_SMS("13800138000", "心率异常警报!");
alertCount = 0;
}
} else {
alertCount = 0;
}
}
推荐使用TP4056充电管理芯片+18650电池方案:
这套系统经过半年实地测试,最远在距基站8公里的山区仍能保持数据上传。关键是要做好天线匹配和电源滤波,这也是为什么我特别强调SIM800C的电源设计。对于想复现项目的朋友,建议先用开发板验证各模块功能,再设计PCB,这样可以避免很多硬件兼容性问题。