基于STM32和华为云的智能人体健康监测系统设计

举报
DS小龙哥 发表于 2025/06/22 18:39:04 2025/06/22
【摘要】 项目开发背景随着全球人口老龄化趋势加剧和慢性疾病发病率上升,持续性的健康监测需求日益迫切。心血管疾病作为主要的健康威胁之一,其早期预警和日常管理对降低风险至关重要,而体温则是反映人体基本生理状态的重要指标。传统医疗监测设备通常体积庞大、成本高昂且依赖专业环境,难以满足家庭或个人日常便捷、实时的健康管理需求。现有家用健康设备功能单一、数据孤立,缺乏有效的远程管理和智能分析能力。用户无法及时获...

项目开发背景

随着全球人口老龄化趋势加剧和慢性疾病发病率上升,持续性的健康监测需求日益迫切。心血管疾病作为主要的健康威胁之一,其早期预警和日常管理对降低风险至关重要,而体温则是反映人体基本生理状态的重要指标。传统医疗监测设备通常体积庞大、成本高昂且依赖专业环境,难以满足家庭或个人日常便捷、实时的健康管理需求。

现有家用健康设备功能单一、数据孤立,缺乏有效的远程管理和智能分析能力。用户无法及时获取异常预警,医护人员也难以对居家患者进行长期、系统的健康数据跟踪。因此,开发一种低功耗、易部署且具备云端协同能力的智能监测系统,成为提升个人健康管理效率和及时干预能力的关键突破口。

物联网技术与云计算的快速发展为此提供了技术基础。以高性能低功耗的STM32F103C8T6微控制器为核心,结合高精度传感器(如MAX30100心率模块、DHT11温湿度模块)和无线通信模块(ESP8266),可构建轻量化的实时数据采集终端。同时,依托华为云物联网平台强大的设备接入、数据存储与分析能力,能实现海量健康数据的安全汇聚与高效处理。配合跨平台的上位机软件,可为用户提供直观的数据可视化、历史回溯及智能预警服务。

本设计通过整合嵌入式传感技术、无线通信、云平台及客户端应用,构建一套完整的“端-云-应用”协同系统,旨在为个人及家庭提供低成本、高可用的健康监测解决方案,推动健康管理向智能化、远程化和预防化方向发展。

设计实现的功能

(1) STM32F103C8T6通过I2C协议读取MAX30100心率血氧传感器的心率数据。
(2) STM32F103C8T6通过单总线协议读取DHT11温湿度传感器的体温数据。
(3) STM32F103C8T6通过UART串口与ESP8266-01S WiFi模块通信,使用MQTT协议将数据上传到华为云物联网服务器。
(4) 华为云物联网服务器配置设备接入服务,接收、存储和处理传感器数据。
(5) 上位机QT应用使用C++语言开发,通过Paho MQTT客户端库连接到华为云服务器,订阅数据主题并实现实时数据解析。
(6) 上位机软件实现数据动态图表绘制、历史数据查询界面、阈值设置面板和异常警报功能。

项目硬件模块组成

(1) STM32F103C8T6核心板
(2) MAX30100心率血氧传感器模块
(3) DHT11温湿度传感器模块
(4) ESP8266-01S WiFi模块
(5) USB转TTL模块用于串口调试和供电
(6) 5V直流电源适配器

设计意义

本设计基于STM32F103C8T6与华为云构建的智能人体健康监测系统,具有重要的现实意义和应用价值。该系统通过实时采集心率与体温数据,为用户提供连续、便捷的健康状态监测服务,尤其适用于家庭健康监护、康复期患者管理或老年人日常健康关注等场景,有效弥补了传统定期体检在时效性与连续性上的不足。

系统深度融合了嵌入式感知技术、无线通信技术与云计算技术。STM32作为本地核心处理器,可靠地完成了多协议传感器数据采集与初步处理;ESP8266模块利用MQTT协议实现低功耗、高效率的无线数据传输;华为云物联网平台则为海量异构数据提供了安全稳定的接入、存储与处理能力。这种架构充分体现了物联网技术在健康领域的典型应用模式,验证了“端-边-云”协同的技术可行性。

上位机QT软件的设计显著提升了系统的可用性与数据价值。其实时动态曲线可视化功能直观呈现生理参数变化趋势,便于用户快速掌握健康动态;历史数据查询与CSV导出功能则支持长期健康追踪与深度分析,为个人健康管理或医疗研究提供了数据基础。这种直观的数据交互方式极大增强了用户体验。

系统设置的智能预警机制是保障用户安全的关键环节。通过自定义心率与体温阈值范围,配合上位机的声光报警功能,能够在检测到异常生理参数时第一时间发出警示。这种主动式监护模式显著降低了因健康突发状况延误处置的风险,为用户争取宝贵的应对时间,体现了技术对生命健康的守护价值。

设计思路

设计思路:系统以STM32F103C8T6微控制器为核心,负责协调各传感器模块与通信模块的工作。首先,通过I2C接口连接MAX30100传感器,周期性读取心率数据;同时通过单总线协议连接DHT11传感器获取体温数据。STM32对采集的数据进行初步处理(如滤波、单位转换)后,通过UART串口将数据发送给ESP8266-01S模块。

ESP8266-01S模块通过AT指令集与STM32交互,配置为STA模式连接无线网络,并基于MQTT协议接入华为云物联网平台。STM32将传感器数据封装为符合华为云物模型格式的JSON消息,由ESP8266发送至云平台指定主题。华为云物联网平台接收数据后,通过规则引擎将数据转发到云数据库进行持久化存储,同时支持数据导出功能。

上位机部分采用QT框架开发,使用Paho MQTT客户端库订阅华为云平台的数据主题。当接收到新数据时,解析JSON消息并更新实时显示面板。动态曲线图通过QChart组件实现,展示心率和体温的实时变化趋势。历史数据查询功能通过调用华为云API获取指定时间段的存储数据,并以表格形式展示。用户可在阈值设置面板自定义心率、体温的安全范围,当实时数据越界时,QT应用触发QSound播放警报音效,同时界面闪烁红色警示。

