简介:这是一份基于STM32F103RCT6的人体健康监测系统完整设计方案,面向嵌入式开发学习者、物联网项目实践者及电子类毕业设计学生。方案以老年人、行动不便人群为应用场景,集成了心率、血氧、血压、体温、姿态检测,并通过ESP8266与华为云IOT平台联动,配合手机APP实现远程监护和摔倒预警,是一套软硬件结合的综合性参考。资源为单个PDF文档,文件大小75.32MB,以图文方式详细梳理系统架构、硬件模块选型、传感器接口、MQTT数据传输和软件设计思路,涵盖PPG传感器、压力传感器、MPU6050陀螺仪、MLX90614红外测温、OLED显示及18650电池供电等模块。目前已有214人学习下载。文档特别适合需要快速搭建同类健康监测项目或撰写设计说明的读者,可直接借鉴其模块划分、数据处理流程和华为云接入方法,减少前期调研与选型试错时间,对完成电子竞赛、课程设计或毕设均有实用价值。
1. 项目整体思路与方案选型
做健康监测类项目,市面上的方案并不少,但很多都存在两个极端:要么是纯本地显示、数据存在单片机里没有后续价值;要么是传感器买一堆、云平台注册完,结果卡在设备接入这一步,最后只能拿串口助手看数据,完全没体现"物联网"的意义。
这个项目之所以选STM32+华为云IoT这套组合,核心是想解决三个问题:
第一,数据从哪来。人体健康监测,基础参数离不开心率、体温、血氧这几项。STM32F103系列作为主控,外接传感器模块采集生理信号,做滤波、换算、校准,这部分是嵌入式侧的核心工作。选STM32而不是Arduino,一方面是因为它资源更充裕、ADC精度可控、定时器更灵活,另一方面是它的工程化程度高,后续想扩展功能(比如加OLED显示、加本地存储)都不至于推倒重来。
第二,数据往哪去。本地显示只是第一步,真正的价值在于数据上云,让用户能在手机或网页上远程查看历史曲线。项目采用ESP8266作为WiFi模块,通过AT指令或MQTT协议,把STM32采集到的数据发送到华为云IoT平台。
为什么选华为云IoT而不是自建服务器?坦白说,自己搭一个TCP服务器接收数据并不难,但后面还有设备管理、数据存储、可视化看板、告警规则这一整套链路,自己从零写代价太高。华为云IoT平台提供了设备接入、消息上报、命令下发、数据转发等功能,前期免费额度够用,而且有现成的开发文档和SDK,非常适合这种单体项目快速落地。
第三,用户怎么用。项目最终呈现的形态是一个完整的闭环:STM32采集数据 → ESP8266上报华为云 → 云端存储和规则引擎处理 → 手机App或网页展示。用户打开手机就能看到心率、体温的历史曲线;超出阈值时,云端能推送告警。这才是"人体健康监测系统"该有的样子,而不是一块冷冰冰的LCD屏幕。
这套方案适合谁参考?如果你是嵌入式专业的在校生,正在做毕业设计或课程项目,这个项目能把单片机、传感器、无线通信、物联网平台串成一条完整的线,写在简历上是很有说服力的。如果你是想入门物联网开发的工程师,这个项目同样值得跟练一遍,因为硬件侧的代码量不大,重点在于理解设备-云平台之间的交互逻辑。
2. 硬件选型与核心电路设计
2.1 主控与传感器选型
主控芯片用了STM32F103C8T6,也就是大家常说的"蓝丸"核心板。选它不是因为性能有多强,而是因为这颗芯片的资料极其丰富、库函数生态成熟,遇到问题几乎都能在网上搜到现成解答,对新手非常友好。F103系列的72MHz主频,12位ADC,足够处理健康监测这类低采样率的信号采集任务。
传感器方面,我选了分立的模块而不是集成方案,原因后面会讲:
| 模块 | 型号 | 接口方式 | 说明 |
|---|---|---|---|
| 心率血氧 | MAX30102 | I2C | 红光+红外双波长,输出心率与血氧饱和度 |
| 体温检测 | DS18B20 | 单总线 | 防水封装,精度±0.5℃ |
| 数据显示 | OLED 0.96寸 | I2C | SSD1306驱动,显示实时数据 |
| WiFi模块 | ESP8266-01S | 串口 | 通过UART与STM32通信,连接华为云IoT |
有人可能会问,为什么不直接用集成的健康监测模组?比如一些厂家的多参数模组,一个模块直接输出心率、血氧、体温。原因有两个:一是成本,分立模块总价不到30块,集成模组动辄上百;二是项目的技术含量体现不出来。做项目讲究的不只是功能实现,而是看中间的处理过程。分开采集体温、心率、血氧,每个环节都有可以展开讲的细节,答辩或面试时也有的说。
2.2 电路连接与引脚分配
连接方案如下:
- MAX30102传感器:SCL接PB6,SDA接PB7(I2C1),INT中断引脚接PB5
- DS18B20传感器:数据线接PB1,采用寄生供电方式,数据线经4.7kΩ上拉电阻接3.3V
- OLED显示屏:SCL接PB6,SDA接PB7(与MAX30102共用I2C1总线,地址不同,不冲突)
- ESP8266:TX接PA3,RX接PA2(USART2),CH_PD引脚接3.3V使能
这里有个容易出问题的细节:ESP8266的供电。ESP8266在WiFi发射瞬间的电流峰值可以达到300mA以上,如果直接从STM32开发板的3.3V引脚取电,很容易造成电压跌落,轻则WiFi掉线,重则单片机复位。正确的做法是给ESP8266单独准备一路3.3V稳压(比如用AMS1117-3.3),或者用一个独立稳压模块供电,同时就近并联一个100μF的电解电容和一个0.1μF的陶瓷电容做储能和去耦。我实际测试发现,不加电容时ESP8266连接WiFi的瞬间,系统电压会掉到2.9V左右,加电容后能稳定在3.25V以上,这个差异直接决定了整个系统能不能稳定工作。
MAX30102也需要注意,它的内部LED驱动电流比较大,如果走杜邦线连接,线材过长会导致信号毛刺明显。建议I2C信号线尽量短,并加上拉电阻(大多数模块厂商已经在板上集成了4.7kΩ上拉,不需要额外添加,但如果用的是裸芯片就需要自己加)。
2.3 电源设计考量
整个系统的供电从USB 5V进入,通过板载稳压得到3.3V。但这里存在一个隐患:MAX30102的心率采集对电源纹波比较敏感,而ESP8266的射频发射会引入不小的纹波干扰。我在调试过程中遇到过心率波形毛刺严重、数值跳动的问题,后期处理方式是:
- 传感器供电和WiFi模块供电分开走线,避免共用一段长导线
- 在MAX30102的VDD引脚就近加一个10μF钽电容做低频滤波
- 必要时在ADC采样结果上做软件滑动平均
硬件电源是这个项目里"看起来不复杂、实际最容易翻车"的部分。如果你后续想做成可穿戴形态,建议换成锂电池+升压方案,并做好电源路径管理,但这属于进阶话题,基础版本不需要考虑。
3. 传感器数据采集与信号处理
3.1 心率血氧采集原理
MAX30102的核心原理是光电容积脉搏波描记法,也就是常说的PPG。简单解释就是:血液对红光和红外光的吸收率不同,动脉血管随着心脏搏动周期性变化,导致反射光强度也在周期性变化。传感器内部集成了红光LED(660nm)和红外LED(880nm),根据两种波长的吸收比变化,可以同时计算出心率和血氧饱和度。
具体来说,心率计算是通过检测PPG波形中峰值的时间间隔,也就是R-R间期,换算每分钟的心跳次数。血氧饱和度则是利用氧合血红蛋白和还原血红蛋白对红光吸收率的差异,通过两路信号的交流分量和直流分量比值查表得到。
3.2 数据读取与算法实现
读取流程分三步:
第一步,初始化传感器。通过I2C写寄存器,配置采样率和LED电流。关键参数可以参考:
- 采样率设置为100Hz,也就是每10ms采一个点
- 脉冲宽度设为411μs,提升信噪比
- LED电流设置在6.4mA~12.8mA之间,太低信号弱,太高容易饱和
第二步,读取FIFO数据。MAX30102内部有32个样本深度的FIFO,每次读取前先检查中断状态寄存器,确认有新数据后再批量读出,避免读取到过期数据。
第三步,算法处理。这一步是项目的软件核心。裸采出来的PPG波形噪声很大,直接做峰值检测得到的心率值会剧烈跳动。我的处理链路是:
- 对原始红外的数据做滑动平均滤波,窗口长度取5个采样点
- 用一阶差分法定位波形峰值,设置自适应阈值(阈值为最近1秒内信号幅值的0.6倍)
- 计算相邻峰值的时间差,取最近5个间隔的中位数作为心率值
- 血氧饱和度通过计算红光和红外的AC/DC比值,查经验表得到
下面给出关键代码片段:
// MAX30102数据读取与心率计算核心逻辑 #define SAMPLE_RATE 100 // 100Hz采样率 #define BUFFER_SIZE 50 // 5秒数据缓冲 uint16_t ir_buffer[BUFFER_SIZE]; uint16_t red_buffer[BUFFER_SIZE]; uint8_t buffer_index = 0; void read_sensor_data(void) { // 检查FIFO数据就绪标志 if (max30102_get_int_status() & 0x40) { max30102_read_fifo(&red_buffer[buffer_index], &ir_buffer[buffer_index]); buffer_index++; if (buffer_index >= BUFFER_SIZE) { buffer_index = 0; process_ppg_data(ir_buffer, red_buffer, BUFFER_SIZE); } } } // 滑动平均滤波 + 峰值检测 void process_ppg_data(uint16_t *ir, uint16_t *red, uint16_t len) { // 1. 滑动平均滤波,窗口为5 float filtered[BUFFER_SIZE]; for (int i = 2; i < len - 2; i++) { filtered[i] = (ir[i-2] + ir[i-1] + ir[i] + ir[i+1] + ir[i+2]) / 5.0f; } // 2. 找峰值点:一阶差分的符号变化 // 3. 相邻峰值间隔换算心率 // 4. 取最近5个间隔的中位数,抗偶发干扰 }3.3 DS18B20体温读取
DS18B20是单总线数字传感器,通信协议比较特殊,需要严格的时序控制。建议直接用官方提供的延时函数来适配时序,不要用HAL_Delay(),因为HAL库的延时精度不够且会阻塞。正确做法是使用DWT或SysTick实现的微秒级延时函数。
读取温度的核心流程:
// DS18B20单总线操作核心(省略时隙函数细节) float read_temperature(void) { uint8_t temp_l, temp_h; float temperature; ds18b20_reset(); ds18b20_write_byte(0xCC); // 跳过ROM匹配 ds18b20_write_byte(0x44); // 启动温度转换 // 等待转换完成,约750ms delay_ms(800); ds18b20_reset(); ds18b20_write_byte(0xCC); ds18b20_write_byte(0xBE); // 读取暂存器 temp_l = ds18b20_read_byte(); temp_h = ds18b20_read_byte(); // 合成12位温度数据 int16_t raw = (temp_h << 8) | temp_l; temperature = raw * 0.0625f; return temperature; }实际测试时我遇到过一个问题:DS18B20第一次上电读到的温度是85℃,很多人以为传感器坏了,其实这是芯片内部的默认上电值,初始化后延时几百毫秒重新读取一次就正常了。
4. 华为云IoT平台接入与数据上报
4.1 设备接入流程
华为云IoT平台的接入流程可以拆成四步:创建产品、定义模型、注册设备、设备鉴权。
第一步,创建产品。登录华为云IoT平台控制台,在"设备接入IoTDA"服务下创建一个产品。这里需要注意选择协议类型,本项目采用MQTT协议,这也是华为云IoT平台最推荐的方式,相比HTTP,MQTT在低带宽、高延迟场景下性能更好,且支持消息推送。
第二步,定义产品模型。产品模型就是设备上报数据的格式约定。我定义了三个属性:心率(int类型,单位bpm)、体温(decimal类型,单位℃)、血氧(int类型,单位%)。定义好之后,平台会自动生成MQTT主题和payload格式的规范说明。
第三步,注册设备。在设备列表中添加真实设备,获取设备ID和设备密钥。设备密钥用于MQTT连接的鉴权,华为云IoT平台使用HMAC-SHA256算法对设备和密钥进行签名认证,具体校验逻辑平台已经封装好了,设备端只需要正确配置参数就行。
第四步,设备端连接。设备端基于MQTT协议连接平台,需要关注三个要素:接入地址、设备ID、鉴权信息。华为云的MQTT接入地址格式为{接入地址}:1883,这里的接入地址是平台分配的,需要从控制台获取。
下面给出ESP8266侧的连接配置和上报一个数据包的AT指令流程:
// ESP8266 MQTT连接华为云IoT核心流程(基于AT指令) // 1. 配置ESP8266为Station模式 AT+CWMODE=1 // 2. 连接WiFi AT+CWJAP="你的WiFi名称","你的WiFi密码" // 3. 连接MQTT Broker(使用AT固件或透明传输模式) // 这里以HELO指令体系为例(不同固件指令有差异) AT+MQTTUSERCFG=0,1,"NULL","设备ID","设备密钥",0,0,"" AT+MQTTCONN=0,"接入地址",1883,1 // 4. 上报数据,payload为JSON格式 AT+MQTTPUB=0,"/huawei/v1/devices/设备ID/datas","{\"services\":[{\"service_id\":\"health\",\"properties\":{\"heart_rate\":75,\"temperature\":36.5,\"spo2\":98}}]}",0,04.2 数据上报格式与状态控制
华为云IoT平台的数据格式是标准JSON,必须符合产品模型里定义的结构,否则平台会拒绝接收。我的上报结构如下:
{ "services": [{ "service_id": "health", "properties": { "heart_rate": 75, "temperature": 36.5, "spo2": 98 } }] }这里有一个细节值得说明:同一份数据拆多条命令上报和合并成一条上报,在平台侧显示的是不同的历史记录结构。合并上报的好处是数据在同一个服务ID下,后续做数据转发和可视化会更方便,我实际推荐这种方式。
在实现过程中,我采用的是ESP8266作为透传模块,STM32通过串口发送MQTT报文。为了保险起见,可以在STM32侧自己组包,把三路传感器数据封装成上面的JSON格式,然后通过AT指令把整个payload透传出去。注意一点:ESP8266的AT固件默认对AT指令的响应字符串长度有限制,如果payload太长会被截断。解决办法是切换成"透传模式"(AT+CIPMODE=1),或者分多次发送。实测下来,直接按上面的AT指令一次发送200字节内的JSON是没有问题的。
4.3 数据可视化与告警配置
数据成功上云之后,华为云IoT平台的设备详情页会自动绘制属性变化曲线,这是一个最基本的可视化。但如果你想在手机上看,推荐把数据转发到应用侧,走IoTDA的数据转发规则,将消息推送到一个云端的消息队列或应用API。
对于项目演示来说,有两个方案:
- 方案一(简单):直接在华为云IoT平台控制台查看设备上报记录和时序折线图。零代码,但演示和交互效果一般。
- 方案二(推荐):在华为云上创建一个简单的API,接收IoTDA转发的消息,再配合一个极简HTML页面展示实时数值和历史曲线。这部分的代码量不大,但如果想彻底做好,需要了解一些华为云的API网关和函数计算的使用。
告警配置这块,平台提供了规则引擎。我设置了一条规则:当心率大于等于120bpm或小于等于50bpm,或者体温大于等于38℃时,触发告警。告警形式可以对接短信、邮件,但需要绑定SMN服务。免费用户测试时,可以先在平台日志里看到告警记录,不用急着配置通知渠道。
5. 实测效果与问题排查档案
5.1 实际运行效果
将代码烧录进STM32后,按下复位,系统启动流程如下:OLED显示开机画面 → 传感器初始化 → ESP8266连接WiFi → 连接华为云IoT平台 → 开始周期性上报数据。
实测数据:体温测量稳定在36.2℃~36.7℃范围(与标准体温计对比偏差约0.3℃),心率在静息状态下62~78bpm之间波动(与实际脉搏对比误差在±4bpm内),血氧稳定性较好,保持在96%~99%区间。整机功耗约180mA(WiFi工作时),对USB供电无压力。
5.2 常见问题排查
这块是这个项目里大家最容易卡住的地方,根据自己的调试经历整理一份问题清单:
| 现象 | 可能原因 | 排查与解决 |
|---|---|---|
| ESP8266连不上WiFi | 供电电压跌落、SSID密码错误 | 独立供电并加电容;串口输出AT+CWJAP响应信息 |
| MQTT连接失败 | 设备ID或密钥算法不匹配 | 核对设备ID是否包含产品ID前缀;重新生成密钥 |
| 心率值严重偏高 | 采样率不匹配、峰值检测阈值过低 | 检查定时器配置是否为10ms一次;调整阈值系数 |
| I2C读取不到MAX30102 | 地址错误、总线卡死 | 用I2C扫描程序找地址;检查SDA/SCL是否接反 |
| 温度数值为85℃ | DS18B20上电默认值 | 读取前延时800ms;检查复位时序 |
| 云端数据显示异常 | JSON格式不匹配 | 用平台提供的"调试"功能检查payload的schema是否符合产品模型 |
这里重点说一下最隐蔽的一个坑:ESP8266串口和STM32串口之间的电平匹配问题。ESP8266的IO电压是3.3V,STM32的电平也是3.3V,两者可以直接连接,但很多同学用的USB转TTL模块如果跳到5V档位,就会直接烧掉ESP8266。这类问题在这个项目中屡见不鲜——不是代码问题,而是硬件接线问题导致的模块损坏。
5.3 防踩坑经验
自己在整个项目调试过程中沉淀了几个重要经验,分享一下:
第一,模块单独验证。别急着把传感器、WiFi模块全部接到STM32上。先把每个模块单独用串口或I2C调试工具验证一遍,确保硬件没问题再集成。这一步能帮你省下50%的调试时间。
第二,串口日志分级。在代码中增加调试信息输出开关,使用形如DEBUG_INFO的宏控制日志级别。首次调试时打开所有日志,联调时只保留关键日志。这个习惯对排查问题帮助巨大,尤其在数据上报链路中,可以通过日志精确判断问题出现在采集端还是传输端还是云端。
第三,转速区分"软件问题"和"硬件问题"。当数据和预期不一致时,先用示波器或多用表确认传感器输出有没有信号、电源有没有异常毛刺,再做软件层面的排查。很多新手一上来就改代码,浪费时间不说,还可能把本来就好的代码改坏。
第四,上云前先本地调试。建议先在本地用串口助手模拟ESP8266收发的数据,确认协议正确再合入主工程。我在刚开始写MQTT报文时,就是因为JSON嵌套层次不对,导致云平台一直提示未知字段,找了半天才发现是结构多套了一层services数组。
6. 扩展方向与最后的一点体会
做完了基础功能之后,这个项目还有很大的扩展空间,可以根据自己的精力去取舍:
- 数据本地存储:给STM32挂一个SD卡模块,SPI接口,把历史数据写到CSV文件里,实现断网情况下的数据补传。
- 显示升级:从0.96寸OLED升级为1.3寸或2.4寸TFT彩屏,用LVGL做一个可视化界面,展示波形图,这个扩展方向也是最近很多人在做的。
- 低功耗优化:把当前的全速运行模式改为定时唤醒模式,让STM32在两次上报间隔进入Stop模式,ESP8266使用AT指令的休眠模式,可以把待机电流降到mA级别,为后续电池供电做铺垫。
- 多参数扩展:增加BMI160加速度计,可以做步数监测和跌倒检测;增加GPS模块,实现位置追踪。这些数据都可以通过同一套IoT链路上报到华为云。
回过来看这个项目,它真正有价值的地方不在于用了多高端的芯片或多复杂的算法,而在于它把一条完整的物联网链路跑通了:从传感器原始信号、到单片机信号处理、再到无线传输、云平台接入、最终数据呈现。这一条链路走通之后,后面做任何物联网相关的项目都会快很多,因为核心的架构思维已经建立起来了。
最后再分享一个运维上的小经验:华为云IoT平台的免费额度到期后,设备会被强制下线,很多人以为是自己代码出了问题,排查半天。遇到设备突然全部掉线的情况,先登录平台控制台看一眼服务状态是否欠费或到期,再做代码层面的排查。这个坑我踩过一次,希望后来的人能少走些弯路。
本文还有配套的精品资源,点击获取