
简介这份PDF以STM32F103C8T6微控制器为核心完整梳理了家庭智能健康监测系统的设计思路与实现方案适合物联网、嵌入式开发学习者、毕业设计及工程参考。内容覆盖心率、血压、血氧、体温等多生理参数监测模块的选型与通信方式OLED显示、蜂鸣器报警、SYN6288语音提醒等功能设计并重点讲解ESP8266模块在AP模式下创建Wi-Fi热点、配置TCP服务器的流程以及基于Qt框架开发Android手机APP进行数据展示、存储和双向控制的方法。资源为单个PDF文件容量约48.97MB内含项目前言、硬件模块组成、整体设计思路、ESP8266工作模式说明、上位机开发步骤等章节结构清晰便于按段落查阅。目前已有104人浏览学习对希望快速搭建同类健康监测系统、理解局域网无线数据传输与移动端联调的读者具有直接参考价值。1. 家庭健康监测不接云平台STM32局域网是更实用的路径把体温计、血氧仪、心率计的数据凑到一起看的场景通常发生在家里老人需要日常记录、或者术后需要在家观察的阶段。市面上很多健康监测设备要么只是蓝牙连手机要么必须把数据传到某个云端服务器才能看。蓝牙方案的有效距离只有十米左右人一走动连接就断云方案则把最敏感的生理数据交给第三方一旦外网出问题设备就成了摆设。用STM32做主控把DS18B20温度传感器和MAX30102心率血氧传感器接起来再通过ESP8266模块接入家庭无线路由器就组成了一套局域网内的智能健康监测系统。所有数据只在自家路由器内部流动手机、平板、电脑在同一局域网内打开网页即可看到实时体温、心率和血氧数值。这套系统不需要公网服务器断外网也能持续工作数据隐私掌握在自己手里成本也能控制在几十元以内。它适合作为嵌入式入门后的综合练习也适合直接做成给家里用的真实设备。2. 从传感器到网页的数据链路STM32健康监测系统的整体架构与选型2.1 整个系统的数据流和角色分配在动手写代码之前先把数据链路走一遍。传感器把生理信号转成电信号STM32通过GPIO或I2C读取原始数据经过滤波和换算得到体温、心率、血氧数值再通过串口把打包好的数据帧发给ESP8266。ESP8266以STA模式连接到路由器与局域网内的接收服务器建立TCP连接把数据帧逐条上报。接收服务器可以是树莓派、旧电脑也可以是一台常开的小主机它解析数据帧、存入SQLite同时提供一个本地Web页面供浏览器访问。DS18B20/MAX30102 → STM32F103C8T6 → 串口 → ESP8266 → 路由器 → 局域网服务器 → 浏览器主控负责采集和协议打包ESP8266只做传输通道服务器负责存储和展示。这样分层的核心原因是让每个模块职责单一。STM32的串口只管往外吐数据帧不需要关心Wi-Fi信号强度和TCP重传ESP8266也不需要理解数据内容服务器则可以根据需要随时改动页面逻辑而不用重新烧录单片机。2.2 主控选型STM32F103C8T6够用什么情况才需要上F407对于这个系统传感器数量不多数据量也很小。体温每1到2秒采一次心率血氧每100毫秒采一组单帧数据不超过32字节用72MHz主频的STM32F103C8T6绰绰有余。这颗芯片有64KB Flash、20KB SRAM三个USART两个I2C资源完全够用。主控型号主频主要优势适合场景STM32F103C8T672MHz资料最多价格便宜引脚够用本项目首选入门到进阶都合适STM32F407VET6168MHz内置以太网MAC算力强需要直接走网线、跑RTOS或多路传感器STM32G431170MHz内置运放适合模拟信号处理用模拟前端做心电、生物电采集选择F103还有一个现实原因网上的参考资料和例程最丰富遇到I2C时序、串口DMA这类问题时容易找到答案。只有当你计划用以太网替代Wi-Fi、需要同时驱动多路传感器或多路串口设备时才有必要考虑F407之类的高性能芯片。2.3 传感器选型体温、心率、血氧各自怎么接体温测量选用DS18B20它走OneWire单总线协议只占用一个GPIO引脚测量范围-55到125摄氏度12位分辨率下精度为正负0.5摄氏度。贴片封装和防水封装都有用导热硅胶贴在皮肤表面即可读取体表温度。心率血氧选用MAX30102这是一颗集成红光和红外LED的反射式传感器通过I2C接口访问内部寄存器输出的是红外光和红光的ADC原始值。需要说明MAX30102读出来的原始值不能直接用需要用滑动窗口配合差分算法提取脉搏波形再换算心率和血氧饱和度。如果想偷懒也可以在模块上接一颗四针OLED直接看数字但那只是验证模块好坏不是系统方案。传感器型号接口关键参数输出内容DS18B20OneWire单总线12位分辨率LSB为0.0625℃温度值MAX30102I2C地址0x57采样率最高1kHzLED电流可调红光/红外原始ADC值DHT11OneWire单总线精度正负2℃环境温湿度不可替代体温传感器2.4 通信模块选型ESP8266还是直接上以太网局域网通信最基本的两种方式一是串口Wi-Fi模块二是以太网。本项目优先选择ESP8266系列模块常见的是ESP-01S和ESP-12F。ESP-01S体积小、价格便宜但Flash容量只有1MBIO口也少ESP-12F是4MB Flash天线性能更好推荐用ESP-12F或者基于它的NodeMCU开发板后续调试时容易接线。ESP8266本身也是一个32位处理器可以独立编程。但在这个项目里建议只把它当作透传工具使用用官方AT指令固件通过串口指令控制它连接Wi-Fi、建立TCP连接、发送数据。把算力留给STM32让ESP8266专注于通信两者分工明确。STM32的串口波特率设为115200ESP8266默认就是这个速率不需要单独改波特率。3. 传感器的驱动与采集DS18B20和MAX30102的时序细节3.1 OneWire总线的DS18B20温度采集时序参数不能省DS18B20的时序要求特别细。初始化时主机拉低总线480到960微秒然后释放总线等待从设备拉低60到240微秒作为应答。写1位时主机先拉低总线1到15微秒然后释放写0时持续拉低60到120微秒。读取时主机拉低1到15微秒后释放然后立即采样总线电平。这些延时必须在主频确定后逐个验证下面给出适用于72MHz主频的HAL版本读取函数。// 读取DS18B20温度单位为摄氏度读取失败时返回-50.0f float DS18B20_ReadTemp(void) { uint8_t tL, tH; int16_t raw; if (DS18B20_Reset() ! 0) { return -50.0f; // 总线上没有设备应答直接返回错误值 } DS18B20_WriteByte(0xCC); // 跳过ROM码单设备挂载时使用 DS18B20_WriteByte(0x44); // 启动温度转换12位精度耗时最长750ms while (DS18B20_ReadBit() 1); // 转换完成后DS18B20会拉低总线并释放 DS18B20_Reset(); DS18B20_WriteByte(0xCC); DS18B20_WriteByte(0xBE); // 读取暂存器内容共9字节 tL DS18B20_ReadByte(); tH DS18B20_ReadByte(); raw (tH 8) | tL; return raw * 0.0625f; // 12位分辨率下每一位代表0.0625℃ }关键一点是读取温度之前必须等待转换完成。DS18B20在上电后默认是12位分辨率转换时间最长750毫秒如果没等到转换结束就发读暂存器指令读到的会是上一次的旧值。可以用延时750毫秒代替查询但查询方式更可靠因为温度传感器在环境稳定时转换速度通常快于最大时间。另外raw * 0.0625f这个换算只对12位分辨率有效如果把分辨率改成9位LSB会变成0.5摄氏度换算系数要同步调整。3.2 MAX30102的I2C配置寄存器参数怎么设MAX30102挂在I2C总线上地址为0x57支持标准模式400kHz。初始化时要重点配置四个寄存器模式配置寄存器、SpO2配置寄存器、LED电流寄存器和FIFO配置寄存器。// MAX30102初始化I2C地址为0x57 void MAX30102_Init(void) { I2C_WriteReg(0x57, 0x09, 0x03); // 模式寄存器SpO2模式红LED与红外LED同时工作 I2C_WriteReg(0x57, 0x0A, 0x43); // SpO2配置4096nA满量程采样率100HzLED脉宽411us I2C_WriteReg(0x57, 0x0E, 0x07); // 红光LED电流6.4mA I2C_WriteReg(0x57, 0x0F, 0x07); // 红外LED电流6.4mA I2C_WriteReg(0x57, 0x08, 0x07); // FIFO配置平均采样数1FIFO滚动使能 I2C_WriteReg(0x57, 0x02, 0x00); // 清FIFO写指针 }采样率设为100Hz意味着每10毫秒产生一组红光和红外数据。LED脉宽411微秒搭配4096nA的满量程ADC范围适合手指这类透射式测量场景。LED电流初始设为6.4mA佩戴后如果发现波形顶端削顶说明电流过大应降低到3.2mA甚至更低如果波形太矮、峰谷不明显则适当调大电流。MAX30102的数据通过FIFO读取。读取状态寄存器0x00获取FIFO内有效样本数再读取FIFO数据寄存器0x07连续读出多个样本。每次读回6字节前3字节是红光ADC后3字节是红外ADC合并时需要拼接成24位整数再右移4位因为ADC有效位是18位。3.3 统一数据帧格式把采集值打包成可传输的结构体传感器数据最终要经过串口发给ESP8266再用TCP传到服务器。为了让接收端能可靠解析定义一套固定长度的二进制数据帧比用文本加逗号拼接的方式更省流量也更抗网络抖动。偏移量内容类型说明0-1帧头uint16固定0xAA55用于识别帧起始2设备IDuint8多设备联动时区分来源单设备固定为13数据长度uint8除帧头和校验字节外的数据长度固定为94-5温度int16实际温度乘以100避免浮点数传输6-8心率uint16单位bpm8-9血氧uint8单位百分比10-11时间戳uint16设备本地秒用于对齐多条曲线12校验字uint8从帧头开始对所有字节进行累加取低8位采用这种格式接收端先找帧头0xAA55再按长度字段截取数据体最后核对校验字。浮点数不直接进网络帧而是放大为整数是因为在单片机端处理浮点序列化和解析都很繁琐放大整数后传输和存储都更高效服务器端再除以相应的放大系数还原。4. 把数据送入局域网STM32通过ESP8266上报TCP的完整做法4.1 串口透传和AT指令两种控制方式怎么选ESP8266的控制方式大体有三种直接用AT指令、刷透传固件后透明传输、在ESP8266的SDK里自主开发。本项目使用AT指令因为代码直观、边界清晰出了问题串口日志直接可见。AT指令模式下STM32作为主机ESP8266作为从机所有操作都是发一行指令读一行响应。工作方式开发量可控性适用场景AT指令低高本项目的合理选择连接、断线判断都清晰透传固件最低中串口一打开就自动连指定服务器ESP8266 SDK开发高最强后续想在ESP8266上做加密或协议扩展时再说AT指令方式需要STM32维护状态机初始化阶段发送AT测试指令确认模块在线随后设置Wi-Fi模式连接热点建立TCP连接之后进入数据发送循环。任何一条指令没有得到预期响应时都要有重试逻辑否则ESP8266会一直停留在异常状态。4.2 AT指令连接路由器和TCP服务器的具体流程// ESP8266以外的流程用STM32串口发送这些AT指令 // 每条指令发送后都要等待串口返回OK再进入下一步 AT\r\n ATRST\r\n ATCWMODE1\r\n // 1Station模式ESP8266只作为终端连接路由器 ATCWJAPHomeWiFi,password123\r\n // 替换为自家路由器的SSID和密码 ATCIPMUX0\r\n // 0单连接模式避免多路Socket干扰数据帧顺序 ATCIPSTARTTCP,192.168.1.100,8080\r\n // 连接局域网内接收服务器的IP和端口ATCWMODE1是Station模式让ESP8266成为Wi-Fi网络的客户端设备与项目要求的局域网方案一致。ATCIPMUX0确保TCP单连接虽然ESP8266支持多路连接但多路Socket会引入数据帧交叉单连接才可以保证数据顺序。ATCIPSTART中填写的IP是接收服务器在同一局域网内的地址端口号要跟服务器监听端口一致比如家用路由器的网段通常是192.168.1.x或192.168.31.x以实际分配为准。连接建立后发送数据的指令是ATCIPSEND长度紧接着发送指定长度的数据体ESP8266会把这段数据原样通过TCP发出。// STM32发送一帧健康数据的核心代码 void ESP8266_SendFrame(uint8_t *frame, uint16_t len) { char cmd[32]; snprintf(cmd, sizeof(cmd), ATCIPSEND%d\r\n, len); UART_SendString(cmd); // 先通知ESP8266要发送的字节数 HAL_Delay(10); // 等待ESP8266返回提示符 UART_SendBytes(frame, len); // 发送数据本体 HAL_Delay(5); UART_SendString(); // 如果开启了半透传模式才需要退出 }发送前必须等待ESP8266返回提示符直接发数据会丢失。发送数据后TCP对端回复的ACK不会在串口显式体现因此连续发送的间隔建议不低于50毫秒否则ESP8266内部发送缓冲区可能溢出。如果一条ATCIPSEND命令发送的数据超过2048字节要分多次发送但本项目单帧只有13字节完全不需要考虑分片。4.3 断线检测和自动重连健康数据不能悄悄丢失局域网通信最怕的是设备端还在采集服务器的连接已经断开而单片机毫不知情。ESP8266在TCP断开后会在串口输出CLOSEDSTM32应监听这个字符串检测到后立即执行重连逻辑。更保险的做法是协议层加心跳每5秒发一帧带心跳标志的数据服务器如果连续15秒没收到任何帧就主动关闭旧Socket并重新监听。// 串口接收中断里做简单的字符串匹配 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { // 伪代码每收到一个字节就拼进缓冲 // 缓冲区匹配到\r\nCLOSED时置位tcpClosedFlag tcpClosedFlag 1; HAL_UART_Receive_IT(huart, rxByte, 1); }重连逻辑放在主循环里一旦检测到tcpClosedFlag先执行ATCIPSTART重新建链再重发最近一次未送达的数据确保接收端不会因为短暂断网而漏掉整段记录。要注意不要在每个心跳里都进行重连否则网络抖动会引发频繁重建Socket反而增加断线概率。4.4 最小服务器端用Python接收TCP数据帧接收端可以用Python在几分钟内搭起来。下面这个脚本绑定所有网卡上的8080端口接收二进制数据帧解析出温度、心率、血氧并打印到控制台。import socket import struct server socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind((0.0.0.0, 8080)) server.listen(1) print(等待设备接入...) while True: conn, addr server.accept() print(f设备已连接: {addr}) while True: header conn.recv(2) if header b\xaa\x55: data conn.recv(13) if len(data) 13: break device_id data[0] temp_raw struct.unpack(h, data[2:4])[0] heart_rate struct.unpack(H, data[4:6])[0] spo2 data[6] temp temp_raw / 100.0 print(f设备{device_id} 体温:{temp:.2f}℃ 心率:{heart_rate} 血氧:{spo2}%) else: break这段代码先读帧头匹配到0xAA55后才继续读其余13字节避免错位解析。struct.unpack(h)表示大端序的16位有符号整数STM32端发送时也要按大端字节序拼接两端约定缺一不可。实际部署时服务器还要增加与STM32相同的校验字校验逻辑校验失败的数据帧直接丢弃。5. 在局域网内搭监控端服务器解析、存储和Web页面展示5.1 用Flask提供Web页面浏览器实时刷新TCP接收数据的脚本只解决了数据进口的问题查看数据还要一个可视化页面。最常见做法是用Flask写一个轻量级Web服务提供两个接口/返回监控页面/api/latest返回最新一帧数据页面通过setInterval每1秒拉取一次数据并更新DOM。from flask import Flask, jsonify, render_template import sqlite3 app Flask(__name__) def get_latest(): conn sqlite3.connect(health.db) cur conn.cursor() cur.execute(SELECT temp, hr, spo2, ts FROM health ORDER BY id DESC LIMIT 1) row cur.fetchone() conn.close() return row app.route(/) def index(): return render_template(index.html) app.route(/api/latest) def latest(): r get_latest() if r: return jsonify({temp: r[0], hr: r[1], spo2: r[2], ts: r[3]}) return jsonify({temp: 0, hr: 0, spo2: 0, ts: 0}) if __name__ __main__: app.run(host0.0.0.0, port5000)前端页面用原生JavaScript即可不需要引入重量级图表库。一个简单的index.html里用fetch(/api/latest)拉取数据把体温、心率和血氧写入对应的HTML元素。如果以后要画趋势曲线再引入Canvas手绘折线即可核心仍是定时获取接口数据。5.2 数据校准传感器原始值到真实数值的差距传感器读取的值和真实生理值之间通常有偏差最容易出问题的两个地方是体温和血氧。DS18B20测量的是探头接触位置的温度贴在手腕或额头与贴在腋下差距可能有几度。实际使用时需要先用一支医用体温计做对照记录温度补偿值在软件里做表格式修正。# 温度补偿分段线性校正offset由标定时确定 def calibrate_temp(raw_temp): if raw_temp 34.0: offset 0.8 elif raw_temp 37.0: offset 0.5 else: offset 0.2 return raw_temp offsetMAX30102的血氧数值受指尖压迫和运动影响很大按压过紧会让毛细血管血流减少测出的血氧偏低手指抖动会让波形出现大量毛刺。建议在软件里做滑动平均滤波对最近5个心率值取平均同时丢弃相邻差值超过30bpm的异常跳变。还要注意MAX30102是消费级传感器测出的血氧误差可能达到正负2%它适合看长期变化趋势不能作为医疗诊断依据这一点要在页面上标注清楚。5.3 SQLite落库和数据趋势异常判断数据只有持续存下来才有分析价值。用SQLite存储时建表要包含时间戳、体温、心率、血氧和事件标记字段。CREATE TABLE IF NOT EXISTS health ( id INTEGER PRIMARY KEY AUTOINCREMENT, ts DATETIME DEFAULT CURRENT_TIMESTAMP, temp REAL, hr INTEGER, spo2 INTEGER, warning INTEGER DEFAULT 0 );记录插入前先做一次简单判断体温超过37.5度或心率超过100次每分钟就把warning字段置为1页面端根据这个字段在数字旁边显示警告色。阈值可以在Web页面提供输入框随时调整比每次改代码重启服务器更实用。定期查询最近24小时的数据计算最高体温、平均心率和血氧波动范围可以为家人健康趋势提供量化的判断依据。SQLite单文件数据库对家庭场景完全够用不需要引入MySQL或PostgreSQL这类重型服务。6. 局域网健康监测的3个排错与进阶技巧6.1 串口日志定位把调试串口和Wi-Fi串口分开项目里有两个串口在同时工作一个连ESP8266通信一个连调试工具。如果共用一个串口AT指令的响应和数据帧会混成一团问题很难定位。正确做法是用USART1连接ESP8266USART2作为调试串口STM32内部通过printf重定向到调试串口输出日志。注意ESP8266的AT响应中CLOSED、ERROR这类关键字要完整匹配不能只判断包含单个字符否则容易把正常TCP数据中的字母误判为错误码。6.2 用看门狗兜底解决Wi-Fi模块偶发无响应ESP8266长时间运行后偶发不响应AT指令这是最常见的问题。与其不断尝试从软件层面修复不如用STM32内置的IWDG独立看门狗兜底。主循环每隔一定时间喂狗如果串口连续3次发送AT指令都没有收到OK响应就主动调用NVIC_SystemReset()重启整个系统。重启后让ESP8266重新连接路由器系统即可恢复。6.3 节省功耗温度与心率分频率采样长期使用时不必让所有传感器都以满速率工作。体温采样频率降到每10秒一次即可因为体表温度变化缓慢心率血氧可以连续采集30秒后进入待机过5分钟再唤醒采集一轮。STM32进入待机模式后电流可以降到微安级再用RTC定时唤醒采集。这个做法对电池供电的设备很有用对插电使用的设备也能降低发热延长传感器寿命。调整采样频率时记得同步修改服务器端的超时判断阈值避免因为数据间隔变长而误判设备掉线。本文还有配套的精品资源点击获取