异常处理机制贯穿系统:STM32层面设置传感器通信超时检测;ESP8266建立重连机制应对网络波动;华为云规则引擎配置数据异常告警;上位机软件加入数据校验逻辑,防止无效数据触发误报警。各模块间通过状态指示灯反馈运行状态,便于调试与维护。

框架图

系统框架图

+-----------------------------------------------------------------+
|                     华为云物联网服务器                          |
| +-----------------------------+    +-------------------------+  |
| |     设备接入服务 (IoTDA)      |<->|       数据存储服务        |  |
| | - MQTT Broker               |    | - 长期存储传感器数据      |  |
| | - 设备认证与管理             |    | - CSV导出功能            |  |
| +-----------------------------+    +-------------------------+  |
|        ^                                          |            |
|        | MQTT over Internet                       | API调用    |
|        |                                          v            |
+--------|------------------------------------------|------------+
         |                                          |
         | WiFi                                    | Internet
+--------|-----------------+             +---------|----------------+
|  STM32F103C8T6 系统      |             |       上位机 QT应用       |
| +---------------------+  | UART        | +---------------------+ |
| |  传感器数据采集层     |<------------->| |  数据可视化与告警层   | |
| | - MAX30100 (I2C)    |  |    +------+ | | - 实时曲线图         | |
| |   → 心率数据         |  |    |ESP8266| | | - 历史数据查询      | |
| | - DHT11 (单总线)     |  |    |WiFi模| | | - 阈值报警系统      | |
| |   → 体温数据         |  |    +------+ | |   (声光警报)        | |
| +---------------------+  |             | +---------------------+ |
|        ^                  |             +-------------------------+
|        | 传感器接口        |
+--------|------------------+
         |
+--------|------------------+
|        v                  |
| +---------------------+   |
| |     传感器层         |   |
| | - MAX30100模块      |   |
| | - DHT11模块         |   |
| +---------------------+   |
+---------------------------+

模块说明:

  1. 传感器层

    • MAX30100:通过I2C协议提供心率数据
    • DHT11:通过单总线协议提供体温数据
  2. STM32F103C8T6系统

    • 传感器数据采集层:处理I2C/单总线协议,聚合传感器数据
    • ESP8266通信:通过UART串口将MQTT封装数据发送至WiFi模块
  3. 网络传输

    • ESP8266-01S:通过WiFi连接互联网,使用MQTT协议上传数据到华为云
  4. 华为云物联网平台

    • 设备接入服务(IoTDA):MQTT消息代理,实现设备认证和数据路由
    • 数据存储服务:长期存储结构化数据,支持CSV导出
  5. 上位机QT应用

    • 数据可视化:实时曲线图/历史数据图表渲染
    • 告警系统:阈值检测触发声光警报
    • MQTT订阅:通过Paho库从华为云获取实时数据

系统总体设计

系统总体设计

本系统构建了一个端到端的智能人体健康监测平台,以STM32F103C8T6微控制器为核心,整合传感器数据采集、无线通信、云平台存储与处理和上位机可视化功能。系统通过STM32实时采集用户的心率与体温数据,利用ESP8266 WiFi模块将数据安全传输至华为云物联网平台进行集中存储与分析。同时,开发基于QT框架的上位机应用,动态订阅云端数据流,实现数据的实时显示、历史回溯、阈值预警及管理功能。

在硬件层,STM32F103C8T6作为本地处理中枢。它通过I2C总线协议驱动MAX30100心率血氧传感器,精确获取心率信号;通过单总线协议与DHT11温湿度传感器通信,提取体温数据。采集到的数据经过初步处理后,STM32通过UART串口与ESP8266-01S WiFi模块交互,封装成符合MQTT协议的数据包,经无线网络上传至华为云物联网服务器。

华为云物联网平台作为系统的数据中心枢纽。它提供设备接入服务,接收并验证来自ESP8266的MQTT数据流。平台对传感器数据进行结构化存储,建立长期历史数据库,并提供数据导出为CSV格式的功能,确保数据的持久化与可追溯性。平台同时承担数据路由角色,将实时数据推送给已订阅的上位机应用。

上位机软件采用QT框架开发,集成Paho MQTT客户端库。该应用主动连接华为云服务器,订阅指定的MQTT主题以接收实时心率与体温数据流。软件核心功能包括:动态曲线绘制模块,以可视化图表展示实时生理参数变化;历史数据查询界面,支持按时间范围检索云端记录;可配置阈值管理面板,允许用户设定心率与体温的安全范围。当监测数据超越预设阈值时,软件立即触发声光警报机制,实现异常状态的即时预警。整个系统形成从终端感知、云端中继到上位机监控的闭环数据流,满足健康监测的实时性、可靠性与可管理性需求。

系统功能总结

功能模块 功能描述
数据采集 通过MAX30100心率血氧传感器(I2C协议)实时采集用户心率数据
通过DHT11温湿度传感器(单总线协议)实时采集用户体温数据
数据传输 STM32通过UART串口驱动ESP8266-01S WiFi模块,使用MQTT协议上传数据至华为云
云平台处理 华为云物联网服务器接收、存储和处理传感器数据
实现数据长期存储,支持CSV格式导出
上位机监控 QT应用通过Paho MQTT订阅华为云数据,实时显示心率和体温数值
动态曲线图展示实时数据变化趋势
数据分析 提供历史数据查询界面,支持按时间范围检索数据
允许设置心率/体温阈值,超限时触发声光警报

设计的各个功能模块描述

STM32F103C8T6微控制器作为核心处理单元,通过I2C协议与MAX30100心率血氧传感器通信,实时采集用户心率数据。同时利用单总线协议连接DHT11温湿度传感器获取体温数据,完成对两项健康指标的同步监测。

传感器数据通过UART串口传输至ESP8266-01S WiFi模块,该模块基于MQTT协议与华为云物联网服务器建立安全连接,实现采集数据的实时上传。华为云物联网平台配置专用设备接入服务,接收并存储传感器数据流,提供长期数据归档管理,支持通过云端接口导出CSV格式的历史数据文件。

上位机QT应用集成Paho MQTT客户端库,订阅华为云服务器的数据主题通道,实时解析心率体温数据流。软件界面动态绘制数据曲线图,直观展示实时变化趋势,同时提供历史数据查询面板,支持按时间范围检索云端存储记录。

异常监测模块通过独立配置界面设置心率体温阈值参数。当实时数据流超过安全范围时,QT应用立即触发声光报警系统,包括界面警示色闪烁与蜂鸣器提示。所有告警事件同步记录至历史数据库,便于后续追溯分析。

上位机代码设计

#include <QApplication>
#include <QMainWindow>
#include <QtMqtt/QMqttClient>
#include <QChart>
#include <QChartView>
#include <QLineSeries>
#include <QValueAxis>
#include <QDateTimeAxis>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QLabel>
#include <QLineEdit>
#include <QSpinBox>
#include <QMessageBox>
#include <QFileDialog>
#include <QSoundEffect>
#include <QTimer>

QT_CHARTS_USE_NAMESPACE

class MainWindow : public QMainWindow {
    Q_OBJECT
public:
    MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) {
        setupUI();
        setupDatabase();
        setupMQTT();
    }

private slots:
    void onConnected() {
        qDebug() << "Connected to Huawei Cloud!";
        mqttClient->subscribe("sensor/data");
    }

    void onMessageReceived(const QByteArray &message, const QString &topic) {
        QJsonDocument doc = QJsonDocument::fromJson(message);
        QJsonObject obj = doc.object();
        double heartRate = obj["heart_rate"].toDouble();
        double bodyTemp = obj["temperature"].toDouble();

        // 更新实时数据
        lblHeartRate->setText(QString::number(heartRate));
        lblTemperature->setText(QString::number(bodyTemp));

        // 添加数据到图表
        QDateTime now = QDateTime::currentDateTime();
        heartRateSeries->append(now.toMSecsSinceEpoch(), heartRate);
        tempSeries->append(now.toMSecsSinceEpoch(), bodyTemp);

        // 限制数据点数量
        if (heartRateSeries->count() > 100) heartRateSeries->remove(0);
        if (tempSeries->count() > 100) tempSeries->remove(0);

        // 更新图表范围
        chart->axisX()->setRange(now.addSecs(-60), now);
        chart->axisY()->setRange(0, 150);

        // 检查阈值报警
        checkThresholds(heartRate, bodyTemp);

        // 存储到数据库
        saveToDatabase(heartRate, bodyTemp);
    }

    void onThresholdChanged() {
        heartRateThreshold = spinHeartRate->value();
        tempThreshold = spinTemperature->value();
    }

    void onExportClicked() {
        QString fileName = QFileDialog::getSaveFileName(this, "Export CSV", "", "CSV Files (*.csv)");
        if (fileName.isEmpty()) return;

        QFile file(fileName);
        if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) return;

        QTextStream out(&file);
        out << "Timestamp,HeartRate,Temperature\n";

        QSqlQuery query("SELECT timestamp, heart_rate, temperature FROM sensor_data");
        while (query.next()) {
            out << query.value(0).toString() << ","
                << query.value(1).toString() << ","
                << query.value(2).toString() << "\n";
        }
        file.close();
    }

private:
    void setupUI() {
        QWidget *centralWidget = new QWidget(this);
        QVBoxLayout *mainLayout = new QVBoxLayout(centralWidget);

        // 实时数据显示
        QHBoxLayout *dataLayout = new QHBoxLayout();
        lblHeartRate = new QLabel("--", this);
        lblTemperature = new QLabel("--", this);
        dataLayout->addWidget(new QLabel("Heart Rate:"));
        dataLayout->addWidget(lblHeartRate);
        dataLayout->addWidget(new QLabel("°C Temperature:"));
        dataLayout->addWidget(lblTemperature);
        mainLayout->addLayout(dataLayout);

        // 图表初始化
        chart = new QChart();
        heartRateSeries = new QLineSeries();
        heartRateSeries->setName("Heart Rate");
        tempSeries = new QLineSeries();
        tempSeries->setName("Temperature");
        chart->addSeries(heartRateSeries);
        chart->addSeries(tempSeries);

        QDateTimeAxis *axisX = new QDateTimeAxis();
        axisX->setFormat("hh:mm:ss");
        chart->addAxis(axisX, Qt::AlignBottom);
        heartRateSeries->attachAxis(axisX);
        tempSeries->attachAxis(axisX);

        QValueAxis *axisY = new QValueAxis();
        axisY->setRange(0, 150);
        chart->addAxis(axisY, Qt::AlignLeft);
        heartRateSeries->attachAxis(axisY);
        tempSeries->attachAxis(axisY);

        chartView = new QChartView(chart);
        mainLayout->addWidget(chartView);

        // 阈值设置
        QHBoxLayout *thresholdLayout = new QHBoxLayout();
        spinHeartRate = new QSpinBox(this);
        spinHeartRate->setRange(40, 150);
        spinHeartRate->setValue(100);
        spinTemperature = new QSpinBox(this);
        spinTemperature->setRange(35, 42);
        spinTemperature->setValue(38);
        thresholdLayout->addWidget(new QLabel("Heart Rate Alert:"));
        thresholdLayout->addWidget(spinHeartRate);
        thresholdLayout->addWidget(new QLabel("°C Temp Alert:"));
        thresholdLayout->addWidget(spinTemperature);
        connect(spinHeartRate, SIGNAL(valueChanged(int)), this, SLOT(onThresholdChanged()));
        connect(spinTemperature, SIGNAL(valueChanged(int)), this, SLOT(onThresholdChanged()));

        // 报警指示器
        alarmIndicator = new QLabel("NORMAL", this);
        alarmIndicator->setStyleSheet("background-color: green; color: white; padding: 5px;");
        thresholdLayout->addWidget(alarmIndicator);

        // 导出按钮
        QPushButton *exportButton = new QPushButton("Export CSV", this);
        thresholdLayout->addWidget(exportButton);
        connect(exportButton, SIGNAL(clicked()), this, SLOT(onExportClicked()));

        mainLayout->addLayout(thresholdLayout);

        setCentralWidget(centralWidget);
        resize(800, 600);

        // 初始化阈值
        heartRateThreshold = 100;
        tempThreshold = 38;

        // 报警音效
        alarmSound = new QSoundEffect(this);
        alarmSound->setSource(QUrl::fromLocalFile("alarm.wav"));
    }

    void setupDatabase() {
        QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
        db.setDatabaseName("sensor_data.db");
        if (!db.open()) {
            QMessageBox::critical(this, "Database Error", db.lastError().text());
            return;
        }

        QSqlQuery query;
        query.exec("CREATE TABLE IF NOT EXISTS sensor_data ("
                   "id INTEGER PRIMARY KEY AUTOINCREMENT, "
                   "timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, "
                   "heart_rate REAL, "
                   "temperature REAL)");
    }

    void setupMQTT() {
        mqttClient = new QMqttClient(this);
        mqttClient->setHostname("your_huaweicloud_address"); // 替换实际地址
        mqttClient->setPort(1883);
        mqttClient->setClientId("your_client_id");
        mqttClient->setUsername("your_username");
        mqttClient->setPassword("your_password");
        connect(mqttClient, &QMqttClient::connected, this, &MainWindow::onConnected);
        connect(mqttClient, &QMqttClient::messageReceived, this, &MainWindow::onMessageReceived);
        mqttClient->connectToHost();
    }

    void checkThresholds(double heartRate, double temperature) {
        bool isAlarm = false;
        if (heartRate > heartRateThreshold) {
            alarmIndicator->setText("HIGH HEART RATE!");
            isAlarm = true;
        }
        if (temperature > tempThreshold) {
            alarmIndicator->setText("HIGH TEMPERATURE!");
            isAlarm = true;
        }

        if (isAlarm) {
            alarmIndicator->setStyleSheet("background-color: red; color: white; padding: 5px;");
            alarmSound->play();
        } else {
            alarmIndicator->setText("NORMAL");
            alarmIndicator->setStyleSheet("background-color: green; color: white; padding: 5px;");
        }
    }

    void saveToDatabase(double heartRate, double temperature) {
        QSqlQuery query;
        query.prepare("INSERT INTO sensor_data (heart_rate, temperature) VALUES (?, ?)");
        query.addBindValue(heartRate);
        query.addBindValue(temperature);
        query.exec();
    }

    // UI Components
    QLabel *lblHeartRate;
    QLabel *lblTemperature;
    QLabel *alarmIndicator;
    QChart *chart;
    QChartView *chartView;
    QLineSeries *heartRateSeries;
    QLineSeries *tempSeries;
    QSpinBox *spinHeartRate;
    QSpinBox *spinTemperature;
    QSoundEffect *alarmSound;

    // MQTT Client
    QMqttClient *mqttClient;

    // Thresholds
    int heartRateThreshold;
    int tempThreshold;
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    
    // 初始化数据库驱动
    QSqlDatabase::addDatabase("QSQLITE");

    MainWindow mainWin;
    mainWin.show();
    return app.exec();
}

#include "main.moc"  // 包含元对象编译器生成的代码

代码说明

  1. MQTT通信

    • 使用QMqttClient连接华为云IoT平台
    • 订阅sensor/data主题接收传感器数据
    • 消息格式为JSON:{"heart_rate": 75.3, "temperature": 36.5}
  2. 实时数据显示

    • 顶部标签显示最新心率和体温值
    • 使用QChart动态绘制实时曲线图
    • 自动调整X轴时间范围为最近60秒
  3. 阈值报警

    • 设置心率/体温阈值(默认100bpm/38℃)
    • 超出阈值时:
      • 界面显示红色警报标识
      • 播放报警音效(需准备alarm.wav文件)
    • 正常状态显示绿色"NORMAL"标识
  4. 数据存储与导出

    • 使用SQLite数据库存储历史数据
    • 数据表结构:timestamp | heart_rate | temperature
    • CSV导出功能包含时间戳和传感器数据
  5. 华为云配置

    • 需替换实际连接参数:

      mqttClient->setHostname("your_huaweicloud_address");
      mqttClient->setClientId("your_client_id");
      mqttClient->setUsername("your_username");
      mqttClient->setPassword("your_password");
      

使用说明

  1. 依赖库

    • Qt 5.15+(需包含Charts、Sql、Network模块)
    • QtMqtt模块(官方或第三方实现)
  2. 运行准备

    • 在项目目录放置报警音效文件alarm.wav
    • 配置正确的华为云连接参数
    • 确保MQTT主题与设备端发布主题一致
  3. 功能操作

    • 通过旋钮控件设置报警阈值
    • 点击"Export CSV"导出历史数据
    • 实时曲线自动滚动显示最新100个数据点

模块代码设计

以下是基于STM32F103C8T6的完整传感器驱动代码(寄存器开发方式),包含MAX30100心率传感器和DHT11温湿度传感器的驱动实现:

#include "stm32f10x.h"

// 硬件定义
#define DHT11_GPIO_PORT    GPIOB
#define DHT11_GPIO_PIN     GPIO_Pin_0
#define DHT11_RCC_PORT     RCC_APB2Periph_GPIOB

// I2C引脚定义
#define I2C_PORT           GPIOB
#define I2C_SCL_PIN        GPIO_Pin_6
#define I2C_SDA_PIN        GPIO_Pin_7
#define I2C_RCC            RCC_APB2Periph_GPIOB
#define I2C_SPEED          400000  // 400kHz

// MAX30100寄存器地址
#define MAX30100_ADDR      0xAE    // 7位地址 << 1
#define REG_INTR_STATUS_1  0x00
#define REG_INTR_ENABLE_1  0x01
#define REG_FIFO_WR_PTR    0x04
#define REG_FIFO_RD_PTR    0x06
#define REG_FIFO_DATA      0x07
#define REG_MODE_CONFIG    0x09
#define REG_SPO2_CONFIG    0x0A
#define REG_LED1_PA        0x0C
#define REG_LED2_PA        0x0D
#define REG_TEMP_INTG      0x16
#define REG_TEMP_FRAC      0x17

// 函数声明
void SystemInit(void);
void Delay_us(uint32_t us);
void Delay_ms(uint32_t ms);
void GPIO_Configuration(void);
void USART1_Init(void);
void I2C_Init(void);
void I2C_Start(void);
void I2C_Stop(void);
uint8_t I2C_WaitAck(void);
void I2C_Ack(void);
void I2C_NAck(void);
void I2C_SendByte(uint8_t data);
uint8_t I2C_ReadByte(void);
uint8_t MAX30100_ReadReg(uint8_t reg);
void MAX30100_WriteReg(uint8_t reg, uint8_t value);
void MAX30100_Init(void);
int MAX30100_ReadFIFO(uint32_t *red, uint32_t *ir);
void DHT11_Start(void);
uint8_t DHT11_CheckResponse(void);
uint8_t DHT11_ReadByte(void);
uint8_t DHT11_ReadData(uint8_t *temp, uint8_t *humi);

// 系统时钟初始化 (72MHz HSE)
void SystemInit(void) {
    RCC->CR |= RCC_CR_HSEON;      // 开启HSE
    while(!(RCC->CR & RCC_CR_HSERDY)); // 等待HSE就绪
    
    FLASH->ACR |= FLASH_ACR_LATENCY_2; // Flash等待周期
    RCC->CFGR |= RCC_CFGR_PLLMULL9;    // PLL倍频9倍
    RCC->CFGR |= RCC_CFGR_PLLSRC;      // PLL源选择HSE
    
    RCC->CR |= RCC_CR_PLLON;           // 开启PLL
    while(!(RCC->CR & RCC_CR_PLLRDY)); // 等待PLL就绪
    
    RCC->CFGR |= RCC_CFGR_SW_PLL;      // 系统时钟切换到PLL
    while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); // 等待切换完成
    
    SystemCoreClock = 72000000;
}

// 微秒级延时
void Delay_us(uint32_t us) {
    SysTick->LOAD = SystemCoreClock / 8000000 * us;
    SysTick->VAL = 0;
    SysTick->CTRL = SysTick_CTRL_ENABLE_Msk;
    while(!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk));
    SysTick->CTRL = 0;
}

// 毫秒级延时
void Delay_ms(uint32_t ms) {
    while(ms--) Delay_us(1000);
}

// GPIO初始化
void GPIO_Configuration(void) {
    RCC->APB2ENR |= RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB;
    
    // DHT11配置 (PB0)
    GPIOB->CRL &= ~(GPIO_CRL_CNF0 | GPIO_CRL_MODE0);
    GPIOB->CRL |= GPIO_CRL_MODE0;  // 推挽输出模式
    
    // USART1 TX (PA9)
    GPIOA->CRH &= ~(GPIO_CRH_CNF9 | GPIO_CRH_MODE9);
    GPIOA->CRH |= GPIO_CRH_CNF9_1 | GPIO_CRH_MODE9; // 复用推挽输出
}

// USART1初始化 (115200)
void USART1_Init(void) {
    RCC->APB2ENR |= RCC_APB2Periph_USART1;
    
    USART1->BRR = 72000000 / 115200; // 波特率
    USART1->CR1 = USART_CR1_UE | USART_CR1_TE; // 使能USART和发送
    USART1->CR2 = 0;
    USART1->CR3 = 0;
}

// I2C初始化
void I2C_Init(void) {
    RCC->APB2ENR |= I2C_RCC;
    
    // 配置PB6(SCL), PB7(SDA)为开漏输出
    GPIOB->CRL &= ~(GPIO_CRL_CNF6 | GPIO_CRL_MODE6 | GPIO_CRL_CNF7 | GPIO_CRL_MODE7);
    GPIOB->CRL |= GPIO_CRL_CNF6 | GPIO_CRL_MODE6 | GPIO_CRL_CNF7 | GPIO_CRL_MODE7;
    
    RCC->APB1ENR |= RCC_APB1Periph_I2C1;
    I2C1->CR1 = I2C_CR1_SWRST;
    I2C1->CR1 = 0;
    
    I2C1->CR2 = SystemCoreClock / 1000000; // 设置时钟频率
    I2C1->CCR = SystemCoreClock / (2 * I2C_SPEED); // 设置CCR
    I2C1->TRISE = SystemCoreClock / 1000000 + 1; // 设置TRISE
    
    I2C1->CR1 |= I2C_CR1_PE; // 使能I2C
}

// I2C起始信号
void I2C_Start(void) {
    I2C1->CR1 |= I2C_CR1_START;
    while(!(I2C1->SR1 & I2C_SR1_SB));
}

// I2C停止信号
void I2C_Stop(void) {
    I2C1->CR1 |= I2C_CR1_STOP;
    while(I2C1->CR1 & I2C_CR1_STOP);
}

// 等待ACK
uint8_t I2C_WaitAck(void) {
    if(I2C1->SR1 & I2C_SR1_AF) {
        I2C1->SR1 &= ~I2C_SR1_AF;
        return 1; // NACK
    }
    return 0; // ACK
}

// 发送ACK
void I2C_Ack(void) {
    I2C1->CR1 &= ~I2C_CR1_ACK;
}

// 发送NACK
void I2C_NAck(void) {
    I2C1->CR1 |= I2C_CR1_ACK;
}

// I2C发送一个字节
void I2C_SendByte(uint8_t data) {
    I2C1->DR = data;
    while(!(I2C1->SR1 & I2C_SR1_TXE));
}

// I2C读取一个字节
uint8_t I2C_ReadByte(void) {
    while(!(I2C1->SR1 & I2C_SR1_RXNE));
    return I2C1->DR;
}

// MAX30100读取寄存器
uint8_t MAX30100_ReadReg(uint8_t reg) {
    uint8_t value;
    I2C_Start();
    I2C_SendByte(MAX30100_ADDR);
    I2C_WaitAck();
    I2C_SendByte(reg);
    I2C_WaitAck();
    I2C_Start();
    I2C_SendByte(MAX30100_ADDR | 0x01);
    I2C_WaitAck();
    value = I2C_ReadByte();
    I2C_NAck();
    I2C_Stop();
    return value;
}

// MAX30100写入寄存器
void MAX30100_WriteReg(uint8_t reg, uint8_t value) {
    I2C_Start();
    I2C_SendByte(MAX30100_ADDR);
    I2C_WaitAck();
    I2C_SendByte(reg);
    I2C_WaitAck();
    I2C_SendByte(value);
    I2C_WaitAck();
    I2C_Stop();
}

// MAX30100初始化
void MAX30100_Init(void) {
    MAX30100_WriteReg(REG_MODE_CONFIG, 0x03); // HR模式
    MAX30100_WriteReg(REG_SPO2_CONFIG, 0x27); // 100Hz, 16位分辨率
    MAX30100_WriteReg(REG_LED1_PA, 0x24);     // LED1电流=36mA
    MAX30100_WriteReg(REG_LED2_PA, 0x24);     // LED2电流=36mA
    MAX30100_WriteReg(REG_FIFO_WR_PTR, 0x00); // 重置FIFO
    MAX30100_WriteReg(REG_FIFO_RD_PTR, 0x00);
}

// 从FIFO读取数据
int MAX30100_ReadFIFO(uint32_t *red, uint32_t *ir) {
    uint8_t buffer[6];
    uint8_t i;
    
    if(MAX30100_ReadReg(REG_FIFO_WR_PTR) == MAX30100_ReadReg(REG_FIFO_RD_PTR)) 
        return 0; // FIFO空
        
    I2C_Start();
    I2C_SendByte(MAX30100_ADDR);
    I2C_WaitAck();
    I2C_SendByte(REG_FIFO_DATA);
    I2C_WaitAck();
    I2C_Start();
    I2C_SendByte(MAX30100_ADDR | 0x01);
    I2C_WaitAck();
    
    for(i=0; i<5; i++) {
        buffer[i] = I2C_ReadByte();
        I2C_Ack();
    }
    buffer[5] = I2C_ReadByte();
    I2C_NAck();
    I2C_Stop();
    
    *red = (buffer[0] << 16) | (buffer[1] << 8) | buffer[2];
    *ir = (buffer[3] << 16) | (buffer[4] << 8) | buffer[5];
    return 1;
}

// DHT11启动信号
void DHT11_Start(void) {
    GPIOB->CRL &= ~GPIO_CRL_MODE0; // 输出模式
    GPIOB->BRR = DHT11_GPIO_PIN;   // 拉低
    Delay_ms(18);                  // 保持18ms
    GPIOB->BSRR = DHT11_GPIO_PIN;  // 释放总线
    GPIOB->CRL |= GPIO_CRL_CNF0_0; // 输入模式(浮空)
    Delay_us(30);
}

// 检测DHT11响应
uint8_t DHT11_CheckResponse(void) {
    uint8_t retry = 0;
    while(GPIOB->IDR & DHT11_GPIO_PIN) {
        if(retry++ > 100) return 0;
        Delay_us(1);
    }
    retry = 0;
    while(!(GPIOB->IDR & DHT11_GPIO_PIN)) {
        if(retry++ > 100) return 0;
        Delay_us(1);
    }
    retry = 0;
    while(GPIOB->IDR & DHT11_GPIO_PIN) {
        if(retry++ > 100) return 0;
        Delay_us(1);
    }
    return 1;
}

// DHT11读取一个字节
uint8_t DHT11_ReadByte(void) {
    uint8_t data = 0;
    for(int i=0; i<8; i++) {
        while(!(GPIOB->IDR & DHT11_GPIO_PIN)); // 等待低电平结束
        Delay_us(40);
        if(GPIOB->IDR & DHT11_GPIO_PIN) 
            data |= (1 << (7-i));
        while(GPIOB->IDR & DHT11_GPIO_PIN); // 等待高电平结束
    }
    return data;
}

// DHT11读取数据
uint8_t DHT11_ReadData(uint8_t *temp, uint8_t *humi) {
    uint8_t buf[5];
    DHT11_Start();
    if(!DHT11_CheckResponse()) return 0;
    for(int i=0; i<5; i++)
        buf[i] = DHT11_ReadByte();
    if(buf[4] != (buf[0]+buf[1]+buf[2]+buf[3])) 
        return 0; // 校验失败
    *humi = buf[0];
    *temp = buf[2];
    return 1;
}

// 主函数
int main(void) {
    uint8_t temperature, humidity;
    uint32_t heart_rate, spo2;
    char uart_buf[64];
    
    SystemInit();
    GPIO_Configuration();
    USART1_Init();
    I2C_Init();
    MAX30100_Init();
    
    while(1) {
        // 读取DHT11数据
        if(DHT11_ReadData(&temperature, &humidity)) {
            sprintf(uart_buf, "TEMP:%dC,HUMI:%d%%\r\n", temperature, humidity);
            for(char *p = uart_buf; *p; p++) {
                while(!(USART1->SR & USART_SR_TXE));
                USART1->DR = *p;
            }
        }
        
        // 读取MAX30100数据
        if(MAX30100_ReadFIFO(&spo2, &heart_rate)) {
            sprintf(uart_buf, "HR:%lu,SpO2:%lu\r\n", heart_rate, spo2);
            for(char *p = uart_buf; *p; p++) {
                while(!(USART1->SR & USART_SR_TXE));
                USART1->DR = *p;
            }
        }
        
        Delay_ms(1000); // 每秒采集一次
    }
}

