这个基于STM32的远程定位监测系统,是我去年为一个养老院项目开发的健康监护解决方案。核心目标是通过物联网技术实现对老人位置和健康状态的实时监测。系统采用STM32F103ZET6作为主控芯片,配合多种传感器模块,构建了一个完整的远程监测平台。
在实际部署中,这个系统成功将定位精度控制在5米范围内,体温监测误差±0.5℃,心率检测响应时间小于2秒。特别值得一提的是,我们通过SIM800C模块的GPRS功能,在2G网络环境下实现了平均每10秒一次的数据上报频率,整套系统在2000mAh锂电池供电下可连续工作72小时。
选择STM32F103ZET6主要基于三点考虑:
提示:对于成本敏感的项目,可降级使用STM32F103C8T6,但需注意Flash容量(64KB)可能需要对MQTT协议栈进行裁剪。
采用DS18B20数字温度传感器,其特性包括:
实际布线时需要注意:
使用PulseSensor光电心率传感器,其工作特点:
我们采用外部中断方式检测心率脉冲:
c复制// 外部中断配置示例
void EXTI0_IRQHandler(void)
{
static u32 lastTime = 0;
if(EXTI_GetITStatus(EXTI_Line0) != RESET){
u32 current = SysTick->VAL;
if(lastTime > 0){
heartRate = 60000/(current - lastTime); // 计算心率(bpm)
}
lastTime = current;
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
NEO-7N GPS模块的关键参数:
实际测试中发现:
SIM800C模块的配置要点:
典型AT指令流程:
code复制AT+CSQ // 检查信号强度
AT+CGATT=1 // 附着GPRS网络
AT+CIPSTART="TCP","api.heclouds.com",80 // 连接OneNET
AT+CIPSEND // 发送数据
完整的启动序列如下:
c复制void Hardware_Init(void)
{
delay_init(); // 延时函数初始化
LED_Init(); // LED指示灯
USART1_Init(115200); // 调试串口
USART2_Init(9600); // GPS串口
USART3_Init(115200); // SIM800C串口
DS18B20_Init(); // 温度传感器
EXTIX_Init(); // 外部中断(心率)
TIM3_Int_Init(1000,7199); // 1s定时器
sim800c_init(); // 通信模块
}
采用状态机管理不同传感器的采集周期:
c复制typedef struct {
float temperature;
u16 heartRate;
float latitude;
float longitude;
u8 batteryLevel;
} SensorData;
为确保数据可靠性,我们实现了:
采用Paho MQTT嵌入式客户端,关键适配点:
c复制void mqtt_publish(const char* topic, const char* msg)
{
MQTTMessage message;
message.qos = QOS1;
message.retained = 0;
message.payload = (void*)msg;
message.payloadlen = strlen(msg);
MQTTPublish(&client, topic, &message);
}
数据点上传格式示例:
json复制{
"datastreams":[
{
"id":"temp",
"datapoints":[{"value":36.5}]
},
{
"id":"heart_rate",
"datapoints":[{"value":75}]
}
]
}
使用STM32的停止模式(Stop Mode):
c复制void Enter_StopMode(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);
SystemInit(); // 唤醒后需重新初始化时钟
}
定时唤醒方案:
解决方案:
优化措施:
排查步骤:
c复制void GPS_Analysis(char *line)
{
if(strstr(line, "$GPRMC")){
char *p = strtok(line, ",");
int field = 0;
while(p != NULL){
switch(field){
case 1: // UTC时间
utc_time = atof(p);
break;
case 2: // 定位状态
valid = (p[0] == 'A');
break;
case 3: // 纬度
latitude = atof(p);
break;
// 其他字段处理...
}
p = strtok(NULL, ",");
field++;
}
}
}
c复制void Upload_SensorData(void)
{
cJSON *root = cJSON_CreateObject();
cJSON *datastreams = cJSON_CreateArray();
cJSON_AddItemToObject(root, "datastreams", datastreams);
// 添加温度数据
cJSON *temp = cJSON_CreateObject();
cJSON_AddStringToObject(temp, "id", "temperature");
cJSON *temp_dp = cJSON_CreateArray();
cJSON_AddItemToObject(temp, "datapoints", temp_dp);
cJSON *temp_val = cJSON_CreateObject();
cJSON_AddNumberToObject(temp_val, "value", sensor_data.temperature);
cJSON_AddItemToArray(temp_dp, temp_val);
cJSON_AddItemToArray(datastreams, temp);
// 其他数据项...
char *json_str = cJSON_PrintUnformatted(root);
mqtt_publish("$dp", json_str);
cJSON_Delete(root);
free(json_str);
}
原始方案每次上传独立JSON包,优化后:
初始版本直接使用malloc,存在内存碎片问题,改进措施:
c复制#define BUF_POOL_SIZE 10
#define BUF_SIZE 256
typedef struct {
u8 buffer[BUF_SIZE];
u16 length;
u8 in_use;
} BufferBlock;
BufferBlock buf_pool[BUF_POOL_SIZE];
BufferBlock* Alloc_Buffer(void)
{
for(int i=0; i<BUF_POOL_SIZE; i++){
if(!buf_pool[i].in_use){
buf_pool[i].in_use = 1;
return &buf_pool[i];
}
}
return NULL;
}
这个系统从原型到量产经历了三个主要版本迭代:
V1.0基础版:
V1.5优化版:
V2.0增强版:
在开发过程中,最耗时的部分是通信稳定性调试。SIM800C在不同网络环境下的表现差异很大,我们最终通过以下措施解决问题: