基于STM32的老年人居家安全关爱系统
体温+心率+跌倒三路监测、一键求救、子女手机APP远程报警,附"摔倒检测抄成计步"翻车实录
独居老人最怕两件事:突发疾病没人知道、摔倒后起不来。市面上的商用养老监护设备动辄上千,其实用一块 STM32 + 三四个传感器就能做出一套能跑的原型:体温、心率、跌倒三路监测,超标或摔倒立刻蜂鸣 + 推送子女手机 APP,老人还能一键求救。本文基于一篇毕设论文改写,完整给出方案、选型、接线和代码——顺便把原文里"摔倒检测算法抄成计步器"的复制粘贴事故拆给你看,这种坑答辩老师一眼就能揪出来。
一、系统功能拆解
| 功能域 | 模块 | 说明 |
|---|---|---|
| 生理监测 | DS18B20 体温 | 体表温度采集,超阈值报警(非医疗级,见文末声明) |
| 生理监测 | PulseSensor 心率 | PPG 光电法,BPM 超阈值报警 |
| 行为监测 | ADXL345 三轴加速度 | 跌倒检测:合加速度突变 + 跌倒后静止姿态 |
| 人机交互 | OLED12864 | 实时显示体温/心率/时间/报警状态 |
| 人机交互 | 一键求救按键 | 老人按下:本地蜂鸣 + APP 推送 |
| 远程通信 | ESP8266 | 数据上服务器,监护人 APP 实时查看 + 远程设阈值 |
| 报警执行 | 蜂鸣器 | 本地报警提醒 |
主逻辑:初始化 → 主循环采集体温/心率/加速度 → OLED 刷新 → 数据上云 → 任一参数超阈值或判定跌倒 → 蜂鸣器响 + APP 告警 → 响应求救按键和 APP 下发的阈值指令。
二、器件选型对比
2.1 主控:STM32F103C8T6 vs ESP32-WROOM-32
C8T6(72MHz/64KB Flash)外设够用、开发生态成熟;ESP32 双核 240MHz 自带 Wi-Fi 蓝牙。本系统需要 ADC(心率)+ I2C×2(OLED、ADXL345)+ 单总线(DS18B20)+ 串口(ESP8266),C8T6 完全够,且毕设资料多,选 C8T6 合理。
2.2 体温:DS18B20 vs LM35
| 对比项 | LM35 | DS18B20 |
|---|---|---|
| 精度 | ±0.25°C(更高) | ±0.5°C |
| 量程 | -55~+150°C(更宽) | -55~+125°C |
| 输出 | 模拟电压 10mV/°C | 单总线数字量 |
| 多点扩展 | 一路一个 ADC 通道 | 一条总线挂多个 |
原文结论是"DS18B20 提供了更高的测量精度和更广泛的温度范围,因此选方案2"——理由完全是反的:精度和量程都是 LM35 赢,DS18B20 真正的优势是数字输出(不用 ADC、抗干扰、可多点挂总线)。结论没错,理由错得离谱,这种"结论对了过程错了"最容易被答辩追问。正确写法:选 DS18B20 是因为数字输出省掉 ADC 通道且免标定。
2.3 心率:PulseSensor vs MAX30100
| 对比项 | PulseSensor | MAX30100 |
|---|---|---|
| 原理 | 单绿光 LED PPG | 红+红外双波长,心率+血氧 |
| 输出 | 模拟量(接 ADC) | I2C 数字量 |
| 价格 | ≈15 元 | ≈25 元 |
选 PulseSensor 做原型没问题。但原文 3.3 节把它写成"红色和红外 LED 的组合发出光束"——PulseSensor 板上只有一颗绿色 LED(约 565nm)和一个光电晶体管,红+红外双波长是 MAX30100/30102 的配置。这是传感器资料抄串了,连着几篇毕设论文都在犯同一个错,可见是同一份错误资料在流传。
2.4 跌倒检测:ADXL345 vs MPU6050
| 对比项 | ADXL345 | MPU6050 |
|---|---|---|
| 传感器 | 三轴加速度 | 三轴加速度+三轴陀螺仪(带 DMP) |
| 分辨率 | 16 位,量程 ±2~±16g | 16 位,±2~±16g |
| 功耗 | 更低 | 略高 |
| 价格 | ≈8 元 | ≈10 元 |
跌倒检测的核心是捕捉"自由落体→冲击→静止姿态"的加速度突变序列,纯加速度计就能做(ADXL345 的 4g 量程 + 100Hz 采样足够);MPU6050 的陀螺仪加分但不必须。选 ADXL345 合理——但注意,原文夸它的理由是"静态角度测量中的高精度",这就跑偏了:跌倒恰恰是动态事件,靠静态角度判断"人歪了"会把手臂抬起、弯腰捡东西全误报成摔倒。选型理由和检测原理完全脱节。
2.5 显示与通信
OLED12864(0.96寸 I2C)显示数据 + 时间足够;通信选 ESP8266-01S(Wi-Fi 直接进家庭路由器),备选 nRF905(433MHz 远距离但要专门网关,家用场景不合适)。
2.6 BOM 成本清单
| 器件 | 参考价 |
|---|---|
| STM32F103C8T6 最小系统板 | ≈15 元 |
| DS18B20(防水探头款) | ≈6 元 |
| PulseSensor | ≈15 元 |
| ADXL345 | ≈8 元 |
| OLED12864(I2C) | ≈10 元 |
| ESP8266-01S | ≈10 元 |
| 蜂鸣器、按键、洞洞板 | ≈8 元 |
| 合计 | ≈72 元 |
三、硬件接线
| 模块 | 引脚 | 说明 |
|---|---|---|
| DS18B20 DQ | PA1 | 4.7kΩ 上拉到 3.3V |
| PulseSensor SIG | PA2→改用 PA4(ADC1_IN4) | ⚠️ 见接线坑 1 |
| ADXL345 SDA/SCL | PB6/PB7(I2C1) | 与 OLED 共总线,地址不冲突(0x53 / 0x3C) |
| OLED SDA/SCL | PB6/PB7(I2C1) | — |
| ESP8266 TX/RX | PA2/PA3(USART2) | 115200bps,复位脚 PA8 |
| 蜂鸣器 | PB0 | 高电平响 |
| 求救按键 | PB12 | 内部上拉,按下接地 |
| 报警 LED | PB1 | — |
接线坑三条:
- 串口和 ADC 别打架:USART2 默认引脚是 PA2/PA3,若心率传感器 ADC 也随手分到 PA4 没问题,但有些例程把 ADC 配到 PA2——两处一撞,要么串口乱码要么心率死值。分配引脚前先查 F103 的引脚复用表。
- I2C 总线挂两个器件要确认地址:ADXL345 是 0x53(ALT_ADDRESS 接地时),OLED SSD1306 是 0x3C,不冲突;但 ADXL345 的 CS 脚必须拉高选 I2C 模式,悬空会通信失败。
- ESP8266 独立 3.3V 供电:01S 模块 Wi-Fi 发射瞬间电流 300mA+,从 C8T6 板载稳压器取电容易电压跌落导致反复重启,配一个 AMS1117 独立 3.3V 或大电容储能。
另外两个"纸面功能"要补齐:原文方案框架里写了"RTC 模块提供时间同步"和"一键报警求救按键",但硬件章节根本没设计这两块电路——RTC 没影(本文用 ESP8266 联网 NTP 对时替代,省一颗 DS1302),求救按键本文直接补上(PB12)。做系统时列在框图里的东西,电路、代码、测试三章必须都能找到对应,否则就是"功能只活在框图里"。
四、软件设计
4.1 主程序框架
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
delay_init();
USART2_Init(115200); /* ESP8266 */
OLED_Init();
DS18B20_Init();
Adxl345_Init(); /* ±4g, 100Hz */
Adc_Init(); /* PulseSensor */
Beep_Init(); Key_Init();
ESP8266_Reset(); /* PA8 拉低500ms */
ESP8266_ConnectWiFi(SSID, PWD);
ESP8266_TCP_Connect(SERVER_IP, PORT);
while (1)
{
temp = DS18B20_GetTemp(); /* 每 1s 一次 */
bpm = HeartRate_Update(); /* ADC 滑窗峰值 */
fall = Fall_Detect_Update(); /* 合加速度算法 */
OLED_Display(temp, bpm, fall);
if (temp > g_temp_th || bpm > g_bpm_th || fall) {
Alarm_Start();
ESP8266_Report(ALARM);
}
if (Key_SOS_Pressed()) { /* 一键求救 */
Alarm_Start();
ESP8266_Report(SOS);
}
Server_Cmd_Parse(); /* APP 阈值下发 */
}
}
4.2 关键子程序一:DS18B20 单总线读温度
float DS18B20_GetTemp(void)
{
u8 tl, th;
s16 raw;
DS18B20_Reset(); /* 主机拉低480us -> 释放 -> 等60~240us响应 */
DS18B20_WriteByte(0xCC); /* 跳过ROM(单器件) */
DS18B20_WriteByte(0x44); /* 启动温度转换 */
delay_ms(750); /* 12位分辨率最长750ms */
DS18B20_Reset();
DS18B20_WriteByte(0xCC);
DS18B20_WriteByte(0xBE); /* 读暂存器 */
tl = DS18B20_ReadByte();
th = DS18B20_ReadByte();
raw = (th << 8) | tl;
return raw * 0.0625f; /* 12位:0.0625°C/LSB */
}
优化点:750ms 阻塞等待会拖慢整个主循环。实战做法是 0x44 转换命令发完就继续干别的,下一轮循环再读结果(DS18B20 转换结果保持到下次转换)。
4.3 关键子程序二:PulseSensor 峰值检测算心率
PPG 信号的处理套路:滑动窗口找 max/min → 动态中点 → 带迟滞的上升沿判定 → 两次心跳间隔 IBI → BPM=60000/IBI:
u16 HR_Update(void)
{
static u16 buf[32], idx;
static u8 rising = 0;
static u32 last_beat;
u16 v = Adc_Read_Avg(10);
u16 mn = 0xFFFF, mx = 0, i, mid;
u32 now = GetTick();
buf[idx++ & 31] = v;
for (i = 0; i < 32; i++) { /* 滑窗极值 */
if (buf[i] < mn) mn = buf[i];
if (buf[i] > mx) mx = buf[i];
}
mid = (mx + mn) / 2;
if (!rising && v > mid + Hyst) { /* 带迟滞的上升沿 */
rising = 1;
if (now - last_beat > 300) { /* 排除 <200BPM 的毛刺 */
ibi = now - last_beat; /* 心跳间隔 ms */
bpm = 60000 / ibi;
last_beat = now;
}
} else if (rising && v < mid - Hyst) {
rising = 0;
}
return bpm;
}
坑:BPM 别用单次 IBI 直接算,手一抖就跳变;实际取最近 5 次 IBI 的中位数再换算,显示才稳。
4.4 关键子程序三:正确的跌倒检测(本篇重点)
先看原文 4.4 节怎么写的:读三轴加速度 → atan 算角度 → “当检测到角度偏移超过设定阈值就代表发生步数”。
两个问题:其一,结尾这句"就代表发生步数"是从计步器论文里复制粘贴的残留——跌倒检测的章节里蹦出"步数",等于把抄袭痕迹钉在了正文里;其二,就算把"步数"改成"摔倒",用静态角度判断跌倒也是错误方案:正常人的弯腰、侧躺、抬手臂都会让"角度偏移超阈值",误报率会高到没法用。
业界通用的三轴加速度跌倒检测是抓"自由落体 → 着地冲击 → 长时间静止"三段特征:
/* 合加速度 SMA = sqrt(x^2+y^2+z^2),单位 g */
#define G_LOW 0.4f /* 自由落体门限:跌落瞬间接近0g */
#define G_HIGH 2.5f /* 着地冲击门限 */
#define QUIET_MS 3000 /* 跌倒后静止确认窗 */
typedef enum { F_NORMAL, F_FALLING, F_IMPACT, F_QUIET } FallState;
u8 Fall_Detect_Update(void)
{
static FallState st = F_NORMAL;
static u32 t_mark;
float sma = Acc_GetSMA_g();
float tilt = Acc_GetTiltAngle(); /* 静止姿态角,判断是否躺倒 */
switch (st) {
case F_NORMAL:
if (sma < G_LOW) { st = F_FALLING; } /* 失重 */
break;
case F_FALLING:
if (sma > G_HIGH) { st = F_IMPACT; t_mark = GetTick(); } /* 冲击 */
else if (sma > 1.2f) st = F_NORMAL; /* 又稳住了,虚惊 */
break;
case F_IMPACT:
if (GetTick() - t_mark > QUIET_MS) {
/* 静止 3s 且姿态角异常(接近水平)-> 判定跌倒 */
if (sma < 1.2f && tilt > 60) return 1;
st = F_NORMAL;
}
break;
}
return 0;
}
这样误报率大幅下降:快速坐下(冲击有但之前没有失重段)、躺下睡觉(无冲击段)都不会触发。想更准,把采样率拉到 100Hz 以上并加 MPU6050 陀螺仪做角速度辅助。
4.5 关键子程序四:一键求救与报警状态机
void SOS_Handler(void)
{
static u32 last_sos;
if (!KEY_SOS_READ()) { /* 按下(低电平) */
if (GetTick() - last_sos > 10000) { /* 10s 防重复 */
last_sos = GetTick();
Beep_On();
LED_AlarmBlink();
ESP8266_Report("{\"type\":\"SOS\"}"); /* APP 立即弹窗 */
}
}
}
五、系统调试验证
| 测试项 | 方法 | 预期结果 |
|---|---|---|
| 体温采集 | DS18B20 与水银体温计对比 | 误差 ±0.5°C 内 |
| 心率采集 | 手指贴 PulseSensor,与手环对照 | BPM 偏差 ≤5 |
| 跌倒检测 | 设备从 1m 高自由落体到软垫 | 失重→冲击→静止三段触发,3s 后报警 |
| 误报测试 | 正常坐下、躺下、弯腰 | 均不触发 |
| 一键求救 | 按下 SOS 键 | 本地蜂鸣 + APP 弹窗,10s 内不重复 |
| 阈值设置 | APP 修改心率阈值 | OLED 与报警判定同步更新 |
| 断网测试 | 关 Wi-Fi 30s | 重连后数据续传,断网期间本地报警不受影响 |
六、常见坑汇总(原文翻车实录)
- 跌倒检测抄成计步器:4.4 节结尾原文"当检测到角度偏移超过设定阈值就代表发生步数"——计步论文的复制残留,答辩时这就是"送命题"。跌倒检测请用失重+冲击+静止三段式合加速度算法。
- 静态角度判跌倒:选型夸 ADXL345"静态角度测量精度",检测流程也全在算角度——弯腰捡东西都会被当成摔倒。跌倒是动态事件,靠的是加速度突变,不是姿态角。
- PulseSensor 被写成"红色和红外 LED 组合":它只有一颗绿光 LED;红+红外双波长是 MAX30100/30102。多篇论文同错,抄资料时记得核对官方规格书。
- LM35 vs DS18B20 选型理由全反:精度(±0.25 vs ±0.5)和量程(150 vs 125°C)都是 LM35 赢,DS18B20 赢在数字输出。结论对、理由错,一样扣分。
- 框图里的功能没电路:RTC 模块、一键求救按键只出现在方案框架图里,硬件设计章节查无此物。框图列了,电路+代码+测试必须三对齐。
- 图注编号错乱:主程序流程图标成"图4.2",温度检测流程图也叫"图4.2",5.3.1 的测试图标成"图5.2"——交稿前逐一核对。
- 参考文献凑数:后三条是"零样本电机健康监测"“极地研究船推进轴冰载荷”“大坝安全监测异常识别”,与老人监护毫无关系。
- 体温≠核心体温:DS18B20 贴体表测的是皮肤温度,比腋下/核心温度低 1~2°C,阈值要按体表修正,且本系统不能当医疗设备用。
七、可扩展方向
- 加 GSM 短信通道:断网时老人报警走 SIM800L 短信,监护人必达;
- 升级 MAX30102:心率+血氧双参数,跌倒后血氧骤降是更危险信号;
- 低功耗长续航:C8T6 进 STOP 模式 + ADXL345 中断唤醒,做电池版随身终端;
- 多传感器融合:ADXL345 + MPU6050 陀螺仪 + 气压计(楼层跌落),提高检测准确率到商用级。
八、资料内容完整工程资料(原理图、PCB、代码、元件清单)

九、资料获取方式

更多推荐


所有评论(0)