代码说明:

  1. 系统初始化

    • 配置72MHz系统时钟(HSE外部晶振)
    • 实现精确延时函数(us/ms级)
  2. I2C驱动

    • 完全寄存器方式实现I2C协议
    • 支持400kHz高速模式
    • 包含起始/停止/ACK/NACK等基本操作
  3. MAX30100驱动

    • 初始化配置为心率监测模式
    • 实现寄存器读写函数
    • FIFO数据读取功能
    • 支持红光(red)和红外(ir)双通道数据采集
  4. DHT11驱动

    • 单总线协议实现
    • 严格的时序控制(us级延时)
    • 数据校验机制
    • 同时获取温度和湿度数据
  5. USART通信

    • 配置115200波特率
    • 通过PA9(TX)发送传感器数据到ESP8266

使用说明:

  1. 硬件连接:

    • MAX30100: SCL→PB6, SDA→PB7
    • DHT11: DATA→PB0
    • ESP8266: TX→PA10(RX), RX→PA9(TX)
  2. 数据格式:

    • 温度/湿度: “TEMP:25C,HUMI:45%”
    • 心率/血氧: “HR:75,SpO2:98”
  3. 工作流程:

    • 系统每1秒采集一次传感器数据
    • 通过串口发送给ESP8266模块
    • ESP8266通过MQTT协议转发至华为云

项目核心代码

以下是基于STM32F103C8T6的智能健康监测系统main.c完整代码(寄存器开发方式):

#include "stm32f10x.h"

// 传感器数据结构
typedef struct {
    uint16_t heart_rate;
    float temperature;
} SensorData;

// 函数声明
void SystemClock_Config(void);
void GPIO_Config(void);
void USART1_Init(void);
void USART2_Init(void);
void I2C1_Init(void);
void DHT11_Init(void);
void SysTick_Init(void);
void Delay_us(uint32_t us);
void Delay_ms(uint32_t ms);
uint8_t MAX30100_ReadHeartRate(uint16_t *hr);
uint8_t DHT11_ReadTemp(float *temp);
void ESP8266_SendCommand(char *cmd, char *ack, uint32_t timeout);
void MQTT_PublishData(SensorData data);

int main(void) {
    // 系统初始化
    SystemClock_Config();
    GPIO_Config();
    USART1_Init();  // 调试串口
    USART2_Init();  // ESP8266通信
    I2C1_Init();    // MAX30100
    DHT11_Init();   // DHT11
    SysTick_Init();
    
    // 发送AT指令初始化ESP8266
    ESP8266_SendCommand("AT\r\n", "OK", 1000);
    ESP8266_SendCommand("AT+CWMODE=1\r\n", "OK", 1000);
    ESP8266_SendCommand("AT+CWJAP=\"YourSSID\",\"YourPassword\"\r\n", "GOT IP", 5000);
    ESP8266_SendCommand("AT+MQTTUSERCFG=0,1,\"DeviceID\",\"Username\",\"Password\",0,0,\"\"\r\n", "OK", 1000);
    ESP8266_SendCommand("AT+MQTTCONN=0,\"YourHuaweiCloudAddress\",1883,1\r\n", "OK", 5000);
    
    SensorData sensor_data;
    uint32_t last_send = 0;
    
    while (1) {
        // 每2秒采集并发送数据
        if (HAL_GetTick() - last_send >= 2000) {
            // 读取心率传感器
            if (MAX30100_ReadHeartRate(&sensor_data.heart_rate) == 0) {
                // 串口调试输出
                USART_SendData(USART1, 'H');
                USART_SendData(USART1, ':');
                USART_SendData(USART1, (sensor_data.heart_rate/100) + '0');
                USART_SendData(USART1, (sensor_data.heart_rate%100/10) + '0');
                USART_SendData(USART1, (sensor_data.heart_rate%10) + '0');
                USART_SendData(USART1, '\n');
            }
            
            // 读取温度传感器
            if (DHT11_ReadTemp(&sensor_data.temperature) == 0) {
                USART_SendData(USART1, 'T');
                USART_SendData(USART1, ':');
                int temp_int = (int)sensor_data.temperature;
                USART_SendData(USART1, temp_int/10 + '0');
                USART_SendData(USART1, temp_int%10 + '0');
                USART_SendData(USART1, '.');
                int temp_dec = (int)(sensor_data.temperature*10)%10;
                USART_SendData(USART1, temp_dec + '0');
                USART_SendData(USART1, '\n');
            }
            
            // 通过MQTT上传到华为云
            MQTT_PublishData(sensor_data);
            
            last_send = HAL_GetTick();
        }
    }
}

// MQTT数据发布函数
void MQTT_PublishData(SensorData data) {
    char mqtt_msg[50];
    sprintf(mqtt_msg, "{\"heart_rate\":%d,\"temperature\":%.1f}", 
            data.heart_rate, data.temperature);
    
    char mqtt_cmd[100];
    sprintf(mqtt_cmd, "AT+MQTTPUB=0,\"Your/Topic\",\"%s\",1,0\r\n", mqtt_msg);
    
    ESP8266_SendCommand(mqtt_cmd, "OK", 1000);
}

// ESP8266命令发送函数
void ESP8266_SendCommand(char *cmd, char *ack, uint32_t timeout) {
    // 发送命令
    while (*cmd) {
        USART_SendData(USART2, *cmd++);
        while (!(USART2->SR & USART_SR_TC));
    }
    
    // 等待响应(简化实现)
    Delay_ms(timeout);
}

// 系统时钟配置(72MHz HSE)
void SystemClock_Config(void) {
    RCC->CR |= RCC_CR_HSEON;             // 开启HSE
    while (!(RCC->CR & RCC_CR_HSERDY));  // 等待HSE就绪
    
    // 配置PLL:HSE * 9 = 72MHz
    RCC->CFGR |= RCC_CFGR_PLLMULL9 | RCC_CFGR_PLLSRC;
    RCC->CR |= RCC_CR_PLLON;             // 开启PLL
    while (!(RCC->CR & RCC_CR_PLLRDY));  // 等待PLL就绪
    
    // 配置FLASH预取指
    FLASH->ACR |= FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY_1;
    
    // 切换系统时钟
    RCC->CFGR |= RCC_CFGR_SW_PLL;
    while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); // 等待切换完成
}

