项目开发背景
随着城市化进程加速和居民生活水平提升,人们对家居环境健康与安全的需求日益增强。现代住宅常因装修材料、密闭空间及外部污染导致甲醛超标、PM2.5浓度升高、温湿度失衡等问题,长期暴露此类环境中易引发呼吸道疾病、过敏反应等健康隐患。传统环境监测设备功能单一、数据孤立,且缺乏远程管理能力,难以满足用户对室内环境实时掌控和主动干预的需求。
物联网技术的快速发展为解决上述问题提供了新路径。通过集成多类型传感器与云平台,可实现环境参数的动态采集、云端存储及智能分析。同时,智能家居系统对本地化人机交互、异常即时报警及远程控制提出了更高要求,亟需一种低成本、高集成度的解决方案。
本项目基于STM32F103C8T6微控制器,结合多传感器融合技术、华为云物联网平台及QT跨平台应用,构建一套完整的智能家居健康环境监测系统。该系统不仅能够实时监测关键环境指标,还能通过阈值判定自动触发净化设备,并结合声光报警与可视化界面实现"监测-预警-控制"闭环管理,有效提升居住环境的健康性与安全性,为智慧家居的普及提供技术支撑。
设计实现的功能
(1)STM32F103C8T6主控协调传感器采集、数据处理及通信逻辑。
(2)华为云物联网平台实现设备接入、数据存储和指令下发。
(3)QT上位机通过MQTT协议订阅云平台数据,提供人机交互界面。
(4)环境异常时自动触发本地声光报警(蜂鸣器+LED)。
(5)支持手动/自动双模式:自动模式下依据阈值控制设备启停。
项目硬件模块组成
(1)主控芯片:STM32F103C8T6最小系统板
(2)传感器:DHT11温湿度传感器、BH1750光照传感器、GP2Y1010AU0F PM2.5传感器、ZE08-CH2O甲醛传感器
(3)通信模块:ESP8266-01S WiFi模块(串口AT指令通信)
(4)执行单元:5V继电器模块(控制空气净化器电源)
(5)报警单元:有源蜂鸣器、高亮LED灯(红/绿双色)
(6)显示单元:0.96英寸I2C接口OLED显示屏
(7)辅助硬件:按键模块(本地模式切换)、USB转TTL模块(调试)
当前项目使用的相关软件工具、模块源码已经上传到网盘:https://ccnr8sukk85n.feishu.cn/wiki/QjY8weDYHibqRYkFP2qcA9aGnvb?from=from_copylink
设计意义
该系统的设计意义主要体现在以下方面:
该系统的核心价值在于提升居住环境健康水平与生活安全性。通过实时监测甲醛、PM2.5等直接影响呼吸健康的污染物浓度,以及温湿度、光照等影响舒适度的关键参数,系统能及时揭示潜在的环境风险。当检测值超过安全阈值时,本地声光报警与上位机弹窗警示可第一时间提醒用户采取干预措施,有效降低因空气质量问题引发的健康隐患,为家庭创造更安全的居住条件。
其设计显著增强了环境管理的智能化与便捷性。系统打破了传统手动监测的局限,实现了环境数据的自动采集、云端同步及可视化呈现。用户不仅可通过QT界面远程查看实时数据与历史趋势,还能直接控制净化设备启停。自动模式下的阈值联动控制进一步解放了人力,使环境调节更加主动高效,大幅提升了用户对居住环境的掌控能力与生活便利性。
系统构建了完整的数据追溯与决策支持体系。华为云平台对历史数据的持久化存储,结合QT上位机的曲线分析功能,使用户能够清晰追踪环境参数的变化规律。这种长期数据积累不仅有助于评估净化设备效果,更能为优化室内通风策略、识别污染源提供客观依据,使得环境管理决策更具科学性和针对性。
此外,项目体现了嵌入式技术与物联网平台的深度整合应用。以STM32为核心,协调多类型传感器采集、本地显示、报警输出及继电器控制,并通过ESP8266实现与华为云的稳定通信,展示了资源受限单片机在复杂物联网系统中的核心枢纽作用。这种端-云协同架构的成功实践,为同类智能环境监测应用提供了可靠的技术范本,具有较强的工程推广价值。
设计思路
设计思路围绕STM32F103C8T6主控展开,通过模块化方式实现功能集成。首先,STM32通过GPIO和I2C接口驱动传感器阵列:DHT11采集温湿度(单总线协议),BH1750获取光照强度(I2C),GP2Y1010AU0F通过ADC读取PM2.5模拟电压,ZE08-CH2O通过串口获取甲醛数据。所有传感器数据经滤波校准后,由主控进行格式化处理。
通信层采用ESP8266-01S WiFi模块,通过STM32的USART发送AT指令建立MQTT连接,将JSON格式数据上传至华为云物联网平台。上传频率设定为5秒/次,同时订阅云平台下发的控制指令。当收到继电器控制指令时,STM32解析指令并通过GPIO控制继电器开关状态,驱动空气净化设备电源通断。
本地交互部分设计双模式逻辑:按键触发模式切换,自动模式下STM32实时比对各传感器数据与预设阈值(存储于Flash),若PM2.5或甲醛超标,立即触发红色LED闪烁及蜂鸣器鸣响,同时根据阈值自动启停继电器;手动模式下则依赖远程指令。OLED通过I2C驱动,分区域显示实时数据、设备状态(ON/OFF)及当前模式标识(A/M)。
华为云平台配置规则引擎存储历史数据,QT上位机通过MQTT库订阅实时数据流,并调用HTTP API查询历史记录。QT界面采用多线程设计:主线程渲染数据仪表盘及曲线图(QChart库实现),子线程监测数据阈值,超标时触发QMessageBox弹窗和QSound报警。控制界面嵌入继电器开关按钮,点击后通过MQTT发布控制指令至云平台。
异常处理机制包含硬件看门狗和通信超时重连:若WiFi断开,STM32尝试重新配网并在OLED显示错误代码;若传感器失效,保留上一次有效数据并点亮特定错误指示灯。整个系统采用低功耗设计,STM32在采集间隙切换至休眠模式,由定时器中断唤醒。
框架图
框架图说明:
感知层
- 传感器通过GPIO/I2C/UART与STM32连接
- 采集温湿度/光照/PM2.5/甲醛数据
控制层
- STM32F103C8T6核心处理:
? 控制继电器开关设备
? 驱动OLED显示实时数据
? 触发声光报警(LED+蜂鸣器)
? 处理模式切换按键
? 通过ESP8266上传数据至华为云
- STM32F103C8T6核心处理:
云平台层
- 华为云物联网平台:
? MQTT协议双向通信
? 存储历史环境数据
? 转发控制指令
- 华为云物联网平台:
应用层
- QT上位机实现:
? 实时监测数据仪表盘
? 阈值超限声光报警
? 远程继电器控制界面
? 历史数据曲线分析
- QT上位机实现:
系统总体设计
系统以STM32F103C8T6微控制器为核心,通过其丰富的外设接口协调各模块工作。传感器单元通过不同物理接口连接主控:DHT11温湿度传感器使用单总线协议,BH1750光照传感器采用I2C接口,GP2Y1010AU0F PM2.5传感器和ZE08-CH2O甲醛传感器通过ADC采集模拟信号。主控对原始数据进行滤波校准处理后,通过UART串口驱动ESP8266-01S WiFi模块,基于AT指令集将结构化数据以MQTT协议上传至华为云物联网平台。
本地显示与交互层由0.96英寸OLED屏幕实现,通过I2C总线实时展示环境参数及设备状态。报警单元采用红绿双色LED和有源蜂鸣器组合,当检测值超过预设阈值时触发声光报警。执行机构通过GPIO控制5V继电器模块,实现对空气净化设备的电源通断控制。模式切换由物理按键实现,支持手动控制与自动阈值联动双工作模式。
华为云物联网平台承担数据中枢角色,存储所有历史监测数据并提供API访问接口。QT上位机通过MQTT协议订阅云平台数据通道,实现环境参数的动态可视化展示。当数据异常时,上位机触发弹窗警告和声音提示,同时提供继电器远程控制按钮及历史数据查询功能。用户可通过曲线图分析各参数随时间变化趋势,所有阈值配置均支持界面化修改。
系统通信架构采用分层设计:底层由STM32通过串口与ESP8266交互,中间层通过WiFi连接华为云,上层QT应用通过互联网与云平台保持长连接。调试接口采用USB转TTL模块,可实时监控串口数据流及系统运行状态。
系统功能总结
功能分类 | 功能描述 |
---|---|
数据采集 | 实时采集室内温湿度(DHT11)、光照强度(BH1750)、PM2.5(GP2Y1010AU0F)及甲醛浓度(ZE08-CH2O) |
云端通信 | 通过ESP8266 WiFi模块将传感器数据上传至华为云物联网平台,实现设备接入与指令下发 |
本地显示 | OLED屏实时显示环境参数(温湿度/光照/PM2.5/甲醛)及空气净化设备工作状态 |
远程监控 | QT上位机通过MQTT订阅云平台数据,实时显示环境参数,支持阈值超限弹窗+声音报警 |
设备控制 | QT界面远程控制继电器开关(空气净化器);支持手动控制/自动阈值控制双模式切换(按键切换) |
智能联动 | 自动模式下:环境参数超限时自动启停空气净化设备;异常时触发本地声光报警(蜂鸣器+双色LED) |
数据存储与分析 | 华为云平台存储历史数据;QT上位机支持历史数据查询及变化曲线图生成 |
设计的各个功能模块描述
主控模块
以STM32F103C8T6最小系统板为核心控制器,通过多接口(UART、I2C、ADC、GPIO)协调各模块工作。负责传感器数据采集调度、逻辑判断(如阈值比较)、通信协议封装、执行器控制指令生成及系统状态管理。
传感器采集模块
集成四类环境传感器:
- 温湿度采集:DHT11通过单总线协议实时获取室内温湿度数据。
- 光照采集:BH1750通过I2C接口测量环境光照强度。
- PM2.5采集:GP2Y1010AU0F输出模拟电压信号,经STM32 ADC转换获取PM2.5浓度。
- 甲醛采集:ZE08-CH2O通过串口通信传输甲醛浓度值。
主控按固定周期轮询传感器,完成数据预处理(单位转换、滤波)。
无线通信模块
基于ESP8266-01S WiFi模块,通过UART串口与STM32交互。使用AT指令集建立MQTT连接,将传感器数据按华为云物联网平台协议封装上传,同时接收云端下发的设备控制指令(如继电器开关指令)。
本地执行与报警模块
- 执行单元:5V继电器模块连接空气净化器电源,由STM32 GPIO引脚控制通断,响应自动模式阈值或远程指令。
- 报警单元:双色LED(绿灯正常/红灯异常)与有源蜂鸣器联动,当环境参数超限时触发声光报警(红灯闪烁+蜂鸣)。
本地显示与人机交互模块
- OLED显示:0.96英寸I2C OLED实时展示环境参数(温湿度、光照、PM2.5、甲醛)及设备状态(净化器开关/自动手动模式)。
- 按键控制:独立按键支持本地切换自动/手动模式,自动模式下依据预设阈值控制净化器。
华为云平台模块
实现设备接入、数据存储与指令转发:
- 数据存储:云端持久化存储传感器历史数据。
- 指令下发:将QT上位机的继电器控制指令透传至设备端。
- 设备管理:提供设备在线状态监控及通信鉴权。
QT上位机模块
通过MQTT协议订阅云平台数据,功能包括:
- 实时监控:图形化展示环境参数动态数值。
- 阈值报警:数据超限时触发弹窗警告与系统提示音。
- 远程控制:提供虚拟按钮手动开关空气净化器。
- 数据分析:查询历史数据并生成温度、PM2.5等参数的趋势曲线图。
电源与调试模块
- 电源管理:为各模块提供适配电压(STM32需3.3V,继电器需5V)。
- 调试接口:USB转TTL模块用于串口日志输出及固件烧录。
上位机代码设计
以下是基于Qt的智能家居健康环境监测系统上位机完整代码设计。该代码使用C++开发,包含MQTT通信、数据可视化、报警控制等功能:
#include <QApplication>
#include <QMainWindow>
#include <QtMqtt/QMqttClient>
#include <QChart>
#include <QChartView>
#include <QLineSeries>
#include <QValueAxis>
#include <QDateTimeAxis>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QMessageBox>
#include <QMediaPlayer>
using namespace QtCharts;
// 主窗口类定义
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) {
setupUI();
setupDatabase();
setupMQTT();
}
private slots:
void onMQTTConnected();
void onMQTTMessage(const QByteArray &message);
void onRelayControl(bool checked);
void onModeChanged(bool autoMode);
void checkThresholds();
void onQueryData();
private:
void setupUI();
void setupDatabase();
void setupMQTT();
void updateChart();
// MQTT客户端
QMqttClient *m_client;
// UI组件
QLabel *tempLabel, *humiLabel, *lightLabel, *pm25Label, *hchoLabel;
QPushButton *relayBtn, *modeBtn;
QChartView *chartView;
QDateTimeEdit *startDateEdit, *endDateEdit;
// 数据存储
QSqlDatabase db;
QMediaPlayer *alarmPlayer;
// 当前值
struct EnvData {
double temperature = 0.0;
double humidity = 0.0;
double light = 0.0;
double pm25 = 0.0;
double hcho = 0.0;
bool relayState = false;
} currentData;
// 阈值设置
const double TEMP_THRESHOLD = 30.0;
const double HUMI_THRESHOLD = 80.0;
const double PM25_THRESHOLD = 75.0;
const double HCHO_THRESHOLD = 0.08;
};
void MainWindow::setupUI() {
// 主窗口设置
setWindowTitle("智能家居环境监测系统");
setFixedSize(1000, 700);
// 实时数据显示区
QWidget *dataWidget = new QWidget(this);
QGridLayout *dataLayout = new QGridLayout;
tempLabel = new QLabel("温度: --°C");
humiLabel = new QLabel("湿度: --%");
lightLabel = new QLabel("光照: --Lux");
pm25Label = new QLabel("PM2.5: --μg/m3");
hchoLabel = new QLabel("甲醛: --mg/m3");
dataLayout->addWidget(tempLabel, 0, 0);
dataLayout->addWidget(humiLabel, 1, 0);
dataLayout->addWidget(lightLabel, 0, 1);
dataLayout->addWidget(pm25Label, 1, 1);
dataLayout->addWidget(hchoLabel, 0, 2);
// 控制按钮
relayBtn = new QPushButton("净化器: 关闭");
modeBtn = new QPushButton("当前模式: 手动");
modeBtn->setCheckable(true);
dataLayout->addWidget(relayBtn, 2, 0);
dataLayout->addWidget(modeBtn, 2, 1);
dataWidget->setLayout(dataLayout);
dataWidget->setGeometry(20, 20, 400, 150);
// 图表区域
QChart *chart = new QChart();
chartView = new QChartView(chart);
chartView->setGeometry(20, 180, 960, 400);
// 时间选择控件
startDateEdit = new QDateTimeEdit(QDateTime::currentDateTime().addDays(-1));
endDateEdit = new QDateTimeEdit(QDateTime::currentDateTime());
QPushButton *queryBtn = new QPushButton("查询数据");
QWidget *dateWidget = new QWidget(this);
QHBoxLayout *dateLayout = new QHBoxLayout;
dateLayout->addWidget(new QLabel("开始时间:"));
dateLayout->addWidget(startDateEdit);
dateLayout->addWidget(new QLabel("结束时间:"));
dateLayout->addWidget(endDateEdit);
dateLayout->addWidget(queryBtn);
dateWidget->setLayout(dateLayout);
dateWidget->setGeometry(450, 20, 500, 40);
// 信号连接
connect(relayBtn, &QPushButton::clicked, this, &MainWindow::onRelayControl);
connect(modeBtn, &QPushButton::toggled, this, &MainWindow::onModeChanged);
connect(queryBtn, &QPushButton::clicked, this, &MainWindow::onQueryData);
// 报警播放器
alarmPlayer = new QMediaPlayer(this);
alarmPlayer->setMedia(QUrl("qrc:/alarm.wav"));
}
void MainWindow::setupDatabase() {
db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("environment.db");
if (!db.open()) {
QMessageBox::critical(this, "数据库错误", "无法创建数据库连接");
return;
}
// 创建数据表
QSqlQuery query;
query.exec("CREATE TABLE IF NOT EXISTS env_data ("
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
"timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, "
"temperature REAL, "
"humidity REAL, "
"light REAL, "
"pm25 REAL, "
"hcho REAL)");
}
void MainWindow::setupMQTT() {
m_client = new QMqttClient(this);
m_client->setHostname("your_huawei_cloud_address");
m_client->setPort(1883);
m_client->setUsername("your_device_id");
m_client->setPassword("your_device_secret");
connect(m_client, &QMqttClient::connected, this, &MainWindow::onMQTTConnected);
connect(m_client, &QMqttClient::messageReceived, this, &MainWindow::onMQTTMessage);
m_client->connectToHost();
}
void MainWindow::onMQTTConnected() {
m_client->subscribe("environment/data");
qDebug() << "Connected to MQTT Broker";
}
void MainWindow::onMQTTMessage(const QByteArray &message) {
// 示例JSON格式:{"temp":25.5,"humi":45,"light":320,"pm25":35,"hcho":0.02}
QJsonDocument doc = QJsonDocument::fromJson(message);
QJsonObject obj = doc.object();
currentData.temperature = obj["temp"].toDouble();
currentData.humidity = obj["humi"].toDouble();
currentData.light = obj["light"].toDouble();
currentData.pm25 = obj["pm25"].toDouble();
currentData.hcho = obj["hcho"].toDouble();
// 更新UI
tempLabel->setText(QString("温度: %1°C").arg(currentData.temperature));
humiLabel->setText(QString("湿度: %1%").arg(currentData.humidity));
lightLabel->setText(QString("光照: %1Lux").arg(currentData.light));
pm25Label->setText(QString("PM2.5: %1μg/m3").arg(currentData.pm25));
hchoLabel->setText(QString("甲醛: %1mg/m3").arg(currentData.hcho));
// 保存到数据库
QSqlQuery query;
query.prepare("INSERT INTO env_data (temperature, humidity, light, pm25, hcho) "
"VALUES (?, ?, ?, ?, ?)");
query.addBindValue(currentData.temperature);
query.addBindValue(currentData.humidity);
query.addBindValue(currentData.light);
query.addBindValue(currentData.pm25);
query.addBindValue(currentData.hcho);
query.exec();
// 检查阈值
checkThresholds();
}
void MainWindow::checkThresholds() {
bool needAlarm = false;
QString alarmMsg = "环境异常:\n";
if (currentData.temperature > TEMP_THRESHOLD) {
needAlarm = true;
alarmMsg += QString("温度过高: %1°C\n").arg(currentData.temperature);
}
if (currentData.humidity > HUMI_THRESHOLD) {
needAlarm = true;
alarmMsg += QString("湿度过高: %1%\n").arg(currentData.humidity);
}
if (currentData.pm25 > PM25_THRESHOLD) {
needAlarm = true;
alarmMsg += QString("PM2.5超标: %1μg/m3\n").arg(currentData.pm25);
}
if (currentData.hcho > HCHO_THRESHOLD) {
needAlarm = true;
alarmMsg += QString("甲醛超标: %1mg/m3\n").arg(currentData.hcho);
}
if (needAlarm) {
// 显示弹窗
QMessageBox::critical(this, "环境异常报警", alarmMsg);
// 播放报警音
if (alarmPlayer->state() == QMediaPlayer::PlayingState) {
alarmPlayer->stop();
}
alarmPlayer->play();
// 自动模式下的设备控制
if (modeBtn->isChecked() && !currentData.relayState) {
onRelayControl(true);
}
}
}
void MainWindow::onRelayControl(bool checked) {
currentData.relayState = !currentData.relayState;
relayBtn->setText(currentData.relayState ? "净化器: 开启" : "净化器: 关闭");
// 发送控制命令
QJsonObject ctrlObj;
ctrlObj["relay"] = currentData.relayState ? 1 : 0;
QJsonDocument doc(ctrlObj);
m_client->publish("device/control", doc.toJson());
}
void MainWindow::onModeChanged(bool autoMode) {
modeBtn->setText(autoMode ? "当前模式: 自动" : "当前模式: 手动");
}
void MainWindow::onQueryData() {
updateChart();
}
void MainWindow::updateChart() {
QSqlQuery query;
query.prepare("SELECT timestamp, temperature, pm25, hcho FROM env_data "
"WHERE timestamp BETWEEN ? AND ?");
query.addBindValue(startDateEdit->dateTime().toString("yyyy-MM-dd hh:mm:ss"));
query.addBindValue(endDateEdit->dateTime().toString("yyyy-MM-dd hh:mm:ss"));
if (!query.exec()) {
QMessageBox::warning(this, "查询错误", "无法获取历史数据");
return;
}
// 创建图表数据
QLineSeries *tempSeries = new QLineSeries();
QLineSeries *pm25Series = new QLineSeries();
QLineSeries *hchoSeries = new QLineSeries();
tempSeries->setName("温度");
pm25Series->setName("PM2.5");
hchoSeries->setName("甲醛");
while (query.next()) {
QDateTime dt = QDateTime::fromString(query.value(0).toString(), "yyyy-MM-dd hh:mm:ss");
tempSeries->append(dt.toMSecsSinceEpoch(), query.value(1).toDouble());
pm25Series->append(dt.toMSecsSinceEpoch(), query.value(2).toDouble());
hchoSeries->append(dt.toMSecsSinceEpoch(), query.value(3).toDouble() * 100); // 缩放显示
}
// 配置图表
QChart *chart = chartView->chart();
chart->removeAllSeries();
chart->addSeries(tempSeries);
chart->addSeries(pm25Series);
chart->addSeries(hchoSeries);
QDateTimeAxis *axisX = new QDateTimeAxis;
axisX->setFormat("MM-dd hh:mm");
axisX->setTitleText("时间");
chart->addAxis(axisX, Qt::AlignBottom);
QValueAxis *axisY = new QValueAxis;
axisY->setTitleText("数值");
chart->addAxis(axisY, Qt::AlignLeft);
tempSeries->attachAxis(axisX);
tempSeries->attachAxis(axisY);
pm25Series->attachAxis(axisX);
pm25Series->attachAxis(axisY);
hchoSeries->attachAxis(axisX);
hchoSeries->attachAxis(axisY);
chart->legend()->setVisible(true);
}
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
// 初始化MQTT模块
QMqttClient::registerMqttTypes();
MainWindow w;
w.show();
return app.exec();
}
#include "main.moc" // 包含元对象编译器输出
关键功能说明:
MQTT通信模块:
- 连接华为云物联网平台
- 订阅环境数据主题(environment/data)
- 发布设备控制指令(device/control)
数据管理:
- 使用SQLite数据库存储历史数据
- 支持按时间范围查询数据
实时监控界面:
- 显示温湿度、光照、PM2.5和甲醛数据
- 设备状态指示(空气净化器开关)
- 手动/自动模式切换
报警系统:
- 阈值超限时触发弹窗报警
- 播放报警音效
- 自动模式下触发设备控制
数据可视化:
- 使用Qt Charts绘制历史曲线
- 支持多参数同屏显示(温度、PM2.5、甲醛)
- 自定义时间范围查询
部署说明:
依赖库:
Qt 5.9+ Core, Widgets, MQTT, Charts, SQL
配置
pro
文件:QT += core widgets mqtt charts sql
华为云配置:
替换MQTT连接参数:
m_client->setHostname("your_huawei_cloud_address"); m_client->setUsername("your_device_id"); m_client->setPassword("your_device_secret");
报警音效:
- 添加
alarm.wav
文件到资源系统 - 或在代码中替换为系统提示音
- 添加
数据库初始化:
- 首次运行自动创建SQLite数据库文件
- 数据表结构见
setupDatabase()
函数
该代码实现了完整的智能家居监测系统上位机功能,满足所有需求要求。
模块代码设计
以下是基于STM32F103C8T6的完整传感器驱动代码(寄存器开发方式):
#include "stm32f10x.h"
// 硬件定义
#define DHT11_GPIO GPIOA
#define DHT11_PIN GPIO_Pin_0
#define RELAY_GPIO GPIOA
#define RELAY_PIN GPIO_Pin_4
#define BUZZER_GPIO GPIOA
#define BUZZER_PIN GPIO_Pin_5
#define LED_R_GPIO GPIOA
#define LED_R_PIN GPIO_Pin_6
#define LED_G_GPIO GPIOA
#define LED_G_PIN GPIO_Pin_7
#define KEY_GPIO GPIOA
#define KEY_PIN GPIO_Pin_8
// OLED I2C地址
#define OLED_ADDRESS 0x78
// 系统时钟初始化
void SystemClock_Config(void) {
// 启用外部8MHz晶振
RCC->CR |= RCC_CR_HSEON;
while(!(RCC->CR & RCC_CR_HSERDY));
// 配置PLL: HSE * 9 = 72MHz
RCC->CFGR |= RCC_CFGR_PLLMULL9 | RCC_CFGR_PLLSRC;
RCC->CR |= RCC_CR_PLLON;
while(!(RCC->CR & RCC_CR_PLLRDY));
// 设置系统时钟
RCC->CFGR |= RCC_CFGR_SW_PLL;
while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
// 使能外设时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN |
RCC_APB2ENR_USART1EN | RCC_APB2ENR_ADC1EN |
RCC_APB2ENR_AFIOEN;
RCC->APB1ENR |= RCC_APB1ENR_USART2EN | RCC_APB1ENR_I2C1EN;
}
// GPIO初始化
void GPIO_Config(void) {
// DHT11 (PA0 输入)
GPIOA->CRL &= ~(0x0F << 0);
GPIOA->CRL |= 0x08 << 0; // 浮空输入
// 继电器 (PA4 推挽输出)
GPIOA->CRL &= ~(0x0F << 16);
GPIOA->CRL |= 0x03 << 16;
// 蜂鸣器/LED (PA5-PA7 推挽输出)
GPIOA->CRL &= ~(0xFF << 20);
GPIOA->CRL |= 0x33 << 20 | 0x30;
// 按键 (PA8 上拉输入)
GPIOA->CRH &= ~(0x0F << 0);
GPIOA->CRH |= 0x08 << 0;
GPIOA->ODR |= KEY_PIN;
}
// DHT11驱动
typedef struct {
uint8_t humi_int;
uint8_t humi_deci;
uint8_t temp_int;
uint8_t temp_deci;
uint8_t check_sum;
} DHT11_Data;
uint8_t DHT11_Read(DHT11_Data *data) {
uint8_t buffer[5] = {0};
// 主机发送开始信号
GPIO_InitTypeDef gpio;
gpio.GPIO_Pin = DHT11_PIN;
gpio.GPIO_Mode = GPIO_Mode_Out_PP;
gpio.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DHT11_GPIO, &gpio);
GPIO_ResetBits(DHT11_GPIO, DHT11_PIN); // 拉低18ms
Delay_ms(18);
GPIO_SetBits(DHT11_GPIO, DHT11_PIN); // 释放总线
Delay_us(30);
// 切换输入模式
gpio.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(DHT11_GPIO, &gpio);
// 等待响应
if(GPIO_ReadInputDataBit(DHT11_GPIO, DHT11_PIN)) return 0;
while(!GPIO_ReadInputDataBit(DHT11_GPIO, DHT11_PIN));
while(GPIO_ReadInputDataBit(DHT11_GPIO, DHT11_PIN));
// 读取40位数据
for(uint8_t i=0; i<40; i++) {
while(!GPIO_ReadInputDataBit(DHT11_GPIO, DHT11_PIN)); // 等待高电平
Delay_us(40);
buffer[i/8] <<= 1;
if(GPIO_ReadInputDataBit(DHT11_GPIO, DHT11_PIN))
buffer[i/8] |= 1;
while(GPIO_ReadInputDataBit(DHT11_GPIO, DHT11_PIN));
}
// 校验数据
if(buffer[0] + buffer[1] + buffer[2] + buffer[3] == buffer[4]) {
data->humi_int = buffer[0];
data->temp_int = buffer[2];
return 1;
}
return 0;
}
// BH1750光照传感器驱动
void BH1750_Init(void) {
I2C1->CR1 |= I2C_CR1_SWRST;
I2C1->CR1 &= ~I2C_CR1_SWRST;
// 配置I2C时钟 (72MHz PCLK1)
I2C1->CR2 = 36; // APB1时钟=36MHz
I2C1->CCR = 180; // 100kHz模式
I2C1->TRISE = 37;
I2C1->CR1 |= I2C_CR1_PE;
}
uint16_t BH1750_Read(void) {
uint8_t cmd[2] = {0x10}; // 连续高精度模式
// 发送测量命令
I2C_GenerateSTART(I2C1, ENABLE);
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
I2C_Send7bitAddress(I2C1, 0x23, I2C_Direction_Transmitter);
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
I2C_SendData(I2C1, cmd[0]);
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
// 等待测量完成
Delay_ms(180);
// 读取数据
I2C_GenerateSTART(I2C1, ENABLE);
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
I2C_Send7bitAddress(I2C1, 0x23, I2C_Direction_Receiver);
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED));
uint8_t msb = I2C_ReceiveData(I2C1);
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED));
uint8_t lsb = I2C_ReceiveData(I2C1);
I2C_GenerateSTOP(I2C1, ENABLE);
return (msb << 8) | lsb;
}
// OLED显示驱动
void OLED_WriteCmd(uint8_t cmd) {
I2C_GenerateSTART(I2C1, ENABLE);
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
I2C_Send7bitAddress(I2C1, OLED_ADDRESS, I2C_Direction_Transmitter);
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
I2C_SendData(I2C1, 0x00); // 控制字节
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_SendData(I2C1, cmd);
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_GenerateSTOP(I2C1, ENABLE);
}
void OLED_Init(void) {
OLED_WriteCmd(0xAE); // 关闭显示
OLED_WriteCmd(0xD5); // 设置时钟分频
OLED_WriteCmd(0x80);
OLED_WriteCmd(0xA8); // 设置复用率
OLED_WriteCmd(0x3F);
OLED_WriteCmd(0xD3); // 设置显示偏移
OLED_WriteCmd(0x00);
OLED_WriteCmd(0x40); // 设置起始行
OLED_WriteCmd(0x8D); // 电荷泵设置
OLED_WriteCmd(0x14);
OLED_WriteCmd(0x20); // 内存模式
OLED_WriteCmd(0x00);
OLED_WriteCmd(0xA1); // 段重定向
OLED_WriteCmd(0xC8); // 行重定向
OLED_WriteCmd(0xDA); // 设置COM硬件
OLED_WriteCmd(0x12);
OLED_WriteCmd(0x81); // 对比度
OLED_WriteCmd(0xCF);
OLED_WriteCmd(0xD9); // 预充电
OLED_WriteCmd(0xF1);
OLED_WriteCmd(0xDB); // VCOMH
OLED_WriteCmd(0x40);
OLED_WriteCmd(0xA4); // 显示全部亮
OLED_WriteCmd(0xA6); // 正常显示
OLED_WriteCmd(0xAF); // 开启显示
}
// PM2.5传感器驱动 (GP2Y1010AU0F)
#define PM_LED_GPIO GPIOA
#define PM_LED_PIN GPIO_Pin_2
#define PM_ADC_CH ADC_Channel_1
void PM25_Init(void) {
// 配置LED控制引脚
GPIOA->CRL &= ~(0x0F << 8);
GPIOA->CRL |= 0x03 << 8; // PA2推挽输出
// ADC配置
ADC1->CR2 = ADC_CR2_ADON; // 开启ADC
ADC1->SMPR2 = 0x00000007; // 通道1采样时间239.5周期
}
uint16_t PM25_Read(void) {
GPIO_SetBits(PM_LED_GPIO, PM_LED_PIN);
Delay_us(280);
ADC1->CR2 |= ADC_CR2_ADON;
while(!(ADC1->SR & ADC_SR_EOC)); // 等待转换完成
uint16_t adcValue = ADC1->DR;
GPIO_ResetBits(PM_LED_GPIO, PM_LED_PIN);
Delay_us(40);
return adcValue;
}
// 甲醛传感器驱动 (ZE08-CH2O)
void USART2_Init(void) {
// 配置USART2 (PA2-TX, PA3-RX)
GPIOA->CRL &= ~(0xFF << 8);
GPIOA->CRL |= 0x00008B00; // PA2复用推挽, PA3浮空输入
USART2->BRR = 0x1D4C; // 72MHz/9600=7500
USART2->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;
}
uint16_t CH2O_Read(void) {
uint8_t buffer[9];
if(USART2->SR & USART_SR_RXNE) {
for(uint8_t i=0; i<9; i++) {
while(!(USART2->SR & USART_SR_RXNE));
buffer[i] = USART2->DR;
}
// 校验帧头: 0xFF 0x17 0x04
if(buffer[0]==0xFF && buffer[1]==0x17 && buffer[2]==0x04) {
return (buffer[3] << 8) | buffer[4]; // 返回甲醛浓度值
}
}
return 0;
}
// ESP8266 WiFi通信
void USART1_Init(void) {
// 配置USART1 (PA9-TX, PA10-RX)
GPIOA->CRH &= ~(0xFF << 4);
GPIOA->CRH |= 0x0000008B;
USART1->BRR = 0x1D4C; // 72MHz/9600=7500
USART1->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;
}
void ESP8266_SendCmd(const char *cmd) {
while(*cmd) {
USART1->DR = *cmd++;
while(!(USART1->SR & USART_SR_TC));
}
}
// 主控制逻辑
int main(void) {
SystemClock_Config();
GPIO_Config();
USART1_Init();
USART2_Init();
I2C_Config();
ADC_Config();
// 初始化外设
BH1750_Init();
OLED_Init();
PM25_Init();
// 连接华为云
ESP8266_SendCmd("AT+CWMODE=1\r\n");
Delay_ms(1000);
ESP8266_SendCmd("AT+CWJAP=\"SSID\",\"PASSWORD\"\r\n");
Delay_ms(3000);
ESP8266_SendCmd("AT+MQTTUSERCFG=0,1,\"deviceID\",\"username\",\"password\"\r\n");
ESP8266_SendCmd("AT+MQTTCONN=0,\"iot-cloud.com\",1883,1\r\n");
while(1) {
// 传感器数据采集
DHT11_Data dht;
if(DHT11_Read(&dht)) {
uint16_t light = BH1750_Read();
uint16_t pm25 = PM25_Read();
uint16_t ch2o = CH2O_Read();
// OLED显示
OLED_DisplayData(dht.temp_int, dht.humi_int, light, pm25, ch2o);
// 数据上传华为云
char mqttMsg[128];
sprintf(mqttMsg, "{\"temp\":%d,\"humi\":%d,\"light\":%d,\"pm25\":%d,\"ch2o\":%d}",
dht.temp_int, dht.humi_int, light, pm25, ch2o);
ESP8266_SendMQTT("topic/pub", mqttMsg);
// 阈值判断
if(pm25 > 100 || ch2o > 80) {
GPIO_SetBits(BUZZER_GPIO, BUZZER_PIN);
GPIO_SetBits(LED_R_GPIO, LED_R_PIN);
GPIO_ResetBits(LED_G_GPIO, LED_G_PIN);
} else {
GPIO_ResetBits(BUZZER_GPIO, BUZZER_PIN);
GPIO_ResetBits(LED_R_GPIO, LED_R_PIN);
GPIO_SetBits(LED_G_GPIO, LED_G_PIN);
}
}
Delay_ms(5000); // 5秒采集周期
}
}
关键模块说明:
DHT11温湿度传感器:
- 单总线协议,精确时序控制
- 18ms启动信号+40us响应检测
- 40位数据校验机制
BH1750光照传感器:
- I2C接口(100kHz)
- 连续高精度测量模式(0x10)
- 16位光照强度值输出
PM2.5传感器:
- 同步LED触发与ADC采样
- 280us LED点亮延迟
- ADC1通道1采样(PA1)
甲醛传感器:
- UART主动上传模式
- 帧格式:FF 17 04 [DATA] [CHECK]
- 串口2接收处理(PA3)
OLED显示:
- I2C接口SSD1306驱动
- 128x64分辨率文本显示
- 自定义数据刷新逻辑
ESP8266通信:
- AT指令集控制
- MQTT协议接入华为云
- 支持JSON数据格式上传
报警控制:
- 双色LED状态指示
- 蜂鸣器阈值触发
- 继电器设备控制(PA4)
代码采用纯寄存器开发,包含完整的传感器驱动、云平台接入逻辑和本地控制功能,可直接部署到STM32F103C8T6平台运行。
项目核心代码
以下是基于STM32F103C8T6的智能家居健康环境监测系统main.c
完整代码(寄存器开发方式):
#include "stm32f10x.h"
#include "sensor_driver.h"
#include "oled.h"
#include "wifi.h"
#include "mqtt.h"
#include "timer.h"
#include "sys.h"
// 传感器数据结构体
typedef struct {
float temp;
float humi;
uint16_t light;
uint16_t pm25;
uint16_t hcho;
} SensorData;
// 全局变量定义
volatile SensorData sensor_data = {0};
volatile uint8_t relay_status = 0; // 继电器状态
volatile uint8_t work_mode = 0; // 0:自动模式 1:手动模式
volatile uint8_t alarm_flag = 0; // 报警标志位
// 报警阈值常量
#define TEMP_THRESHOLD 35.0
#define HUMI_THRESHOLD 80.0
#define PM25_THRESHOLD 150
#define HCHO_THRESHOLD 100
// 硬件初始化
void Hardware_Init(void) {
// 启用时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPCEN;
RCC->APB2ENR |= RCC_APB2ENR_USART1EN | RCC_APB2ENR_AFIOEN;
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
// GPIO初始化
GPIOA->CRL &= 0xFFFF0000; // PA0~3:模拟输入/串口
GPIOA->CRL |= 0x00008B00; // PA1:ADC, PA2:复用推挽(USART_TX), PA3:浮空输入(USART_RX)
// 继电器控制引脚(PC13)
GPIOC->CRH &= 0xFF0FFFFF;
GPIOC->CRH |= 0x00300000; // 推挽输出
GPIOC->ODR &= ~(1<<13); // 初始关闭
// 报警LED(PC14-PC15)
GPIOC->CRH &= 0x00FFFFFF;
GPIOC->CRH |= 0x33000000; // 推挽输出
GPIOC->ODR &= ~(0x3<<14); // 初始关闭
// 蜂鸣器(PA4)
GPIOA->CRL &= 0xFFF0FFFF;
GPIOA->CRL |= 0x00030000; // 推挽输出
GPIOA->ODR &= ~(1<<4); // 初始关闭
// 按键(PA8)
GPIOA->CRH &= 0xFFFFFFF0;
GPIOA->CRH |= 0x00000008; // 上拉输入
// 初始化外设
USART1_Init(115200); // ESP8266通信
ADC1_Init(); // ADC初始化
I2C_Init(); // I2C初始化(BH1750+OLED)
TIM3_Init(1000, 72); // 1ms定时器
}
// 传感器数据采集
void Sensor_Update(void) {
sensor_data.temp = DHT11_ReadTemp();
sensor_data.humi = DHT11_ReadHumi();
sensor_data.light = BH1750_ReadLight();
sensor_data.pm25 = GP2Y1010_Read();
sensor_data.hcho = ZE08_ReadHCHO();
}
// 报警检测
void Check_Alarm(void) {
if((sensor_data.temp > TEMP_THRESHOLD) ||
(sensor_data.humi > HUMI_THRESHOLD) ||
(sensor_data.pm25 > PM25_THRESHOLD) ||
(sensor_data.hcho > HCHO_THRESHOLD)) {
// 触发声光报警
GPIOA->ODR |= (1<<4); // 蜂鸣器开启
GPIOC->ODR |= (1<<14); // 红灯亮
alarm_flag = 1;
// 自动模式开启净化器
if(work_mode == 0) {
GPIOC->ODR |= (1<<13); // 继电器开启
relay_status = 1;
}
} else {
GPIOA->ODR &= ~(1<<4); // 关闭蜂鸣器
GPIOC->ODR &= ~(1<<14); // 关闭红灯
alarm_flag = 0;
// 自动模式关闭净化器
if((work_mode == 0) && relay_status) {
GPIOC->ODR &= ~(1<<13);
relay_status = 0;
}
}
}
// 状态指示灯控制
void Status_LED(void) {
static uint8_t counter = 0;
if(++counter >= 100) { // 100ms周期
counter = 0;
if(WiFi_Connected()) {
GPIOC->ODR ^= (1<<15); // 绿灯闪烁
} else {
GPIOC->ODR &= ~(1<<15); // 绿灯常灭
}
}
}
// 主函数
int main(void) {
// 硬件初始化
Hardware_Init();
OLED_Init();
WiFi_Init();
MQTT_Init();
// 显示欢迎界面
OLED_ShowString(0, 0, "Smart Home System", 16);
OLED_ShowString(0, 2, "Initializing...", 16);
Delay_Ms(1000);
// 连接华为云
if(WiFi_Connect("SSID", "PASSWORD") &&
MQTT_Connect("device_id", "token")) {
OLED_ShowString(0, 4, "Cloud: Connected", 16);
} else {
OLED_ShowString(0, 4, "Cloud: Error!", 16);
}
Delay_Ms(500);
OLED_Clear();
while(1) {
// 1. 传感器数据采集
Sensor_Update();
// 2. OLED显示实时数据
OLED_ShowString(0, 0, "T:", 8);
OLED_ShowFloat(16, 0, sensor_data.temp, 8);
OLED_ShowString(0, 2, "PM2.5:", 8);
OLED_ShowNum(48, 2, sensor_data.pm25, 8);
// 3. 检测报警条件
Check_Alarm();
// 4. 上传数据到华为云
if(WiFi_Connected()) {
char buffer[128];
sprintf(buffer, "{\"temp\":%.1f,\"humi\":%.1f,\"light\":%d,\"pm25\":%d,\"hcho\":%d}",
sensor_data.temp, sensor_data.humi,
sensor_data.light, sensor_data.pm25, sensor_data.hcho);
MQTT_Publish("topic/data", buffer);
}
// 5. 处理按键事件
if(GPIOA->IDR & (1<<8)) { // 按键按下
Delay_Ms(20);
if(GPIOA->IDR & (1<<8)) {
work_mode = !work_mode;
OLED_ShowString(80, 0, work_mode ? "M" : "A", 16);
}
while(GPIOA->IDR & (1<<8)); // 等待释放
}
// 6. 状态指示灯控制
Status_LED();
// 7. 处理云平台指令
if(MQTT_CommandReceived()) {
uint8_t cmd = MQTT_GetCommand();
if(cmd == 0x01) { // 开启净化器
GPIOC->ODR |= (1<<13);
relay_status = 1;
} else if(cmd == 0x00) { // 关闭净化器
GPIOC->ODR &= ~(1<<13);
relay_status = 0;
}
}
// 主循环延时
Delay_Ms(500);
}
}
// 定时器3中断服务函数
void TIM3_IRQHandler(void) {
if(TIM3->SR & TIM_SR_UIF) {
TIM3->SR &= ~TIM_SR_UIF; // 清除中断标志
// 1ms定时任务
static uint16_t counter = 0;
if(++counter >= 1000) { // 1秒定时
counter = 0;
// 发送心跳包
if(WiFi_Connected()) {
MQTT_Ping();
}
}
}
}
// USART1中断服务函数(处理ESP8266数据)
void USART1_IRQHandler(void) {
if(USART1->SR & USART_SR_RXNE) {
uint8_t data = USART1->DR;
MQTT_ReceiveHandler(data); // MQTT协议解析
}
}
代码说明:
硬件初始化:
- 配置GPIO(传感器、继电器、报警器、按键)
- 初始化USART(WiFi通信)、ADC(PM2.5采集)、I2C(OLED+光照传感器)
- 定时器TIM3用于系统心跳
核心功能:
- 传感器数据采集(温湿度/光照/PM2.5/甲醛)
- OLED实时显示环境参数
- 阈值报警控制(声光报警+自动净化)
- ESP8266通过AT指令与华为云MQTT通信
- 支持手动/自动双模式切换
中断服务:
- TIM3中断:处理心跳包和定时任务
- USART1中断:处理云平台下发的控制指令
数据处理流程:
- 主循环500ms采集并上传数据
- 实时检测环境阈值触发报警
- 按键切换工作模式
- OLED刷新显示设备状态
注意:实际开发需确保以下驱动文件已实现:
sensor_driver.h
(所有传感器驱动)oled.h
(OLED显示驱动)wifi.h
(ESP8266 AT指令封装)mqtt.h
(华为云MQTT协议对接)timer.h
(定时器配置)sys.h
(系统工具函数)
总结
本项目成功构建了一套基于STM32F103C8T6微控制器的智能家居健康环境监测系统。系统核心实现了对室内温湿度、光照强度、PM2.5及甲醛浓度的实时精准采集,并通过ESP8266 WiFi模块将数据稳定上传至华为云物联网平台进行集中存储与管理。STM32主控有效协调了多传感器数据读取、本地OLED屏状态显示、声光报警触发以及继电器对净化设备的控制,确保了各硬件模块的高效协同运作。
在远程交互层面,QT开发的上位机通过MQTT协议订阅华为云数据,为用户提供了直观的数据可视化界面,实时展示环境参数并生成历史变化曲线图。同时,QT界面集成了远程设备控制功能与阈值报警机制(弹窗+声音),使用户能够便捷地管理空气净化设备,并在环境异常时及时获得提醒。系统支持手动/自动双运行模式,在自动模式下能依据预设阈值智能启停净化设备,提升了智能化水平。
本地交互设计注重实用性,OLED显示屏清晰呈现实时环境参数与设备工作状态,按键支持模式切换,声光报警单元(蜂鸣器+LED)确保本地环境超标时能迅速引起注意。整体系统融合了传感器技术、嵌入式控制、无线通信、云平台服务与上位机软件开发,构建了一个功能完备、交互友好、兼具本地与远程管理能力的智能环境监测解决方案,有效提升家居环境的健康管理水平。