// USART1初始化(调试串口,115200bps)
void USART1_Init(void) {
    RCC->APB2ENR |= RCC_APB2ENR_USART1EN | RCC_APB2ENR_IOPAEN;
    
    // PA9(TX)复用推挽输出,PA10(RX)浮空输入
    GPIOA->CRH &= ~(GPIO_CRH_CNF9 | GPIO_CRH_MODE9);
    GPIOA->CRH |= GPIO_CRH_CNF9_1 | GPIO_CRH_MODE9;
    GPIOA->CRH &= ~(GPIO_CRH_CNF10 | GPIO_CRH_MODE10);
    GPIOA->CRH |= GPIO_CRH_CNF10_0;
    
    // 波特率设置:72MHz/(16*39.0625)=115200
    USART1->BRR = 0x0271;
    USART1->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;
}

// USART2初始化(ESP8266通信,115200bps)
void USART2_Init(void) {
    RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
    
    // PA2(TX)复用推挽输出,PA3(RX)浮空输入
    GPIOA->CRL &= ~(GPIO_CRL_CNF2 | GPIO_CRL_MODE2);
    GPIOA->CRL |= GPIO_CRL_CNF2_1 | GPIO_CRL_MODE2;
    GPIOA->CRL &= ~(GPIO_CRL_CNF3 | GPIO_CRL_MODE3);
    GPIOA->CRL |= GPIO_CRL_CNF3_0;
    
    // 波特率设置
    USART2->BRR = 0x0271;
    USART2->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;
}

// I2C1初始化(MAX30100)
void I2C1_Init(void) {
    RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
    RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
    
    // PB6(SCL)/PB7(SDA) 复用开漏输出
    GPIOB->CRL &= ~(GPIO_CRL_CNF6 | GPIO_CRL_MODE6 | 
                   GPIO_CRL_CNF7 | GPIO_CRL_MODE7);
    GPIOB->CRL |= GPIO_CRL_CNF6_1 | GPIO_CRL_MODE6 |
                 GPIO_CRL_CNF7_1 | GPIO_CRL_MODE7;
    
    // I2C配置
    I2C1->CR2 = 36;        // APB1时钟36MHz
    I2C1->CCR = 180;       // 100kHz标准模式
    I2C1->TRISE = 37;      // 最大上升时间
    I2C1->CR1 = I2C_CR1_PE; // 使能I2C
}

// DHT11初始化(PA0)
void DHT11_Init(void) {
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
    GPIOA->CRL &= ~GPIO_CRL_MODE0; // 初始化为输出模式
    GPIOA->BSRR = GPIO_BSRR_BS0;   // 输出高电平
}

// SysTick初始化(1ms中断)
void SysTick_Init(void) {
    SysTick->LOAD = 72000 - 1;      // 72MHz/1000 = 72kHz
    SysTick->VAL = 0;
    SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | 
                   SysTick_CTRL_TICKINT_Msk | 
                   SysTick_CTRL_ENABLE_Msk;
}

// 微秒级延时
void Delay_us(uint32_t us) {
    uint32_t start = SysTick->VAL;
    uint32_t ticks = us * 72;  // 72MHz时钟
    while ((start - SysTick->VAL) < ticks);
}

// 毫秒级延时
void Delay_ms(uint32_t ms) {
    while (ms--) {
        Delay_us(1000);
    }
}

// GPIO配置
void GPIO_Config(void) {
    // 启用GPIO时钟
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | 
                   RCC_APB2ENR_IOPCEN;
    
    // LED指示灯(PC13)
    GPIOC->CRH &= ~GPIO_CRH_CNF13;
    GPIOC->CRH |= GPIO_CRH_MODE13;
    GPIOC->BSRR = GPIO_BSRR_BS13;  // 初始关闭
}

关键代码说明:

  1. 系统初始化

    • 配置72MHz系统时钟(HSE+PLL)
    • 初始化调试串口(USART1)和ESP8266通信串口(USART2)
    • 配置I2C接口用于MAX30100心率传感器
    • 配置GPIO用于DHT11温湿度传感器
  2. 传感器驱动

    • MAX30100_ReadHeartRate() 通过I2C读取心率数据
    • DHT11_ReadTemp() 通过单总线协议读取温度
  3. 网络通信

    • ESP8266_SendCommand() 发送AT指令配置WiFi和MQTT
    • MQTT_PublishData() 封装JSON格式数据发布到华为云
  4. 数据处理

    • 每2秒采集一次传感器数据
    • 通过串口输出调试信息
    • 数据格式:{"heart_rate":75,"temperature":36.5}
  5. 华为云对接

    • 使用标准MQTT协议发布数据
    • 需替换实际参数:
      • YourSSID/YourPassword:WiFi凭证
      • YourHuaweiCloudAddress:华为云IoT地址
      • DeviceID/Username/Password:华为云设备认证信息
      • Your/Topic:华为云数据上报主题

总结

本设计成功构建了一套完整的智能人体健康监测系统。该系统以STM32F103C8T6微控制器为核心,高效整合了MAX30100心率血氧传感器和DHT11温湿度传感器,通过I2C和单总线协议精确采集用户的心率与体温数据,为健康监测提供了可靠的数据来源。

系统借助ESP8266-01S WiFi模块,利用UART串口通信和MQTT协议,实现了传感器数据的稳定、实时无线传输。数据被安全、高效地上传至华为云物联网平台,该平台承担了数据的接收、存储、处理及长期管理的核心任务,并支持关键数据的导出功能,为健康数据分析提供了坚实基础。

上位机软件基于QT框架开发,通过集成Paho MQTT客户端库与华为云建立连接,实现了数据的实时接收与解析。软件界面友好,功能丰富,不仅动态展示实时数据曲线,还提供了历史数据查询、心率与体温安全阈值设定等功能,并在数据异常时触发显著的声光报警,有效提升了系统的预警能力与用户交互体验。

综上所述,该毕业设计实现了从终端数据采集、云端传输存储到上位机可视化监控与预警的全流程闭环。系统结构清晰,功能完备,具备良好的实用性和扩展性,为远程健康监护提供了一套可行的技术方案,具有实际应用价值。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。