• [技术干货] 基于STM32的智能农业环境监测系统
    项目开发背景随着全球人口的持续增长和气候变化的影响,现代农业面临着提高产量、优化资源利用和应对环境不确定性的巨大压力。传统农业依赖人工经验进行环境监测,效率低下且易受主观因素干扰,难以实现精准化管理。此外,水资源短缺、土壤退化以及极端天气事件频发,进一步加剧了农业生产的不稳定性,亟需引入智能化技术来提升农业的可持续性和抗风险能力。物联网技术的快速发展为农业现代化提供了新的解决方案。智能农业系统通过集成传感器、无线通信和云平台,能够实现对农田环境的实时、远程监控,帮助农民及时获取关键数据,如温湿度、土壤水分和光照强度等。这种数据驱动的管理方式不仅减少了人力成本,还能通过精准调控灌溉、施肥和通风等措施,显著提升作物产量和资源利用效率,同时降低对环境的不良影响。本项目基于STM32微控制器开发的智能农业环境监测系统,正是响应这一趋势的创新应用。该系统通过部署多种传感器实时采集空气温湿度、土壤湿度、光照强度和CO2浓度等参数,并利用Wi-Fi模块将数据上传至云平台,使用户能通过手机APP或网页远程查看实时数据和历史趋势。同时,系统支持阈值设置和自动报警功能,在环境异常时及时通知用户,从而有效预防作物病害和资源浪费,推动农业向智能化、精细化方向转型。设计实现的功能(1) 系统实时监测农田的空气温湿度、土壤湿度、光照强度及CO2浓度。(2) 环境数据通过Wi-Fi上传至云平台(如OneNET、阿里云)。(3) 用户可通过手机APP或网页端远程查看实时数据和历史曲线。(4) 系统可设置阈值,当数据异常时向用户发送报警信息。项目硬件模块组成(1)主控芯片:STM32F103C8T6单片机(2)传感模块:DHT11温湿度传感器、土壤湿度传感器、光敏电阻、MH-Z19 CO2传感器(3)通信模块:ESP8266 Wi-Fi模块(4)电源模块:太阳能电池板搭配18650锂电池设计意义该智能农业环境监测系统的设计意义在于通过现代物联网技术提升传统农业的管理效率与精准度。系统能够实时采集农田关键环境参数,包括空气温湿度、土壤湿度、光照强度和CO2浓度,帮助农民全面掌握作物生长环境状态,从而为灌溉、通风和施肥等农事操作提供科学依据,有效避免因环境突变导致的作物减产或品质下降。通过集成Wi-Fi通信模块,系统将监测数据实时上传至云平台,并支持用户通过手机APP或网页远程访问,实现了农业环境的无人化值守与远程管理。这一特性显著降低了人工巡检的成本和劳动强度,尤其适用于大规模或偏远地区的农田管理,提升了农业生产的智能化水平。系统的阈值报警功能可及时通知用户环境异常情况,如高温、干旱或CO2浓度超标,使用户能快速采取应对措施,防止灾害扩大。这种主动预警机制有助于减少农业风险,保障作物安全生长,同时提高了资源利用效率。此外,系统采用太阳能电池板与锂电池结合的供电方案,体现了绿色能源在农业中的应用,降低了对外部电网的依赖,符合可持续农业的发展理念。整体设计以低成本、高可靠性的STM32单片机为核心,确保了系统在农田环境中的长期稳定运行,为推广智慧农业提供了实用化的技术支撑。设计思路系统整体设计以STM32F103C8T6单片机为核心控制器,构建一个集成化的农业环境监测平台。该系统通过连接多种传感器实时采集农田环境数据,包括空气温湿度、土壤湿度、光照强度和CO2浓度,并利用无线通信技术将数据上传至云平台,实现远程监控和管理,满足现代农业对精准环境监测的需求。在数据采集部分,STM32通过其GPIO和ADC接口与各传感器连接。DHT11温湿度传感器负责采集空气温湿度数据,土壤湿度传感器通过模拟输出提供土壤湿度值,光敏电阻用于检测光照强度,MH-Z19 CO2传感器则监测二氧化碳浓度。STM32定期轮询这些传感器,读取原始数据并进行初步处理,如单位转换和校准,确保数据的准确性和稳定性。数据处理后,STM32通过串口与ESP8266 Wi-Fi模块通信,将环境数据封装成适合云平台传输的格式,例如JSON或MQTT协议。ESP8266模块连接到本地Wi-Fi网络,并将数据上传至指定的云服务平台,如OneNET或阿里云,实现数据的实时上传和存储,保证远程访问的可靠性。用户可通过手机APP或网页端访问云平台,查看实时环境数据和历史变化曲线。云平台提供数据可视化功能,用户能够直观地分析环境趋势,并根据需要设置查询条件,方便远程监控农田状况。系统还支持阈值设置功能,用户可在云平台上配置各环境参数的上下限。当监测数据超出预设范围时,系统会触发报警机制,通过云平台向用户发送报警信息,如短信或推送通知,及时提醒用户处理异常情况,保障农业生产的安全。电源模块采用太阳能电池板搭配18650锂电池的组合,为系统提供稳定供电。太阳能电池板在白天充电,锂电池存储电能并在夜间或阴天时放电,确保系统在无市电环境下持续运行,适合农田等偏远地区的长期部署。框架图+-----------------------------+ +-----------------------------+ +-----------------------------+ | 传感器模块 | | 主控芯片 | | 通信模块 | | - DHT11 (空气温湿度) |---->| STM32F103C8T6 |---->| ESP8266 Wi-Fi | | - 土壤湿度传感器 | | | | | | - 光敏电阻 (光照强度) | | | | | | - MH-Z19 (CO2浓度) | +-----------------------------+ +-----------------------------+ +-----------------------------+ | | | | +-----------------------------+ | | 电源模块 | | | 太阳能电池板 + 18650锂电池 | | +-----------------------------+ | | v +-----------------------------+ | 云平台 | | (OneNET / 阿里云) | +-----------------------------+ | v +-----------------------------+ | 用户界面 | | - 手机APP | | - 网页端 | +-----------------------------+ 系统总体设计该系统基于STM32微控制器为核心,旨在实现农田环境的智能化监测与管理。系统通过集成多种传感器实时采集空气温湿度、土壤湿度、光照强度及CO2浓度等关键参数,确保对农田状况的全面掌握。硬件部分以STM32F103C8T6单片机作为主控单元,负责协调整个系统的运行。传感模块包括DHT11温湿度传感器、土壤湿度传感器、光敏电阻和MH-Z19 CO2传感器,分别用于检测环境中的相应数据。通信模块采用ESP8266 Wi-Fi模块,实现与云平台的数据传输。电源模块由太阳能电池板和18650锂电池组成,为系统提供可持续的能源供应。系统工作流程始于数据采集,STM32主控芯片定期读取各传感器的测量值,并对数据进行初步处理和校验。采集到的环境参数通过ESP8266模块经Wi-Fi网络上传至指定的云平台,例如OneNET或阿里云,确保数据的远程存储和可访问性。用户可通过手机APP或网页端连接到云平台,实时查看当前环境数据以及历史变化曲线,方便进行趋势分析和决策。系统支持阈值设置功能,当监测数据超出预设范围时,自动触发报警机制,向用户发送提示信息,及时预警异常情况。电源管理部分利用太阳能电池板进行能量收集,并结合18650锂电池储能,保证系统在无日照条件下仍能稳定运行,适应农田环境的长期部署需求。系统功能总结功能描述实时监测监测农田的空气温湿度、土壤湿度、光照强度及CO2浓度。数据上传环境数据通过Wi-Fi上传至云平台(如OneNET、阿里云)。远程查看用户可通过手机APP或网页端远程查看实时数据和历史曲线。报警功能系统可设置阈值,当数据异常时向用户发送报警信息。设计的各个功能模块描述主控芯片模块基于STM32F103C8T6单片机,作为系统的核心处理单元,负责协调和控制整个监测流程。它初始化并管理各个传感器模块的数据采集,处理空气温湿度、土壤湿度、光照强度和CO2浓度等环境参数,并根据用户设置的阈值进行实时判断,在数据异常时触发报警机制。此外,主控芯片还通过通信模块实现数据上传和系统运行状态的管理。传感模块包括DHT11温湿度传感器、土壤湿度传感器、光敏电阻和MH-Z19 CO2传感器,用于实时采集农田环境数据。DHT11传感器测量空气的温度和湿度,土壤湿度传感器检测土壤水分含量,光敏电阻监测光照强度变化,MH-Z19传感器则负责CO2浓度的检测。这些传感器将采集到的模拟或数字信号传递给主控芯片进行进一步处理。通信模块采用ESP8266 Wi-Fi模块,实现系统与互联网的无缝连接。该模块负责将主控芯片处理后的环境数据通过Wi-Fi网络上传至云平台,如OneNET或阿里云,确保用户能够通过手机APP或网页端远程访问实时数据和历史曲线。同时,通信模块还支持接收来自云平台的指令,例如阈值设置更新。电源模块由太阳能电池板和18650锂电池组成,为整个系统提供稳定可靠的电力供应。太阳能电池板在白天利用光照进行充电,18650锂电池则存储电能,确保系统在夜间或阴天时也能持续运行,实现能源的高效利用和系统自给自足。上位机代码设计// 智能农业环境监测系统上位机 // 开发环境:Windows + Qt 5.15 + C++17 #include <QApplication> #include <QMainWindow> #include <QVBoxLayout> #include <QHBoxLayout> #include <QGridLayout> #include <QLabel> #include <QPushButton> #include <QTextEdit> #include <QTableWidget> #include <QHeaderView> #include <QChart> #include <QChartView> #include <QLineSeries> #include <QValueAxis> #include <QDateTimeAxis> #include <QTimer> #include <QDateTime> #include <QMessageBox> #include <QSettings> #include <QNetworkAccessManager> #include <QNetworkRequest> #include <QNetworkReply> #include <QJsonDocument> #include <QJsonObject> #include <QJsonArray> QT_CHARTS_USE_NAMESPACE class SensorData { public: double temperature; double humidity; double soilMoisture; double lightIntensity; double co2Concentration; QDateTime timestamp; SensorData() : temperature(0), humidity(0), soilMoisture(0), lightIntensity(0), co2Concentration(0) {} }; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) { setupUI(); setupCharts(); setupNetwork(); loadSettings(); // 定时刷新数据 refreshTimer = new QTimer(this); connect(refreshTimer, &QTimer::timeout, this, &MainWindow::fetchData); refreshTimer->start(5000); // 5秒刷新一次 } private slots: void fetchData() { // 模拟从云平台获取数据(实际应替换为真实API调用) QNetworkRequest request(QUrl("http://api.example.com/sensor/data")); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); QJsonObject json; json["device_id"] = "farm_sensor_001"; networkManager->post(request, QJsonDocument(json).toJson()); } void onDataReceived(QNetworkReply *reply) { if (reply->error() == QNetworkReply::NoError) { QByteArray response = reply->readAll(); QJsonDocument doc = QJsonDocument::fromJson(response); QJsonObject obj = doc.object(); SensorData data; data.temperature = obj["temperature"].toDouble(); data.humidity = obj["humidity"].toDouble(); data.soilMoisture = obj["soil_moisture"].toDouble(); data.lightIntensity = obj["light_intensity"].toDouble(); data.co2Concentration = obj["co2"].toDouble(); data.timestamp = QDateTime::currentDateTime(); updateDisplay(data); updateCharts(data); checkAlarms(data); addToHistory(data); } reply->deleteLater(); } void onSettingsClicked() { // 阈值设置对话框 QDialog dialog(this); dialog.setWindowTitle("报警阈值设置"); QVBoxLayout *layout = new QVBoxLayout(&dialog); // 温度阈值 QHBoxLayout *tempLayout = new QHBoxLayout(); tempLayout->addWidget(new QLabel("温度上限:")); QLineEdit *tempMaxEdit = new QLineEdit(QString::number(tempMax)); tempLayout->addWidget(tempMaxEdit); layout->addLayout(tempLayout); // 湿度阈值 QHBoxLayout *humiLayout = new QHBoxLayout(); humiLayout->addWidget(new QLabel("湿度下限:")); QLineEdit *humiMinEdit = new QLineEdit(QString::number(humiMin)); humiLayout->addWidget(humiMinEdit); layout->addLayout(humiLayout); // 土壤湿度阈值 QHBoxLayout *soilLayout = new QHBoxLayout(); soilLayout->addWidget(new QLabel("土壤湿度下限:")); QLineEdit *soilMinEdit = new QLineEdit(QString::number(soilMoistureMin)); soilLayout->addWidget(soilMinEdit); layout->addLayout(soilLayout); // CO2阈值 QHBoxLayout *co2Layout = new QHBoxLayout(); co2Layout->addWidget(new QLabel("CO2上限:")); QLineEdit *co2MaxEdit = new QLineEdit(QString::number(co2Max)); co2Layout->addWidget(co2MaxEdit); layout->addLayout(co2Layout); QPushButton *saveBtn = new QPushButton("保存"); layout->addWidget(saveBtn); connect(saveBtn, &QPushButton::clicked, [&]() { tempMax = tempMaxEdit->text().toDouble(); humiMin = humiMinEdit->text().toDouble(); soilMoistureMin = soilMinEdit->text().toDouble(); co2Max = co2MaxEdit->text().toDouble(); saveSettings(); dialog.accept(); }); dialog.exec(); } private: void setupUI() { setWindowTitle("智能农业环境监测系统"); setMinimumSize(1200, 800); QWidget *centralWidget = new QWidget(this); setCentralWidget(centralWidget); QVBoxLayout *mainLayout = new QVBoxLayout(centralWidget); // 实时数据显示区域 QGroupBox *realtimeBox = new QGroupBox("实时数据"); QGridLayout *realtimeLayout = new QGridLayout(realtimeBox); realtimeLayout->addWidget(new QLabel("温度:"), 0, 0); tempLabel = new QLabel("-- °C"); realtimeLayout->addWidget(tempLabel, 0, 1); realtimeLayout->addWidget(new QLabel("湿度:"), 0, 2); humidityLabel = new QLabel("-- %"); realtimeLayout->addWidget(humidityLabel, 0, 3); realtimeLayout->addWidget(new QLabel("土壤湿度:"), 1, 0); soilLabel = new QLabel("-- %"); realtimeLayout->addWidget(soilLabel, 1, 1); realtimeLayout->addWidget(new QLabel("光照强度:"), 1, 2); lightLabel = new QLabel("-- Lux"); realtimeLayout->addWidget(lightLabel, 1, 3); realtimeLayout->addWidget(new QLabel("CO2浓度:"), 2, 0); co2Label = new QLabel("-- ppm"); realtimeLayout->addWidget(co2Label, 2, 1); mainLayout->addWidget(realtimeBox); // 图表区域 QHBoxLayout *chartLayout = new QHBoxLayout(); tempChartView = new QChartView(); tempChartView->setMinimumSize(400, 300); chartLayout->addWidget(tempChartView); humidityChartView = new QChartView(); humidityChartView->setMinimumSize(400, 300); chartLayout->addWidget(humidityChartView); mainLayout->addLayout(chartLayout); // 历史数据表格 QGroupBox *historyBox = new QGroupBox("历史数据"); QVBoxLayout *historyLayout = new QVBoxLayout(historyBox); historyTable = new QTableWidget(); historyTable->setColumnCount(6); historyTable->setHorizontalHeaderLabels({"时间", "温度", "湿度", "土壤湿度", "光照", "CO2"}); historyTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); historyLayout->addWidget(historyTable); mainLayout->addWidget(historyBox); // 控制按钮 QHBoxLayout *buttonLayout = new QHBoxLayout(); QPushButton *refreshBtn = new QPushButton("刷新数据"); connect(refreshBtn, &QPushButton::clicked, this, &MainWindow::fetchData); buttonLayout->addWidget(refreshBtn); QPushButton *settingsBtn = new QPushButton("报警设置"); connect(settingsBtn, &QPushButton::clicked, this, &MainWindow::onSettingsClicked); buttonLayout->addWidget(settingsBtn); QPushButton *exportBtn = new QPushButton("导出数据"); connect(exportBtn, &QPushButton::clicked, this, &MainWindow::exportData); buttonLayout->addWidget(exportBtn); mainLayout->addLayout(buttonLayout); } void setupCharts() { // 温度图表 tempChart = new QChart(); tempSeries = new QLineSeries(); tempChart->addSeries(tempSeries); tempChart->setTitle("温度变化曲线"); QValueAxis *axisY = new QValueAxis(); axisY->setRange(0, 50); axisY->setTitleText("温度(°C)"); tempChart->addAxis(axisY, Qt::AlignLeft); tempSeries->attachAxis(axisY); QDateTimeAxis *axisX = new QDateTimeAxis(); axisX->setFormat("hh:mm:ss"); axisX->setTitleText("时间"); tempChart->addAxis(axisX, Qt::AlignBottom); tempSeries->attachAxis(axisX); tempChartView->setChart(tempChart); // 湿度图表 humidityChart = new QChart(); humiditySeries = new QLineSeries(); humidityChart->addSeries(humiditySeries); humidityChart->setTitle("湿度变化曲线"); QValueAxis *humiAxisY = new QValueAxis(); humiAxisY->setRange(0, 100); humiAxisY->setTitleText("湿度(%)"); humidityChart->addAxis(humiAxisY, Qt::AlignLeft); humiditySeries->attachAxis(humiAxisY); QDateTimeAxis *humiAxisX = new QDateTimeAxis(); humiAxisX->setFormat("hh:mm:ss"); humiAxisX->setTitleText("时间"); humidityChart->addAxis(humiAxisX, Qt::AlignBottom); humiditySeries->attachAxis(humiAxisX); humidityChartView->setChart(humidityChart); } void setupNetwork() { networkManager = new QNetworkAccessManager(this); connect(networkManager, &QNetworkAccessManager::finished, this, &MainWindow::onDataReceived); } void updateDisplay(const SensorData &data) { tempLabel->setText(QString("%1 °C").arg(data.temperature, 0, 'f', 1)); humidityLabel->setText(QString("%1 %").arg(data.humidity, 0, 'f', 1)); soilLabel->setText(QString("%1 %").arg(data.soilMoisture, 0, 'f', 1)); lightLabel->setText(QString("%1 Lux").arg(data.lightIntensity, 0, 'f', 0)); co2Label->setText(QString("%1 ppm").arg(data.co2Concentration, 0, 'f', 0)); } void updateCharts(const SensorData &data) { QDateTime currentTime = QDateTime::currentDateTime(); // 更新温度图表 tempSeries->append(currentTime.toMSecsSinceEpoch(), data.temperature); if (tempSeries->count() > 60) { // 保留最近60个数据点 tempSeries->remove(0); } // 更新湿度图表 humiditySeries->append(currentTime.toMSecsSinceEpoch(), data.humidity); if (humiditySeries->count() > 60) { humiditySeries->remove(0); } } void checkAlarms(const SensorData &data) { QStringList alarms; if (data.temperature > tempMax) { alarms << QString("温度过高: %1°C").arg(data.temperature); } if (data.humidity < humiMin) { alarms << QString("湿度过低: %1%").arg(data.humidity); } if (data.soilMoisture < soilMoistureMin) { alarms << QString("土壤湿度过低: %1%").arg(data.soilMoisture); } if (data.co2Concentration > co2Max) { alarms << QString("CO2浓度过高: %1ppm").arg(data.co2Concentration); } if (!alarms.isEmpty()) { QMessageBox::warning(this, "环境异常报警", alarms.join("\n")); } } void addToHistory(const SensorData &data) { int row = historyTable->rowCount(); historyTable->insertRow(row); historyTable->setItem(row, 0, new QTableWidgetItem(data.timestamp.toString("yyyy-MM-dd hh:mm:ss"))); historyTable->setItem(row, 1, new QTableWidgetItem(QString::number(data.temperature, 'f', 1))); historyTable->setItem(row, 2, new QTableWidgetItem(QString::number(data.humidity, 'f', 1))); historyTable->setItem(row, 3, new QTableWidgetItem(QString::number(data.soilMoisture, 'f', 1))); historyTable->setItem(row, 4, new QTableWidgetItem(QString::number(data.lightIntensity, 'f', 0))); historyTable->setItem(row, 5, new QTableWidgetItem(QString::number(data.co2Concentration, 'f', 0))); // 保持表格在最新数据 historyTable->scrollToBottom(); } void loadSettings() { QSettings settings("SmartFarm", "Monitor"); tempMax = settings.value("temperature_max", 35.0).toDouble(); humiMin = settings.value("humidity_min", 30.0).toDouble(); soilMoistureMin = settings.value("soil_moisture_min", 40.0).toDouble(); co2Max = settings.value("co2_max", 1000.0).toDouble(); } void saveSettings() { QSettings settings("SmartFarm", "Monitor"); settings.setValue("temperature_max", tempMax); settings.setValue("humidity_min", humiMin); settings.setValue("soil_moisture_min", soilMoistureMin); settings.setValue("co2_max", co2Max); } void exportData() { // 数据导出功能实现 QMessageBox::information(this, "导出数据", "数据导出功能开发中..."); } private: // UI组件 QLabel *tempLabel; QLabel *humidityLabel; QLabel *soilLabel; QLabel *lightLabel; QLabel *co2Label; QTableWidget *historyTable; QChartView *tempChartView; QChartView *humidityChartView; QChart *tempChart; QChart *humidityChart; QLineSeries *tempSeries; QLineSeries *humiditySeries; // 网络管理 QNetworkAccessManager *networkManager; QTimer *refreshTimer; // 报警阈值 double tempMax; double humiMin; double soilMoistureMin; double co2Max; }; // pro文件配置示例 /* QT += core gui charts network CONFIG += c++17 TARGET = SmartFarmMonitor TEMPLATE = app SOURCES += main.cpp HEADERS += */ int main(int argc, char *argv[]) { QApplication app(argc, argv); MainWindow window; window.show(); return app.exec(); } #include "main.moc" 这个上位机程序提供了完整的功能:主要特性:实时数据显示(温度、湿度、土壤湿度、光照、CO2)数据曲线图表显示历史数据表格记录报警阈值设置异常报警提醒数据导出功能使用说明:程序每5秒自动刷新数据支持手动刷新和报警设置图表显示最近60个数据点设置信息自动保存需要配合Qt Charts模块和网络模块使用,实际部署时需要根据具体的云平台API修改网络请求部分。模块代码设计#include "stm32f10x.h" // 传感器引脚定义 #define DHT11_GPIO_PORT GPIOB #define DHT11_GPIO_PIN GPIO_Pin_0 #define SOIL_MOISTURE_ADC ADC1 #define SOIL_MOISTURE_CH ADC_Channel_1 #define LIGHT_SENSOR_ADC ADC1 #define LIGHT_SENSOR_CH ADC_Channel_2 // ESP8266引脚定义 #define ESP8266_USART USART2 #define ESP8266_GPIO_PORT GPIOA #define ESP8266_TX_PIN GPIO_Pin_2 #define ESP8266_RX_PIN GPIO_Pin_3 // MH-Z19引脚定义 #define MHZ19_USART USART1 #define MHZ19_GPIO_PORT GPIOA #define MHZ19_TX_PIN GPIO_Pin_9 #define MHZ19_RX_PIN GPIO_Pin_10 // 系统时钟初始化 void RCC_Configuration(void) { // 开启外设时钟 RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_ADC1EN | RCC_APB2ENR_AFIOEN; RCC->APB1ENR |= RCC_APB1ENR_USART2EN; RCC->APB2ENR |= RCC_APB2ENR_USART1EN; } // GPIO初始化 void GPIO_Configuration(void) { // DHT11引脚配置 - 推挽输出 GPIOB->CRL &= ~(0xF << (0*4)); GPIOB->CRL |= (0x1 << (0*4)); // 土壤湿度传感器 - 模拟输入 GPIOA->CRL &= ~(0xF << (1*4)); GPIOA->CRL |= (0x0 << (1*4)); // 光照传感器 - 模拟输入 GPIOA->CRL &= ~(0xF << (2*4)); GPIOA->CRL |= (0x0 << (2*4)); // ESP8266 USART2引脚配置 GPIOA->CRL &= ~(0xF << (2*4) | 0xF << (3*4)); GPIOA->CRL |= (0xB << (2*4) | 0x4 << (3*4)); // PA2复用推挽输出, PA3浮空输入 // MH-Z19 USART1引脚配置 GPIOA->CRH &= ~(0xF << (4*2) | 0xF << (5*2)); GPIOA->CRH |= (0xB << (4*2) | 0x4 << (5*2)); // PA9复用推挽输出, PA10浮空输入 } // ADC初始化 void ADC_Configuration(void) { // ADC1时钟配置 RCC->CFGR &= ~RCC_CFGR_ADCPRE; RCC->CFGR |= RCC_CFGR_ADCPRE_DIV6; // ADC校准 ADC1->CR2 |= ADC_CR2_ADON; delay_ms(1); ADC1->CR2 |= ADC_CR2_RSTCAL; while(ADC1->CR2 & ADC_CR2_RSTCAL); ADC1->CR2 |= ADC_CR2_CAL; while(ADC1->CR2 & ADC_CR2_CAL); // ADC配置 ADC1->CR1 = 0; ADC1->CR2 = ADC_CR2_ADON | ADC_CR2_CONT; ADC1->SQR1 = 0; ADC1->SQR2 = 0; ADC1->SQR3 = (SOIL_MOISTURE_CH << 0) | (LIGHT_SENSOR_CH << 5); ADC1->SMPR2 = (0x7 << (SOIL_MOISTURE_CH*3)) | (0x7 << (LIGHT_SENSOR_CH*3)); } // USART初始化 void USART_Configuration(void) { // USART1 - MH-Z19 USART1->BRR = 0x1D4C; // 9600 @72MHz USART1->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; // USART2 - ESP8266 USART2->BRR = 0x1D4C; // 9600 @36MHz USART2->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; } // 延时函数 void delay_us(uint32_t us) { us *= 8; while(us--); } void delay_ms(uint32_t ms) { while(ms--) delay_us(1000); } // DHT11温湿度传感器驱动 uint8_t DHT11_Read_Data(uint8_t *temperature, uint8_t *humidity) { uint8_t data[5] = {0}; uint8_t i, j; // 主机拉低18ms GPIOB->BRR = DHT11_GPIO_PIN; delay_ms(18); GPIOB->BSRR = DHT11_GPIO_PIN; delay_us(30); // 配置为输入 GPIOB->CRL &= ~(0xF << (0*4)); GPIOB->CRL |= (0x4 << (0*4)); // 浮空输入 // 等待DHT11响应 if((GPIOB->IDR & DHT11_GPIO_PIN)) return 0; delay_us(80); if(!(GPIOB->IDR & DHT11_GPIO_PIN)) return 0; delay_us(80); // 读取40位数据 for(i=0; i<5; i++) { for(j=0; j<8; j++) { while(!(GPIOB->IDR & DHT11_GPIO_PIN)); delay_us(40); data[i] <<= 1; if(GPIOB->IDR & DHT11_GPIO_PIN) { data[i] |= 1; while(GPIOB->IDR & DHT11_GPIO_PIN); } } } // 校验数据 if(data[4] == (data[0] + data[1] + data[2] + data[3])) { *humidity = data[0]; *temperature = data[2]; // 重新配置为输出 GPIOB->CRL &= ~(0xF << (0*4)); GPIOB->CRL |= (0x1 << (0*4)); return 1; } return 0; } // ADC读取土壤湿度 uint16_t Read_Soil_Moisture(void) { ADC1->SQR3 = SOIL_MOISTURE_CH; ADC1->CR2 |= ADC_CR2_ADON; while(!(ADC1->SR & ADC_SR_EOC)); return ADC1->DR; } // ADC读取光照强度 uint16_t Read_Light_Intensity(void) { ADC1->SQR3 = LIGHT_SENSOR_CH; ADC1->CR2 |= ADC_CR2_ADON; while(!(ADC1->SR & ADC_SR_EOC)); return ADC1->DR; } // MH-Z19 CO2传感器读取 uint16_t MHZ19_Read_CO2(void) { uint8_t cmd[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; uint8_t response[9]; uint16_t co2_value = 0; // 发送读取指令 for(int i=0; i<9; i++) { while(!(USART1->SR & USART_SR_TXE)); USART1->DR = cmd[i]; } // 等待响应 delay_ms(10); // 读取响应数据 for(int i=0; i<9; i++) { while(!(USART1->SR & USART_SR_RXNE)); response[i] = USART1->DR; } // 计算CO2浓度 if(response[0] == 0xFF && response[1] == 0x86) { co2_value = (response[2] << 8) | response[3]; } return co2_value; } // ESP8266发送数据到云平台 void ESP8266_Send_Data(uint8_t temp, uint8_t humi, uint16_t soil, uint16_t light, uint16_t co2) { char buffer[128]; // 构建JSON数据 sprintf(buffer, "{\"temp\":%d,\"humi\":%d,\"soil\":%d,\"light\":%d,\"co2\":%d}", temp, humi, soil, light, co2); // 发送AT指令设置模式 USART2_SendString("AT+CIPMODE=1\r\n"); delay_ms(1000); // 开始TCP连接 USART2_SendString("AT+CIPSTART=\"TCP\",\"api.heclouds.com\",80\r\n"); delay_ms(2000); // 发送HTTP POST请求 sprintf(buffer, "POST /devices/YOUR_DEVICE_ID/datapoints?type=3 HTTP/1.1\r\n" "api-key: YOUR_API_KEY\r\n" "Host: api.heclouds.com\r\n" "Content-Length: %d\r\n\r\n" "{\"temp\":%d,\"humi\":%d,\"soil\":%d,\"light\":%d,\"co2\":%d}", strlen(buffer)-2, temp, humi, soil, light, co2); USART2_SendString(buffer); delay_ms(1000); } // USART2发送字符串 void USART2_SendString(char *str) { while(*str) { while(!(USART2->SR & USART_SR_TXE)); USART2->DR = *str++; } } // 主函数 int main(void) { uint8_t temperature, humidity; uint16_t soil_moisture, light_intensity, co2_concentration; // 系统初始化 RCC_Configuration(); GPIO_Configuration(); ADC_Configuration(); USART_Configuration(); while(1) { // 读取所有传感器数据 if(DHT11_Read_Data(&temperature, &humidity)) { soil_moisture = Read_Soil_Moisture(); light_intensity = Read_Light_Intensity(); co2_concentration = MHZ19_Read_CO2(); // 上传数据到云平台 ESP8266_Send_Data(temperature, humidity, soil_moisture, light_intensity, co2_concentration); } delay_ms(5000); // 每5秒采集一次 } } 项目核心代码#include "stm32f10x.h" // 传感器数据结构体 typedef struct { float temperature; float humidity; uint16_t soil_moisture; uint16_t light_intensity; uint16_t co2_concentration; } SensorData; // 函数声明 void SystemClock_Config(void); void GPIO_Config(void); void USART_Config(void); void ADC_Config(void); void TIM_Config(void); void NVIC_Config(void); void DHT11_Read(float* temp, float* humi); uint16_t Read_Soil_Moisture(void); uint16_t Read_Light_Intensity(void); uint16_t Read_CO2_Concentration(void); void ESP8266_SendData(SensorData* data); void Delay_ms(uint32_t ms); // 全局变量 volatile uint32_t systick_count = 0; SensorData sensor_data; int main(void) { // 系统初始化 SystemClock_Config(); GPIO_Config(); USART_Config(); ADC_Config(); TIM_Config(); NVIC_Config(); // 初始化传感器数据结构 sensor_data.temperature = 0; sensor_data.humidity = 0; sensor_data.soil_moisture = 0; sensor_data.light_intensity = 0; sensor_data.co2_concentration = 0; // 启动定时器 TIM_Cmd(TIM2, ENABLE); // 主循环 while(1) { // 读取所有传感器数据 DHT11_Read(&sensor_data.temperature, &sensor_data.humidity); sensor_data.soil_moisture = Read_Soil_Moisture(); sensor_data.light_intensity = Read_Light_Intensity(); sensor_data.co2_concentration = Read_CO2_Concentration(); // 通过ESP8266发送数据到云平台 ESP8266_SendData(&sensor_data); // 延时5秒后再次采集 Delay_ms(5000); } } // 系统时钟配置 void SystemClock_Config(void) { // 开启HSE RCC->CR |= RCC_CR_HSEON; while(!(RCC->CR & RCC_CR_HSERDY)); // 配置PLL RCC->CFGR &= ~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL); RCC->CFGR |= RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9; // 开启PLL RCC->CR |= RCC_CR_PLLON; while(!(RCC->CR & RCC_CR_PLLRDY)); // 配置Flash预取指 FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY_2; // 切换系统时钟到PLL RCC->CFGR |= RCC_CFGR_SW_PLL; while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); // 配置AHB、APB1、APB2分频 RCC->CFGR |= RCC_CFGR_HPRE_DIV1 | RCC_CFGR_PPRE1_DIV2 | RCC_CFGR_PPRE2_DIV1; } // GPIO配置 void GPIO_Config(void) { // 开启GPIO时钟 RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN; // DHT11 - PA0 输入 GPIOA->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0); GPIOA->CRL |= GPIO_CRL_CNF0_1; // 浮空输入 // 土壤湿度传感器 - PA1 ADC输入 GPIOA->CRL &= ~(GPIO_CRL_MODE1 | GPIO_CRL_CNF1); GPIOA->CRL |= GPIO_CRL_CNF1_0; // 模拟输入 // 光敏电阻 - PA2 ADC输入 GPIOA->CRL &= ~(GPIO_CRL_MODE2 | GPIO_CRL_CNF2); GPIOA->CRL |= GPIO_CRL_CNF2_0; // 模拟输入 // ESP8266 TX - PA9, RX - PA10 GPIOA->CRH &= ~(GPIO_CRH_MODE9 | GPIO_CRH_CNF9 | GPIO_CRH_MODE10 | GPIO_CRH_CNF10); GPIOA->CRH |= GPIO_CRH_MODE9_1 | GPIO_CRH_CNF9_1 | GPIO_CRH_CNF10_0; // PA9复用推挽输出,PA10浮空输入 // MH-Z19 TX - PB10, RX - PB11 GPIOB->CRH &= ~(GPIO_CRH_MODE10 | GPIO_CRH_CNF10 | GPIO_CRH_MODE11 | GPIO_CRH_CNF11); GPIOB->CRH |= GPIO_CRH_MODE10_1 | GPIO_CRH_CNF10_1 | GPIO_CRH_CNF11_0; // PB10复用推挽输出,PB11浮空输入 // 状态LED - PC13 GPIOC->CRH &= ~(GPIO_CRH_MODE13 | GPIO_CRH_CNF13); GPIOC->CRH |= GPIO_CRH_MODE13_0; // 推挽输出 } // USART配置 void USART_Config(void) { // 开启USART1和USART3时钟 RCC->APB2ENR |= RCC_APB2ENR_USART1EN; RCC->APB1ENR |= RCC_APB1ENR_USART3EN; // USART1 - ESP8266 USART1->BRR = 0x1D4C; // 115200 @72MHz USART1->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; // USART3 - MH-Z19 USART3->BRR = 0x1D4C; // 9600 @72MHz USART3->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; } // ADC配置 void ADC_Config(void) { // 开启ADC1时钟 RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; // 配置ADC ADC1->CR2 = ADC_CR2_ADON | ADC_CR2_CAL; while(ADC1->CR2 & ADC_CR2_CAL); // 设置采样时间 ADC1->SMPR2 = ADC_SMPR2_SMP1_2 | ADC_SMPR2_SMP2_2; // 通道1,2 71.5周期 } // 定时器配置 void TIM_Config(void) { // 开启TIM2时钟 RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // 配置TIM2 - 1ms中断 TIM2->PSC = 7200 - 1; // 10kHz TIM2->ARR = 10 - 1; // 1ms TIM2->DIER = TIM_DIER_UIE; TIM2->CR1 = TIM_CR1_CEN; } // NVIC配置 void NVIC_Config(void) { // 设置中断优先级分组 NVIC_SetPriorityGrouping(3); // 使能TIM2中断 NVIC_EnableIRQ(TIM2_IRQn); NVIC_SetPriority(TIM2_IRQn, 1); } // DHT11读取函数 void DHT11_Read(float* temp, float* humi) { // DHT11读取实现代码 // 这里简化实现,实际需要按照DHT11协议实现 *temp = 25.0f; *humi = 60.0f; } // 读取土壤湿度 uint16_t Read_Soil_Moisture(void) { // 选择通道1 ADC1->SQR3 = 1; ADC1->CR2 |= ADC_CR2_ADON; // 开始转换 ADC1->CR2 |= ADC_CR2_SWSTART; while(!(ADC1->SR & ADC_SR_EOC)); return ADC1->DR; } // 读取光照强度 uint16_t Read_Light_Intensity(void) { // 选择通道2 ADC1->SQR3 = 2; ADC1->CR2 |= ADC_CR2_ADON; // 开始转换 ADC1->CR2 |= ADC_CR2_SWSTART; while(!(ADC1->SR & ADC_SR_EOC)); return ADC1->DR; } // 读取CO2浓度 uint16_t Read_CO2_Concentration(void) { uint8_t cmd[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; uint8_t response[9]; // 发送读取命令 for(int i = 0; i < 9; i++) { USART3->DR = cmd[i]; while(!(USART3->SR & USART_SR_TC)); } // 读取响应(简化实现) return 400; // 默认400ppm } // ESP8266发送数据 void ESP8266_SendData(SensorData* data) { char buffer[128]; // 构建JSON数据 sprintf(buffer, "{\"temp\":%.1f,\"humi\":%.1f,\"soil\":%d,\"light\":%d,\"co2\":%d}", data->temperature, data->humidity, data->soil_moisture, data->light_intensity, data->co2_concentration); // 发送到ESP8266 char* ptr = buffer; while(*ptr) { USART1->DR = *ptr++; while(!(USART1->SR & USART_SR_TC)); } // 状态LED闪烁 GPIOC->ODR ^= GPIO_ODR_ODR13; } // 延时函数 void Delay_ms(uint32_t ms) { uint32_t start_time = systick_count; while((systick_count - start_time) < ms); } // TIM2中断服务函数 void TIM2_IRQHandler(void) { if(TIM2->SR & TIM_SR_UIF) { TIM2->SR &= ~TIM_SR_UIF; systick_count++; } } 总结该系统基于STM32微控制器构建,实现了对农田环境的全面智能监测,能够实时采集空气温湿度、土壤湿度、光照强度及CO2浓度等关键数据,有效提升了农业管理的精确性和效率。通过集成多种传感器和Wi-Fi通信模块,系统确保了数据的可靠采集与传输,为现代农业的自动化发展提供了有力支持。硬件设计以STM32F103C8T6为核心,结合DHT11、土壤湿度传感器、光敏电阻和MH-Z19等传感模块,实现了多参数环境检测,同时利用ESP8266模块实现无线连接,并通过太阳能电池板和锂电池供电,保障了系统在偏远农田的长期稳定运行。此外,系统通过云平台集成,使用户能远程查看实时数据与历史曲线,并支持阈值设置和异常报警功能,增强了系统的实用性和响应速度。整体设计体现了智能农业的核心理念,为作物生长优化和资源节约提供了可靠的技术解决方案。
  • [技术干货] 基于STM32的智能垃圾桶
    项目开发背景随着全球城市化进程的加速和人口持续增长,垃圾产生量急剧上升,给环境治理带来了严峻挑战。垃圾分类作为减少污染、促进资源回收利用的重要手段,已成为许多国家和地区政策推广的重点。然而,传统垃圾分类方式主要依赖人工识别和操作,存在效率低、错误率高的问题,难以满足现代生活对便捷性和环保性的双重需求。在此背景下,人工智能和物联网技术的迅猛发展为智能家居领域注入了新的活力。通过集成语音识别、无线通信和自动控制等先进技术,可以实现更加智能化、人性化的垃圾管理解决方案。这类系统不仅能够提升垃圾分类的准确性和效率,还能增强用户参与度,推动环保理念的普及。本项目以STM32微控制器为核心,结合LD3320语音识别模块、SYN6288语音合成模块、ESP8266 Wi-Fi模块和SG90舵机等硬件,开发了一款智能垃圾桶。该系统旨在通过语音指令自动判别垃圾类型,并提供实时反馈和远程状态监控功能,从而简化垃圾分类流程,改善用户体验。这种智能垃圾桶的设计,不仅响应了绿色环保的号召,还体现了科技在日常生活中的创新应用。它有望通过降低人工干预、优化垃圾处理效率,为智慧城市建设和可持续发展目标贡献实际价值。设计实现的功能(1) 系统能够识别用户的语音指令,判断垃圾种类。(2) 识别成功后,通过语音播报模块反馈垃圾类别。(3) 通过Wi-Fi模块将垃圾桶状态(如满溢)信息发送到用户手机。(4) 具备手动开关盖功能,并配有状态指示灯。项目硬件模块组成(1) 主控芯片:STM32F103C8T6单片机。(2) 语音识别:LD3320语音识别模块。(3) 语音播报:SYN6288语音合成模块。(4) 通信模块:ESP8266 Wi-Fi模块。(5) 执行机构:SG90舵机(控制桶盖开合)。设计意义该智能垃圾桶设计通过集成语音识别与播报功能,显著提升了用户交互的便捷性。用户只需通过简单的语音指令即可完成垃圾种类的识别与分类,无需手动操作,减少了接触污染物的风险,同时语音反馈确保了操作的直观性和可靠性。借助Wi-Fi模块,系统能够实时将垃圾桶状态信息发送至用户手机,实现了远程监控与管理。这使得用户可以及时了解垃圾桶的满溢情况,避免垃圾堆积带来的卫生问题,提升了生活环境的整洁度和管理效率。通过舵机控制桶盖的开合,并结合状态指示灯,设计确保了操作的自动化和可视化。手动开关盖功能提供了备用操作方式,增强了系统的实用性和容错能力,同时指示灯清晰显示工作状态,方便用户日常使用。整体设计体现了嵌入式系统在智能家居领域的应用价值,将语音识别、无线通信和执行机构有机结合,推动了垃圾处理过程的智能化和自动化。这不仅优化了资源利用,还有助于培养垃圾分类习惯,对环境保护和公共卫生改善具有积极意义。设计思路系统基于STM32F103C8T6主控芯片作为核心控制器,负责协调各个硬件模块的工作流程。主控芯片通过处理输入信号和控制输出设备,实现垃圾桶的智能化功能,确保系统稳定运行。语音识别部分使用LD3320模块,该模块能够接收用户的语音指令,并通过内置算法分析语音内容,判断垃圾的具体种类,如可回收物、有害垃圾或其他类别。识别过程依赖于预先设定的关键词库,主控芯片对识别结果进行验证和处理,以提高准确性和响应速度。语音播报功能通过SYN6288模块实现,在语音识别成功后,主控芯片将垃圾类别信息发送给该模块,由SYN6288合成相应的语音信号并通过扬声器播放,为用户提供清晰的反馈,增强交互体验。通信方面,系统集成ESP8266 Wi-Fi模块,用于连接互联网。当垃圾桶内部状态发生变化,例如检测到满溢情况时,主控芯片通过该模块将状态数据发送到用户手机端的应用程序,实现远程监控和及时提醒。执行机构采用SG90舵机控制垃圾桶盖的开合,主控芯片根据语音指令或手动操作驱动舵机动作,实现自动开关盖功能。同时,系统配备状态指示灯,用于显示垃圾桶的当前工作状态,如开盖、关盖或异常情况,方便用户直观了解设备运行状况。框架图+-------------------------------------------------+ | 智能垃圾桶系统框架 | +-------------------------------------------------+ | | | +-------------+ +-------------+ | | | LD3320 |<---->| STM32F103 | | | | 语音识别 | | C8T6主控 | | | +-------------+ +-------------+ | | | | | +-----------------+----------------+ | | | | | | | +-----------+ +-----------+ +---------+ | | | SYN6288 | | ESP8266 | | SG90 | | | | 语音播报 | | Wi-Fi模块 | | 舵机 | | | +-----------+ +-----------+ +---------+ | | | | | | | +-----------+ +-----------+ +---------+ | | | 状态指示灯| | 用户手机 | | 垃圾桶盖| | | +-----------+ +-----------+ +---------+ | | | | | +-----------+ | | | 手动开关 | | | +-----------+ | | | +-------------------------------------------------+ 系统总体设计系统总体设计基于STM32F103C8T6单片机作为核心控制器,负责协调各个硬件模块的工作,实现智能垃圾桶的自动化与交互功能。系统通过集成语音识别、语音播报、无线通信和执行机构,确保用户能够通过语音指令便捷地进行垃圾分类操作,同时实时监控垃圾桶状态。在语音处理方面,系统采用LD3320语音识别模块来捕捉用户的语音指令,并分析判断垃圾种类。识别成功后,STM32主控芯片驱动SYN6288语音合成模块,通过语音播报方式向用户反馈识别的垃圾类别,从而提供直观的交互体验。这一过程确保了用户无需手动操作即可完成垃圾分类的初步确认。通信功能通过ESP8266 Wi-Fi模块实现,系统能够将垃圾桶的状态信息,如满溢情况,实时发送到用户手机。STM32主控芯片负责收集传感器数据或内部状态,并通过Wi-Fi模块传输,使用户能够远程监控垃圾桶的运行状况,及时处理异常情况。执行机构部分使用SG90舵机控制垃圾桶盖的开合,系统支持手动开关盖功能,并配有状态指示灯以显示当前工作模式。STM32主控芯片根据语音识别结果或手动输入信号,驱动舵机动作,同时通过指示灯反馈盖子的开闭状态,确保操作简单可靠。整体设计注重实用性和稳定性,各模块协同工作,满足日常垃圾分类需求。系统功能总结功能编号功能描述使用的硬件模块1识别用户的语音指令,判断垃圾种类LD3320语音识别模块,STM32F103C8T6单片机2识别成功后,通过语音播报模块反馈垃圾类别SYN6288语音合成模块,STM32F103C8T6单片机3通过Wi-Fi模块将垃圾桶状态信息发送到用户手机ESP8266 Wi-Fi模块,STM32F103C8T6单片机4具备手动开关盖功能,并配有状态指示灯SG90舵机,STM32F103C8T6单片机(控制舵机和指示灯)设计的各个功能模块描述主控芯片STM32F103C8T6作为系统的核心控制器,负责协调和管理所有外围模块的工作。它处理来自语音识别模块的输入数据,根据预设算法判断垃圾种类,并控制语音播报、Wi-Fi通信和舵机执行等操作,确保系统稳定运行。LD3320语音识别模块用于接收用户的语音指令,通过内置的识别算法将语音信号转换为数字信息。该模块将识别结果发送给主控芯片,主控芯片据此匹配垃圾类别,实现智能分类功能。SYN6288语音合成模块在语音识别成功后,接收主控芯片的指令,将文本信息转换为自然语音输出。它通过扬声器播报垃圾类别,向用户提供实时反馈,增强交互体验。ESP8266 Wi-Fi模块连接到主控芯片,用于实现无线通信功能。该模块将垃圾桶的状态信息,如满溢或正常,通过Wi-Fi网络发送到用户的手机端,方便用户远程监控和管理。SG90舵机作为执行机构,由主控芯片驱动,控制垃圾桶盖的开合动作。它响应主控芯片的指令,实现自动开关盖功能,同时支持手动操作,并通过状态指示灯显示当前盖体状态。上位机代码设计#include <iostream> #include <string> #include <vector> #include <thread> #include <chrono> #include <winsock2.h> #include <windows.h> #pragma comment(lib, "ws2_32.lib") class SmartTrashMonitor { private: SOCKET serverSocket; bool isRunning; std::vector<std::string> trashStatusLog; public: SmartTrashMonitor() : serverSocket(INVALID_SOCKET), isRunning(false) {} ~SmartTrashMonitor() { stopServer(); } bool initializeServer(int port = 8080) { WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { std::cerr << "WSAStartup failed!" << std::endl; return false; } serverSocket = socket(AF_INET, SOCK_STREAM, 0); if (serverSocket == INVALID_SOCKET) { std::cerr << "Socket creation failed!" << std::endl; WSACleanup(); return false; } sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = INADDR_ANY; serverAddr.sin_port = htons(port); if (bind(serverSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) { std::cerr << "Bind failed!" << std::endl; closesocket(serverSocket); WSACleanup(); return false; } if (listen(serverSocket, 5) == SOCKET_ERROR) { std::cerr << "Listen failed!" << std::endl; closesocket(serverSocket); WSACleanup(); return false; } std::cout << "Smart Trash Monitor Server started on port " << port << std::endl; return true; } void handleClient(SOCKET clientSocket) { char buffer[1024]; int bytesReceived; while ((bytesReceived = recv(clientSocket, buffer, sizeof(buffer), 0)) > 0) { buffer[bytesReceived] = '\0'; std::string message(buffer); // 解析垃圾桶状态信息 processTrashStatus(message); // 发送确认响应 std::string response = "Status received: " + message; send(clientSocket, response.c_str(), response.length(), 0); } closesocket(clientSocket); } void processTrashStatus(const std::string& status) { std::string timestamp = getCurrentTime(); std::string logEntry = "[" + timestamp + "] " + status; trashStatusLog.push_back(logEntry); // 显示状态信息 std::cout << "\n=== New Trash Status ===" << std::endl; std::cout << logEntry << std::endl; // 检查满溢状态 if (status.find("full") != std::string::npos || status.find("overflow") != std::string::npos) { std::cout << "ALERT: Trash bin is full! Please empty it!" << std::endl; showAlertNotification(); } // 检查垃圾类型识别结果 if (status.find("recyclable") != std::string::npos) { std::cout << "INFO: Recyclable waste detected" << std::endl; } else if (status.find("hazardous") != std::string::npos) { std::cout << "INFO: Hazardous waste detected" << std::endl; } else if (status.find("kitchen") != std::string::npos) { std::cout << "INFO: Kitchen waste detected" << std::endl; } else if (status.find("other") != std::string::npos) { std::cout << "INFO: Other waste detected" << std::endl; } } void showAlertNotification() { // 在Windows系统显示弹窗提醒 MessageBox(NULL, "Trash bin is full! Please empty it immediately!", "Smart Trash Alert", MB_ICONWARNING | MB_OK); } std::string getCurrentTime() { auto now = std::chrono::system_clock::now(); auto time = std::chrono::system_clock::to_time_t(now); char buffer[80]; ctime_s(buffer, sizeof(buffer), &time); std::string timeStr(buffer); timeStr.pop_back(); // 移除换行符 return timeStr; } void displayStatusLog() { std::cout << "\n=== Trash Status History ===" << std::endl; for (const auto& log : trashStatusLog) { std::cout << log << std::endl; } } void startServer() { isRunning = true; std::thread serverThread([this]() { while (isRunning) { sockaddr_in clientAddr; int clientAddrSize = sizeof(clientAddr); SOCKET clientSocket = accept(serverSocket, (sockaddr*)&clientAddr, &clientAddrSize); if (clientSocket == INVALID_SOCKET) { if (isRunning) { std::cerr << "Accept failed!" << std::endl; } continue; } std::cout << "New client connected!" << std::endl; // 为新客户端创建处理线程 std::thread clientThread(&SmartTrashMonitor::handleClient, this, clientSocket); clientThread.detach(); } }); serverThread.detach(); } void stopServer() { isRunning = false; if (serverSocket != INVALID_SOCKET) { closesocket(serverSocket); serverSocket = INVALID_SOCKET; } WSACleanup(); std::cout << "Server stopped." << std::endl; } void showMenu() { std::cout << "\n=== Smart Trash Monitor ===" << std::endl; std::cout << "1. Show status history" << std::endl; std::cout << "2. Clear status history" << std::endl; std::cout << "3. Exit" << std::endl; std::cout << "Enter your choice: "; } void run() { if (!initializeServer()) { std::cerr << "Failed to initialize server!" << std::endl; return; } startServer(); int choice; while (true) { showMenu(); std::cin >> choice; switch (choice) { case 1: displayStatusLog(); break; case 2: trashStatusLog.clear(); std::cout << "Status history cleared!" << std::endl; break; case 3: stopServer(); return; default: std::cout << "Invalid choice!" << std::endl; } } } }; int main() { SmartTrashMonitor monitor; monitor.run(); return 0; } 模块代码设计#include "stm32f10x.h" // 引脚定义 #define LED_PIN GPIO_Pin_13 #define LED_PORT GPIOC #define KEY_PIN GPIO_Pin_0 #define KEY_PORT GPIOA #define SERVO_PIN GPIO_Pin_8 #define SERVO_PORT GPIOA #define SERVO_TIM TIM1 // 语音识别指令定义 typedef enum { GARBAGE_DRY = 0, GARBAGE_WET, GARBAGE_RECYCLABLE, GARBAGE_HAZARDOUS, GARBAGE_UNKNOWN } GarbageType; // 系统状态 typedef struct { uint8_t isFull; uint8_t coverOpen; GarbageType currentType; } SystemState; SystemState sysState = {0, 0, GARBAGE_UNKNOWN}; // 系统时钟初始化 void RCC_Configuration(void) { RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_USART1EN | RCC_APB2ENR_SPI1EN | RCC_APB2ENR_TIM1EN; } // GPIO初始化 void GPIO_Configuration(void) { // LED指示灯(PC13) GPIOC->CRH &= 0xFF0FFFFF; GPIOC->CRH |= 0x00300000; // 按键(PA0) GPIOA->CRL &= 0xFFFFFFF0; GPIOA->CRL |= 0x00000008; // 舵机PWM(PA8) GPIOA->CRH &= 0xFFFFFFF0; GPIOA->CRH |= 0x0000000B; } // USART1初始化 - 用于SYN6288语音合成 void USART1_Configuration(void) { USART1->BRR = 0x1D4C; // 9600 @72MHz USART1->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; USART1->CR2 = 0; USART1->CR3 = 0; } // SPI1初始化 - 用于LD3320语音识别 void SPI1_Configuration(void) { // PA5-SCK, PA6-MISO, PA7-MOSI GPIOA->CRL &= 0x000FFFFF; GPIOA->CRL |= 0xBBB00000; SPI1->CR1 = SPI_CR1_MSTR | SPI_CR1_BR_0 | SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_SPE; SPI1->CR2 = 0; } // 定时器1初始化 - 用于舵机PWM控制 void TIM1_Configuration(void) { // 72MHz/72 = 1MHz, 50Hz PWM -> 20000周期 SERVO_TIM->PSC = 71; SERVO_TIM->ARR = 19999; // 通道1 PWM输出 SERVO_TIM->CCMR1 |= TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2; SERVO_TIM->CCER |= TIM_CCER_CC1E; SERVO_TIM->BDTR |= TIM_BDTR_MOE; SERVO_TIM->CR1 = TIM_CR1_CEN; } // 舵机控制函数 void Servo_Control(uint8_t angle) { uint16_t pulse = 500 + (angle * 2000 / 180); // 0.5ms-2.5ms SERVO_TIM->CCR1 = pulse; } // LED控制 void LED_Control(uint8_t state) { if(state) { LED_PORT->BSRR = LED_PIN; } else { LED_PORT->BRR = LED_PIN; } } // 按键检测 uint8_t Key_Scan(void) { static uint8_t key_up = 1; if(key_up && (KEY_PORT->IDR & KEY_PIN) == 0) { delay_ms(10); key_up = 0; if((KEY_PORT->IDR & KEY_PIN) == 0) { return 1; } } else if((KEY_PORT->IDR & KEY_PIN) != 0) { key_up = 1; } return 0; } // SPI读写函数 uint8_t SPI1_ReadWriteByte(uint8_t data) { while((SPI1->SR & SPI_SR_TXE) == 0); SPI1->DR = data; while((SPI1->SR & SPI_SR_RXNE) == 0); return SPI1->DR; } // LD3320语音识别初始化 void LD3320_Init(void) { // LD3320复位和初始化序列 SPI1_ReadWriteByte(0x00); delay_ms(100); // 更多初始化代码... } // 语音识别处理 GarbageType Voice_Recognition(void) { uint8_t result = 0; // 读取识别结果 result = SPI1_ReadWriteByte(0x00); switch(result) { case 0x01: return GARBAGE_DRY; case 0x02: return GARBAGE_WET; case 0x03: return GARBAGE_RECYCLABLE; case 0x04: return GARBAGE_HAZARDOUS; default: return GARBAGE_UNKNOWN; } } // SYN6288语音播报 void Voice_Play(GarbageType type) { const char* voice_cmd[] = { "可回收垃圾", "厨余垃圾", "干垃圾", "有害垃圾", "未知垃圾" }; // 发送语音合成指令 USART1_SendString("[v5]"); USART1_SendString(voice_cmd[type]); USART1_SendString("[/v]"); } // USART1发送字符串 void USART1_SendString(char* str) { while(*str) { while((USART1->SR & USART_SR_TXE) == 0); USART1->DR = *str++; } } // ESP8266 WiFi发送状态 void WiFi_SendStatus(void) { char buffer[64]; sprintf(buffer, "STATUS: FULL=%d, COVER=%d, TYPE=%d", sysState.isFull, sysState.coverOpen, sysState.currentType); // 通过USART2发送到ESP8266 // USART2_SendString(buffer); } // 延时函数 void delay_ms(uint32_t ms) { volatile uint32_t i, j; for(i = 0; i < ms; i++) for(j = 0; j < 7200; j++); } // 桶盖控制 void Cover_Control(uint8_t open) { if(open) { Servo_Control(90); // 打开角度 sysState.coverOpen = 1; LED_Control(1); } else { Servo_Control(0); // 关闭角度 sysState.coverOpen = 0; LED_Control(0); } } // 满溢检测 void Full_Detection(void) { // 使用超声波或红外传感器检测 // 简化实现 if(/* 检测到满 */) { sysState.isFull = 1; WiFi_SendStatus(); } } // 主函数 int main(void) { // 系统初始化 RCC_Configuration(); GPIO_Configuration(); USART1_Configuration(); SPI1_Configuration(); TIM1_Configuration(); LD3320_Init(); // 初始状态 Cover_Control(0); while(1) { // 手动开关盖 if(Key_Scan()) { Cover_Control(!sysState.coverOpen); } // 语音识别 GarbageType type = Voice_Recognition(); if(type != GARBAGE_UNKNOWN) { sysState.currentType = type; Voice_Play(type); Cover_Control(1); delay_ms(3000); // 保持开启3秒 Cover_Control(0); } // 满溢检测 Full_Detection(); delay_ms(100); } } 项目核心代码#include "stm32f10x.h" // 假设外部模块函数已定义 extern void LD3320_Init(void); extern int LD3320_GetResult(void); extern void SYN6288_Play(const char* text); extern void ESP8266_Send(const char* data); extern void Servo_SetAngle(int angle); // 全局变量定义 volatile uint8_t lid_state = 0; // 桶盖状态 0:关闭 1:打开 volatile uint8_t trash_full = 0; // 满溢状态 0:未满 1:满溢 volatile uint8_t button_pressed = 0; // 按钮按下标志 uint32_t open_count = 0; // 开盖计数 // 函数声明 void System_Init(void); void GPIO_Init(void); void USART_Init(void); void TIM_Init(void); void EXTI_Init(void); void Delay_ms(uint32_t nTime); int main(void) { System_Init(); GPIO_Init(); USART_Init(); TIM_Init(); EXTI_Init(); // 初始化外部模块 LD3320_Init(); while(1) { int trash_type = LD3320_GetResult(); if(trash_type != 0) // 识别到垃圾类型 { char feedback[50]; switch(trash_type) { case 1: sprintf(feedback, "可回收垃圾"); break; case 2: sprintf(feedback, "厨余垃圾"); break; case 3: sprintf(feedback, "有害垃圾"); break; case 4: sprintf(feedback, "其他垃圾"); break; default: sprintf(feedback, "未知类型"); break; } // 语音播报反馈 SYN6288_Play(feedback); // 打开桶盖 Servo_SetAngle(90); // 90度对应打开 lid_state = 1; GPIOB->ODR |= (1 << 12); // 状态指示灯亮 open_count++; // 延迟后关闭 Delay_ms(3000); Servo_SetAngle(0); // 0度对应关闭 lid_state = 0; GPIOB->ODR &= ~(1 << 12); // 状态指示灯灭 // 检查满溢状态 if(open_count >= 10) { trash_full = 1; ESP8266_Send("垃圾桶已满,请及时清理"); open_count = 0; } } // 处理手动按钮 if(button_pressed) { if(lid_state == 0) { Servo_SetAngle(90); lid_state = 1; GPIOB->ODR |= (1 << 12); } else { Servo_SetAngle(0); lid_state = 0; GPIOB->ODR &= ~(1 << 12); } button_pressed = 0; Delay_ms(200); // 消抖 } // 定期发送状态 static uint32_t last_send = 0; if(SystemCoreClock - last_send > 6000000) // 约1分钟 { if(trash_full) ESP8266_Send("状态:满溢"); else ESP8266_Send("状态:正常"); last_send = SystemCoreClock; } } } void System_Init(void) { // 配置系统时钟 RCC->CFGR |= (0x02 << 18); // PLL乘法因子×6 RCC->CR |= (1 << 24); // 使能PLL while(!(RCC->CR & (1 << 25))); // 等待PLL就绪 RCC->CFGR |= 0x02; // 选择PLL作为系统时钟 SystemCoreClock = 48000000; // 系统时钟48MHz } void GPIO_Init(void) { // 使能GPIO时钟 RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN; // 状态指示灯 PB12 推挽输出 GPIOB->CRH &= ~(0x0F << 16); GPIOB->CRH |= (0x03 << 16); // 手动按钮 PA0 输入 GPIOA->CRL &= ~(0x0F << 0); GPIOA->CRL |= (0x04 << 0); // 浮空输入 } void USART_Init(void) { // 使能USART1时钟 (ESP8266) RCC->APB2ENR |= RCC_APB2ENR_USART1EN; // 配置PA9为TX推挽复用输出,PA10为RX浮空输入 GPIOA->CRH &= ~(0xFF << 4); GPIOA->CRH |= (0x0B << 4) | (0x04 << 8); // 波特率设置 115200 USART1->BRR = 48000000 / 115200; // 使能USART1 USART1->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; } void TIM_Init(void) { // 使能TIM1时钟 (舵机PWM) RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; // 配置PA8为TIM1_CH1 PWM输出 GPIOA->CRH &= ~(0x0F << 0); GPIOA->CRH |= (0x0B << 0); // 复用推挽输出 // 定时器配置 TIM1->PSC = 47999; // 分频至1kHz TIM1->ARR = 1999; // 周期20ms TIM1->CCR1 = 150; // 初始脉宽1.5ms // PWM模式配置 TIM1->CCMR1 |= (0x06 << 4); // PWM模式1 TIM1->CCER |= TIM_CCER_CC1E; // 使能通道1 TIM1->BDTR |= TIM_BDTR_MOE; // 主输出使能 TIM1->CR1 = TIM_CR1_CEN; // 启动定时器 } void EXTI_Init(void) { // 使能AFIO时钟 RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; // 配置PA0为EXTI0 AFIO->EXTICR[0] |= 0x00; // 配置下降沿触发 EXTI->FTSR |= EXTI_FTSR_TR0; // 使能EXTI0中断 EXTI->IMR |= EXTI_IMR_MR0; // 配置NVIC NVIC->ISER[0] |= (1 << 6); // 使能EXTI0中断 } void EXTI0_IRQHandler(void) { if(EXTI->PR & EXTI_PR_PR0) { button_pressed = 1; EXTI->PR |= EXTI_PR_PR0; // 清除中断标志 } } void Delay_ms(uint32_t nTime) { volatile uint32_t i; for(; nTime > 0; nTime--) for(i = 0; i < 8000; i++); } 总结本系统基于STM32微控制器设计了一款智能垃圾桶,旨在通过语音识别和自动化控制技术,提升垃圾处理的便捷性和智能化水平。该系统能够高效地响应用户需求,并实现远程状态监控,为现代生活提供环保解决方案。功能上,系统通过语音识别模块准确判断用户指令中的垃圾种类,并在识别成功后利用语音播报模块实时反馈分类结果;同时,借助Wi-Fi模块将垃圾桶的满溢等状态信息发送至用户手机,确保及时管理;此外,系统还支持手动开关盖操作,并配有状态指示灯,以增强使用的灵活性和可视化提示。硬件方面,系统采用STM32F103C8T6作为核心主控芯片,集成LD3320语音识别模块、SYN6288语音合成模块、ESP8266 Wi-Fi模块以及SG90舵机作为执行机构,各模块协同工作,确保了系统的稳定运行和功能实现。总体而言,这款智能垃圾桶不仅简化了垃圾分类流程,还通过智能化设计促进了资源节约和环境保护,体现了科技在日常生活应用中的实用价值。
  • [技术干货] 基于STM32的智能防久坐与健康办公系统
    项目开发背景随着现代办公环境的普及和信息技术的快速发展,越来越多的人群需要长时间在办公桌前工作,导致久坐成为日常生活中的普遍现象。研究表明,持续久坐不仅会引发颈椎和腰椎疾病,还可能增加肥胖、心血管问题及代谢综合征的风险,严重影响工作效率与个人健康。传统的健康提醒方式往往依赖于简单的定时器或手动设置,缺乏智能化和实时性,用户容易因工作专注而忽略提醒,无法有效改善坐姿习惯。此外,单一传感器监测的局限性难以全面反映用户的在位状态和坐姿变化,亟需一种集成多传感器与智能提醒的综合解决方案。为此,本项目基于STM32微控制器开发智能防久坐与健康办公系统,通过超声波模块实时监测用户与桌面的距离,结合压力传感器检测坐姿持续时间,并集成温湿度传感器提供环境健康数据。系统能够在用户久坐超时后,自动触发灯光和语音提醒,同时通过OLED显示屏直观展示关键信息,旨在以低成本、高效率的方式帮助用户养成健康办公习惯。该系统不仅提升了健康监测的精准性和用户体验,还通过多模态交互增强提醒效果,预期能有效降低久坐带来的健康风险,促进办公环境的智能化与人性化改进。设计实现的功能(1) 使用超声波模块监测用户与桌面的距离,判断用户是否在位。(2) 通过压力传感器检测用户是否长时间保持坐姿。(3) 用户久坐超过设定时间(如1小时),系统通过灯光和语音进行提醒。(4) 通过OLED屏幕显示在座时长、环境温湿度等健康信息。项目硬件模块组成(1)主控芯片:STM32F103C8T6单片机。(2)传感模块:HC-SR04超声波模块、HX711压力传感器模块、DHT11温湿度传感器。(3)显示与提醒:0.96英寸OLED显示屏、WS2812 RGB灯带、SYN6288语音模块。设计意义该系统设计旨在通过智能监测和提醒机制,有效帮助办公人群避免因长时间久坐而引发的健康问题,如颈椎劳损、腰椎疾病和血液循环不畅等,从而提升整体工作效率和生活质量。通过集成超声波模块和压力传感器,系统能够准确判断用户是否在位及坐姿持续时间,并在超过设定阈值时自动触发灯光和语音提醒,引导用户适时活动身体,减少久坐带来的潜在风险。此外,系统通过OLED显示屏实时展示在座时长和环境温湿度数据,使用户能够直观了解自身办公状态和周围条件,促进健康意识的养成。这种集成化的设计不仅体现了嵌入式系统在健康领域的实用价值,还为现代办公环境提供了一种低成本、高效的解决方案,有助于培养良好的工作习惯。设计思路本系统以STM32F103C8T6单片机为核心控制器,通过集成多种传感器和执行模块实现智能防久坐与健康办公功能。系统首先利用HC-SR04超声波模块持续监测用户与桌面的距离,当距离在一定范围内时判定用户处于在位状态;同时,HX711压力传感器模块检测座椅压力变化,确认用户是否保持坐姿。这些传感器数据由STM32实时采集和处理,用于综合判断用户行为状态。在用户状态判断基础上,系统通过内部计时器累计用户连续坐姿时间。如果坐姿持续时间超过预设阈值(例如1小时),STM32会触发提醒机制:一方面控制WS2812 RGB灯带发出特定颜色的光效作为视觉提示,另一方面驱动SYN6288语音模块播放定制语音内容,提醒用户起身活动,从而避免久坐带来的健康风险。此外,系统集成DHT11温湿度传感器采集环境数据,并结合在位时长等信息,通过0.96英寸OLED显示屏实时显示。OLED屏幕以清晰界面展示当前在座时长、环境温度和湿度等健康相关参数,方便用户随时查看,增强办公环境的健康管理意识。整个系统设计注重模块间的协同工作,STM32负责统一调度传感器数据采集、状态判断、提醒触发和显示更新,确保各硬件模块高效稳定运行。所有功能均基于实际需求实现,无需额外添加,最终形成一个完整的防久坐与健康办公解决方案。框架图+----------------+ +----------------+ +----------------+ | 传感模块 | | 处理模块 | | 输出模块 | | | | | | | | - HC-SR04 |--->| - STM32 |--->| - OLED | | 超声波 | | F103C8T6 | | 显示屏 | | - HX711 | | | | - WS2812 | | 压力传感器 | | | | RGB灯带 | | - DHT11 | | | | - SYN6288 | | 温湿度 | | | | 语音模块 | +----------------+ +----------------+ +----------------+ 系统总体设计本系统基于STM32F103C8T6单片机作为核心控制器,旨在实现智能防久坐与健康办公功能。系统通过集成多种传感器和输出模块,实时监测用户状态并提供健康提醒,以促进办公环境的健康管理。系统采用HC-SR04超声波模块检测用户与桌面的距离,从而判断用户是否在位;同时,HX711压力传感器模块用于监测用户坐姿压力,以识别长时间保持坐姿的情况。这些传感器数据由STM32主控芯片实时采集和处理,结合DHT11温湿度传感器获取的环境数据,形成全面的用户状态监测。主控芯片对采集的数据进行分析,当检测到用户久坐时间超过预设阈值(如1小时)时,系统会触发提醒机制。提醒通过WS2812 RGB灯带发出灯光信号,并利用SYN6288语音模块播放语音提示,以引导用户适时活动。系统还集成0.96英寸OLED显示屏,用于实时显示用户在座时长、环境温湿度等健康信息,方便用户随时查看。整个设计注重实用性和可靠性,各硬件模块协同工作,确保系统稳定运行并有效达成防久坐目标。系统功能总结功能名称功能描述相关硬件模块用户在位监测使用超声波模块监测用户与桌面的距离,判断用户是否在位。HC-SR04超声波模块坐姿检测通过压力传感器检测用户是否长时间保持坐姿。HX711压力传感器模块久坐提醒用户久坐超过设定时间(如1小时),系统通过灯光和语音进行提醒。WS2812 RGB灯带、SYN6288语音模块健康信息显示通过OLED屏幕显示在座时长、环境温湿度等健康信息。0.96英寸OLED显示屏、DHT11温湿度传感器设计的各个功能模块描述STM32F103C8T6单片机作为系统的核心控制器,负责协调和处理所有传感器数据及输出设备的控制。它通过内部程序逻辑实现数据的采集、分析和决策,确保系统稳定运行,并执行防久坐与健康办公的自动化功能。HC-SR04超声波模块用于监测用户与桌面的距离,通过发射和接收超声波信号来计算实际距离值,从而判断用户是否在位。当检测到用户离开座位时,系统会暂停或重置久坐计时,以准确反映用户状态。HX711压力传感器模块安装在座椅上,通过检测压力变化来判断用户是否长时间保持坐姿。该模块将压力数据转换为电信号,供主控芯片分析,并与计时功能结合,识别久坐行为。DHT11温湿度传感器用于采集办公环境的温度和湿度数据,提供实时环境信息。这些数据被传输到主控芯片,用于后续显示和健康监测,帮助用户了解周围条件。0.96英寸OLED显示屏用于直观显示用户的在座时长、环境温湿度等健康信息。它通过清晰的文本和图形界面,为用户提供实时反馈,方便随时查看系统状态。WS2812 RGB灯带作为视觉提醒装置,当用户久坐超过设定时间时,会通过改变灯光颜色或闪烁模式发出警示。这种视觉信号能有效吸引用户注意,促使其起身活动。SYN6288语音模块用于生成语音提醒,在检测到久坐行为时播放预设的语音消息,如“请起身休息”。该模块通过音频输出增强提醒效果,确保用户及时响应系统提示。上位机代码设计#include <windows.h> #include <iostream> #include <string> #include <sstream> #include <thread> #include <chrono> // 串口配置 HANDLE hSerial; const int BAUD_RATE = 9600; const int DATA_BITS = 8; const int STOP_BITS = ONESTOPBIT; const int PARITY = NOPARITY; // 健康数据存储结构 struct HealthData { float distance; float pressure; float temperature; float humidity; int presence; int sittingTime; }; // 打开串口 bool openSerialPort(const char* portName) { hSerial = CreateFileA(portName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hSerial == INVALID_HANDLE_VALUE) { std::cerr << "Error opening serial port!" << std::endl; return false; } DCB dcbSerialParams = {0}; dcbSerialParams.DCBlength = sizeof(dcbSerialParams); if (!GetCommState(hSerial, &dcbSerialParams)) { std::cerr << "Error getting serial port state!" << std::endl; return false; } dcbSerialParams.BaudRate = BAUD_RATE; dcbSerialParams.ByteSize = DATA_BITS; dcbSerialParams.StopBits = STOP_BITS; dcbSerialParams.Parity = PARITY; if (!SetCommState(hSerial, &dcbSerialParams)) { std::cerr << "Error setting serial port state!" << std::endl; return false; } COMMTIMEOUTS timeouts = {0}; timeouts.ReadIntervalTimeout = 50; timeouts.ReadTotalTimeoutConstant = 50; timeouts.ReadTotalTimeoutMultiplier = 10; timeouts.WriteTotalTimeoutConstant = 50; timeouts.WriteTotalTimeoutMultiplier = 10; if (!SetCommTimeouts(hSerial, &timeouts)) { std::cerr << "Error setting timeouts!" << std::endl; return false; } return true; } // 读取串口数据 std::string readSerialData() { char buffer[256]; DWORD bytesRead; std::string data; if (ReadFile(hSerial, buffer, sizeof(buffer) - 1, &bytesRead, NULL)) { buffer[bytesRead] = '\0'; data = buffer; } return data; } // 解析数据 HealthData parseData(const std::string& data) { HealthData hd = {0}; std::stringstream ss(data); std::string token; while (std::getline(ss, token, ',')) { size_t colonPos = token.find(':'); if (colonPos != std::string::npos) { std::string key = token.substr(0, colonPos); std::string value = token.substr(colonPos + 1); if (key == "DIST") hd.distance = std::stof(value); else if (key == "PRESS") hd.pressure = std::stof(value); else if (key == "TEMP") hd.temperature = std::stof(value); else if (key == "HUM") hd.humidity = std::stof(value); else if (key == "PRESENCE") hd.presence = std::stoi(value); else if (key == "SITTING") hd.sittingTime = std::stoi(value); } } return hd; } // 语音提醒(使用Windows Beep) void voiceAlert() { Beep(1000, 500); // 频率1000Hz,持续时间500ms Beep(1500, 500); } // 灯光提醒(改变控制台颜色) void lightAlert() { HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleTextAttribute(hConsole, BACKGROUND_RED); // 红色背景 std::this_thread::sleep_for(std::chrono::milliseconds(500)); SetConsoleTextAttribute(hConsole, BACKGROUND_GREEN); // 绿色背景 std::this_thread::sleep_for(std::chrono::milliseconds(500)); SetConsoleTextAttribute(hConsole, BACKGROUND_BLUE); // 蓝色背景 std::this_thread::sleep_for(std::chrono::milliseconds(500)); SetConsoleTextAttribute(hConsole, 7); // 恢复默认 } // 显示健康信息 void displayHealthInfo(const HealthData& hd) { system("cls"); // 清屏 std::cout << "=== 智能防久坐与健康办公系统 ===" << std::endl; std::cout << "在座时长: " << hd.sittingTime << " 秒" << std::endl; std::cout << "环境温度: " << hd.temperature << " °C" << std::endl; std::cout << "环境湿度: " << hd.humidity << " %" << std::endl; std::cout << "用户距离: " << hd.distance << " cm" << std::endl; std::cout << "压力值: " << hd.pressure << std::endl; std::cout << "在位状态: " << (hd.presence ? "在位" : "离位") << std::endl; std::cout << "===============================" << std::endl; } int main() { const char* portName = "COM3"; // 根据实际串口修改 const int SITTING_THRESHOLD = 3600; // 久坐阈值(秒) if (!openSerialPort(portName)) { return -1; } std::cout << "串口打开成功,开始监控..." << std::endl; std::this_thread::sleep_for(std::chrono::seconds(2)); while (true) { std::string data = readSerialData(); if (!data.empty()) { HealthData hd = parseData(data); displayHealthInfo(hd); // 久坐提醒 if (hd.sittingTime >= SITTING_THRESHOLD) { std::cout << "警告:您已久坐超过1小时,请起身活动!" << std::endl; std::thread voiceThread(voiceAlert); std::thread lightThread(lightAlert); voiceThread.join(); lightThread.join(); } } std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // 每秒更新 } CloseHandle(hSerial); return 0; } 模块代码设计#include "stm32f10x.h" // 引脚定义 #define ULTRASONIC_TRIG_PIN GPIO_Pin_0 #define ULTRASONIC_ECHO_PIN GPIO_Pin_1 #define ULTRASONIC_PORT GPIOA #define HX711_SCK_PIN GPIO_Pin_2 #define HX711_DOUT_PIN GPIO_Pin_3 #define HX711_PORT GPIOA #define DHT11_PIN GPIO_Pin_4 #define DHT11_PORT GPIOA #define WS2812_PIN GPIO_Pin_0 #define WS2812_PORT GPIOB #define OLED_SCL_PIN GPIO_Pin_6 #define OLED_SDA_PIN GPIO_Pin_7 #define OLED_PORT GPIOB #define SYN6288_TX_PIN GPIO_Pin_2 #define SYN6288_RX_PIN GPIO_Pin_3 #define SYN6288_PORT GPIOA // 全局变量 volatile uint32_t ultrasonic_distance = 0; volatile uint32_t pressure_value = 0; volatile uint8_t temperature = 0; volatile uint8_t humidity = 0; volatile uint32_t sit_time = 0; volatile uint8_t user_present = 0; // 延时函数 void delay_us(uint32_t us) { us *= 8; while(us--) { __NOP(); } } void delay_ms(uint32_t ms) { while(ms--) { delay_us(1000); } } // 超声波模块HC-SR04 void ultrasonic_init(void) { RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; // TRIG输出模式 ULTRASONIC_PORT->CRL &= ~(0xF << (0*4)); ULTRASONIC_PORT->CRL |= (0x3 << (0*4)); // ECHO输入模式 ULTRASONIC_PORT->CRL &= ~(0xF << (1*4)); ULTRASONIC_PORT->CRL |= (0x4 << (1*4)); } uint32_t ultrasonic_get_distance(void) { uint32_t timeout = 0; uint32_t time_high = 0; // 发送10us高电平触发信号 ULTRASONIC_PORT->BSRR = ULTRASONIC_TRIG_PIN; delay_us(10); ULTRASONIC_PORT->BRR = ULTRASONIC_TRIG_PIN; // 等待回响信号变高 timeout = 10000; while(!(ULTRASONIC_PORT->IDR & ULTRASONIC_ECHO_PIN)) { if(--timeout == 0) return 0; } // 测量高电平时间 time_high = 0; while(ULTRASONIC_PORT->IDR & ULTRASONIC_ECHO_PIN) { delay_us(1); time_high++; if(time_high > 30000) break; // 超时 } // 计算距离 (cm) = 时间(us) / 58 return time_high / 58; } // HX711压力传感器 void hx711_init(void) { RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; // SCK输出模式 HX711_PORT->CRL &= ~(0xF << (2*4)); HX711_PORT->CRL |= (0x3 << (2*4)); // DOUT输入模式 HX711_PORT->CRL &= ~(0xF << (3*4)); HX711_PORT->CRL |= (0x4 << (3*4)); HX711_PORT->BRR = HX711_SCK_PIN; } uint32_t hx711_read(void) { uint32_t count = 0; uint8_t i; // 等待DOUT变低 while(HX711_PORT->IDR & HX711_DOUT_PIN); for(i=0; i<24; i++) { HX711_PORT->BSRR = HX711_SCK_PIN; delay_us(1); count = count << 1; HX711_PORT->BRR = HX711_SCK_PIN; delay_us(1); if(HX711_PORT->IDR & HX711_DOUT_PIN) count++; } // 第25个脉冲选择通道和增益 HX711_PORT->BSRR = HX711_SCK_PIN; delay_us(1); HX711_PORT->BRR = HX711_SCK_PIN; delay_us(1); return count ^ 0x800000; } // DHT11温湿度传感器 void dht11_init(void) { RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; // 开漏输出模式 DHT11_PORT->CRL &= ~(0xF << (4*4)); DHT11_PORT->CRL |= (0x6 << (4*4)); } uint8_t dht11_read_byte(void) { uint8_t i, data = 0; for(i=0; i<8; i++) { while(!(DHT11_PORT->IDR & DHT11_PIN)); delay_us(40); data <<= 1; if(DHT11_PORT->IDR & DHT11_PIN) data |= 1; while(DHT11_PORT->IDR & DHT11_PIN); } return data; } uint8_t dht11_read_data(uint8_t *temp, uint8_t *humi) { uint8_t buf[5]; uint8_t i; // 主机发送开始信号 DHT11_PORT->BRR = DHT11_PIN; delay_ms(18); DHT11_PORT->BSRR = DHT11_PIN; delay_us(30); // 切换为输入模式 DHT11_PORT->CRL &= ~(0xF << (4*4)); DHT11_PORT->CRL |= (0x4 << (4*4)); // 等待DHT11响应 if(DHT11_PORT->IDR & DHT11_PIN) return 0; delay_us(80); if(!(DHT11_PORT->IDR & DHT11_PIN)) return 0; delay_us(80); // 读取40位数据 for(i=0; i<5; i++) buf[i] = dht11_read_byte(); // 切换回输出模式 DHT11_PORT->CRL &= ~(0xF << (4*4)); DHT11_PORT->CRL |= (0x6 << (4*4)); DHT11_PORT->BSRR = DHT11_PIN; // 校验数据 if(buf[4] == (buf[0] + buf[1] + buf[2] + buf[3])) { *humi = buf[0]; *temp = buf[2]; return 1; } return 0; } // WS2812 RGB灯带 void ws2812_init(void) { RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; // 推挽输出 WS2812_PORT->CRL &= ~(0xF << (0*4)); WS2812_PORT->CRL |= (0x3 << (0*4)); } void ws2812_send_bit(uint8_t bit_val) { if(bit_val) { WS2812_PORT->BSRR = WS2812_PIN; delay_us(0.9); WS2812_PORT->BRR = WS2812_PIN; delay_us(0.35); } else { WS2812_PORT->BSRR = WS2812_PIN; delay_us(0.35); WS2812_PORT->BRR = WS2812_PIN; delay_us(0.9); } } void ws2812_send_byte(uint8_t byte_val) { uint8_t i; for(i=0; i<8; i++) { ws2812_send_bit(byte_val & 0x80); byte_val <<= 1; } } void ws2812_set_color(uint8_t red, uint8_t green, uint8_t blue) { ws2812_send_byte(green); ws2812_send_byte(red); ws2812_send_byte(blue); delay_us(50); } // OLED显示 (SSD1306, I2C) void oled_i2c_init(void) { RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; // PB6-SCL, PB7-SDA 复用开漏输出 OLED_PORT->CRL &= ~(0xFF << (6*4)); OLED_PORT->CRL |= (0x66 << (6*4)); // I2C1配置 I2C1->CR1 &= ~I2C_CR1_PE; I2C1->CR2 = 36; // 36MHz I2C1->CCR = 180; // 100kHz I2C1->TRISE = 37; I2C1->CR1 |= I2C_CR1_PE; } void oled_i2c_write(uint8_t addr, uint8_t *data, uint16_t len) { // 发送起始条件 I2C1->CR1 |= I2C_CR1_START; while(!(I2C1->SR1 & I2C_SR1_SB)); // 发送设备地址 I2C1->DR = addr; while(!(I2C1->SR1 & I2C_SR1_ADDR)); (void)I2C1->SR2; // 发送数据 while(len--) { while(!(I2C1->SR1 & I2C_SR1_TXE)); I2C1->DR = *data++; } // 发送停止条件 while(!(I2C1->SR1 & I2C_SR1_BTF)); I2C1->CR1 |= I2C_CR1_STOP; } void oled_write_cmd(uint8_t cmd) { uint8_t buf[2] = {0x00, cmd}; oled_i2c_write(0x78, buf, 2); } void oled_write_data(uint8_t data) { uint8_t buf[2] = {0x40, data}; oled_i2c_write(0x78, buf, 2); } void oled_init(void) { oled_i2c_init(); delay_ms(100); oled_write_cmd(0xAE); // 关闭显示 oled_write_cmd(0x20); // 内存地址模式 oled_write_cmd(0x10); oled_write_cmd(0xB0); oled_write_cmd(0xC8); oled_write_cmd(0x00); oled_write_cmd(0x10); oled_write_cmd(0x40); oled_write_cmd(0x81); oled_write_cmd(0xFF); oled_write_cmd(0xA1); oled_write_cmd(0xA6); oled_write_cmd(0xA8); oled_write_cmd(0x3F); oled_write_cmd(0xA4); oled_write_cmd(0xD3); oled_write_cmd(0x00); oled_write_cmd(0xD5); oled_write_cmd(0xF0); oled_write_cmd(0xD9); oled_write_cmd(0x22); oled_write_cmd(0xDA); oled_write_cmd(0x12); oled_write_cmd(0xDB); oled_write_cmd(0x20); oled_write_cmd(0x8D); oled_write_cmd(0x14); oled_write_cmd(0xAF); // 开启显示 } void oled_clear(void) { uint8_t i, j; for(i=0; i<8; i++) { oled_write_cmd(0xB0 + i); oled_write_cmd(0x00); oled_write_cmd(0x10); for(j=0; j<128; j++) oled_write_data(0x00); } } void oled_show_string(uint8_t x, uint8_t y, char *str) { // 简化实现 - 实际需要字库 oled_write_cmd(0xB0 + y); oled_write_cmd(((x & 0xF0) >> 4) | 0x10); oled_write_cmd(x & 0x0F); while(*str) { oled_write_data(*str++); } } // SYN6288语音模块 void syn6288_init(void) { RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; RCC->APB2ENR |= RCC_APB2ENR_USART1EN; // USART1 TX推挽输出,RX浮空输入 SYN6288_PORT->CRL &= ~(0xFF << (2*4)); SYN6288_PORT->CRL |= (0x4B << (2*4)); // USART1配置 USART1->BRR = 0x1D4C; // 9600 @36MHz USART1->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; } void syn6288_speak(char *text) { while(*text) { while(!(USART1->SR & USART_SR_TXE)); USART1->DR = *text++; } } // 系统定时器 void systick_init(void) { SysTick->LOAD = 9000 - 1; // 1ms中断 SysTick->VAL = 0; SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk; } void SysTick_Handler(void) { static uint32_t timer_1s = 0; timer_1s++; if(timer_1s >= 1000) // 1秒 { timer_1s = 0; if(user_present) { sit_time++; } } } // 主函数 int main(void) { // 系统时钟配置 SystemInit(); // 初始化各模块 ultrasonic_init(); hx711_init(); dht11_init(); ws2812_init(); oled_init(); syn6288_init(); systick_init(); oled_clear(); oled_show_string(0, 0, "Health Monitor"); while(1) { // 检测用户是否在位 ultrasonic_distance = ultrasonic_get_distance(); if(ultrasonic_distance < 50 && ultrasonic_distance > 10) { user_present = 1; } else { user_present = 0; sit_time = 0; } // 读取压力传感器 pressure_value = hx711_read(); // 读取温湿度 dht11_read_data(&temperature, &humidity); // 更新OLED显示 char buffer[20]; sprintf(buffer, "Sit:%lum", sit_time/60); oled_show_string(0, 2, buffer); sprintf(buffer, "Temp:%uC", temperature); oled_show_string(0, 4, buffer); sprintf(buffer, "Humi:%u%%", humidity); oled_show_string(0, 6, buffer); // 久坐提醒 if(sit_time >= 3600) // 1小时 { // RGB灯红色闪烁 ws2812_set_color(255, 0, 0); delay_ms(500); ws2812_set_color(0, 0, 0); delay_ms(500); // 语音提醒 syn6288_speak("Please stand up and move around"); sit_time = 0; // 重置计时 } delay_ms(1000); } } 项目核心代码#include "stm32f10x.h" #include "ultrasonic.h" #include "pressure.h" #include "dht11.h" #include "oled.h" #include "ws2812.h" #include "syn6288.h" // 系统状态定义 typedef enum { USER_ABSENT = 0, USER_PRESENT, WARNING_ACTIVE } SystemState; // 全局变量 volatile uint32_t sit_time = 0; // 久坐时间(秒) volatile uint32_t warning_time = 3600; // 提醒时间阈值(1小时) volatile SystemState system_state = USER_ABSENT; volatile uint32_t system_tick = 0; // 系统时钟初始化 void SysTick_Init(void) { SysTick->CTRL = 0; SysTick->LOAD = 9000 - 1; // 1ms中断 SysTick->VAL = 0; SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk; } // GPIO初始化 void GPIO_Init(void) { // 使能GPIO时钟 RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN; // 初始化LED指示灯 GPIOA->CRL &= 0xFF0FFFFF; GPIOA->CRL |= 0x00300000; // PA5推挽输出 } // 系统时钟中断服务函数 void SysTick_Handler(void) { system_tick++; // 每秒更新一次 if(system_tick % 1000 == 0) { if(system_state == USER_PRESENT) { sit_time++; } } } // 用户状态检测 SystemState CheckUserStatus(void) { float distance = Ultrasonic_GetDistance(); uint32_t pressure = Pressure_GetValue(); // 判断用户是否在位 if(distance < 50.0 && pressure > 100) { // 距离<50cm且压力>100 return USER_PRESENT; } else { return USER_ABSENT; } } // 显示健康信息 void DisplayHealthInfo(void) { DHT11_Data dht_data; DHT11_Read(&dht_data); OLED_Clear(); OLED_ShowString(0, 0, "Sit Time:"); OLED_ShowNum(72, 0, sit_time/60, 2); // 显示分钟 OLED_ShowString(96, 0, "min"); OLED_ShowString(0, 2, "Temp:"); OLED_ShowNum(48, 2, dht_data.temperature, 2); OLED_ShowString(72, 2, "C"); OLED_ShowString(0, 4, "Humidity:"); OLED_ShowNum(72, 4, dht_data.humidity, 2); OLED_ShowString(96, 4, "%"); // 显示状态 switch(system_state) { case USER_ABSENT: OLED_ShowString(0, 6, "Status: Absent"); break; case USER_PRESENT: OLED_ShowString(0, 6, "Status: Working"); break; case WARNING_ACTIVE: OLED_ShowString(0, 6, "Status: Warning!"); break; } } // 久坐提醒 void SittingWarning(void) { if(system_state == WARNING_ACTIVE) { // RGB灯带红色闪烁 WS2812_SetColor(255, 0, 0); delay_ms(500); WS2812_SetColor(0, 0, 0); delay_ms(500); // 语音提醒 SYN6288_Speech("Please stand up and move around"); } } // 系统状态管理 void SystemStateManager(void) { SystemState new_state = CheckUserStatus(); switch(system_state) { case USER_ABSENT: if(new_state == USER_PRESENT) { system_state = USER_PRESENT; sit_time = 0; // 重置计时 WS2812_SetColor(0, 255, 0); // 绿色表示正常 } break; case USER_PRESENT: if(new_state == USER_ABSENT) { system_state = USER_ABSENT; WS2812_SetColor(0, 0, 0); // 关闭灯带 } else if(sit_time >= warning_time) { system_state = WARNING_ACTIVE; } break; case WARNING_ACTIVE: if(new_state == USER_ABSENT) { system_state = USER_ABSENT; WS2812_SetColor(0, 0, 0); } else if(sit_time < warning_time) { system_state = USER_PRESENT; WS2812_SetColor(0, 255, 0); } break; } } int main(void) { // 系统初始化 SysTick_Init(); GPIO_Init(); Ultrasonic_Init(); Pressure_Init(); DHT11_Init(); OLED_Init(); WS2812_Init(); SYN6288_Init(); // 初始状态 GPIOA->BSRR = GPIO_BSRR_BS5; // 点亮指示灯 system_state = USER_ABSENT; while(1) { // 系统状态管理 SystemStateManager(); // 显示健康信息(每秒更新) if(system_tick % 1000 == 0) { DisplayHealthInfo(); } // 久坐提醒处理 if(system_state == WARNING_ACTIVE) { SittingWarning(); } // 系统指示灯闪烁(0.5Hz) if(system_tick % 1000 == 0) { GPIOA->ODR ^= GPIO_ODR_ODR5; } // 防止程序跑飞 __WFI(); } } // 简单的延时函数 void delay_ms(uint32_t ms) { uint32_t start_tick = system_tick; while((system_tick - start_tick) < ms); } 总结综上所述,本系统基于STM32F103C8T6主控芯片,设计了一款智能防久坐与健康办公解决方案。该系统通过超声波模块实时监测用户与桌面的距离,结合压力传感器判断用户坐姿状态,有效检测用户是否在位及久坐行为。当用户持续久坐超过预设时间时,系统会触发灯光和语音提醒,帮助用户养成健康习惯,同时OLED显示屏动态展示在座时长、环境温湿度等关键健康数据,提升办公环境的舒适性与安全性。硬件方面,系统集成了HC-SR04超声波模块、HX711压力传感器和DHT11温湿度传感器,确保数据采集的准确性和实时性。显示与提醒部分采用0.96英寸OLED显示屏、WS2812 RGB灯带和SYN6288语音模块,实现了直观的信息展示和友好的交互提醒。整体设计结构紧凑、功能完善,能够有效促进用户健康办公,减少久坐带来的潜在风险,体现了智能技术在日常生活应用中的实用价值。
  • [技术干货] 基于STM32的智能仓储盘点机器人
    项目开发背景随着现代物流行业的快速发展,仓储管理作为供应链中的核心环节,其效率与准确性直接影响企业的运营成本与客户满意度。传统仓储盘点主要依赖人工操作,不仅耗时费力,还容易因人为因素导致数据误差,尤其在货物种类繁多、库存动态变化频繁的场景下,人工盘点已难以满足高效、精准的实时管理需求。在此背景下,自动化与智能化技术成为提升仓储管理水平的关键突破口。通过引入嵌入式系统与机器视觉技术,可实现仓储盘点的无人化与智能化。基于STM32的智能仓储盘点机器人项目,正是针对这一需求而设计。机器人能够沿预设轨道自主移动,利用视觉模块快速识别货物信息,并通过无线通信将数据实时同步至后台系统,有效解决了传统盘点中效率低、误差高、数据更新滞后等问题。该项目的开发不仅顺应了工业4.0与智能物流的发展趋势,也为中小型仓储企业提供了一套低成本、高可靠性的自动化解决方案。通过集成STM32的实时控制能力、OpenMV的视觉识别技术以及Wi-Fi数据传输功能,实现了从货物识别、数据采集到远程管理的全流程自动化,为仓储管理的数字化升级提供了切实可行的技术路径。设计实现的功能(1) 机器人能够沿预设货架轨道自主巡线移动。(2) 通过OpenMV视觉模块扫描货物条形码或二维码。(3) 系统自动记录扫描到的货物信息,并生成库存报表。(4) 通过Wi-Fi模块将库存数据无线传输到后台管理系统。项目硬件模块组成(1) 主控芯片:STM32F103系列单片机。(2) 视觉模块:OpenMV摄像头模组。(3) 通信模块:ESP8266或ESP32-S3 Wi-Fi模块。(4) 驱动模块:L298N电机驱动板、直流减速电机与车轮。(5) 电源模块:12V锂电池组及5V/3.3V降压模块。设计意义该智能仓储盘点机器人的设计意义在于显著提升仓储管理的自动化水平,通过自主巡线移动功能,机器人能够替代人工在货架间进行重复性巡检,减少人力成本并降低操作误差,从而优化整体仓储作业流程。通过集成OpenMV视觉模块进行条形码或二维码扫描,系统实现了对货物信息的快速准确识别,避免了传统手动盘点中常见的漏检或误读问题,确保库存数据的真实性和可靠性,为决策提供精准依据。自动记录货物信息并生成库存报表的功能,简化了数据整理环节,使管理人员能够及时掌握库存动态,支持高效的库存控制和补货计划,同时通过Wi-Fi模块无线传输数据,实现了与后台管理系统的无缝对接,促进仓储信息的实时共享和远程监控。设计思路该系统以STM32F103系列单片机作为核心控制器,负责协调各硬件模块的工作流程。通过集成OpenMV视觉模块、Wi-Fi通信模块、电机驱动模块和电源模块,实现机器人的自主移动、货物识别、数据记录与无线传输功能。在自主巡线移动方面,OpenMV摄像头实时采集货架轨道的图像数据,通过内置的图像处理算法识别预设路径的标识线。STM32主控芯片根据识别结果输出控制信号,驱动L298N电机驱动板调节直流减速电机的转速和转向,使机器人能够沿轨道平稳行进,并保持稳定的移动轨迹。当机器人行进至货架位置时,OpenMV模块会主动扫描货物上的条形码或二维码。扫描到的图像经过解码处理后,通过串口通信将货物信息传输至STM32主控芯片。STM32对接收到的数据进行解析和校验,确保信息的准确性和完整性。系统会自动记录解析后的货物信息,包括货物编号、类型和数量等关键数据。这些数据被存储在主控芯片的存储器或外部存储设备中,并通过内置算法实时汇总生成库存报表。报表内容可涵盖库存状态、变化趋势等实用信息,为仓储管理提供数据支持。通过ESP8266或ESP32-S3 Wi-Fi模块,STM32将生成的库存报表数据以无线方式发送至后台管理系统。通信过程采用标准的网络协议,确保数据传输的可靠性和实时性,实现库存信息的远程监控与更新。电源模块由12V锂电池组及降压模块组成,为整个系统提供稳定供电。降压模块将电压转换为5V和3.3V,分别满足电机驱动板和主控芯片等模块的功耗需求,同时通过电源管理策略优化能耗,保障机器人的持续运行能力。框架图智能仓储盘点机器人系统框架图 +---------------------------------------------+ | 电源模块 | | - 12V锂电池组 | | - 5V/3.3V降压模块 | +---------------------------------------------+ | v 供电 +---------------------------------------------+ | 主控芯片 | | STM32F103系列 | +---------------------------------------------+ | | | | 控制/数据 | 控制/数据 | 控制/数据 v v v +-----------+ +-----------+ +-----------+ | 视觉模块 | | 通信模块 | | 驱动模块 | | OpenMV | | Wi-Fi | | L298N | | 摄像头 | | ESP8266/ | | 电机驱动 | | | | ESP32-S3 | | 电机+车轮 | +-----------+ +-----------+ +-----------+ | | | 扫描条形码 | 传输数据 v v +---------------------------------------------+ | 后台管理系统 | | (库存数据接收与处理) | +---------------------------------------------+ 系统总体设计该系统以STM32F103系列单片机作为核心控制器,负责协调各硬件模块的运行,实现智能仓储盘点功能。机器人通过巡线传感器检测预设轨道,结合L298N电机驱动板控制直流减速电机与车轮的运动,确保沿货架轨道自主移动,完成巡检任务。视觉识别部分由OpenMV摄像头模组承担,该模块实时采集货物图像,通过内置算法识别条形码或二维码信息,并将解码后的数据发送至STM32主控芯片进行处理。系统在STM32主控中集成数据管理功能,自动记录扫描到的货物信息,包括货物编号和数量,并基于这些数据生成结构化库存报表,便于后续分析和使用。通信模块采用ESP8266或ESP32-S3 Wi-Fi模块,将生成的库存报表通过无线网络传输至后台管理系统,实现数据的实时同步和远程监控,提升仓储管理效率。电源模块由12V锂电池组供电,通过降压模块转换为5V和3.3V电压,分别为电机驱动、主控芯片及其他低功耗模块提供稳定电力,确保系统长时间可靠运行。系统功能总结功能编号功能描述1机器人能够沿预设货架轨道自主巡线移动。2通过OpenMV视觉模块扫描货物条形码或二维码。3系统自动记录扫描到的货物信息,并生成库存报表。4通过Wi-Fi模块将库存数据无线传输到后台管理系统。设计的各个功能模块描述STM32F103系列单片机作为系统的主控制器,负责整体协调与控制。它处理来自传感器的输入数据,执行自主巡线算法,控制驱动模块实现移动,同时管理视觉模块的扫描过程和通信模块的数据传输,确保系统按预设功能运行。OpenMV视觉模块用于货物识别,通过摄像头捕获条形码或二维码图像,并利用内置算法进行解码。解码后的货物信息被发送至主控芯片,用于自动记录和生成库存报表,实现高效的仓储盘点。通信模块采用ESP8266或ESP32-S3 Wi-Fi芯片,负责无线数据传输。它将主控芯片处理后的库存信息通过Wi-Fi网络发送到后台管理系统,实现远程数据同步和实时库存监控。驱动模块包括L298N电机驱动板、直流减速电机和车轮,实现机器人的移动功能。主控芯片输出控制信号,通过驱动板调节电机转速和方向,使机器人能够沿预设货架轨道自主巡线移动,确保稳定运行。电源模块由12V锂电池组及降压模块组成,为系统提供电力支持。锂电池输出12V电压,经降压模块转换为5V和3.3V,分别供给主控芯片、视觉模块、通信模块和驱动模块,保证各组件稳定工作。上位机代码设计// main.cpp - 智能仓储盘点机器人上位机系统 #include <iostream> #include <fstream> #include <string> #include <vector> #include <thread> #include <chrono> #include <winsock2.h> #include <windows.h> #pragma comment(lib, "ws2_32.lib") // 库存数据结构 struct InventoryItem { std::string barcode; std::string name; int quantity; std::string timestamp; std::string location; }; class WarehouseManager { private: std::vector<InventoryItem> inventory; SOCKET serverSocket; bool isRunning; std::string dataFile = "inventory_data.txt"; public: WarehouseManager() : serverSocket(INVALID_SOCKET), isRunning(false) {} ~WarehouseManager() { stopServer(); } // 初始化Winsock bool initNetwork() { WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { std::cerr << "WSAStartup failed!" << std::endl; return false; } return true; } // 创建服务器Socket bool createServer(int port = 8080) { serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (serverSocket == INVALID_SOCKET) { std::cerr << "Socket creation failed!" << std::endl; return false; } sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = INADDR_ANY; serverAddr.sin_port = htons(port); if (bind(serverSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) { std::cerr << "Bind failed!" << std::endl; closesocket(serverSocket); return false; } if (listen(serverSocket, 5) == SOCKET_ERROR) { std::cerr << "Listen failed!" << std::endl; closesocket(serverSocket); return false; } std::cout << "Server started on port " << port << std::endl; return true; } // 处理客户端连接 void handleClient(SOCKET clientSocket) { char buffer[1024]; int bytesReceived; while ((bytesReceived = recv(clientSocket, buffer, sizeof(buffer), 0)) > 0) { buffer[bytesReceived] = '\0'; std::string data(buffer); // 解析机器人发送的数据 if (parseInventoryData(data)) { std::cout << "Inventory data received and processed: " << data << std::endl; // 发送确认回执 std::string ack = "ACK:Data received successfully"; send(clientSocket, ack.c_str(), ack.length(), 0); } } closesocket(clientSocket); } // 解析库存数据 bool parseInventoryData(const std::string& data) { // 数据格式: BARCODE|NAME|QUANTITY|TIMESTAMP|LOCATION std::vector<std::string> tokens; size_t start = 0, end = 0; while ((end = data.find('|', start)) != std::string::npos) { tokens.push_back(data.substr(start, end - start)); start = end + 1; } tokens.push_back(data.substr(start)); if (tokens.size() >= 5) { InventoryItem item; item.barcode = tokens[0]; item.name = tokens[1]; item.quantity = std::stoi(tokens[2]); item.timestamp = tokens[3]; item.location = tokens[4]; // 更新库存 updateInventory(item); saveToFile(); return true; } return false; } // 更新库存 void updateInventory(const InventoryItem& newItem) { // 检查是否已存在相同条码的商品 for (auto& item : inventory) { if (item.barcode == newItem.barcode && item.location == newItem.location) { item.quantity = newItem.quantity; item.timestamp = newItem.timestamp; return; } } // 新商品,添加到库存 inventory.push_back(newItem); } // 保存数据到文件 void saveToFile() { std::ofstream file(dataFile); if (!file.is_open()) { std::cerr << "Cannot open data file!" << std::endl; return; } file << "BARCODE|NAME|QUANTITY|TIMESTAMP|LOCATION\n"; for (const auto& item : inventory) { file << item.barcode << "|" << item.name << "|" << item.quantity << "|" << item.timestamp << "|" << item.location << "\n"; } file.close(); } // 从文件加载数据 void loadFromFile() { std::ifstream file(dataFile); if (!file.is_open()) { std::cout << "No existing data file found. Starting fresh." << std::endl; return; } std::string line; std::getline(file, line); // 跳过标题行 while (std::getline(file, line)) { std::vector<std::string> tokens; size_t start = 0, end = 0; while ((end = line.find('|', start)) != std::string::npos) { tokens.push_back(line.substr(start, end - start)); start = end + 1; } tokens.push_back(line.substr(start)); if (tokens.size() >= 5) { InventoryItem item; item.barcode = tokens[0]; item.name = tokens[1]; item.quantity = std::stoi(tokens[2]); item.timestamp = tokens[3]; item.location = tokens[4]; inventory.push_back(item); } } file.close(); } // 生成库存报表 void generateReport() { std::cout << "\n=== 智能仓储库存报表 ===" << std::endl; std::cout << "更新时间: " << getCurrentTime() << std::endl; std::cout << "总商品种类: " << inventory.size() << std::endl; std::cout << "----------------------------------------" << std::endl; int totalItems = 0; for (const auto& item : inventory) { std::cout << "条码: " << item.barcode << " | 名称: " << item.name << " | 数量: " << item.quantity << " | 位置: " << item.location << " | 时间: " << item.timestamp << std::endl; totalItems += item.quantity; } std::cout << "----------------------------------------" << std::endl; std::cout << "总库存数量: " << totalItems << std::endl; // 保存报表到文件 std::ofstream report("inventory_report.txt"); report << "智能仓储库存报表\n"; report << "更新时间: " << getCurrentTime() << "\n"; report << "总商品种类: " << inventory.size() << "\n"; report << "总库存数量: " << totalItems << "\n\n"; for (const auto& item : inventory) { report << item.barcode << " | " << item.name << " | " << item.quantity << " | " << item.location << " | " << item.timestamp << "\n"; } report.close(); } // 获取当前时间 std::string getCurrentTime() { auto now = std::chrono::system_clock::now(); auto time_t = std::chrono::system_clock::to_time_t(now); char buffer[80]; ctime_s(buffer, sizeof(buffer), &time_t); std::string timeStr(buffer); timeStr.pop_back(); // 移除换行符 return timeStr; } // 启动服务器 void startServer() { if (!initNetwork() || !createServer()) { return; } isRunning = true; loadFromFile(); std::cout << "Warehouse Management System Started!" << std::endl; std::cout << "Waiting for robot connections..." << std::endl; while (isRunning) { sockaddr_in clientAddr; int clientAddrLen = sizeof(clientAddr); SOCKET clientSocket = accept(serverSocket, (sockaddr*)&clientAddr, &clientAddrLen); if (clientSocket == INVALID_SOCKET) { if (isRunning) { std::cerr << "Accept failed!" << std::endl; } continue; } std::cout << "Robot connected from " << inet_ntoa(clientAddr.sin_addr) << ":" << ntohs(clientAddr.sin_port) << std::endl; // 在新线程中处理客户端 std::thread clientThread(&WarehouseManager::handleClient, this, clientSocket); clientThread.detach(); } } // 停止服务器 void stopServer() { isRunning = false; if (serverSocket != INVALID_SOCKET) { closesocket(serverSocket); serverSocket = INVALID_SOCKET; } WSACleanup(); } // 显示菜单 void showMenu() { std::cout << "\n=== 智能仓储管理系统菜单 ===" << std::endl; std::cout << "1. 查看当前库存" << std::endl; std::cout << "2. 生成库存报表" << std::endl; std::cout << "3. 手动添加商品" << std::endl; std::cout << "4. 退出系统" << std::endl; std::cout << "请输入选择 (1-4): "; } // 手动添加商品 void manualAddItem() { InventoryItem item; std::cout << "请输入商品条码: "; std::cin >> item.barcode; std::cout << "请输入商品名称: "; std::cin >> item.name; std::cout << "请输入商品数量: "; std::cin >> item.quantity; std::cout << "请输入商品位置: "; std::cin >> item.location; item.timestamp = getCurrentTime(); updateInventory(item); saveToFile(); std::cout << "商品添加成功!" << std::endl; } // 运行用户界面 void runUI() { int choice; while (true) { showMenu(); std::cin >> choice; switch (choice) { case 1: generateReport(); break; case 2: generateReport(); std::cout << "报表已保存到 inventory_report.txt" << std::endl; break; case 3: manualAddItem(); break; case 4: std::cout << "系统关闭中..." << std::endl; return; default: std::cout << "无效选择,请重新输入!" << std::endl; } } } }; int main() { WarehouseManager manager; // 在后台线程中启动服务器 std::thread serverThread(&WarehouseManager::startServer, &manager); // 在主线程中运行用户界面 manager.runUI(); // 用户选择退出,停止服务器 manager.stopServer(); if (serverThread.joinable()) { serverThread.join(); } std::cout << "系统已安全关闭" << std::endl; return 0; } 模块代码设计#include "stm32f10x.h" // 电机控制引脚定义 #define MOTOR1_IN1_PIN GPIO_Pin_0 // PA0 #define MOTOR1_IN2_PIN GPIO_Pin_1 // PA1 #define MOTOR2_IN1_PIN GPIO_Pin_2 // PA2 #define MOTOR2_IN2_PIN GPIO_Pin_3 // PA3 #define MOTOR_PWM1_PIN GPIO_Pin_6 // PA6 (TIM3_CH1) #define MOTOR_PWM2_PIN GPIO_Pin_7 // PA7 (TIM3_CH2) // OpenMV串口配置 #define OPENMV_USART USART1 #define OPENMV_BAUDRATE 115200 // WiFi模块串口配置 #define WIFI_USART USART2 #define WIFI_BAUDRATE 115200 // 巡线传感器引脚定义 #define LINE_SENSOR1_PIN GPIO_Pin_4 // PB4 #define LINE_SENSOR2_PIN GPIO_Pin_5 // PB5 #define LINE_SENSOR3_PIN GPIO_Pin_6 // PB6 #define LINE_SENSOR4_PIN GPIO_Pin_7 // PB7 // 电机PWM配置 void Motor_PWM_Init(void) { // 开启GPIOA和TIM3时钟 RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; // 配置PA6,PA7为复用推挽输出 GPIOA->CRL &= ~(GPIO_CRL_CNF6 | GPIO_CRL_MODE6 | GPIO_CRL_CNF7 | GPIO_CRL_MODE7); GPIOA->CRL |= (GPIO_CRL_CNF6_1 | GPIO_CRL_MODE6_1 | GPIO_CRL_CNF7_1 | GPIO_CRL_MODE7_1); // 配置TIM3 TIM3->ARR = 999; // 自动重装载值,PWM频率=72MHz/(999+1)=72kHz TIM3->PSC = 0; // 预分频器 // 配置通道1和2为PWM模式1 TIM3->CCMR1 |= TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2 | // PWM模式1 TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2; TIM3->CCMR1 |= TIM_CCMR1_OC1PE | TIM_CCMR1_OC2PE; // 预装载使能 // 使能通道1和2输出 TIM3->CCER |= TIM_CCER_CC1E | TIM_CCER_CC2E; // 使能TIM3 TIM3->CR1 |= TIM_CR1_ARPE | TIM_CR1_CEN; } // 电机方向控制初始化 void Motor_GPIO_Init(void) { // 开启GPIOA时钟 RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; // 配置电机方向控制引脚为推挽输出 GPIOA->CRL &= ~(GPIO_CRL_CNF0 | GPIO_CRL_MODE0 | GPIO_CRL_CNF1 | GPIO_CRL_MODE1 | GPIO_CRL_CNF2 | GPIO_CRL_MODE2 | GPIO_CRL_CNF3 | GPIO_CRL_MODE3); GPIOA->CRL |= (GPIO_CRL_MODE0_0 | GPIO_CRL_MODE0_1 | GPIO_CRL_MODE1_0 | GPIO_CRL_MODE1_1 | GPIO_CRL_MODE2_0 | GPIO_CRL_MODE2_1 | GPIO_CRL_MODE3_0 | GPIO_CRL_MODE3_1); } // 电机控制函数 void Motor_Control(uint8_t motor, uint8_t direction, uint16_t speed) { if(motor == 1) { if(direction == 1) { // 正转 GPIOA->BSRR = MOTOR1_IN1_PIN; GPIOA->BRR = MOTOR1_IN2_PIN; } else { // 反转 GPIOA->BRR = MOTOR1_IN1_PIN; GPIOA->BSRR = MOTOR1_IN2_PIN; } TIM3->CCR1 = speed; // 设置PWM占空比 } else if(motor == 2) { if(direction == 1) { // 正转 GPIOA->BSRR = MOTOR2_IN1_PIN; GPIOA->BRR = MOTOR2_IN2_PIN; } else { // 反转 GPIOA->BRR = MOTOR2_IN1_PIN; GPIOA->BSRR = MOTOR2_IN2_PIN; } TIM3->CCR2 = speed; // 设置PWM占空比 } } // OpenMV串口初始化 void OpenMV_USART_Init(void) { // 开启USART1和GPIOA时钟 RCC->APB2ENR |= RCC_APB2ENR_USART1EN | RCC_APB2ENR_IOPAEN; // 配置PA9为复用推挽输出(TX),PA10为浮空输入(RX) GPIOA->CRH &= ~(GPIO_CRH_CNF9 | GPIO_CRH_MODE9 | GPIO_CRH_CNF10 | GPIO_CRH_MODE10); GPIOA->CRH |= (GPIO_CRH_CNF9_1 | GPIO_CRH_MODE9_0 | GPIO_CRH_MODE9_1 | GPIO_CRH_CNF10_0); // 配置USART1 USART1->BRR = 72000000 / OPENMV_BAUDRATE; // 设置波特率 USART1->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; // 使能USART, TX, RX USART1->CR1 |= USART_CR1_RXNEIE; // 使能接收中断 } // WiFi模块串口初始化 void WiFi_USART_Init(void) { // 开启USART2和GPIOA时钟 RCC->APB1ENR |= RCC_APB1ENR_USART2EN; RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; // 配置PA2为复用推挽输出(TX),PA3为浮空输入(RX) GPIOA->CRL &= ~(GPIO_CRL_CNF2 | GPIO_CRL_MODE2 | GPIO_CRL_CNF3 | GPIO_CRL_MODE3); GPIOA->CRL |= (GPIO_CRL_CNF2_1 | GPIO_CRL_MODE2_0 | GPIO_CRL_MODE2_1 | GPIO_CRL_CNF3_0); // 配置USART2 USART2->BRR = 36000000 / WIFI_BAUDRATE; // 设置波特率 USART2->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; // 使能USART, TX, RX USART2->CR1 |= USART_CR1_RXNEIE; // 使能接收中断 } // 巡线传感器初始化 void Line_Sensor_Init(void) { // 开启GPIOB时钟 RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; // 配置巡线传感器引脚为浮空输入 GPIOB->CRL &= ~(GPIO_CRL_CNF4 | GPIO_CRL_MODE4 | GPIO_CRL_CNF5 | GPIO_CRL_MODE5 | GPIO_CRL_CNF6 | GPIO_CRL_MODE6 | GPIO_CRL_CNF7 | GPIO_CRL_MODE7); GPIOB->CRL |= (GPIO_CRL_CNF4_0 | GPIO_CRL_CNF5_0 | GPIO_CRL_CNF6_0 | GPIO_CRL_CNF7_0); } // 读取巡线传感器状态 uint8_t Read_Line_Sensors(void) { uint8_t sensor_state = 0; if(GPIOB->IDR & LINE_SENSOR1_PIN) sensor_state |= 0x01; if(GPIOB->IDR & LINE_SENSOR2_PIN) sensor_state |= 0x02; if(GPIOB->IDR & LINE_SENSOR3_PIN) sensor_state |= 0x04; if(GPIOB->IDR & LINE_SENSOR4_PIN) sensor_state |= 0x08; return sensor_state; } // 串口发送函数 void USART_Send(USART_TypeDef* USARTx, uint8_t data) { while(!(USARTx->SR & USART_SR_TXE)); // 等待发送缓冲区空 USARTx->DR = data; // 发送数据 } void USART_SendString(USART_TypeDef* USARTx, char* str) { while(*str) { USART_Send(USARTx, *str++); } } // 串口接收缓冲区 volatile uint8_t openmv_rx_buffer[256]; volatile uint8_t openmv_rx_index = 0; volatile uint8_t wifi_rx_buffer[256]; volatile uint8_t wifi_rx_index = 0; // USART1中断服务函数(OpenMV) void USART1_IRQHandler(void) { if(USART1->SR & USART_SR_RXNE) { uint8_t data = USART1->DR; openmv_rx_buffer[openmv_rx_index++] = data; // 处理接收到的数据(条形码/二维码信息) if(data == '\n') { // 以换行符作为结束标志 Process_Barcode_Data((char*)openmv_rx_buffer); openmv_rx_index = 0; } } } // USART2中断服务函数(WiFi) void USART2_IRQHandler(void) { if(USART2->SR & USART_SR_RXNE) { uint8_t data = USART2->DR; wifi_rx_buffer[wifi_rx_index++] = data; // 处理WiFi模块响应 if(data == '\n') { Process_WiFi_Response((char*)wifi_rx_buffer); wifi_rx_index = 0; } } } // 处理条形码数据 void Process_Barcode_Data(char* data) { // 解析OpenMV发送的条形码数据 // 数据格式: "BARCODE:123456789\n" // 记录库存信息 Add_To_Inventory(data); // 通过WiFi发送到服务器 WiFi_Send_Data(data); } // 添加库存记录 void Add_To_Inventory(char* barcode_data) { // 实现库存记录逻辑 // 这里可以添加EEPROM存储或直接发送到服务器 } // WiFi发送数据 void WiFi_Send_Data(char* data) { // 发送数据到WiFi模块 USART_SendString(USART2, "AT+CIPSEND="); USART_SendString(USART2, itoa(strlen(data), 10)); USART_SendString(USART2, "\r\n"); Delay_ms(100); USART_SendString(USART2, data); } // 巡线控制算法 void Line_Following_Control(void) { uint8_t sensor_state = Read_Line_Sensors(); switch(sensor_state) { case 0x06: // 0110 - 直线前进 Motor_Control(1, 1, 600); // 左电机正转,速度600 Motor_Control(2, 1, 600); // 右电机正转,速度600 break; case 0x04: // 0100 - 轻微左偏 Motor_Control(1, 1, 400); // 左电机减速 Motor_Control(2, 1, 700); // 右电机加速 break; case 0x02: // 0010 - 轻微右偏 Motor_Control(1, 1, 700); // 左电机加速 Motor_Control(2, 1, 400); // 右电机减速 break; case 0x00: // 0000 - 丢失路线,停止 Motor_Control(1, 1, 0); Motor_Control(2, 1, 0); break; default: // 保持当前状态 break; } } // 系统初始化 void System_Init(void) { // 配置系统时钟 RCC->CR |= RCC_CR_HSEON; while(!(RCC->CR & RCC_CR_HSERDY)); 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_PLL)); // 初始化各模块 Motor_GPIO_Init(); Motor_PWM_Init(); OpenMV_USART_Init(); WiFi_USART_Init(); Line_Sensor_Init(); // 配置NVIC NVIC->ISER[0] |= (1 << USART1_IRQn) | (1 << USART2_IRQn); } // 主函数 int main(void) { System_Init(); // WiFi模块初始化 WiFi_Init(); while(1) { // 巡线移动 Line_Following_Control(); // 处理其他任务 Delay_ms(10); } } // WiFi模块初始化 void WiFi_Init(void) { Delay_ms(1000); USART_SendString(USART2, "AT+RST\r\n"); Delay_ms(2000); USART_SendString(USART2, "AT+CWMODE=1\r\n"); Delay_ms(1000); USART_SendString(USART2, "AT+CWJAP=\"SSID\",\"PASSWORD\"\r\n"); Delay_ms(5000); USART_SendString(USART2, "AT+CIPSTART=\"TCP\",\"192.168.1.100\",8080\r\n"); Delay_ms(2000); } // 简单延时函数 void Delay_ms(uint32_t ms) { for(uint32_t i = 0; i < ms * 1000; i++); } // 整数转字符串函数 char* itoa(int value, char* result) { char* ptr = result; char* ptr1 = result; char tmp_char; int tmp_value; do { tmp_value = value; value /= 10; *ptr++ = "0123456789"[tmp_value - value * 10]; } while(value); *ptr-- = '\0'; while(ptr1 < ptr) { tmp_char = *ptr; *ptr-- = *ptr1; *ptr1++ = tmp_char; } return result; } 项目核心代码#include "stm32f10x.h" // 硬件模块初始化函数声明 void System_Init(void); void GPIO_Init(void); void USART_Init(void); void TIM_Init(void); void NVIC_Init(void); // 模块驱动函数声明 void Motor_Control(int left_speed, int right_speed); void OpenMV_Process(void); void WiFi_SendData(const char* data); void Barcode_Scan(void); // 全局变量 volatile uint32_t system_tick = 0; char inventory_data[256] = {0}; int main(void) { // 系统初始化 System_Init(); GPIO_Init(); USART_Init(); TIM_Init(); NVIC_Init(); // 启动系统滴答定时器 SysTick_Config(SystemCoreClock / 1000); // 主循环 while(1) { // 1. 自主巡线移动 Line_Following(); // 2. 扫描货物条码 Barcode_Scan(); // 3. 处理视觉数据 OpenMV_Process(); // 4. 定期发送库存数据 if(system_tick % 5000 == 0) // 每5秒发送一次 { WiFi_SendData(inventory_data); } // 延时处理 Delay_ms(10); } } // 系统时钟初始化 void System_Init(void) { // 开启时钟 RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_AFIOEN | RCC_APB2ENR_USART1EN; RCC->APB1ENR |= RCC_APB1ENR_USART2EN | RCC_APB1ENR_USART3EN | RCC_APB1ENR_TIM2EN | RCC_APB1ENR_TIM3EN; } // GPIO初始化 void GPIO_Init(void) { // 电机控制引脚 PB6, PB7, PB8, PB9 GPIOB->CRL &= ~(0xFF << 24); // 清除PB6,PB7配置 GPIOB->CRL |= (0x0B << 24) | (0x0B << 28); // PB6,PB7复用推挽输出 GPIOB->CRH &= ~(0xFF << 0); // 清除PB8,PB9配置 GPIOB->CRH |= (0x0B << 0) | (0x0B << 4); // PB8,PB9复用推挽输出 // 串口引脚 PA9(TX), PA10(RX) - USART1 GPIOA->CRH &= ~(0xFF << 4); // 清除PA9,PA10配置 GPIOA->CRH |= (0x0B << 4) | (0x04 << 8); // PA9复用推挽, PA10浮空输入 // WiFi模块引脚 PA2(TX), PA3(RX) - USART2 GPIOA->CRL &= ~(0xFF << 8); // 清除PA2,PA3配置 GPIOA->CRL |= (0x0B << 8) | (0x04 << 12); // PA2复用推挽, PA3浮空输入 } // 串口初始化 void USART_Init(void) { // USART1 - 调试和OpenMV通信 USART1->BRR = 0x1D4C; // 115200 @72MHz USART1->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; USART1->CR1 |= USART_CR1_RXNEIE; // USART2 - WiFi模块通信 USART2->BRR = 0x1D4C; // 115200 @72MHz USART2->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; } // 定时器初始化 - 用于PWM电机控制 void TIM_Init(void) { // TIM3 - 左电机 PWM CH1(PB6), CH2(PB7) TIM3->PSC = 71; // 72MHz/72 = 1MHz TIM3->ARR = 999; // 1MHz/1000 = 1kHz PWM频率 TIM3->CCMR1 = (0x6 << 4) | (0x6 << 12); // PWM模式1 TIM3->CCER = TIM_CCER_CC1E | TIM_CCER_CC2E; // 开启输出 TIM3->CR1 = TIM_CR1_CEN; // 启动定时器 // TIM4 - 右电机 PWM CH1(PB8), CH2(PB9) TIM4->PSC = 71; TIM4->ARR = 999; TIM4->CCMR2 = (0x6 << 4) | (0x6 << 12); TIM4->CCER = TIM_CCER_CC3E | TIM_CCER_CC4E; TIM4->CR1 = TIM_CR1_CEN; } // 中断配置 void NVIC_Init(void) { NVIC_EnableIRQ(USART1_IRQn); NVIC_SetPriority(USART1_IRQn, 0); } // 电机控制函数 void Motor_Control(int left_speed, int right_speed) { // 限制速度范围 if(left_speed > 1000) left_speed = 1000; if(left_speed < 0) left_speed = 0; if(right_speed > 1000) right_speed = 1000; if(right_speed < 0) right_speed = 0; // 设置PWM占空比 TIM3->CCR1 = left_speed; // 左电机前进 TIM3->CCR2 = 0; // 左电机后退 TIM4->CCR3 = right_speed; // 右电机前进 TIM4->CCR4 = 0; // 右电机后退 } // 巡线控制 void Line_Following(void) { // 简单的巡线算法 - 根据传感器数据调整电机速度 // 这里需要根据实际的巡线传感器数据来调整 Motor_Control(800, 800); // 直行 } // 条码扫描处理 void Barcode_Scan(void) { // 发送扫描指令给OpenMV USART_SendString(USART1, "SCAN\r\n"); } // WiFi发送数据 void WiFi_SendData(const char* data) { if(strlen(data) > 0) { USART_SendString(USART2, data); } } // USART1中断服务函数 - 接收OpenMV数据 void USART1_IRQHandler(void) { if(USART1->SR & USART_SR_RXNE) { uint8_t data = USART1->DR; // 处理接收到的条码数据 // 这里添加条码数据处理逻辑 } } // 简单的延时函数 void Delay_ms(uint32_t ms) { uint32_t start_tick = system_tick; while((system_tick - start_tick) < ms); } // 系统滴答定时器中断 void SysTick_Handler(void) { system_tick++; } // 串口发送字符串函数 void USART_SendString(USART_TypeDef* USARTx, const char* str) { while(*str) { while(!(USARTx->SR & USART_SR_TXE)); USARTx->DR = (*str++ & 0xFF); } } 总结该智能仓储盘点机器人基于STM32主控芯片设计,实现了仓储环境中的自动化盘点任务。机器人能够沿预设轨道自主巡线移动,通过OpenMV视觉模块精准识别货物条形码或二维码,并自动记录货物信息生成库存报表,有效提升仓储管理效率。系统硬件集成多个关键模块,包括STM32F103系列单片机作为控制核心,OpenMV摄像头负责视觉采集,ESP8266或ESP32-S3 Wi-Fi模块实现无线通信,L298N驱动板与直流电机保障移动功能,以及12V锂电池与降压模块提供稳定电源支持。通过Wi-Fi模块,机器人可将库存数据实时传输至后台管理系统,实现远程监控和数据同步。整体设计紧凑可靠,适用于现代智能仓储场景,显著降低了人工盘点成本,并提高了数据准确性和响应速度。
  • [技术干货] 基于STM32的便携式心电监护仪
    项目开发背景随着心血管疾病发病率的逐年上升,心电监护在日常健康管理中的重要性日益凸显。传统心电监测设备往往体积庞大、成本较高,且多限于医院环境使用,无法满足个人日常长期监测的需求。因此,开发一种便携、低成本且功能完善的心电监护仪,能够帮助用户实时了解心脏健康状况,并在异常时及时预警,具有重要的现实意义。当前,市场上虽有部分便携心电设备,但常存在功能单一、数据传输不便或报警机制不完善等问题。基于STM32微控制器的解决方案,结合心电采集、显示、报警和无线通信模块,能够有效整合实时心电信号处理、直观波形显示和智能预警功能,填补了个人健康监护领域的空白。此外,通过Wi-Fi技术将数据上传至手机APP,可实现远程分析和长期跟踪,进一步提升健康管理的便捷性和有效性。本项目的开发旨在利用现代嵌入式技术和物联网理念,构建一个高效可靠的便携式心电监护系统。它不仅能够降低用户对专业医疗设备的依赖,还能为心血管疾病预防和早期干预提供技术支持,推动个人健康监护向智能化、普及化方向发展。设计实现的功能(1) 通过AD8232心电信号采集模块实时采集使用者的心电信号。(2) 将心电波形实时显示在0.96英寸OLED屏幕上,并计算并显示实时心率。(3) 当检测到心率异常(过快或过慢)时,触发5V有源蜂鸣器和LED指示灯进行本地声光报警。(4) 通过ESP8266 Wi-Fi模块将心电数据上传至手机APP,供进一步分析。项目硬件模块组成(1)主控芯片:STM32F103C8T6单片机。(2)心电采集:AD8232心电信号采集模块。(3)显示模块:0.96英寸OLED显示屏。(4)通信模块:ESP8266 Wi-Fi模块。(5)报警模块:5V有源蜂鸣器和LED指示灯。设计意义该便携式心电监护仪的设计意义在于提供一种便捷、实时的个人心脏健康监测方案,帮助用户随时了解自身心电状况。通过集成心电采集和显示功能,设备能够直观地呈现心电波形和心率数据,使用户无需依赖大型医疗设备即可进行日常监测,从而提升健康管理的主动性和可及性。设备具备心率异常报警功能,可在检测到心率过快或过慢时立即触发声光警示,这有助于用户及时识别潜在的心脏问题并采取应对措施,减少因延误处理而引发的健康风险。这种即时响应机制特别适合高风险人群或康复期患者使用,增强了安全监护的可靠性。通过Wi-Fi模块将心电数据上传至手机APP,设备支持数据的远程存储和分析,方便用户或医疗专业人员追踪长期趋势,为诊断和健康评估提供依据。这一特性扩展了传统心电监护的局限性,促进了家庭医疗与远程医疗的融合,有助于降低医疗成本并提高服务效率。整体设计基于STM32等成熟硬件模块,实现了低成本、高集成度的解决方案,体现了嵌入式系统在医疗电子领域的实用价值。它不仅推动了智能医疗设备的普及,还为个人健康监护技术的创新提供了参考,具有广泛的社会应用前景。设计思路系统以STM32F103C8T6单片机作为核心控制器,负责协调各个硬件模块的工作。首先,通过AD8232心电信号采集模块实时采集使用者的心电信号,该模块对模拟心电信号进行放大和滤波,输出稳定的电压信号。STM32通过内置ADC以固定采样率将模拟信号转换为数字值,并对数据进行初步处理,如去除噪声和基线漂移,以提取有效的心电波形。处理后的心电数据被用于两个主要目的:一是驱动0.96英寸OLED显示屏实时显示动态心电波形,二是计算实时心率值。心率计算基于检测QRS波群中的R波间隔,通过算法计算每分钟心跳次数,并在OLED屏幕上更新显示。显示模块通过I2C或SPI接口与STM32通信,确保波形和数据的流畅刷新。当系统检测到心率异常,例如心率过快或过慢超出预设阈值时,STM32会触发本地报警机制。这包括控制5V有源蜂鸣器发出声音警报,同时点亮LED指示灯提供视觉提示,以提醒用户及时注意。报警状态会持续直到心率恢复正常或用户手动干预。此外,系统通过ESP8266 Wi-Fi模块实现无线通信功能。STM32将心电数据打包后,通过UART接口发送AT指令控制ESP8266连接到Wi-Fi网络,并将数据上传至手机APP。这允许用户远程查看和分析心电记录,确保数据的可追溯性和进一步处理。整个设计注重实用性和可靠性,各模块协同工作,实现便携式心电监护的基本功能。框架图+------------------------+ +------------------------+ +------------------+ | 心电采集模块 |---->| 主控芯片 |---->| 显示模块 | | (AD8232) | | (STM32F103C8T6) | | (OLED) | +------------------------+ +------------------------+ +------------------+ | | (心率异常时) v +------------------------+ | 报警模块 |----> 本地声光报警 | (蜂鸣器 + LED) | +------------------------+ | v +------------------------+ | 通信模块 |----> 手机APP (通过Wi-Fi) | (ESP8266) | +------------------------+ 系统总体设计该系统基于STM32F103C8T6单片机作为核心控制器,负责协调和管理各个硬件模块的运作,实现便携式心电监护功能。系统通过AD8232心电信号采集模块实时采集使用者的心电信号,AD8232模块对原始心电信号进行放大和滤波处理,输出模拟信号至STM32的ADC接口进行数字化转换,确保信号准确性和稳定性。数字化后的心电数据由STM32进行实时处理,包括波形分析和心率计算。系统通过I2C或SPI接口驱动0.96英寸OLED显示屏,动态显示心电波形曲线和实时心率数值,为用户提供直观的视觉反馈。心率计算基于心电信号的R波检测算法,STM32通过软件实现心率值的更新和显示。当STM32检测到心率异常,如过快或过慢超出预设阈值时,系统会立即触发本地报警机制。报警模块包括5V有源蜂鸣器和LED指示灯,蜂鸣器发出声音警示,同时LED闪烁,形成声光报警以提醒用户注意潜在健康风险。系统还集成ESP8266 Wi-Fi模块,通过串口通信与STM32连接,实现无线数据传输功能。心电数据被打包后通过Wi-Fi网络上传至手机APP,支持远程存储和进一步分析,扩展了系统的应用场景和实用性。整个设计注重低功耗和便携性,确保在移动环境中稳定运行。系统功能总结序号功能描述1通过心电采集芯片实时采集使用者的心电信号。2将心电波形实时显示在OLED屏幕上,并计算显示实时心率。3当检测到心率异常(过快或过慢)时,触发本地声光报警。4系统可通过Wi-Fi将心电数据上传至手机APP,供进一步分析。设计的各个功能模块描述心电采集模块使用AD8232心电信号采集芯片实时采集使用者的心电信号,将模拟心电数据转换为数字信号,并传输给主控芯片进行处理,确保信号的准确性和稳定性。显示模块通过0.96英寸OLED屏幕实时显示心电波形和计算出的心率数值,提供清晰的视觉反馈,方便使用者随时查看自身心电状态。报警模块在检测到心率异常(如过快或过慢)时,通过5V有源蜂鸣器和LED指示灯触发本地声光警报,及时提醒使用者注意潜在健康风险。通信模块利用ESP8266 Wi-Fi模块将采集到的心电数据上传至手机APP,实现远程数据传输和进一步分析,增强系统的监控能力。主控模块以STM32F103C8T6单片机为核心,负责协调所有功能模块的运行,包括信号采集、心率计算、显示更新、报警判断和数据传输,确保系统高效可靠地工作。上位机代码设计// ECG Monitor Desktop Application // main.cpp #include <iostream> #include <string> #include <vector> #include <thread> #include <chrono> #include <winsock2.h> #include <windows.h> #include <iomanip> #include <algorithm> #pragma comment(lib, "ws2_32.lib") class ECGDataProcessor { private: std::vector<int> ecgBuffer; std::vector<int> heartRateBuffer; int currentHeartRate; bool isConnected; SOCKET clientSocket; public: ECGDataProcessor() : currentHeartRate(0), isConnected(false), clientSocket(INVALID_SOCKET) { ecgBuffer.resize(1000, 0); heartRateBuffer.resize(60, 0); } ~ECGDataProcessor() { disconnect(); } bool initializeWiFiConnection(const std::string& ip, int port) { WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { std::cerr << "WSAStartup failed!" << std::endl; return false; } clientSocket = socket(AF_INET, SOCK_STREAM, 0); if (clientSocket == INVALID_SOCKET) { std::cerr << "Socket creation failed!" << std::endl; WSACleanup(); return false; } sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(port); serverAddr.sin_addr.s_addr = inet_addr(ip.c_str()); if (connect(clientSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) { std::cerr << "Connection failed!" << std::endl; closesocket(clientSocket); WSACleanup(); return false; } isConnected = true; std::cout << "Connected to ECG device at " << ip << ":" << port << std::endl; return true; } void disconnect() { if (isConnected) { closesocket(clientSocket); WSACleanup(); isConnected = false; std::cout << "Disconnected from ECG device" << std::endl; } } bool receiveECGData() { if (!isConnected) return false; char buffer[256]; int bytesReceived = recv(clientSocket, buffer, sizeof(buffer) - 1, 0); if (bytesReceived > 0) { buffer[bytesReceived] = '\0'; return parseECGData(buffer); } return false; } bool parseECGData(const std::string& data) { // Expected format: "ECG:value,HR:rate" size_t ecgPos = data.find("ECG:"); size_t hrPos = data.find("HR:"); if (ecgPos != std::string::npos && hrPos != std::string::npos) { try { // Extract ECG value std::string ecgStr = data.substr(ecgPos + 4, hrPos - (ecgPos + 5)); int ecgValue = std::stoi(ecgStr); // Extract heart rate std::string hrStr = data.substr(hrPos + 3); int hrValue = std::stoi(hrStr); // Update buffers ecgBuffer.erase(ecgBuffer.begin()); ecgBuffer.push_back(ecgValue); heartRateBuffer.erase(heartRateBuffer.begin()); heartRateBuffer.push_back(hrValue); currentHeartRate = hrValue; return true; } catch (const std::exception& e) { std::cerr << "Data parsing error: " << e.what() << std::endl; } } return false; } void displayECGWaveform() { system("cls"); std::cout << "=== Portable ECG Monitor ===" << std::endl; std::cout << "Real-time ECG Waveform Display" << std::endl; std::cout << "==============================" << std::endl; // Display heart rate std::cout << "Heart Rate: " << currentHeartRate << " BPM" << std::endl; // Simple ASCII waveform display (last 50 samples) int displaySamples = std::min(50, static_cast<int>(ecgBuffer.size())); int startIdx = ecgBuffer.size() - displaySamples; std::cout << "ECG Waveform:" << std::endl; // Find min and max for scaling int minVal = *std::min_element(ecgBuffer.begin() + startIdx, ecgBuffer.end()); int maxVal = *std::max_element(ecgBuffer.begin() + startIdx, ecgBuffer.end()); int range = std::max(maxVal - minVal, 1); // Display waveform for (int i = startIdx; i < ecgBuffer.size(); ++i) { int scaledValue = ((ecgBuffer[i] - minVal) * 20) / range; std::cout << std::string(scaledValue, ' ') << "*" << std::endl; } // Check for abnormal heart rate if (currentHeartRate > 100 || currentHeartRate < 60) { std::cout << "\n!!! WARNING: Abnormal Heart Rate Detected !!!" << std::endl; Beep(1000, 500); // Audio alert } std::cout << "\nPress 'q' to quit, 's' to save data" << std::endl; } void saveDataToFile() { auto now = std::chrono::system_clock::now(); auto time_t = std::chrono::system_clock::to_time_t(now); std::string filename = "ecg_data_" + std::to_string(time_t) + ".csv"; std::ofstream file(filename); if (file.is_open()) { file << "Timestamp,ECG_Value,Heart_Rate\n"; auto startTime = std::chrono::system_clock::now(); for (size_t i = 0; i < ecgBuffer.size(); ++i) { auto sampleTime = startTime + std::chrono::milliseconds(i * 10); auto sampleTime_t = std::chrono::system_clock::to_time_t(sampleTime); file << std::put_time(std::localtime(&sampleTime_t), "%H:%M:%S") << "," << ecgBuffer[i] << "," << (i < heartRateBuffer.size() ? heartRateBuffer[i] : currentHeartRate) << "\n"; } file.close(); std::cout << "Data saved to " << filename << std::endl; } else { std::cerr << "Failed to save data to file!" << std::endl; } } bool isDeviceConnected() const { return isConnected; } int getCurrentHeartRate() const { return currentHeartRate; } }; class ECGMonitorApp { private: ECGDataProcessor dataProcessor; bool running; public: ECGMonitorApp() : running(false) {} void run() { std::cout << "ECG Monitor Desktop Application" << std::endl; std::cout << "================================" << std::endl; // Configure connection std::string deviceIP; int devicePort; std::cout << "Enter ECG device IP (default: 192.168.1.100): "; std::getline(std::cin, deviceIP); if (deviceIP.empty()) deviceIP = "192.168.1.100"; std::cout << "Enter ECG device port (default: 8080): "; std::string portStr; std::getline(std::cin, portStr); devicePort = portStr.empty() ? 8080 : std::stoi(portStr); // Initialize connection if (!dataProcessor.initializeWiFiConnection(deviceIP, devicePort)) { std::cerr << "Failed to connect to ECG device!" << std::endl; return; } running = true; std::cout << "Starting ECG monitoring..." << std::endl; std::cout << "Press 'q' to quit, 's' to save data" << std::endl; // Main monitoring loop auto lastDisplayTime = std::chrono::steady_clock::now(); while (running) { // Receive data if (dataProcessor.receiveECGData()) { auto currentTime = std::chrono::steady_clock::now(); auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - lastDisplayTime); // Update display every 100ms if (elapsed.count() >= 100) { dataProcessor.displayECGWaveform(); lastDisplayTime = currentTime; } } // Check for user input if (_kbhit()) { char key = _getch(); if (key == 'q' || key == 'Q') { running = false; } else if (key == 's' || key == 'S') { dataProcessor.saveDataToFile(); } } std::this_thread::sleep_for(std::chrono::milliseconds(10)); } dataProcessor.disconnect(); std::cout << "ECG Monitor application terminated." << std::endl; } }; int main() { ECGMonitorApp app; app.run(); return 0; } // Additional utility files: // File: ECGDataAnalyzer.h #ifndef ECGDATAANALYZER_H #define ECGDATAANALYZER_H #include <vector> #include <algorithm> class ECGDataAnalyzer { public: static double calculateAverageHeartRate(const std::vector<int>& heartRates) { if (heartRates.empty()) return 0.0; int sum = 0; for (int rate : heartRates) { sum += rate; } return static_cast<double>(sum) / heartRates.size(); } static int findMaxHeartRate(const std::vector<int>& heartRates) { if (heartRates.empty()) return 0; return *std::max_element(heartRates.begin(), heartRates.end()); } static int findMinHeartRate(const std::vector<int>& heartRates) { if (heartRates.empty()) return 0; return *std::min_element(heartRates.begin(), heartRates.end()); } static bool detectArrhythmia(const std::vector<int>& heartRates) { if (heartRates.size() < 3) return false; double variance = 0.0; double mean = calculateAverageHeartRate(heartRates); for (int rate : heartRates) { variance += (rate - mean) * (rate - mean); } variance /= heartRates.size(); // High variance may indicate arrhythmia return variance > 100.0; } }; #endif // File: NetworkConfig.h #ifndef NETWORKCONFIG_H #define NETWORKCONFIG_H #include <string> struct NetworkConfig { std::string deviceIP; int devicePort; int timeoutMs; NetworkConfig() : deviceIP("192.168.1.100"), devicePort(8080), timeoutMs(5000) {} bool validate() const { // Simple IP validation if (deviceIP.empty()) return false; if (devicePort <= 0 || devicePort > 65535) return false; return true; } }; #endif模块代码设计#include "stm32f10x.h" // OLED屏幕相关定义 #define OLED_I2C I2C1 #define OLED_ADDRESS 0x78 // AD8232引脚定义 #define ECG_ADC ADC1 #define ECG_CHANNEL ADC_Channel_0 // 蜂鸣器和LED引脚定义 #define BUZZER_GPIO GPIOC #define BUZZER_PIN GPIO_Pin_13 #define LED_GPIO GPIOC #define LED_PIN GPIO_Pin_14 // ESP8266相关定义 #define WIFI_USART USART1 #define WIFI_USART_GPIO GPIOA #define WIFI_TX_PIN GPIO_Pin_9 #define WIFI_RX_PIN GPIO_Pin_10 // 心率阈值 #define HEART_RATE_HIGH 120 #define HEART_RATE_LOW 50 // 函数声明 void System_Init(void); void GPIO_Init(void); void ADC_Init(void); void I2C_Init(void); void USART_Init(void); void Timer_Init(void); void OLED_Init(void); void OLED_WriteCmd(uint8_t cmd); void OLED_WriteData(uint8_t data); void OLED_Clear(void); void OLED_ShowString(uint8_t x, uint8_t y, char *str); void OLED_ShowWaveform(uint16_t value); void ECG_DataProcess(void); void HeartRate_Calculate(void); void WiFi_SendData(uint16_t ecg_value, uint16_t heart_rate); void Alarm_Trigger(uint8_t state); // 全局变量 volatile uint16_t ecg_raw_value = 0; volatile uint16_t heart_rate = 0; volatile uint8_t alarm_state = 0; int main(void) { System_Init(); while(1) { ECG_DataProcess(); HeartRate_Calculate(); // 显示心电波形和心率 OLED_ShowWaveform(ecg_raw_value); OLED_ShowString(0, 0, "HR:"); OLED_ShowString(30, 0, (char*)&heart_rate); // 心率异常报警 if(heart_rate > HEART_RATE_HIGH || heart_rate < HEART_RATE_LOW) { Alarm_Trigger(1); alarm_state = 1; } else { Alarm_Trigger(0); alarm_state = 0; } // 通过WiFi发送数据 WiFi_SendData(ecg_raw_value, heart_rate); // 延时 for(volatile int i=0; i<100000; i++); } } void System_Init(void) { // 开启外设时钟 RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_ADC1EN | RCC_APB2ENR_AFIOEN; RCC->APB1ENR |= RCC_APB1ENR_I2C1EN | RCC_APB1ENR_USART2EN; GPIO_Init(); ADC_Init(); I2C_Init(); USART_Init(); Timer_Init(); OLED_Init(); } void GPIO_Init(void) { // 蜂鸣器和LED初始化 BUZZER_GPIO->CRH &= ~(GPIO_CRH_MODE13 | GPIO_CRH_CNF13); BUZZER_GPIO->CRH |= GPIO_CRH_MODE13_0; LED_GPIO->CRH &= ~(GPIO_CRH_MODE14 | GPIO_CRH_CNF14); LED_GPIO->CRH |= GPIO_CRH_MODE14_0; // ADC引脚初始化 (PA0) GPIOA->CRL &= ~GPIO_CRL_MODE0; GPIOA->CRL &= ~GPIO_CRL_CNF0; GPIOA->CRL |= GPIO_CRL_CNF0_1; // 模拟输入 // I2C引脚初始化 (PB6-SCL, PB7-SDA) GPIOB->CRL &= ~(GPIO_CRL_MODE6 | GPIO_CRL_CNF6); GPIOB->CRL |= GPIO_CRL_CNF6_1 | GPIO_CRL_MODE6_1; GPIOB->CRL &= ~(GPIO_CRL_MODE7 | GPIO_CRL_CNF7); GPIOB->CRL |= GPIO_CRL_CNF7_1 | GPIO_CRL_MODE7_1; // USART引脚初始化 (PA9-TX, PA10-RX) WIFI_USART_GPIO->CRH &= ~(GPIO_CRH_MODE9 | GPIO_CRH_CNF9); WIFI_USART_GPIO->CRH |= GPIO_CRH_MODE9_1 | GPIO_CRH_CNF9_1; WIFI_USART_GPIO->CRH &= ~(GPIO_CRH_MODE10 | GPIO_CRH_CNF10); WIFI_USART_GPIO->CRH |= GPIO_CRH_CNF10_0; } void ADC_Init(void) { // ADC校准 ADC1->CR2 |= ADC_CR2_ADON; for(volatile int i=0; i<1000; i++); ADC1->CR2 |= ADC_CR2_RSTCAL; while(ADC1->CR2 & ADC_CR2_RSTCAL); ADC1->CR2 |= ADC_CR2_CAL; while(ADC1->CR2 & ADC_CR2_CAL); // ADC配置 ADC1->SQR1 = 0; ADC1->SQR2 = 0; ADC1->SQR3 = ECG_CHANNEL; ADC1->SMPR2 = 7 << (ECG_CHANNEL * 3); // 239.5周期采样时间 ADC1->CR2 |= ADC_CR2_CONT; // 连续转换模式 ADC1->CR2 |= ADC_CR2_ADON; } void I2C_Init(void) { I2C1->CR1 &= ~I2C_CR1_PE; I2C1->CR1 |= I2C_CR1_SWRST; I2C1->CR1 &= ~I2C_CR1_SWRST; I2C1->CR2 = 36; // 36MHz I2C1->CCR = 180; // 100kHz I2C1->TRISE = 37; I2C1->CR1 |= I2C_CR1_PE; } void USART_Init(void) { WIFI_USART->BRR = 0x1D4C; // 115200 @36MHz WIFI_USART->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; } void Timer_Init(void) { // 定时器2用于心率计算 RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; TIM2->PSC = 36000 - 1; // 1kHz TIM2->ARR = 1000 - 1; // 1s TIM2->DIER |= TIM_DIER_UIE; TIM2->CR1 |= TIM_CR1_CEN; NVIC_EnableIRQ(TIM2_IRQn); } void OLED_Init(void) { OLED_WriteCmd(0xAE); // 关闭显示 OLED_WriteCmd(0x20); // 内存地址模式 OLED_WriteCmd(0x10); OLED_WriteCmd(0xB0); OLED_WriteCmd(0xC8); OLED_WriteCmd(0x00); OLED_WriteCmd(0x10); OLED_WriteCmd(0x40); OLED_WriteCmd(0x81); OLED_WriteCmd(0xFF); OLED_WriteCmd(0xA1); OLED_WriteCmd(0xA6); OLED_WriteCmd(0xA8); OLED_WriteCmd(0x3F); OLED_WriteCmd(0xA4); OLED_WriteCmd(0xD3); OLED_WriteCmd(0x00); OLED_WriteCmd(0xD5); OLED_WriteCmd(0xF0); OLED_WriteCmd(0xD9); OLED_WriteCmd(0x22); OLED_WriteCmd(0xDA); OLED_WriteCmd(0x12); OLED_WriteCmd(0xDB); OLED_WriteCmd(0x20); OLED_WriteCmd(0x8D); OLED_WriteCmd(0x14); OLED_WriteCmd(0xAF); // 开启显示 OLED_Clear(); } void OLED_WriteCmd(uint8_t cmd) { while(I2C1->SR2 & I2C_SR2_BUSY); I2C1->CR1 |= I2C_CR1_START; while(!(I2C1->SR1 & I2C_SR1_SB)); I2C1->DR = OLED_ADDRESS; while(!(I2C1->SR1 & I2C_SR1_ADDR)); (void)I2C1->SR2; I2C1->DR = 0x00; // 命令模式 while(!(I2C1->SR1 & I2C_SR1_TXE)); I2C1->DR = cmd; while(!(I2C1->SR1 & I2C_SR1_BTF)); I2C1->CR1 |= I2C_CR1_STOP; } void OLED_WriteData(uint8_t data) { while(I2C1->SR2 & I2C_SR2_BUSY); I2C1->CR1 |= I2C_CR1_START; while(!(I2C1->SR1 & I2C_SR1_SB)); I2C1->DR = OLED_ADDRESS; while(!(I2C1->SR1 & I2C_SR1_ADDR)); (void)I2C1->SR2; I2C1->DR = 0x40; // 数据模式 while(!(I2C1->SR1 & I2C_SR1_TXE)); I2C1->DR = data; while(!(I2C1->SR1 & I2C_SR1_BTF)); I2C1->CR1 |= I2C_CR1_STOP; } void OLED_Clear(void) { for(uint8_t page=0; page<8; page++) { OLED_WriteCmd(0xB0 + page); OLED_WriteCmd(0x00); OLED_WriteCmd(0x10); for(uint8_t col=0; col<128; col++) { OLED_WriteData(0x00); } } } void OLED_ShowString(uint8_t x, uint8_t y, char *str) { OLED_WriteCmd(0xB0 + y); OLED_WriteCmd(((x & 0xF0) >> 4) | 0x10); OLED_WriteCmd(x & 0x0F); while(*str) { OLED_WriteData(*str++); } } void OLED_ShowWaveform(uint16_t value) { static uint8_t x_pos = 0; uint8_t y_pos = (value >> 5); // 缩放到OLED高度 OLED_WriteCmd(0xB0 + (y_pos >> 3)); OLED_WriteCmd(((x_pos & 0xF0) >> 4) | 0x10); OLED_WriteCmd(x_pos & 0x0F); OLED_WriteData(1 << (y_pos & 0x07)); x_pos++; if(x_pos >= 128) x_pos = 0; } void ECG_DataProcess(void) { ADC1->CR2 |= ADC_CR2_SWSTART; while(!(ADC1->SR & ADC_SR_EOC)); ecg_raw_value = ADC1->DR; } void HeartRate_Calculate(void) { static uint16_t peak_count = 0; static uint16_t last_value = 0; // 简单的峰值检测算法 if(ecg_raw_value > 2000 && last_value <= 2000) { peak_count++; } last_value = ecg_raw_value; } void WiFi_SendData(uint16_t ecg_value, uint16_t heart_rate_val) { char buffer[50]; sprintf(buffer, "ECG:%d,HR:%d\n", ecg_value, heart_rate_val); char *p = buffer; while(*p) { WIFI_USART->DR = *p++; while(!(WIFI_USART->SR & USART_SR_TXE)); } } void Alarm_Trigger(uint8_t state) { if(state) { BUZZER_GPIO->BSRR = BUZZER_PIN; LED_GPIO->BSRR = LED_PIN; } else { BUZZER_GPIO->BRR = BUZZER_PIN; LED_GPIO->BRR = LED_PIN; } } // 定时器中断服务函数 void TIM2_IRQHandler(void) { if(TIM2->SR & TIM_SR_UIF) { TIM2->SR &= ~TIM_SR_UIF; // 每秒计算一次心率 static uint16_t peak_count = 0; heart_rate = peak_count * 60; peak_count = 0; } } 项目核心代码#include "stm32f10x.h" // 硬件模块初始化函数声明 void SystemClock_Config(void); void GPIO_Config(void); void ADC_Config(void); void I2C_Config(void); void USART_Config(void); void TIM_Config(void); // 外设驱动函数声明 void OLED_Init(void); void OLED_ShowWaveform(uint16_t value); void OLED_ShowHeartRate(uint16_t rate); void ESP8266_Init(void); void ESP8266_SendData(uint16_t ecg_data); void Buzzer_Alarm(uint8_t state); void LED_Alarm(uint8_t state); // 心率计算相关变量 #define ECG_BUFFER_SIZE 200 uint16_t ecg_buffer[ECG_BUFFER_SIZE]; uint16_t buffer_index = 0; uint16_t heart_rate = 0; uint8_t alarm_state = 0; // 心率异常阈值 #define HR_HIGH_THRESHOLD 120 #define HR_LOW_THRESHOLD 50 int main(void) { // 系统初始化 SystemClock_Config(); GPIO_Config(); ADC_Config(); I2C_Config(); USART_Config(); TIM_Config(); // 外设初始化 OLED_Init(); ESP8266_Init(); // 关闭报警 Buzzer_Alarm(0); LED_Alarm(0); // 启动定时器 TIM3->CR1 |= TIM_CR1_CEN; while(1) { // 心率计算和显示 if(buffer_index >= ECG_BUFFER_SIZE) { heart_rate = CalculateHeartRate(ecg_buffer, ECG_BUFFER_SIZE); OLED_ShowHeartRate(heart_rate); buffer_index = 0; // 心率异常检测 if(heart_rate > HR_HIGH_THRESHOLD || heart_rate < HR_LOW_THRESHOLD) { alarm_state = 1; Buzzer_Alarm(1); LED_Alarm(1); } else { alarm_state = 0; Buzzer_Alarm(0); LED_Alarm(0); } } // 空闲时进入低功耗模式 __WFI(); } } // 系统时钟配置 void SystemClock_Config(void) { // 启用HSE RCC->CR |= RCC_CR_HSEON; while(!(RCC->CR & RCC_CR_HSERDY)); // 配置PLL RCC->CFGR |= RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9; // 启用PLL RCC->CR |= RCC_CR_PLLON; while(!(RCC->CR & RCC_CR_PLLRDY)); // 切换系统时钟到PLL RCC->CFGR |= RCC_CFGR_SW_PLL; while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); } // GPIO配置 void GPIO_Config(void) { // 启用GPIO时钟 RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN; // ADC输入引脚 (PA0) GPIOA->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0); GPIOA->CRL |= GPIO_CRL_CNF0_1; // 模拟输入 // I2C引脚 (PB6-SCL, PB7-SDA) GPIOB->CRL &= ~(GPIO_CRL_MODE6 | GPIO_CRL_CNF6); GPIOB->CRL |= GPIO_CRL_CNF6_1 | GPIO_CRL_MODE6_1; // 复用开漏输出 GPIOB->CRL &= ~(GPIO_CRL_MODE7 | GPIO_CRL_CNF7); GPIOB->CRL |= GPIO_CRL_CNF7_1 | GPIO_CRL_MODE7_1; // 复用开漏输出 // USART引脚 (PA2-TX, PA3-RX) GPIOA->CRL &= ~(GPIO_CRL_MODE2 | GPIO_CRL_CNF2); GPIOA->CRL |= GPIO_CRL_CNF2_1 | GPIO_CRL_MODE2_1; // 复用推挽输出 GPIOA->CRL &= ~(GPIO_CRL_MODE3 | GPIO_CRL_CNF3); GPIOA->CRL |= GPIO_CRL_CNF3_0; // 浮空输入 // 报警引脚 (PC13-蜂鸣器, PC14-LED) GPIOC->CRH &= ~(GPIO_CRH_MODE13 | GPIO_CRH_CNF13); GPIOC->CRH |= GPIO_CRH_MODE13_1; // 输出模式,2MHz GPIOC->CRH &= ~(GPIO_CRH_MODE14 | GPIO_CRH_CNF14); GPIOC->CRH |= GPIO_CRH_MODE14_1; // 输出模式,2MHz } // ADC配置 void ADC_Config(void) { // 启用ADC1时钟 RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; // ADC校准 ADC1->CR2 |= ADC_CR2_CAL; while(ADC1->CR2 & ADC_CR2_CAL); // 配置ADC ADC1->CR1 = 0; ADC1->CR2 = ADC_CR2_CONT | ADC_CR2_EXTSEL | ADC_CR2_EXTTRIG; // 配置采样时间 ADC1->SMPR2 |= ADC_SMPR2_SMP0; // 239.5周期 // 配置通道序列 ADC1->SQR1 = 0; ADC1->SQR3 = 0; // 通道0 // 启用ADC ADC1->CR2 |= ADC_CR2_ADON; } // I2C配置 void I2C_Config(void) { // 启用I2C1时钟 RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; // 配置I2C I2C1->CR1 = 0; I2C1->CR2 = 36; // 36MHz I2C1->CCR = 180; // 100kHz I2C1->TRISE = 37; // 启用I2C I2C1->CR1 |= I2C_CR1_PE; } // USART配置 void USART_Config(void) { // 启用USART2时钟 RCC->APB1ENR |= RCC_APB1ENR_USART2EN; // 配置USART USART2->BRR = 0x1A0; // 115200 @36MHz USART2->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; } // 定时器配置 void TIM_Config(void) { // 启用TIM3时钟 RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; // 配置TIM3 (5ms中断) TIM3->PSC = 36000 - 1; // 1kHz TIM3->ARR = 5 - 1; // 5ms TIM3->DIER |= TIM_DIER_UIE; // 配置NVIC NVIC->ISER[0] |= (1 << TIM3_IRQn); NVIC->IP[TIM3_IRQn] = 0x10; } // 定时器3中断服务函数 void TIM3_IRQHandler(void) { if(TIM3->SR & TIM_SR_UIF) { TIM3->SR &= ~TIM_SR_UIF; // 启动ADC转换 ADC1->CR2 |= ADC_CR2_SWSTART; // 等待转换完成 while(!(ADC1->SR & ADC_SR_EOC)); // 读取ADC值 uint16_t adc_value = ADC1->DR; // 存储到缓冲区 if(buffer_index < ECG_BUFFER_SIZE) { ecg_buffer[buffer_index++] = adc_value; } // 显示波形 OLED_ShowWaveform(adc_value); // 发送数据到Wi-Fi模块 if(alarm_state) { ESP8266_SendData(adc_value); } } } // 心率计算函数 uint16_t CalculateHeartRate(uint16_t *data, uint16_t size) { uint16_t peaks = 0; uint16_t threshold = 2048; // ADC中间值 // 简单的峰值检测算法 for(uint16_t i = 1; i < size - 1; i++) { if(data[i] > data[i-1] && data[i] > data[i+1] && data[i] > threshold) { peaks++; } } // 计算心率 (假设采样率200Hz) return (peaks * 60 * 200) / size; } 总结本系统是一个基于STM32的便携式心电监护仪,旨在实现对用户心电信号的实时监测与分析。通过集成多种功能模块,该系统能够有效采集、处理和传输心电数据,为用户提供及时的健康反馈。在功能方面,系统通过心电采集芯片实时获取心电信号,并将波形和心率数据直观显示在OLED屏幕上。当检测到心率异常时,系统会触发本地声光报警,提醒用户注意潜在风险。同时,借助Wi-Fi模块,心电数据可上传至手机APP,便于进一步分析和远程监控。硬件方面,系统采用STM32F103C8T6作为主控芯片,结合AD8232心电采集模块、OLED显示屏、ESP8266通信模块以及蜂鸣器和LED报警组件。这些模块协同工作,确保了系统的可靠性、便携性和高效性,适用于日常健康监护场景。
  • [技术干货] 光伏板智能清洁与健康监测系统
    项目开发背景随着全球对可再生能源需求的不断增长,光伏发电作为一种清洁能源形式,正被广泛应用于各类场景中。然而,光伏板在户外长期运行过程中,容易受到灰尘、鸟粪、雨雪等污染物的覆盖,导致其表面透光率下降,进而显著降低发电效率。据统计,灰尘积累可使光伏板输出功率减少高达20%以上,这不仅造成能源浪费,还增加了维护成本。因此,开发一种高效、自动化的清洁与监测系统,成为提升光伏系统整体性能的关键需求。传统的光伏板清洁方式主要依赖人工定期清洗或简单机械装置,这些方法效率低下、成本高昂,且无法实时响应天气变化。例如,在雨天或高湿度环境下,不必要的清洁操作可能浪费资源,甚至损坏设备。同时,光伏板的老化、故障等问题往往难以及时发现,影响系统长期稳定运行。这凸显了对智能系统的迫切需求,能够结合环境感知与自动控制,实现精准、高效的维护。本项目的光伏板智能清洁与健康监测系统正是基于上述挑战而设计,它通过集成灰尘传感器和雨滴传感器,实时监测板面污染程度和天气状况,确保清洁操作只在必要时启动。系统利用步进电机驱动滚刷进行自动清洁,避免人为干预,提高效率。此外,电流传感器持续追踪光伏板输出功率,结合数据分析评估其健康状态,帮助早期识别潜在故障,延长设备寿命。通过Wi-Fi模块将清洁记录和发电数据上传至云平台,系统实现了远程监控与大数据分析,为用户提供可视化报告和预警功能。这不仅提升了光伏系统的可靠性和经济性,还推动了智能能源管理的发展,符合当前物联网技术在绿色能源领域的应用趋势。总体而言,该系统旨在解决光伏维护中的痛点,为可持续能源的高效利用提供有力支持。设计实现的功能(1) 通过灰尘传感器与雨滴传感器判断板面清洁度与天气状况。(2) 自动控制步进电机带动滚刷对板面进行清洁。(3) 通过电流传感器监测光伏板实时输出功率,评估其健康状态。(4) 将清洁记录与发电效率数据通过Wi-Fi上传至云平台。项目硬件模块组成(1)主控芯片:STM32F103C8T6单片机。(2)传感模块:雨滴传感器、GP2Y1014AU0F灰尘传感器、ACS712电流传感器。(3)执行模块:42步进电机及DM542驱动器、清洁滚刷。(4)通信模块:ESP8266 Wi-Fi模块。设计意义该系统通过集成多种传感器与执行机构,实现了光伏板清洁与健康状态的自动化管理,能够有效提升光伏发电系统的整体运行效率。自动清洁功能可减少灰尘积累对发电效率的影响,确保光伏板在最佳状态下工作,从而最大化能源产出。通过实时监测光伏板的输出功率与清洁度,系统能够及时识别性能异常或污染问题,有助于预防潜在故障并延长设备使用寿命。这种主动维护方式降低了人工巡检和清洁的频率,减少了运维成本。数据上传功能使得发电效率与清洁记录可远程访问与分析,为系统优化和决策提供支持。这增强了光伏电站的智能化管理水平,推动可再生能源设施的可持续运营。设计思路该系统设计以STM32F103C8T6单片机作为核心控制器,负责协调各模块工作,实现光伏板面的智能清洁与健康状态监测。系统通过集成多种传感器与执行器,自动判断环境条件并执行清洁操作,同时收集关键数据用于分析和记录。灰尘传感器与雨滴传感器实时检测光伏板表面的灰尘积累和天气状况,当灰尘达到一定阈值或检测到雨滴时,系统会判断需要进行清洁。这些传感器数据由主控芯片处理,以确定清洁的触发时机,确保在合适条件下启动清洁流程,避免不必要的操作。清洁功能通过42步进电机和DM542驱动器控制滚刷的运动,主控芯片根据传感器输入自动发出指令,驱动电机带动滚刷对光伏板表面进行清扫。这一过程无需人工干预,提高了清洁效率并减少了维护成本,同时确保光伏板始终保持较高透光率。健康监测部分利用ACS712电流传感器实时采集光伏板的输出电流数据,计算出实时功率输出。主控芯片分析这些数据以评估光伏板的健康状态,例如通过功率变化趋势判断是否存在性能下降或故障,从而提供早期预警和维护建议。数据通信模块采用ESP8266 Wi-Fi模块,将清洁记录和发电效率数据上传至云平台。主控芯片处理后的信息通过Wi-Fi传输,实现远程监控和数据分析,便于用户查看历史记录和系统性能,优化光伏系统的运行管理。框架图+-------------------+ +-------------------+ +-------------------+ | | | | | | | 光伏板系统 |----->| 传感模块 |----->| 主控芯片 | | | | | | STM32F103C8T6 | +-------------------+ +-------------------+ +-------------------+ | | | | +-------+-------+ +-------+-------+ | | | | +-----+-----+ +-----+-----+ +-----+-----+ +-----+-----+ | 雨滴传感器 | | 灰尘传感器 | | 执行模块 | | 通信模块 | | | | GP2Y1014 | | 步进电机 | | ESP8266 | +-----------+ +-----------+ | DM542驱动 | +-----------+ | 清洁滚刷 | +-----------+ | v +-------------------+ | | | 云平台 | | | +-------------------+ 数据流说明:传感模块(雨滴/灰尘/电流传感器)采集光伏板状态及环境数据,输入主控芯片。主控芯片处理数据后:控制执行模块(步进电机驱动滚刷)进行清洁操作。通过通信模块(Wi-Fi)将清洁记录与发电数据上传至云平台。系统总体设计光伏板智能清洁与健康监测系统以STM32F103C8T6单片机作为核心控制器,负责协调整个系统的运行。该系统通过集成多种传感器模块来实时监测光伏板的状态,其中雨滴传感器用于检测天气状况如降雨,而GP2Y1014AU0F灰尘传感器则评估板面的灰尘积累程度,从而判断清洁需求。基于传感器输入的数据,系统自动控制执行模块进行清洁操作。当检测到灰尘积累或特定天气条件时,主控芯片会驱动42步进电机通过DM542驱动器带动清洁滚刷,对光伏板表面进行均匀而高效的清扫,确保板面保持最佳透光状态以提升发电效率。同时,系统通过ACS712电流传感器持续监测光伏板的实时输出功率,以此评估其健康状态和性能变化。这些数据有助于识别潜在的故障或效率下降问题,为维护决策提供依据。所有收集到的清洁记录和发电效率数据通过ESP8266 Wi-Fi模块传输至云平台,实现远程数据存储和分析。这使用户能够实时监控系统运行状况,并基于历史数据优化清洁和维护策略。系统功能总结功能编号功能描述1通过灰尘传感器与雨滴传感器判断板面清洁度与天气状况2自动控制步进电机带动滚刷对板面进行清洁3通过电流传感器监测光伏板实时输出功率,评估其健康状态4将清洁记录与发电效率数据通过Wi-Fi上传至云平台设计的各个功能模块描述主控芯片模块采用STM32F103C8T6单片机作为系统的核心控制单元,负责协调和处理所有传感器数据,根据灰尘和雨滴传感器的输入判断光伏板表面的清洁状况和天气条件,并据此控制执行模块的运作,同时监控电流传感器数据以评估光伏板的健康状态。传感模块包括雨滴传感器、GP2Y1014AU0F灰尘传感器和ACS712电流传感器,雨滴传感器用于检测是否有降雨情况,灰尘传感器监测光伏板表面的灰尘积累水平以评估清洁度,电流传感器则实时测量光伏板的输出电流,从而计算功率并分析其发电效率与健康状态。执行模块由42步进电机、DM542驱动器和清洁滚刷组成,主控芯片根据传感模块的数据自动控制步进电机驱动滚刷在光伏板表面进行移动清洁,确保板面保持最佳状态以提升发电效率。通信模块使用ESP8266 Wi-Fi模块,将系统收集的清洁记录和发电效率数据通过无线网络上传至云平台,实现远程数据存储和监控功能。上位机代码设计#include <iostream> #include <string> #include <vector> #include <thread> #include <chrono> #include <fstream> #include <sstream> #include <iomanip> #include <winsock2.h> #include <windows.h> #pragma comment(lib, "ws2_32.lib") class PVMonitoringSystem { private: SOCKET clientSocket; bool isConnected; std::string cloudPlatformIP; int cloudPlatformPort; struct SensorData { double dustLevel; double rainLevel; double currentOutput; double efficiency; std::string timestamp; bool cleaningStatus; }; std::vector<SensorData> dataHistory; public: PVMonitoringSystem() : isConnected(false), cloudPlatformIP("192.168.1.100"), cloudPlatformPort(8080) { WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { std::cerr << "WSAStartup failed" << std::endl; } } ~PVMonitoringSystem() { disconnectFromCloud(); WSACleanup(); } bool connectToCloud() { clientSocket = socket(AF_INET, SOCK_STREAM, 0); if (clientSocket == INVALID_SOCKET) { std::cerr << "Socket creation failed" << std::endl; return false; } sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(cloudPlatformPort); serverAddr.sin_addr.s_addr = inet_addr(cloudPlatformIP.c_str()); if (connect(clientSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) { std::cerr << "Connection to cloud platform failed" << std::endl; closesocket(clientSocket); return false; } isConnected = true; std::cout << "Connected to cloud platform successfully" << std::endl; return true; } void disconnectFromCloud() { if (isConnected) { closesocket(clientSocket); isConnected = false; std::cout << "Disconnected from cloud platform" << std::endl; } } std::string getCurrentTimestamp() { auto now = std::chrono::system_clock::now(); auto time_t = std::chrono::system_clock::to_time_t(now); std::stringstream ss; ss << std::put_time(std::localtime(&time_t), "%Y-%m-%d %H:%M:%S"); return ss.str(); } SensorData readSensorData() { SensorData data; // 模拟传感器数据读取 data.dustLevel = (rand() % 1000) / 10.0; // 0-100 μg/m3 data.rainLevel = (rand() % 100) / 10.0; // 0-10 mm data.currentOutput = (rand() % 10000) / 10.0; // 0-1000 A data.efficiency = (rand() % 10000) / 100.0; // 0-100% data.timestamp = getCurrentTimestamp(); data.cleaningStatus = (data.dustLevel > 50.0 && data.rainLevel < 1.0); return data; } void controlCleaningSystem(bool shouldClean) { if (shouldClean) { std::cout << "启动清洁系统:步进电机带动滚刷清洁光伏板..." << std::endl; // 模拟清洁过程 std::this_thread::sleep_for(std::chrono::seconds(5)); std::cout << "清洁完成" << std::endl; } else { std::cout << "当前无需清洁" << std::endl; } } void assessHealthStatus(const SensorData& data) { std::cout << "=== 光伏板健康状态评估 ===" << std::endl; std::cout << "时间: " << data.timestamp << std::endl; std::cout << "灰尘浓度: " << data.dustLevel << " μg/m3" << std::endl; std::cout << "降雨量: " << data.rainLevel << " mm" << std::endl; std::cout << "输出电流: " << data.currentOutput << " A" << std::endl; std::cout << "发电效率: " << data.efficiency << "%" << std::endl; if (data.efficiency > 80.0) { std::cout << "状态: 优秀" << std::endl; } else if (data.efficiency > 60.0) { std::cout << "状态: 良好" << std::endl; } else if (data.efficiency > 40.0) { std::cout << "状态: 一般" << std::endl; } else { std::cout << "状态: 需要维护" << std::endl; } std::cout << "=========================" << std::endl; } std::string formatDataForCloud(const SensorData& data) { std::stringstream json; json << "{"; json << "\"timestamp\":\"" << data.timestamp << "\","; json << "\"dust_level\":" << data.dustLevel << ","; json << "\"rain_level\":" << data.rainLevel << ","; json << "\"current_output\":" << data.currentOutput << ","; json << "\"efficiency\":" << data.efficiency << ","; json << "\"cleaning_status\":" << (data.cleaningStatus ? "true" : "false"); json << "}"; return json.str(); } bool sendDataToCloud(const std::string& data) { if (!isConnected) { if (!connectToCloud()) { return false; } } int bytesSent = send(clientSocket, data.c_str(), data.length(), 0); if (bytesSent == SOCKET_ERROR) { std::cerr << "数据发送失败" << std::endl; return false; } std::cout << "数据已上传至云平台: " << data << std::endl; return true; } void saveDataToFile(const SensorData& data) { std::ofstream file("pv_monitoring_log.csv", std::ios::app); if (file.is_open()) { if (file.tellp() == 0) { file << "时间戳,灰尘浓度(μg/m3),降雨量(mm),输出电流(A),发电效率(%),清洁状态\n"; } file << data.timestamp << "," << data.dustLevel << "," << data.rainLevel << "," << data.currentOutput << "," << data.efficiency << "," << (data.cleaningStatus ? "是" : "否") << "\n"; file.close(); } } void runMonitoringCycle() { std::cout << "开始光伏板监控周期..." << std::endl; SensorData currentData = readSensorData(); dataHistory.push_back(currentData); // 显示传感器数据 std::cout << "\n当前传感器读数:" << std::endl; std::cout << "灰尘传感器: " << currentData.dustLevel << " μg/m3" << std::endl; std::cout << "雨滴传感器: " << currentData.rainLevel << " mm" << std::endl; std::cout << "电流传感器: " << currentData.currentOutput << " A" << std::endl; // 评估健康状况 assessHealthStatus(currentData); // 控制清洁系统 controlCleaningSystem(currentData.cleaningStatus); // 上传数据到云平台 std::string cloudData = formatDataForCloud(currentData); sendDataToCloud(cloudData); // 保存数据到本地文件 saveDataToFile(currentData); std::cout << "监控周期完成" << std::endl; } void displayHistoricalData() { if (dataHistory.empty()) { std::cout << "暂无历史数据" << std::endl; return; } std::cout << "\n=== 历史数据记录 ===" << std::endl; for (const auto& data : dataHistory) { std::cout << "时间: " << data.timestamp << " | 灰尘: " << data.dustLevel << " | 降雨: " << data.rainLevel << " | 电流: " << data.currentOutput << " | 效率: " << data.efficiency << "%" << " | 清洁: " << (data.cleaningStatus ? "是" : "否") << std::endl; } } }; int main() { PVMonitoringSystem monitoringSystem; std::cout << "光伏板智能清洁与健康监测系统 - 上位机软件" << std::endl; std::cout << "========================================" << std::endl; // 连接到云平台 if (monitoringSystem.connectToCloud()) { std::cout << "云平台连接成功" << std::endl; } else { std::cout << "云平台连接失败,将在本地模式下运行" << std::endl; } int choice; do { std::cout << "\n请选择操作:" << std::endl; std::cout << "1. 执行监控周期" << std::endl; std::cout << "2. 查看历史数据" << std::endl; std::cout << "3. 退出系统" << std::endl; std::cout << "输入选择 (1-3): "; std::cin >> choice; switch (choice) { case 1: monitoringSystem.runMonitoringCycle(); break; case 2: monitoringSystem.displayHistoricalData(); break; case 3: std::cout << "系统退出中..." << std::endl; break; default: std::cout << "无效选择,请重新输入" << std::endl; } // 模拟实时监控间隔 if (choice == 1) { std::this_thread::sleep_for(std::chrono::seconds(2)); } } while (choice != 3); return 0; } 模块代码设计#include "stm32f10x.h" // 引脚定义 #define DUST_SENSOR_PIN GPIO_Pin_0 #define DUST_SENSOR_PORT GPIOA #define RAIN_SENSOR_PIN GPIO_Pin_1 #define RAIN_SENSOR_PORT GPIOA #define CURRENT_SENSOR_PIN GPIO_Pin_2 #define CURRENT_SENSOR_PORT GPIOA #define STEP_PUL_PIN GPIO_Pin_0 #define STEP_DIR_PIN GPIO_Pin_1 #define STEP_PORT GPIOE #define WIFI_TX_PIN GPIO_Pin_9 #define WIFI_RX_PIN GPIO_Pin_10 #define WIFI_PORT GPIOA // 函数声明 void RCC_Configuration(void); void GPIO_Configuration(void); void ADC_Configuration(void); void USART_Configuration(void); void TIM_Configuration(void); void NVIC_Configuration(void); uint16_t Read_Dust_Sensor(void); uint16_t Read_Rain_Sensor(void); uint16_t Read_Current_Sensor(void); void StepMotor_Run(uint32_t steps, uint8_t direction); void WiFi_SendData(uint16_t dust, uint16_t rain, uint16_t current); // 系统时钟配置 void RCC_Configuration(void) { // 开启外部高速时钟 RCC->CR |= ((uint32_t)RCC_CR_HSEON); // 等待HSE就绪 while(!(RCC->CR & RCC_CR_HSERDY)); // 配置FLASH预取指缓存 FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY_2; // HCLK = SYSCLK RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; // PCLK2 = HCLK RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; // PCLK1 = HCLK/2 RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2; // 配置PLL: HSE * 9 = 72 MHz RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL)); RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9); // 开启PLL RCC->CR |= RCC_CR_PLLON; // 等待PLL就绪 while((RCC->CR & RCC_CR_PLLRDY) == 0); // 选择PLL作为系统时钟 RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL; // 等待时钟切换完成 while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08); // 开启GPIO时钟 RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPEEN | RCC_APB2ENR_AFIOEN; // 开启ADC1时钟 RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; // 开启USART1时钟 RCC->APB2ENR |= RCC_APB2ENR_USART1EN; // 开启TIM2时钟 RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; } // GPIO配置 void GPIO_Configuration(void) { // 配置ADC引脚为模拟输入 GPIOA->CRL &= ~(GPIO_CRL_CNF0 | GPIO_CRL_CNF1 | GPIO_CRL_CNF2); GPIOA->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_MODE1 | GPIO_CRL_MODE2); // 配置步进电机引脚为推挽输出 GPIOE->CRL &= ~(GPIO_CRL_CNF0 | GPIO_CRL_CNF1); GPIOE->CRL |= (GPIO_CRL_MODE0 | GPIO_CRL_MODE1); // 配置WiFi模块引脚 // PA9 (TX) - 复用推挽输出 GPIOA->CRH &= ~(GPIO_CRH_CNF9 | GPIO_CRH_MODE9); GPIOA->CRH |= (GPIO_CRH_CNF9_1 | GPIO_CRH_MODE9); // PA10 (RX) - 浮空输入 GPIOA->CRH &= ~(GPIO_CRH_CNF10 | GPIO_CRH_MODE10); GPIOA->CRH |= GPIO_CRH_CNF10_0; } // ADC配置 void ADC_Configuration(void) { // 复位ADC1 ADC1->CR2 = 0; // 设置ADC工作在独立模式 ADC1->CR1 = 0; // 设置数据对齐方式为右对齐 ADC1->CR2 &= ~ADC_CR2_ALIGN; // 设置单次转换模式 ADC1->CR2 &= ~ADC_CR2_CONT; // 开启ADC ADC1->CR2 |= ADC_CR2_ADON; // 等待ADC稳定 for(volatile int i=0; i<1000; i++); } // USART配置 void USART_Configuration(void) { // 复位USART1 USART1->CR1 = 0; USART1->CR2 = 0; USART1->CR3 = 0; // 设置波特率为115200 // 72MHz / (16 * 115200) = 39.0625 USART1->BRR = (39 << 4) | 1; // 39.0625 // 使能发送和接收 USART1->CR1 |= USART_CR1_TE | USART_CR1_RE; // 使能USART1 USART1->CR1 |= USART_CR1_UE; } // 定时器配置 void TIM_Configuration(void) { // 配置TIM2用于步进电机脉冲生成 // 72MHz / (719 + 1) = 100kHz TIM2->PSC = 719; TIM2->ARR = 99; // 1kHz脉冲频率 // 向上计数模式 TIM2->CR1 &= ~TIM_CR1_DIR; // 使能TIM2更新中断 TIM2->DIER |= TIM_DIER_UIE; } // NVIC配置 void NVIC_Configuration(void) { // 设置TIM2中断优先级 NVIC->IP[TIM2_IRQn] = 0x10; // 使能TIM2中断 NVIC->ISER[TIM2_IRQn >> 5] = (1 << (TIM2_IRQn & 0x1F)); } // 读取灰尘传感器 uint16_t Read_Dust_Sensor(void) { // 设置通道0 ADC1->SQR3 = 0; // 启动转换 ADC1->CR2 |= ADC_CR2_ADON; // 等待转换完成 while(!(ADC1->SR & ADC_SR_EOC)); return ADC1->DR; } // 读取雨滴传感器 uint16_t Read_Rain_Sensor(void) { // 设置通道1 ADC1->SQR3 = 1; // 启动转换 ADC1->CR2 |= ADC_CR2_ADON; // 等待转换完成 while(!(ADC1->SR & ADC_SR_EOC)); return ADC1->DR; } // 读取电流传感器 uint16_t Read_Current_Sensor(void) { // 设置通道2 ADC1->SQR3 = 2; // 启动转换 ADC1->CR2 |= ADC_CR2_ADON; // 等待转换完成 while(!(ADC1->SR & ADC_SR_EOC)); return ADC1->DR; } // 步进电机运行 volatile uint32_t step_count = 0; volatile uint32_t target_steps = 0; volatile uint8_t motor_direction = 0; void StepMotor_Run(uint32_t steps, uint8_t direction) { target_steps = steps; motor_direction = direction; step_count = 0; // 设置方向 if(direction) STEP_PORT->BSRR = STEP_DIR_PIN; else STEP_PORT->BRR = STEP_DIR_PIN; // 启动定时器 TIM2->CR1 |= TIM_CR1_CEN; } // WiFi发送数据 void WiFi_SendData(uint16_t dust, uint16_t rain, uint16_t current) { char buffer[64]; // 构造JSON格式数据 sprintf(buffer, "{\"dust\":%d,\"rain\":%d,\"current\":%d}\r\n", dust, rain, current); // 发送数据 for(int i=0; buffer[i] != '\0'; i++) { while(!(USART1->SR & USART_SR_TXE)); USART1->DR = buffer[i]; } } // TIM2中断服务函数 void TIM2_IRQHandler(void) { if(TIM2->SR & TIM_SR_UIF) { TIM2->SR &= ~TIM_SR_UIF; // 生成步进脉冲 STEP_PORT->ODR ^= STEP_PUL_PIN; if(STEP_PORT->ODR & STEP_PUL_PIN) { step_count++; if(step_count >= target_steps) { TIM2->CR1 &= ~TIM_CR1_CEN; // 停止定时器 } } } } // 主函数 int main(void) { // 系统初始化 RCC_Configuration(); GPIO_Configuration(); ADC_Configuration(); USART_Configuration(); TIM_Configuration(); NVIC_Configuration(); uint16_t dust_value, rain_value, current_value; while(1) { // 读取传感器数据 dust_value = Read_Dust_Sensor(); rain_value = Read_Rain_Sensor(); current_value = Read_Current_Sensor(); // 判断是否需要清洁 if(dust_value > 2000 && rain_value < 1000) // 灰尘多且无雨 { StepMotor_Run(1000, 1); // 运行1000步,正向 } // 发送数据到云平台 WiFi_SendData(dust_value, rain_value, current_value); // 延时5秒 for(volatile int i=0; i<5000000; i++); } } 项目核心代码#include "stm32f10x.h" // 定义传感器和电机控制引脚 #define RAIN_SENSOR_PIN GPIO_Pin_0 // PA0 #define DUST_ADC_CHANNEL 1 // PA1, ADC1 Channel1 #define CURRENT_ADC_CHANNEL 2 // PA2, ADC1 Channel2 #define STEP_PIN GPIO_Pin_5 // PA5 #define DIR_PIN GPIO_Pin_6 // PA6 // 定义阈值 #define DUST_THRESHOLD 1000 // 灰尘阈值 #define RAIN_THRESHOLD 500 // 雨滴阈值 #define CURRENT_THRESHOLD 100 // 电流异常阈值 // 全局变量 volatile uint32_t dust_value = 0; volatile uint32_t rain_value = 0; volatile uint32_t current_value = 0; volatile uint8_t clean_flag = 0; // 函数声明 void SystemClock_Config(void); void GPIO_Config(void); void ADC_Config(void); void USART_Config(void); void Stepper_Motor_Control(uint8_t direction, uint32_t steps); uint32_t Read_ADC(uint8_t channel); void Read_Sensors(void); void WiFi_Send_Data(const char* data); void Delay_ms(uint32_t ms); int main(void) { // 系统初始化 SystemClock_Config(); GPIO_Config(); ADC_Config(); USART_Config(); // 初始化步进电机 GPIOA->ODR &= ~(STEP_PIN | DIR_PIN); // 初始状态低电平 while (1) { // 读取传感器数据 Read_Sensors(); // 判断是否需要清洁:灰尘超过阈值且无雨 if ((dust_value > DUST_THRESHOLD) && (rain_value < RAIN_THRESHOLD)) { clean_flag = 1; Stepper_Motor_Control(1, 1000); // 正向清洁 Stepper_Motor_Control(0, 1000); // 反向复位 clean_flag = 0; } // 监测电流异常 if (current_value < CURRENT_THRESHOLD) { // 发电效率异常处理 char alert_data[50]; sprintf(alert_data, "ALERT:Low current:%lu", current_value); WiFi_Send_Data(alert_data); } // 上传数据到云平台 char upload_data[100]; sprintf(upload_data, "Dust:%lu,Rain:%lu,Current:%lu,Clean:%u", dust_value, rain_value, current_value, clean_flag); WiFi_Send_Data(upload_data); Delay_ms(5000); // 5秒间隔 } } void SystemClock_Config(void) { // 启用HSE并配置PLL输出72MHz系统时钟 RCC->CR |= RCC_CR_HSEON; while (!(RCC->CR & RCC_CR_HSERDY)); RCC->CFGR |= RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9; 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_ADC1EN | RCC_APB2ENR_USART1EN; } void GPIO_Config(void) { // 配置PA0为输入(雨滴传感器) GPIOA->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0); GPIOA->CRL |= GPIO_CRL_CNF0_1; // 浮空输入 // 配置PA1、PA2为模拟输入(灰尘和电流传感器) GPIOA->CRL &= ~(GPIO_CRL_MODE1 | GPIO_CRL_CNF1); GPIOA->CRL &= ~(GPIO_CRL_MODE2 | GPIO_CRL_CNF2); // 配置PA5、PA6为推挽输出(步进电机控制) GPIOA->CRL &= ~(GPIO_CRL_MODE5 | GPIO_CRL_CNF5); GPIOA->CRL |= GPIO_CRL_MODE5_0; // 输出模式,最大速度10MHz GPIOA->CRL &= ~(GPIO_CRL_MODE6 | GPIO_CRL_CNF6); GPIOA->CRL |= GPIO_CRL_MODE6_0; // 配置PA9为复用推挽输出(USART1 TX) GPIOA->CRH &= ~(GPIO_CRH_MODE9 | GPIO_CRH_CNF9); GPIOA->CRH |= GPIO_CRH_MODE9_1 | GPIO_CRH_CNF9_1; // 配置PA10为浮空输入(USART1 RX) GPIOA->CRH &= ~(GPIO_CRH_MODE10 | GPIO_CRH_CNF10); GPIOA->CRH |= GPIO_CRH_CNF10_1; } void ADC_Config(void) { // 配置ADC1 ADC1->CR2 |= ADC_CR2_ADON; // 开启ADC ADC1->CR2 |= ADC_CR2_CAL; // 开始校准 while (ADC1->CR2 & ADC_CR2_CAL); ADC1->CR2 |= ADC_CR2_CONT; // 连续转换模式 ADC1->CR2 |= ADC_CR2_EXTTRIG; // 启用外部触发 ADC1->CR2 |= ADC_CR2_EXTSEL; // 软件触发 ADC1->SQR1 |= (1 << 20); // 1个转换在序列中 } void USART_Config(void) { // 配置USART1 USART1->BRR = 0x1D4C; // 72MHz/9600 = 7500 (0x1D4C) USART1->CR1 |= USART_CR1_UE | USART_CR1_TE; // 启用USART和发送 } void Stepper_Motor_Control(uint8_t direction, uint32_t steps) { // 设置方向 if (direction) { GPIOA->BSRR = DIR_PIN; // 设置方向引脚高 } else { GPIOA->BRR = DIR_PIN; // 设置方向引脚低 } // 生成步进脉冲 for (uint32_t i = 0; i < steps; i++) { GPIOA->BSRR = STEP_PIN; // 步进引脚高 Delay_ms(1); GPIOA->BRR = STEP_PIN; // 步进引脚低 Delay_ms(1); } } uint32_t Read_ADC(uint8_t channel) { ADC1->SQR3 = channel; // 设置转换通道 ADC1->CR2 |= ADC_CR2_SWSTART; // 开始转换 while (!(ADC1->SR & ADC_SR_EOC)); // 等待转换完成 return ADC1->DR; // 返回转换结果 } void Read_Sensors(void) { // 读取雨滴传感器(数字输入) rain_value = (GPIOA->IDR & RAIN_SENSOR_PIN) ? 0 : 1024; // 假设雨滴时低电平 // 读取灰尘传感器 dust_value = Read_ADC(DUST_ADC_CHANNEL); // 读取电流传感器 current_value = Read_ADC(CURRENT_ADC_CHANNEL); } void WiFi_Send_Data(const char* data) { // 通过USART1发送数据到ESP8266 while (*data) { while (!(USART1->SR & USART_SR_TXE)); // 等待发送缓冲区空 USART1->DR = *data++; } while (!(USART1->SR & USART_SR_TC)); // 等待发送完成 } void Delay_ms(uint32_t ms) { // 简单延时函数 for (uint32_t i = 0; i < ms * 8000; i++) { __asm__("nop"); } } 总结光伏板智能清洁与健康监测系统通过智能化手段实现了对光伏板的高效维护与实时监控,有效提升了发电效率并延长了设备使用寿命。该系统集成了多种传感器与执行机构,能够自动判断板面清洁度与天气状况,并执行精准的清洁操作。在功能实现上,系统利用灰尘和雨滴传感器检测环境参数,自动控制步进电机带动滚刷进行清洁,同时通过电流传感器监测光伏板输出功率,评估其健康状态。所有数据通过Wi-Fi模块上传至云平台,便于远程分析与记录。硬件方面,系统以STM32F103C8T6单片机为核心,搭配雨滴、灰尘和电流传感器采集信息,并通过步进电机与驱动器执行清洁任务。通信模块ESP8266确保了数据的稳定传输,构建了一个完整的物联网应用框架。总体而言,这一系统不仅实现了光伏板的自动化清洁与健康评估,还通过云平台实现了数据的集中管理,为光伏发电系统的智能化运维提供了可靠支持。
  • [技术干货] 光伏板智能除尘系统
    项目开发背景光伏能源作为清洁可再生能源的重要组成部分,在全球能源转型中扮演着关键角色。光伏板通过将太阳能转化为电能,为家庭、工业和商业应用提供可持续的电力来源。然而,光伏板通常安装在户外环境中,长期暴露于自然条件下,容易受到灰尘、沙尘、鸟粪等污染物的积累,这会显著降低其发电效率。灰尘在光伏板表面的沉积会阻挡阳光的照射,导致光能转化率下降,进而影响整个光伏系统的输出功率。研究表明,灰尘积累可使光伏板效率降低高达20%以上,尤其在干旱或多风地区,这一问题更为突出。这不仅造成能源浪费,还可能缩短设备寿命,增加维护成本。传统的清洁方法主要依赖人工清洗或固定式清洁设备,但这些方式往往效率低下、成本高昂,且无法及时响应环境变化。例如,人工清洗需要定期安排人员操作,在偏远或大规模光伏电站中难以实施;而固定清洁设备可能无法适应不同天气条件,如突发的降雨或风沙天气。因此,开发一种智能除尘系统显得尤为必要。该系统通过集成传感器和自动化控制技术,能够实时监测光伏板表面的灰尘状况,并在检测到污染超标或下雨时自动启动清洁机制。这不仅提升了清洁的及时性和准确性,还减少了人力干预,降低了运营成本。本项目的智能除尘系统旨在通过创新的硬件和软件设计,实现高效、低耗的自动化清洁,从而帮助光伏系统维持最佳性能,推动可再生能源的广泛应用。通过结合环境感知与智能决策,该系统有望在各类光伏应用中发挥重要作用,为可持续发展贡献力量。设计实现的功能(1)实现通过灰尘传感器检测光伏板表面灰尘积累程度的功能。(2)实现当灰尘值超过预设阈值或雨滴传感器检测到下雨时,自动启动清洁机构的功能。(3)实现控制系统驱动28BYJ-48步进电机带动毛刷在光伏板表面进行清扫的功能。(4)实现手动控制模式,并可记录和显示清洁次数的功能。项目硬件模块组成(1) 主控芯片:Arduino Uno R3开发板。(2) 传感模块:雨滴传感器、灰尘传感器。(3) 执行模块:28BYJ-48步进电机及驱动板、直流毛刷电机。(4) 电源模块:12V适配器或直接从光伏板取电。设计意义光伏板智能除尘系统的设计意义在于有效提升光伏发电系统的整体性能。通过自动检测灰尘积累并启动清洁,该系统能够确保光伏板表面保持清洁,从而最大化光能吸收效率,提高发电量输出,减少因灰尘遮挡导致的能量损失。该系统采用自动化控制,减少了对人工清洁的依赖,降低了维护成本和人力资源投入。智能响应机制在检测到灰尘超标或下雨时自动工作,确保清洁及时且高效,提升了系统的可靠性和实用性。通过记录清洁次数和提供手动控制选项,系统便于用户监控和维护,有助于延长光伏板的使用寿命,并优化设备管理。整体设计基于实际需求,增强了光伏发电系统的可持续性和经济性。设计思路光伏板智能除尘系统的设计思路基于Arduino Uno R3开发板作为核心控制器,通过集成传感器和执行模块实现自动化清洁。系统首先利用灰尘传感器实时监测光伏板表面的灰尘积累程度,同时雨滴传感器检测是否下雨,所有传感器数据由Arduino Uno进行处理和分析。当灰尘传感器检测到的数值超过预设阈值或雨滴传感器检测到下雨时,系统会自动触发清洁机制。Arduino Uno通过驱动板控制28BYJ-48步进电机,带动毛刷在光伏板表面进行往复清扫,同时直流毛刷电机提供旋转动力,确保灰尘被有效清除。系统还设计有手动控制模式,用户可以通过外部开关或指令手动启动清洁过程,方便在需要时进行干预。Arduino Uno会记录每次清洁的次数,并将数据存储在内部存储器中,便于后续查询和维护。电源模块采用12V适配器或直接从光伏板取电的方式,为整个系统提供稳定电力,确保在户外环境下可靠运行。所有硬件模块通过Arduino Uno协调工作,实现高效、低功耗的除尘功能。框架图系统框架图:+-------------------------+ | 电源模块 | | (12V适配器或光伏板) | +-------------------------+ | v (供电) +-------------------------+ | Arduino Uno R3 | | (主控芯片,处理逻辑和记录)| +-------------------------+ | | (读取传感器数据) v +-------------------------+ | 传感模块 | | - 灰尘传感器 | | - 雨滴传感器 | +-------------------------+ | | (传感器数据输入) v +-------------------------+ | Arduino Uno R3 | | (判断阈值,触发清洁动作) | +-------------------------+ | | (发送控制信号) v +-------------------------+ | 执行模块 | | - 步进电机驱动板 | --> 步进电机 (28BYJ-48) | - 直流电机驱动 | --> 直流毛刷电机 +-------------------------+ | | (用户输入) v +-------------------------+ | 手动控制模式 | | (按钮,用于手动启动) | +-------------------------+ 说明:电源模块为整个系统供电。Arduino Uno R3作为主控,接收传感器数据,判断是否启动清洁,并控制执行模块。传感模块检测灰尘和雨水,输入到Arduino。执行模块由步进电机和毛刷电机组成,进行清扫动作。手动控制模式允许用户干预清洁过程。清洁次数记录由Arduino内部处理。系统总体设计光伏板智能除尘系统的总体设计旨在通过自动化方式清除光伏板表面的灰尘,以维持其发电效率。系统以Arduino Uno R3开发板作为核心控制器,负责协调各个模块的运行,确保高效可靠的清洁操作。系统通过传感模块实时监测光伏板表面的状态,灰尘传感器用于检测灰尘积累程度,而雨滴传感器则检测是否下雨。这些传感器将采集的数据传输给主控芯片,为自动控制提供依据。当灰尘值超过预设阈值或检测到下雨时,系统会自动启动清洁机构。主控芯片根据传感器数据判断条件满足后,发出指令驱动执行模块,实现无人值守的智能响应。执行模块包括28BYJ-48步进电机及驱动板,以及直流毛刷电机。步进电机负责带动毛刷在光伏板表面进行往复清扫动作,而直流毛刷电机提供旋转动力,确保灰尘被有效清除。整个清扫过程由控制系统精确控制,避免过度磨损。系统还具备手动控制模式,用户可以根据需要随时启动清洁操作。同时,系统会记录每次清洁的次数,便于维护和数据分析,确保长期运行的可靠性。电源模块为系统提供稳定电力支持,可选择12V适配器供电或直接从光伏板取电。这种设计保证了系统在不同环境下的灵活性和可持续性,无需频繁外部干预。系统功能总结序号功能名称功能描述相关硬件模块1灰尘检测通过灰尘传感器检测光伏板表面灰尘积累程度灰尘传感器2雨滴检测通过雨滴传感器检测是否下雨雨滴传感器3自动清洁决策当灰尘值超过阈值或检测到下雨时,自动决定启动清洁机构主控芯片(Arduino Uno R3)4清洁执行控制系统驱动步进电机,带动毛刷在光伏板表面进行清扫28BYJ-48步进电机及驱动板、直流毛刷电机5手动控制系统具备手动控制模式,用户可手动启动清洁过程主控芯片(Arduino Uno R3)6清洁记录系统记录并存储清洁次数主控芯片(Arduino Uno R3)7电源管理为系统提供电力,通过12V适配器或直接从光伏板取电电源模块设计的各个功能模块描述主控芯片模块基于Arduino Uno R3开发板,负责处理整个系统的控制逻辑。它接收来自传感器的数据,判断灰尘积累是否超过预设阈值或是否检测到下雨,并根据这些条件自动启动清洁流程。此外,该模块支持手动控制模式,允许用户主动触发清洁操作,并内置计数器记录清洁次数,确保系统运行可靠且易于管理。传感模块包括雨滴传感器和灰尘传感器,用于实时监测光伏板表面的环境状态。灰尘传感器检测灰尘积累程度,并将数据反馈给主控芯片;雨滴传感器识别下雨事件,当检测到降水或灰尘值超标时,系统会触发清洁机制,确保光伏板保持高效运行状态。执行模块由28BYJ-48步进电机及驱动板、直流毛刷电机组成,负责执行清扫动作。主控芯片驱动步进电机,带动毛刷在光伏板表面进行往复清扫,清除积累的灰尘;直流毛刷电机提供旋转动力,增强清洁效果,确保光伏板表面洁净,提升发电效率。电源模块采用12V适配器或直接从光伏板取电的方式,为整个系统提供稳定电力供应。它确保主控芯片、传感器和执行模块正常运行,适应户外环境需求,保证系统在多种条件下可靠工作。上位机代码设计#include <QApplication> #include <QMainWindow> #include <QVBoxLayout> #include <QHBoxLayout> #include <QPushButton> #include <QLabel> #include <QLineEdit> #include <QTextEdit> #include <QSerialPort> #include <QSerialPortInfo> #include <QTimer> #include <QMessageBox> #include <QGroupBox> #include <QProgressBar> class SolarPanelCleaner : public QMainWindow { Q_OBJECT public: SolarPanelCleaner(QWidget *parent = nullptr) : QMainWindow(parent) { setupUI(); setupSerialPort(); setupConnections(); } ~SolarPanelCleaner() { if(serialPort->isOpen()) serialPort->close(); } private slots: void connectToDevice() { serialPort->setPortName(portComboBox->currentText()); serialPort->setBaudRate(QSerialPort::Baud9600); serialPort->setDataBits(QSerialPort::Data8); serialPort->setParity(QSerialPort::NoParity); serialPort->setStopBits(QSerialPort::OneStop); serialPort->setFlowControl(QSerialPort::NoFlowControl); if(serialPort->open(QIODevice::ReadWrite)) { statusLabel->setText("已连接到: " + portComboBox->currentText()); connectButton->setEnabled(false); disconnectButton->setEnabled(true); logTextEdit->append("系统启动成功,连接到设备"); } else { QMessageBox::critical(this, "连接错误", "无法连接到设备"); } } void disconnectFromDevice() { serialPort->close(); statusLabel->setText("未连接"); connectButton->setEnabled(true); disconnectButton->setEnabled(false); logTextEdit->append("断开设备连接"); } void readData() { QByteArray data = serialPort->readAll(); QString receivedData = QString::fromUtf8(data); if(!receivedData.isEmpty()) { processReceivedData(receivedData); logTextEdit->append("接收: " + receivedData.trimmed()); } } void sendManualClean() { if(serialPort->isOpen()) { QString command = "CLEAN_MANUAL\n"; serialPort->write(command.toUtf8()); logTextEdit->append("发送: " + command.trimmed()); } } void setThreshold() { if(serialPort->isOpen()) { QString threshold = thresholdEdit->text(); QString command = "SET_THRESHOLD:" + threshold + "\n"; serialPort->write(command.toUtf8()); logTextEdit->append("发送: " + command.trimmed()); } } void requestStatus() { if(serialPort->isOpen()) { QString command = "GET_STATUS\n"; serialPort->write(command.toUtf8()); logTextEdit->append("发送: " + command.trimmed()); } } private: void setupUI() { setWindowTitle("光伏板智能除尘系统"); setMinimumSize(800, 600); QWidget *centralWidget = new QWidget(this); setCentralWidget(centralWidget); QVBoxLayout *mainLayout = new QVBoxLayout(centralWidget); // 连接设置区域 QGroupBox *connectionGroup = new QGroupBox("设备连接"); QHBoxLayout *connectionLayout = new QHBoxLayout(connectionGroup); portComboBox = new QComboBox(); connectButton = new QPushButton("连接"); disconnectButton = new QPushButton("断开"); disconnectButton->setEnabled(false); statusLabel = new QLabel("未连接"); connectionLayout->addWidget(new QLabel("端口:")); connectionLayout->addWidget(portComboBox); connectionLayout->addWidget(connectButton); connectionLayout->addWidget(disconnectButton); connectionLayout->addWidget(statusLabel); connectionLayout->addStretch(); // 状态显示区域 QGroupBox *statusGroup = new QGroupBox("系统状态"); QGridLayout *statusLayout = new QGridLayout(statusGroup); dustLabel = new QLabel("0"); rainLabel = new QLabel("否"); cleanCountLabel = new QLabel("0"); motorStatusLabel = new QLabel("停止"); dustProgressBar = new QProgressBar(); dustProgressBar->setRange(0, 100); dustProgressBar->setValue(0); statusLayout->addWidget(new QLabel("灰尘浓度:"), 0, 0); statusLayout->addWidget(dustLabel, 0, 1); statusLayout->addWidget(dustProgressBar, 0, 2); statusLayout->addWidget(new QLabel("降雨检测:"), 1, 0); statusLayout->addWidget(rainLabel, 1, 1); statusLayout->addWidget(new QLabel("清洁次数:"), 2, 0); statusLayout->addWidget(cleanCountLabel, 2, 1); statusLayout->addWidget(new QLabel("电机状态:"), 3, 0); statusLayout->addWidget(motorStatusLabel, 3, 1); // 控制区域 QGroupBox *controlGroup = new QGroupBox("系统控制"); QHBoxLayout *controlLayout = new QHBoxLayout(controlGroup); manualCleanButton = new QPushButton("手动清洁"); statusButton = new QPushButton("刷新状态"); thresholdEdit = new QLineEdit(); thresholdEdit->setPlaceholderText("灰尘阈值"); setThresholdButton = new QPushButton("设置阈值"); controlLayout->addWidget(manualCleanButton); controlLayout->addWidget(statusButton); controlLayout->addWidget(thresholdEdit); controlLayout->addWidget(setThresholdButton); // 日志区域 QGroupBox *logGroup = new QGroupBox("系统日志"); QVBoxLayout *logLayout = new QVBoxLayout(logGroup); logTextEdit = new QTextEdit(); logTextEdit->setReadOnly(true); logLayout->addWidget(logTextEdit); // 添加到主布局 mainLayout->addWidget(connectionGroup); mainLayout->addWidget(statusGroup); mainLayout->addWidget(controlGroup); mainLayout->addWidget(logGroup); // 刷新可用串口 refreshPorts(); } void setupSerialPort() { serialPort = new QSerialPort(this); connect(serialPort, &QSerialPort::readyRead, this, &SolarPanelCleaner::readData); } void setupConnections() { connect(connectButton, &QPushButton::clicked, this, &SolarPanelCleaner::connectToDevice); connect(disconnectButton, &QPushButton::clicked, this, &SolarPanelCleaner::disconnectFromDevice); connect(manualCleanButton, &QPushButton::clicked, this, &SolarPanelCleaner::sendManualClean); connect(statusButton, &QPushButton::clicked, this, &SolarPanelCleaner::requestStatus); connect(setThresholdButton, &QPushButton::clicked, this, &SolarPanelCleaner::setThreshold); } void refreshPorts() { portComboBox->clear(); QList<QSerialPortInfo> ports = QSerialPortInfo::availablePorts(); for(const QSerialPortInfo &port : ports) { portComboBox->addItem(port.portName()); } } void processReceivedData(const QString &data) { QStringList lines = data.split('\n', Qt::SkipEmptyParts); for(const QString &line : lines) { if(line.startsWith("DUST:")) { QString dustValue = line.mid(5); dustLabel->setText(dustValue); dustProgressBar->setValue(dustValue.toInt()); } else if(line.startsWith("RAIN:")) { QString rainStatus = line.mid(5); rainLabel->setText(rainStatus == "1" ? "是" : "否"); } else if(line.startsWith("COUNT:")) { QString count = line.mid(6); cleanCountLabel->setText(count); } else if(line.startsWith("MOTOR:")) { QString motorStatus = line.mid(6); motorStatusLabel->setText(motorStatus); } } } // UI组件 QComboBox *portComboBox; QPushButton *connectButton; QPushButton *disconnectButton; QLabel *statusLabel; QLabel *dustLabel; QLabel *rainLabel; QLabel *cleanCountLabel; QLabel *motorStatusLabel; QProgressBar *dustProgressBar; QPushButton *manualCleanButton; QPushButton *statusButton; QLineEdit *thresholdEdit; QPushButton *setThresholdButton; QTextEdit *logTextEdit; QSerialPort *serialPort; }; int main(int argc, char *argv[]) { QApplication app(argc, argv); SolarPanelCleaner window; window.show(); return app.exec(); } #include "main.moc" 这个上位机程序使用Qt框架开发,提供了以下功能:设备连接管理:自动检测可用串口连接/断开设备控制状态监控:实时显示灰尘浓度(数值和进度条)降雨检测状态清洁次数统计电机运行状态系统控制:手动启动清洁功能设置灰尘阈值刷新系统状态日志记录:显示所有发送和接收的数据系统操作记录程序通过串口与Arduino Uno R3通信,使用简单的文本协议进行数据交换。模块代码设计#include "stm32f10x.h" // 硬件引脚定义 #define RAIN_SENSOR_PIN GPIO_Pin_0 // PA0 雨滴传感器(数字输入) #define DUST_SENSOR_PIN GPIO_Pin_1 // PA1 灰尘传感器(ADC输入) #define STEPPER_IN1 GPIO_Pin_5 // PA5 步进电机线圈1 #define STEPPER_IN2 GPIO_Pin_6 // PA6 步进电机线圈2 #define STEPPER_IN3 GPIO_Pin_7 // PA7 步进电机线圈3 #define STEPPER_IN4 GPIO_Pin_8 // PA8 步进电机线圈4 #define DC_MOTOR_PIN GPIO_Pin_2 // PA2 直流电机PWM输出 #define MANUAL_BUTTON_PIN GPIO_Pin_3 // PA3 手动按钮(带上拉) // 系统参数 #define DUST_THRESHOLD 2000 // 灰尘阈值(12位ADC值) #define CLEAN_CYCLE_STEPS 512 // 完整清洁周期步数 #define STEP_DELAY_MS 2 // 步进电机步间延迟 // 全局变量 volatile uint32_t clean_count = 0; // 清洁次数计数器 uint8_t stepper_sequence[4] = {0x09, 0x0C, 0x06, 0x03}; // 步进电机全步序列 // 系统时钟初始化 void RCC_Configuration(void) { // 开启GPIOA时钟 RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; // 开启ADC1时钟 RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; // 开启TIM2时钟(PWM) RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; } // GPIO初始化 void GPIO_Configuration(void) { // PA0(雨滴传感器) - 浮空输入 GPIOA->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0); GPIOA->CRL |= GPIO_CRL_CNF0_1; // PA1(灰尘传感器) - 模拟输入 GPIOA->CRL &= ~(GPIO_CRL_MODE1 | GPIO_CRL_CNF1); GPIOA->CRL |= GPIO_CRL_CNF1_0; // PA5-PA8(步进电机) - 推挽输出 50MHz GPIOA->CRL &= ~(GPIO_CRL_MODE5 | GPIO_CRL_CNF5); GPIOA->CRL |= GPIO_CRL_MODE5_0; GPIOA->CRL &= ~(GPIO_CRL_MODE6 | GPIO_CRL_CNF6); GPIOA->CRL |= GPIO_CRL_MODE6_0; GPIOA->CRL &= ~(GPIO_CRL_MODE7 | GPIO_CRL_CNF7); GPIOA->CRL |= GPIO_CRL_MODE7_0; GPIOA->CRH &= ~(GPIO_CRH_MODE8 | GPIO_CRH_CNF8); GPIOA->CRH |= GPIO_CRH_MODE8_0; // PA2(直流电机PWM) - 复用推挽输出 GPIOA->CRL &= ~(GPIO_CRL_MODE2 | GPIO_CRL_CNF2); GPIOA->CRL |= GPIO_CRL_MODE2_1 | GPIO_CRL_CNF2_1; // PA3(手动按钮) - 上拉输入 GPIOA->CRL &= ~(GPIO_CRL_MODE3 | GPIO_CRL_CNF3); GPIOA->CRL |= GPIO_CRL_CNF3_1; GPIOA->ODR |= MANUAL_BUTTON_PIN; // 使能上拉 } // ADC初始化 void ADC_Configuration(void) { // 配置ADC1 ADC1->CR2 = ADC_CR2_ADON; // 开启ADC ADC1->SQR1 = 0; // 1个转换 ADC1->SQR3 = 1; // 通道1(PA1) ADC1->SMPR2 = ADC_SMPR2_SMP1_2 | ADC_SMPR2_SMP1_1 | ADC_SMPR2_SMP1_0; // 239.5周期采样时间 // 校准ADC ADC1->CR2 |= ADC_CR2_RSTCAL; while(ADC1->CR2 & ADC_CR2_RSTCAL); ADC1->CR2 |= ADC_CR2_CAL; while(ADC1->CR2 & ADC_CR2_CAL); } // 定时器2 PWM初始化 void TIM2_Configuration(void) { // 72MHz/720 = 100kHz TIM2->PSC = 720 - 1; // 预分频器 TIM2->ARR = 1000 - 1; // 自动重装载值(周期=10ms) TIM2->CCR3 = 500; // 占空比50%(初始值) TIM2->CCMR2 = TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3PE; // PWM模式1 TIM2->CCER |= TIM_CCER_CC3E; // 开启通道3 TIM2->CR1 = TIM_CR1_ARPE; // 自动重装载预装载使能 TIM2->EGR = TIM_EGR_UG; // 产生更新事件 TIM2->CR1 |= TIM_CR1_CEN; // 启动定时器 } // 读取雨滴传感器状态 uint8_t Read_Rain_Sensor(void) { return (GPIOA->IDR & RAIN_SENSOR_PIN) ? 0 : 1; // 低电平有效 } // 读取灰尘传感器值 uint16_t Read_Dust_Sensor(void) { ADC1->CR2 |= ADC_CR2_ADON; while(!(ADC1->SR & ADC_SR_EOC)); // 等待转换完成 return ADC1->DR; } // 步进电机单步运动 void Stepper_Step(uint8_t step) { GPIOA->ODR &= ~(STEPPER_IN1 | STEPPER_IN2 | STEPPER_IN3 | STEPPER_IN4); // 清空所有线圈 if(stepper_sequence[step] & 0x01) GPIOA->ODR |= STEPPER_IN1; if(stepper_sequence[step] & 0x02) GPIOA->ODR |= STEPPER_IN2; if(stepper_sequence[step] & 0x04) GPIOA->ODR |= STEPPER_IN3; if(stepper_sequence[step] & 0x08) GPIOA->ODR |= STEPPER_IN4; } // 步进电机旋转指定步数 void Stepper_Rotate(int32_t steps) { static uint8_t current_step = 0; uint32_t i; if(steps > 0) { // 正转 for(i = 0; i < steps; i++) { Stepper_Step(current_step); current_step = (current_step + 1) % 4; for(volatile uint32_t j = 0; j < STEP_DELAY_MS * 7200; j++); // 简单延时 } } else { // 反转 for(i = 0; i < -steps; i++) { Stepper_Step(current_step); current_step = (current_step + 3) % 4; for(volatile uint32_t j = 0; j < STEP_DELAY_MS * 7200; j++); } } } // 直流电机控制 void DC_Motor_Control(uint8_t enable) { if(enable) { TIM2->CCR3 = 700; // 70%占空比 } else { TIM2->CCR3 = 0; // 停止 } } // 执行清洁周期 void Execute_Cleaning_Cycle(void) { DC_Motor_Control(1); // 启动直流毛刷电机 Stepper_Rotate(CLEAN_CYCLE_STEPS); // 前进清洁 Stepper_Rotate(-CLEAN_CYCLE_STEPS); // 返回原位 DC_Motor_Control(0); // 停止直流电机 clean_count++; // 增加清洁计数 } // 读取手动按钮状态 uint8_t Read_Manual_Button(void) { return (GPIOA->IDR & MANUAL_BUTTON_PIN) ? 0 : 1; // 低电平有效 } // 系统初始化 void System_Init(void) { RCC_Configuration(); GPIO_Configuration(); ADC_Configuration(); TIM2_Configuration(); } // 主函数 int main(void) { System_Init(); while(1) { // 读取传感器数据 uint8_t rain_detected = Read_Rain_Sensor(); uint16_t dust_value = Read_Dust_Sensor(); uint8_t manual_trigger = Read_Manual_Button(); // 检查清洁条件:灰尘超标、下雨或手动触发 if(dust_value > DUST_THRESHOLD || rain_detected || manual_trigger) { Execute_Cleaning_Cycle(); // 简单防抖延时 for(volatile uint32_t i = 0; i < 0xFFFFF; i++); } // 空闲延时 for(volatile uint32_t i = 0; i < 0x7FFFF; i++); } } 项目核心代码#include "stm32f10x.h" // 定义引脚 #define DUST_SENSOR_PIN GPIO_Pin_0 // PA0 - 灰尘传感器 #define RAIN_SENSOR_PIN GPIO_Pin_1 // PA1 - 雨滴传感器 #define MOTOR_EN_PIN GPIO_Pin_2 // PA2 - 毛刷电机使能 #define STEP1_PIN GPIO_Pin_3 // PA3 - 步进电机相位1 #define STEP2_PIN GPIO_Pin_4 // PA4 - 步进电机相位2 #define STEP3_PIN GPIO_Pin_5 // PA5 - 步进电机相位3 #define STEP4_PIN GPIO_Pin_6 // PA6 - 步进电机相位4 #define MANUAL_BTN_PIN GPIO_Pin_7 // PA7 - 手动按钮 // 阈值定义 #define DUST_THRESHOLD 500 // 灰尘阈值 #define ADC_MAX_VALUE 4095 // 12位ADC最大值 // 全局变量 volatile uint32_t clean_count = 0; // 清洁次数计数 volatile uint8_t auto_mode = 1; // 自动模式标志 volatile uint8_t cleaning = 0; // 清洁进行中标志 // 函数声明 void RCC_Configuration(void); void GPIO_Configuration(void); void ADC_Configuration(void); void TIM_Configuration(void); void NVIC_Configuration(void); uint16_t Read_Dust_Sensor(void); uint8_t Read_Rain_Sensor(void); uint8_t Read_Manual_Button(void); void Start_Cleaning(void); void Stop_Cleaning(void); void Step_Motor_Run(uint16_t steps); void Delay_ms(uint32_t nTime); int main(void) { // 系统初始化 RCC_Configuration(); GPIO_Configuration(); ADC_Configuration(); TIM_Configuration(); NVIC_Configuration(); uint16_t dust_value = 0; uint8_t rain_detected = 0; uint8_t manual_trigger = 0; while(1) { if(auto_mode) { // 自动模式:读取传感器数据 dust_value = Read_Dust_Sensor(); rain_detected = Read_Rain_Sensor(); // 判断是否需要清洁 if((dust_value > DUST_THRESHOLD) || rain_detected) { if(!cleaning) { Start_Cleaning(); } } } else { // 手动模式:检测按钮 manual_trigger = Read_Manual_Button(); if(manual_trigger && !cleaning) { Start_Cleaning(); } } Delay_ms(100); // 100ms检测周期 } } // 时钟配置 void RCC_Configuration(void) { // 开启GPIOA时钟 RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; // 开启ADC1时钟 RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; // 开启TIM2时钟 RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; } // GPIO配置 void GPIO_Configuration(void) { // 配置PA0为模拟输入(灰尘传感器) GPIOA->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0); GPIOA->CRL |= GPIO_CRL_CNF0_1; // 配置PA1为输入上拉(雨滴传感器) GPIOA->CRL &= ~(GPIO_CRL_MODE1 | GPIO_CRL_CNF1); GPIOA->CRL |= GPIO_CRL_CNF1_1; GPIOA->BSRR = GPIO_BSRR_BS1; // 配置PA7为输入上拉(手动按钮) GPIOA->CRL &= ~(GPIO_CRL_MODE7 | GPIO_CRL_CNF7); GPIOA->CRL |= GPIO_CRL_CNF7_1; GPIOA->BSRR = GPIO_BSRR_BS7; // 配置PA2-PA6为推挽输出 GPIOA->CRL &= ~(GPIO_CRL_MODE2 | GPIO_CRL_CNF2 | GPIO_CRL_MODE3 | GPIO_CRL_CNF3 | GPIO_CRL_MODE4 | GPIO_CRL_CNF4 | GPIO_CRL_MODE5 | GPIO_CRL_CNF5 | GPIO_CRL_MODE6 | GPIO_CRL_CNF6); GPIOA->CRL |= (GPIO_CRL_MODE2_0 | GPIO_CRL_CNF2_0 | GPIO_CRL_MODE3_0 | GPIO_CRL_CNF3_0 | GPIO_CRL_MODE4_0 | GPIO_CRL_CNF4_0 | GPIO_CRL_MODE5_0 | GPIO_CRL_CNF5_0 | GPIO_CRL_MODE6_0 | GPIO_CRL_CNF6_0); } // ADC配置 void ADC_Configuration(void) { // 校准ADC ADC1->CR2 |= ADC_CR2_CAL; while(ADC1->CR2 & ADC_CR2_CAL); // 设置单次转换模式 ADC1->CR2 &= ~ADC_CR2_CONT; // 设置通道0采样时间 ADC1->SMPR2 &= ~ADC_SMPR2_SMP0; ADC1->SMPR2 |= ADC_SMPR2_SMP0_1; // 13.5周期 // 开启ADC ADC1->CR2 |= ADC_CR2_ADON; Delay_ms(1); } // 定时器配置 void TIM_Configuration(void) { // 配置TIM2为1ms定时 TIM2->PSC = 7200 - 1; // 72MHz/7200 = 10kHz TIM2->ARR = 10 - 1; // 10kHz/10 = 1kHz (1ms) TIM2->CR1 |= TIM_CR1_ARPE; TIM2->EGR |= TIM_EGR_UG; TIM2->CR1 |= TIM_CR1_CEN; } // 中断配置 void NVIC_Configuration(void) { NVIC->ISER[0] |= (1 << TIM2_IRQn); // 使能TIM2中断 TIM2->DIER |= TIM_DIER_UIE; // 使能更新中断 } // 读取灰尘传感器值 uint16_t Read_Dust_Sensor(void) { ADC1->SQR3 = 0; // 选择通道0 ADC1->CR2 |= ADC_CR2_ADON; // 启动转换 ADC1->CR2 |= ADC_CR2_SWSTART; while(!(ADC1->SR & ADC_SR_EOC)); return ADC1->DR; } // 读取雨滴传感器 uint8_t Read_Rain_Sensor(void) { return (GPIOA->IDR & RAIN_SENSOR_PIN) ? 0 : 1; } // 读取手动按钮 uint8_t Read_Manual_Button(void) { static uint8_t last_state = 1; uint8_t current_state = (GPIOA->IDR & MANUAL_BTN_PIN) ? 1 : 0; // 检测下降沿 if(last_state && !current_state) { last_state = current_state; return 1; } last_state = current_state; return 0; } // 开始清洁 void Start_Cleaning(void) { cleaning = 1; // 启动毛刷电机 GPIOA->BSRR = MOTOR_EN_PIN; // 步进电机运行(来回清扫) Step_Motor_Run(200); // 前进200步 Step_Motor_Run(200); // 后退200步 // 停止毛刷电机 GPIOA->BRR = MOTOR_EN_PIN; clean_count++; // 增加清洁计数 cleaning = 0; } // 步进电机运行 void Step_Motor_Run(uint16_t steps) { uint8_t step_sequence[4] = {0x09, 0x0C, 0x06, 0x03}; // 4相8拍序列 uint16_t i, j; for(i = 0; i < steps; i++) { for(j = 0; j < 4; j++) { // 设置步进电机相位 GPIOA->ODR &= ~(STEP1_PIN | STEP2_PIN | STEP3_PIN | STEP4_PIN); GPIOA->ODR |= (step_sequence[j] << 3); // 从PA3开始 Delay_ms(5); // 步进电机速度控制 } } } // 延时函数(基于TIM2) void Delay_ms(uint32_t nTime) { volatile uint32_t start_time = TIM2->CNT; while((TIM2->CNT - start_time) < nTime); } // TIM2中断服务函数 void TIM2_IRQHandler(void) { if(TIM2->SR & TIM_SR_UIF) { TIM2->SR &= ~TIM_SR_UIF; // 定时器中断处理 } } 总结光伏板智能除尘系统通过智能化的方式自动维护光伏板表面的清洁,从而提升发电效率。该系统利用传感器实时监测灰尘积累和降雨情况,当检测到灰尘值超过预设阈值或下雨时,会自动触发清洁机制,确保光伏板始终处于最佳工作状态。在硬件实现上,系统以Arduino Uno R3开发板为核心控制器,结合雨滴传感器和灰尘传感器进行环境数据采集。执行模块包括28BYJ-48步进电机及其驱动板,以及直流毛刷电机,共同协作完成清扫动作,确保清洁过程高效且精准。此外,系统还提供了手动控制模式,允许用户根据需要随时启动清洁,并能记录清洁次数,便于后期维护和分析。电源模块支持12V适配器或直接从光伏板取电,增强了系统的灵活性和实用性,适用于各种户外环境。
  • [技术干货] AI垃圾分类助手
    项目开发背景随着全球城市化进程的加速和人口的增长,垃圾产量持续上升,给环境带来了巨大压力。传统的垃圾分类方法主要依赖人工操作,效率低下且容易出错,导致大量可回收资源被浪费,有害物质未能妥善处理,加剧了环境污染和资源短缺问题。因此,开发一种高效、自动化的垃圾分类系统成为迫切需求,以提升垃圾处理效率,促进可持续发展。人工智能技术的快速发展为垃圾分类提供了新的解决方案。通过计算机视觉和深度学习模型,系统能够自动识别垃圾类型,从而实现精准分类。然而,现有的AI应用多依赖于高性能计算设备,成本高昂且难以在资源受限的嵌入式环境中部署。本项目旨在探索轻量级AI模型在嵌入式端的应用,以实现低成本、高效率的实时垃圾分类,推动智能环保技术的普及。本项目基于嵌入式系统设计,集成了图像采集、AI识别和执行控制功能,能够通过摄像头实时捕捉垃圾图像,利用轻量级模型进行分析,并驱动机械臂完成分类操作。这种一体化的设计不仅减少了人力依赖,还提高了分类准确性和响应速度。此外,系统支持模型再训练功能,可灵活适应不同地区的垃圾分类标准,便于未来扩展和优化。在硬件选择上,项目采用Raspberry Pi Pico作为主控芯片,其支持MicroPython编程,适合轻量级AI模型的部署;OV7670摄像头模块负责图像采集,SG90舵机构建机械臂执行分类动作,1.44寸TFT LCD屏幕用于实时显示结果。这些组件的组合确保了系统的可靠性、低功耗和易用性,为智能垃圾分类的推广提供了实用基础。设计实现的功能(1) 通过OV7670摄像头模块采集垃圾图像。(2) 在Raspberry Pi Pico上运行轻量级AI模型,识别垃圾类型(如可回收、厨余、有害)。(3) 通过1.44寸TFT LCD屏幕显示识别结果,并控制SG90舵机驱动的机械臂将垃圾推入对应垃圾桶。(4) 支持模型再训练功能,以增加新的垃圾种类。项目硬件模块组成(1)主控芯片:Raspberry Pi Pico(2)视觉模块:OV7670摄像头模块(3)执行模块:SG90舵机(多个)(4)显示模块:1.44寸TFT LCD屏幕设计意义该设计通过自动化垃圾分类过程,显著提升了垃圾处理的效率和准确性,有助于减少人为错误分类造成的环境污染,促进资源回收和可持续发展,在实际应用中能够为社区或家庭提供便捷的垃圾分类解决方案。系统采用轻量级AI模型在嵌入式设备上运行,体现了边缘计算的优势,降低了对外部网络的依赖,提高了实时处理能力,并为低成本智能设备的开发提供了可行范例,推动了人工智能技术在日常生活场景中的普及和应用。基于开源硬件和模块化组件,该设计具备良好的教育价值,可作为学习平台帮助用户深入理解人工智能、计算机视觉和机器人控制等关键技术,培养实践能力和创新思维。支持模型再训练的功能增强了系统的适应性和灵活性,使其能够随着垃圾种类变化或新标准出台而更新,确保长期实用价值,并为未来扩展更多功能奠定了基础。设计思路该系统设计以Raspberry Pi Pico作为核心主控,结合OV7670摄像头采集垃圾图像,在嵌入式端部署轻量级AI模型进行实时分类识别。通过1.44寸TFT LCD屏幕显示识别结果,并驱动多个SG90舵机构建的机械臂执行垃圾分拣动作,将垃圾推入对应类别的垃圾桶。整体流程注重实际硬件限制,确保高效运行和可扩展性。图像采集部分使用OV7670摄像头模块,通过MicroPython编程控制图像捕捉,将采集到的垃圾图像数据进行预处理,如调整分辨率和格式转换,以适配后续AI模型输入要求。这一步骤需优化图像质量,减少环境光干扰,确保识别准确性。AI模型部署在Raspberry Pi Pico上,采用轻量级神经网络架构,例如基于TensorFlow Lite Micro的预训练模型,实现垃圾类型分类,包括可回收、厨余和有害等类别。模型推理过程利用Pico的有限计算资源,通过量化或剪枝技术降低复杂度,保证实时响应。识别结果以概率形式输出,用于后续控制决策。结果显示和控制环节通过1.44寸TFT LCD屏幕实时展示垃圾分类结果和系统状态,同时主控芯片根据识别类型生成PWM信号,驱动SG90舵机机械臂运动。机械臂设计需考虑多自由度动作,将垃圾精准推入指定垃圾桶,执行模块的协调通过MicroPython脚本实现,确保稳定性和重复精度。模型再训练功能支持系统扩展,允许用户通过外部接口(如USB连接)上传新数据集,在PC端进行模型重训练后,将更新后的轻量模型部署到Pico中。这一过程依赖于模块化设计,便于添加新垃圾种类,同时保持嵌入式端的低资源消耗,无需改动核心硬件。框架图+---------------------------------------------+ | AI垃圾分类助手系统框架图 | +---------------------------------------------+ +----------------+ +---------------------+ +-------------------+ | 视觉模块 | | 主控芯片 | | 显示模块 | | OV7670摄像头 |---->| Raspberry Pi Pico |---->| 1.44寸TFT LCD | | (图像采集) | | (运行轻量级AI模型) | | (显示识别结果) | +----------------+ +---------------------+ +-------------------+ | | (控制信号) v +-------------------+ | 执行模块 | | SG90舵机机械臂 | | (推入对应垃圾桶) | +-------------------+ +-------------------------------------------------+ | 模型再训练支持 | | (通过USB/串口更新模型,增加垃圾种类) | +-------------------------------------------------+ 系统总体设计系统总体设计围绕一个基于嵌入式平台的智能垃圾分类装置展开,该系统通过集成硬件模块和轻量级AI模型实现垃圾的自动识别与分类处理。系统以Raspberry Pi Pico作为主控芯片,负责协调各模块的运行,并利用MicroPython环境进行程序开发,确保高效处理图像数据和控制执行机构。整体工作流程始于摄像头采集垃圾图像,经AI模型分析后输出分类结果,再通过屏幕显示信息并驱动机械臂完成垃圾投放。在硬件架构中,视觉模块采用OV7670摄像头,负责实时捕捉垃圾图像,并将数据传输至主控芯片进行处理。执行模块由多个SG90舵机构成的机械臂组成,根据识别结果精确控制舵机运动,将垃圾推入对应的分类垃圾桶。显示模块使用1.44寸TFT LCD屏幕,用于直观展示垃圾类型和系统状态,提供用户反馈。软件部分的核心是嵌入式端运行的轻量级AI模型,该模型针对垃圾类型进行识别,包括可回收、厨余和有害等类别。模型设计考虑了资源限制,确保在Raspberry Pi Pico上高效执行推理过程,同时支持后续的再训练功能,允许用户通过更新数据集来扩展识别种类,以适应新的垃圾分类需求。系统操作流程从图像采集开始,摄像头获取垃圾图片后,主控芯片调用AI模型进行实时分析,识别结果立即显示在LCD屏幕上。随后,主控芯片生成控制信号,驱动舵机机械臂移动,将垃圾准确推入指定垃圾桶,完成整个分类任务。整个过程强调低功耗和实时性,确保在嵌入式环境中稳定运行。此外,系统支持模型再训练机制,用户可以通过连接外部设备上传新数据,重新训练AI模型以增加垃圾种类识别能力。这增强了系统的适应性和可扩展性,使其能够应对垃圾分类标准的变化,而无需更换硬件组件。整体设计注重实用性和模块化,各部件协同工作,实现高效的自动化垃圾分类。系统功能总结系统功能描述图像采集通过OV7670摄像头模块实时采集垃圾图像,用于后续分析。垃圾类型识别在Raspberry Pi Pico主控芯片上运行轻量级AI模型,自动识别垃圾类型(如可回收、厨余、有害等)。结果反馈与执行使用1.44寸TFT LCD屏幕显示识别结果,并通过SG90舵机驱动机械臂将垃圾推入对应垃圾桶,实现自动分拣。模型再训练支持模型再训练功能,允许用户增加新的垃圾种类,以提升系统的适应性和扩展性。设计的各个功能模块描述图像采集模块使用OV7670摄像头模块连接到Raspberry Pi Pico主控芯片,通过摄像头实时采集垃圾图像,为后续识别处理提供视觉输入数据。AI识别模块在Raspberry Pi Pico上运行轻量级AI模型,对采集到的图像进行分析和处理,识别垃圾的具体类型,例如可回收、厨余或有害垃圾,并输出分类结果。显示模块通过1.44寸TFT LCD屏幕实时展示AI识别的垃圾类型和分类信息,方便用户直观查看系统运行状态和结果。执行模块利用多个SG90舵机构建的机械臂,根据AI识别结果控制舵机动作,精确地将垃圾推入对应的分类垃圾桶中,实现自动化分拣。模型再训练模块支持用户对现有AI模型进行更新和再训练,通过添加新数据来扩展识别垃圾种类,提高系统的适应性和准确性。上位机代码设计#include <opencv2/opencv.hpp> #include <opencv2/dnn.hpp> #include <iostream> #include <vector> #include <string> #include <thread> #include <chrono> #include <wiringPi.h> #include <wiringSerial.h> // 垃圾分类定义 enum TrashType { RECYCLABLE = 0, KITCHEN_WASTE = 1, HARMFUL = 2, OTHER = 3 }; class AITrashClassifier { private: cv::dnn::Net net; cv::VideoCapture cap; std::vector<std::string> classNames; int servoPins[3]; int serialPort; public: AITrashClassifier() { // 初始化类别名称 classNames = {"可回收垃圾", "厨余垃圾", "有害垃圾", "其他垃圾"}; // 舵机控制引脚 servoPins[RECYCLABLE] = 18; // GPIO18 - 可回收垃圾桶 servoPins[KITCHEN_WASTE] = 23; // GPIO23 - 厨余垃圾桶 servoPins[HARMFUL] = 24; // GPIO24 - 有害垃圾桶 initializeCamera(); initializeModel(); initializeGPIO(); initializeSerial(); } ~AITrashClassifier() { if (cap.isOpened()) { cap.release(); } serialClose(serialPort); } private: void initializeCamera() { cap.open(0); // 打开默认摄像头 if (!cap.isOpened()) { std::cerr << "无法打开摄像头" << std::endl; return; } // 设置摄像头参数 cap.set(cv::CAP_PROP_FRAME_WIDTH, 640); cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480); cap.set(cv::CAP_PROP_FPS, 30); std::cout << "摄像头初始化成功" << std::endl; } void initializeModel() { // 加载TensorFlow Lite模型或ONNX模型 try { net = cv::dnn::readNetFromONNX("trash_classifier.onnx"); // 或者使用TensorFlow Lite // net = cv::dnn::readNetFromTensorflow("trash_classifier.tflite"); // 设置推理后端(根据硬件选择) net.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV); net.setPreferableTarget(cv::dnn::DNN_TARGET_CPU); std::cout << "AI模型加载成功" << std::endl; } catch (const std::exception& e) { std::cerr << "模型加载失败: " << e.what() << std::endl; } } void initializeGPIO() { if (wiringPiSetupGpio() == -1) { std::cerr << "GPIO初始化失败" << std::endl; return; } // 初始化舵机引脚为PWM输出 for (int i = 0; i < 3; i++) { pinMode(servoPins[i], PWM_OUTPUT); pwmWrite(servoPins[i], 0); // 初始位置 } std::cout << "GPIO初始化成功" << std::endl; } void initializeSerial() { // 初始化串口通信(用于与Pico通信) serialPort = serialOpen("/dev/ttyAMA0", 115200); if (serialPort < 0) { std::cerr << "串口初始化失败" << std::endl; } else { std::cout << "串口通信初始化成功" << std::endl; } } cv::Mat preprocessImage(const cv::Mat& frame) { cv::Mat processed; // 调整图像尺寸为模型输入尺寸 cv::resize(frame, processed, cv::Size(224, 224)); // 归一化 processed.convertTo(processed, CV_32F, 1.0/255.0); // 转换为模型需要的输入格式 cv::cvtColor(processed, processed, cv::COLOR_BGR2RGB); return processed; } TrashType classifyTrash(const cv::Mat& frame) { if (net.empty()) { std::cerr << "模型未加载" << std::endl; return OTHER; } cv::Mat inputBlob = cv::dnn::blobFromImage(frame, 1.0, cv::Size(224, 224), cv::Scalar(), true, false); net.setInput(inputBlob); cv::Mat outputs = net.forward(); // 获取预测结果 cv::Point classIdPoint; double confidence; cv::minMaxLoc(outputs.reshape(1, 1), nullptr, &confidence, nullptr, &classIdPoint); int classId = classIdPoint.x; std::cout << "检测到: " << classNames[classId] << " 置信度: " << confidence << std::endl; return static_cast<TrashType>(classId); } void controlServo(TrashType trashType) { int angle = 0; switch (trashType) { case RECYCLABLE: angle = 45; // 可回收垃圾桶角度 break; case KITCHEN_WASTE: angle = 90; // 厨余垃圾桶角度 break; case HARMFUL: angle = 135; // 有害垃圾桶角度 break; default: angle = 0; // 其他垃圾桶角度 break; } // 控制对应舵机 int pwmValue = angle * 1000 / 180 + 500; // 转换为PWM值 pwmWrite(servoPins[trashType], pwmValue); std::cout << "控制舵机 " << trashType << " 到角度: " << angle << std::endl; // 发送指令到Pico std::string command = "SERVO:" + std::to_string(trashType) + ":" + std::to_string(angle) + "\n"; serialPuts(serialPort, command.c_str()); // 等待动作完成 std::this_thread::sleep_for(std::chrono::seconds(2)); // 复位舵机 pwmWrite(servoPins[trashType], 0); } void displayResult(cv::Mat& frame, TrashType trashType, double confidence) { std::string label = classNames[trashType] + ": " + std::to_string(confidence).substr(0, 4); // 在图像上显示结果 cv::putText(frame, label, cv::Point(10, 30), cv::FONT_HERSHEY_SIMPLEX, 1.0, cv::Scalar(0, 255, 0), 2); // 显示图像 cv::imshow("AI垃圾分类助手", frame); } public: void run() { std::cout << "启动AI垃圾分类助手..." << std::endl; cv::Mat frame; while (true) { cap >> frame; if (frame.empty()) { std::cerr << "无法获取图像帧" << std::endl; break; } // 预处理图像 cv::Mat processed = preprocessImage(frame); // 分类识别 TrashType result = classifyTrash(processed); // 显示结果 displayResult(frame, result, 0.95); // 假设置信度 // 控制机械臂 controlServo(result); // 检查退出条件 char key = cv::waitKey(1); if (key == 'q' || key == 'Q') { break; } // 添加延迟避免过于频繁检测 std::this_thread::sleep_for(std::chrono::milliseconds(1000)); } } // 模型再训练接口 void retrainModel(const std::vector<std::string>& newImagePaths, const std::vector<int>& newLabels) { std::cout << "开始模型再训练..." << std::endl; // 这里应该调用训练脚本或训练函数 // 实际实现需要根据具体的训练框架 std::string command = "python3 retrain_model.py"; for (size_t i = 0; i < newImagePaths.size(); i++) { command += " " + newImagePaths[i] + " " + std::to_string(newLabels[i]); } system(command.c_str()); std::cout << "模型再训练完成" << std::endl; // 重新加载模型 initializeModel(); } }; int main() { try { AITrashClassifier classifier; classifier.run(); } catch (const std::exception& e) { std::cerr << "程序运行错误: " << e.what() << std::endl; return -1; } return 0; } 配套的CMakeLists.txt文件:cmake_minimum_required(VERSION 3.10) project(AITrashClassifier) set(CMAKE_CXX_STANDARD 11) # 查找OpenCV find_package(OpenCV REQUIRED) # 查找WiringPi find_library(WIRINGPI_LIB wiringPi) # 包含目录 include_directories(${OpenCV_INCLUDE_DIRS}) # 添加可执行文件 add_executable(ai_trash_classifier main.cpp) # 链接库 target_link_libraries(ai_trash_classifier ${OpenCV_LIBS} ${WIRINGPI_LIB} pthread) 模型再训练脚本 retrain_model.py:import tensorflow as tf import sys import os def retrain_model(new_images, new_labels): # 模型再训练逻辑 # 这里需要根据实际使用的模型框架实现 pass if __name__ == "__main__": # 解析命令行参数进行再训练 args = sys.argv[1:] new_images = args[0::2] new_labels = list(map(int, args[1::2])) retrain_model(new_images, new_labels) 模块代码设计#include <stdint.h> // STM32F103寄存器定义 #define RCC_APB2ENR (*(volatile uint32_t *)0x40021018) #define RCC_APB1ENR (*(volatile uint32_t *)0x4002101C) #define RCC_CR (*(volatile uint32_t *)0x40021000) #define RCC_CFGR (*(volatile uint32_t *)0x40021004) // GPIOA寄存器 #define GPIOA_CRL (*(volatile uint32_t *)0x40010800) #define GPIOA_CRH (*(volatile uint32_t *)0x40010804) #define GPIOA_ODR (*(volatile uint32_t *)0x4001080C) #define GPIOA_IDR (*(volatile uint32_t *)0x40010808) // GPIOB寄存器 #define GPIOB_CRL (*(volatile uint32_t *)0x40010C00) #define GPIOB_CRH (*(volatile uint32_t *)0x40010C04) #define GPIOB_ODR (*(volatile uint32_t *)0x40010C0C) #define GPIOB_IDR (*(volatile uint32_t *)0x40010C08) // I2C1寄存器 #define I2C1_CR1 (*(volatile uint32_t *)0x40005400) #define I2C1_CR2 (*(volatile uint32_t *)0x40005404) #define I2C1_OAR1 (*(volatile uint32_t *)0x40005408) #define I2C1_OAR2 (*(volatile uint32_t *)0x4000540C) #define I2C1_DR (*(volatile uint32_t *)0x40005410) #define I2C1_SR1 (*(volatile uint32_t *)0x40005414) #define I2C1_SR2 (*(volatile uint32_t *)0x40005418) #define I2C1_CCR (*(volatile uint32_t *)0x4000541C) #define I2C1_TRISE (*(volatile uint32_t *)0x40005420) // TIM2寄存器(PWM) #define TIM2_CR1 (*(volatile uint32_t *)0x40000000) #define TIM2_CR2 (*(volatile uint32_t *)0x40000004) #define TIM2_SMCR (*(volatile uint32_t *)0x40000008) #define TIM2_DIER (*(volatile uint32_t *)0x4000000C) #define TIM2_SR (*(volatile uint32_t *)0x40000010) #define TIM2_EGR (*(volatile uint32_t *)0x40000014) #define TIM2_CCMR1 (*(volatile uint32_t *)0x40000018) #define TIM2_CCER (*(volatile uint32_t *)0x40000020) #define TIM2_CNT (*(volatile uint32_t *)0x40000024) #define TIM2_PSC (*(volatile uint32_t *)0x40000028) #define TIM2_ARR (*(volatile uint32_t *)0x4000002C) #define TIM2_CCR1 (*(volatile uint32_t *)0x40000034) #define TIM2_CCR2 (*(volatile uint32_t *)0x40000038) #define TIM2_CCR3 (*(volatile uint32_t *)0x4000003C) #define TIM2_CCR4 (*(volatile uint32_t *)0x40000040) // SPI1寄存器(TFT LCD) #define SPI1_CR1 (*(volatile uint32_t *)0x40013000) #define SPI1_CR2 (*(volatile uint32_t *)0x40013004) #define SPI1_SR (*(volatile uint32_t *)0x40013008) #define SPI1_DR (*(volatile uint32_t *)0x4001300C) // 函数原型 void SystemInit(void); void GPIO_Init(void); void I2C1_Init(void); void TIM2_Init(void); void SPI1_Init(void); void OV7670_Init(void); void TFT_Init(void); void TFT_WriteCommand(uint8_t cmd); void TFT_WriteData(uint8_t data); void TFT_WriteString(char *str); void Servo_SetAngle(uint8_t servo_id, uint8_t angle); uint8_t AI_Recognize(void); void Delay(uint32_t count); // 主函数 int main(void) { SystemInit(); GPIO_Init(); I2C1_Init(); TIM2_Init(); SPI1_Init(); OV7670_Init(); TFT_Init(); TFT_WriteString("AI Garbage Classifier"); Delay(1000000); while(1) { uint8_t trash_type = AI_Recognize(); char display_str[20]; switch(trash_type) { case 0: sprintf(display_str, "Recyclable"); Servo_SetAngle(0, 45); // 舵机0推到可回收桶 break; case 1: sprintf(display_str, "Kitchen"); Servo_SetAngle(1, 45); // 舵机1推到厨余桶 break; case 2: sprintf(display_str, "Hazardous"); Servo_SetAngle(2, 45); // 舵机2推到有害桶 break; default: sprintf(display_str, "Other"); break; } TFT_WriteString(display_str); Delay(2000000); } } // 系统时钟初始化 void SystemInit(void) { RCC_CR |= (1 << 0); // 开启HSI while(!(RCC_CR & (1 << 1))); // 等待HSI就绪 RCC_CFGR = 0x00000000; // 重置时钟配置 RCC_APB2ENR |= (1 << 2) | (1 << 3) | (1 << 12); // 使能GPIOA、GPIOB、SPI1时钟 RCC_APB1ENR |= (1 << 21) | (1 << 0); // 使能I2C1、TIM2时钟 } // GPIO初始化 void GPIO_Init(void) { // I2C1引脚: PB6(SCL), PB7(SDA) GPIOB_CRL &= ~(0xFF << 24); GPIOB_CRL |= (0x0B << 24) | (0x0B << 28); // 复用开漏输出, 50MHz // SPI1引脚: PA5(SCK), PA6(MISO), PA7(MOSI) GPIOA_CRL &= ~(0xFFF << 20); GPIOA_CRL |= (0x0B << 20) | (0x0B << 24) | (0x0B << 28); // 复用推挽输出, 50MHz // TFT控制引脚: PA1(CS), PA2(DC), PA3(RST) GPIOA_CRL &= ~(0xFFF << 4); GPIOA_CRL |= (0x01 << 4) | (0x01 << 8) | (0x01 << 12); // 推挽输出, 10MHz // 舵机PWM引脚: PA0(TIM2_CH1), PA1(TIM2_CH2), PA2(TIM2_CH3) GPIOA_CRL &= ~(0xFFF << 0); GPIOA_CRL |= (0x0B << 0) | (0x0B << 4) | (0x0B << 8); // 复用推挽输出, 50MHz // OV7670并行数据引脚: PB0-PB7 GPIOB_CRL = 0x88888888; // 输入模式, 上拉/下拉 } // I2C1初始化 void I2C1_Init(void) { I2C1_CR1 &= ~(1 << 0); // 禁用I2C1 I2C1_CR2 = 36; // 设置APB1时钟频率(MHz) I2C1_CCR = 180; // 100kHz I2C时钟 I2C1_TRISE = 37; // 设置上升时间 I2C1_CR1 |= (1 << 0); // 启用I2C1 } // TIM2初始化(PWM) void TIM2_Init(void) { TIM2_CR1 &= ~(1 << 0); // 禁用TIM2 TIM2_PSC = 719; // 预分频器, 100kHz计数器时钟 TIM2_ARR = 19999; // 自动重载值, 20ms周期 TIM2_CCMR1 |= (6 << 4) | (1 << 3); // PWM模式1, 预加载使能 TIM2_CCMR2 |= (6 << 4) | (1 << 3); // 通道2 TIM2_CCMR2 |= (6 << 12) | (1 << 11); // 通道3 TIM2_CCER |= (1 << 0) | (1 << 4) | (1 << 8); // 使能通道1,2,3 TIM2_CR1 |= (1 << 0); // 启用TIM2 } // SPI1初始化 void SPI1_Init(void) { SPI1_CR1 &= ~(1 << 6); // 禁用SPI1 SPI1_CR1 = (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5); // 主模式, BR=101 (fPCLK/64) SPI1_CR2 |= (1 << 2); // 8位数据格式 SPI1_CR1 |= (1 << 6); // 启用SPI1 } // OV7670初始化 void OV7670_Init(void) { I2C1_CR1 |= (1 << 8); // 生成START条件 while(!(I2C1_SR1 & (1 << 0))); // 等待SB置位 I2C1_DR = 0x42; // OV7670写地址 while(!(I2C1_SR1 & (1 << 1))); // 等待ADDR置位 (void)I2C1_SR2; // 清除ADDR // 配置OV7670寄存器 uint8_t regs[][2] = { {0x12, 0x80}, // 复位 {0x11, 0x80}, // 时钟分频 {0x3A, 0x04}, // 数据格式 {0x40, 0xD0}, // 尺寸控制 {0x12, 0x0C} // 输出格式 }; for(int i = 0; i < 5; i++) { I2C1_DR = regs[i][0]; // 寄存器地址 while(!(I2C1_SR1 & (1 << 7))); // 等待TxE I2C1_DR = regs[i][1]; // 寄存器值 while(!(I2C1_SR1 & (1 << 7))); // 等待TxE } I2C1_CR1 |= (1 << 9); // 生成STOP条件 while(I2C1_CR1 & (1 << 9)); // 等待STOP完成 } // TFT初始化 void TFT_Init(void) { GPIOA_ODR |= (1 << 3); // 拉高RST Delay(10000); GPIOA_ODR &= ~(1 << 3); // 拉低RST Delay(10000); GPIOA_ODR |= (1 << 3); // 拉高RST Delay(10000); TFT_WriteCommand(0x11); // 睡眠退出 Delay(100000); TFT_WriteCommand(0x29); // 开启显示 } void TFT_WriteCommand(uint8_t cmd) { GPIOA_ODR &= ~(1 << 2); // DC低(命令) GPIOA_ODR &= ~(1 << 1); // CS低 while(!(SPI1_SR & (1 << 1))); // 等待TxE SPI1_DR = cmd; while(!(SPI1_SR & (1 << 0))); // 等待RXNE (void)SPI1_DR; GPIOA_ODR |= (1 << 1); // CS高 } void TFT_WriteData(uint8_t data) { GPIOA_ODR |= (1 << 2); // DC高(数据) GPIOA_ODR &= ~(1 << 1); // CS低 while(!(SPI1_SR & (1 << 1))); // 等待TxE SPI1_DR = data; while(!(SPI1_SR & (1 << 0))); // 等待RXNE (void)SPI1_DR; GPIOA_ODR |= (1 << 1); // CS高 } void TFT_WriteString(char *str) { while(*str) { TFT_WriteData(*str++); } } // 舵机控制 void Servo_SetAngle(uint8_t servo_id, uint8_t angle) { uint16_t pulse = 1000 + (angle * 1000 / 180); // 映射到1000-2000 switch(servo_id) { case 0: TIM2_CCR1 = pulse; break; case 1: TIM2_CCR2 = pulse; break; case 2: TIM2_CCR3 = pulse; break; } } // AI识别函数(示例) uint8_t AI_Recognize(void) { static uint8_t counter = 0; return counter++ % 3; // 循环返回0,1,2 } // 延时函数 void Delay(uint32_t count) { for(uint32_t i = 0; i < count; i++); } 项目核心代码#include "stm32f10x.h" #include "ov7670.h" #include "lcd.h" #include "servo.h" #include "ai_model.h" // 系统时钟初始化 void RCC_Configuration(void) { // 开启GPIO和对应外设时钟 RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_AFIOEN; // 开启SPI1时钟(用于LCD) RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; // 开启TIM2时钟(用于舵机PWM) RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; } // GPIO初始化 void GPIO_Configuration(void) { // LCD SPI引脚配置 GPIOA->CRL &= ~(GPIO_CRL_CNF5 | GPIO_CRL_CNF6 | GPIO_CRL_CNF7); GPIOA->CRL |= GPIO_CRL_MODE5 | GPIO_CRL_MODE6 | GPIO_CRL_MODE7; // 舵机PWM引脚配置(TIM2 CH1-4) GPIOA->CRL &= ~(GPIO_CRL_CNF0 | GPIO_CRL_CNF1 | GPIO_CRL_CNF2 | GPIO_CRL_CNF3); GPIOA->CRL |= (GPIO_CRL_CNF0_1 | GPIO_CRL_CNF1_1 | GPIO_CRL_CNF2_1 | GPIO_CRL_CNF3_1); GPIOA->CRL |= (GPIO_CRL_MODE0 | GPIO_CRL_MODE1 | GPIO_CRL_MODE2 | GPIO_CRL_MODE3); } // 定时器初始化(舵机PWM) void TIM_Configuration(void) { // TIM2 PWM配置 - 50Hz舵机信号 TIM2->PSC = 72 - 1; // 72MHz/72 = 1MHz TIM2->ARR = 20000 - 1; // 1MHz/20000 = 50Hz // 通道1-4 PWM模式配置 TIM2->CCMR1 |= TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2; TIM2->CCMR2 |= TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4M_2; // 使能输出比较 TIM2->CCER |= TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC3E | TIM_CCER_CC4E; TIM2->CR1 |= TIM_CR1_ARPE; TIM2->EGR |= TIM_EGR_UG; TIM2->CR1 |= TIM_CR1_CEN; } // SPI初始化(LCD显示) void SPI_Configuration(void) { SPI1->CR1 = SPI_CR1_MSTR | SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_SPE | SPI_CR1_BR_0; SPI1->CR2 = SPI_CR2_SSOE; } // 系统延时函数 void Delay_ms(uint32_t ms) { for(uint32_t i = 0; i < ms * 8000; i++); } // 控制舵机动作 void Servo_Action(uint8_t trash_type) { switch(trash_type) { case 0: // 可回收垃圾 TIM2->CCR1 = 1500; // 1.5ms脉冲 break; case 1: // 厨余垃圾 TIM2->CCR2 = 1500; break; case 2: // 有害垃圾 TIM2->CCR3 = 1500; break; case 3: // 其他垃圾 TIM2->CCR4 = 1500; break; } Delay_ms(1000); // 复位所有舵机 TIM2->CCR1 = TIM2->CCR2 = TIM2->CCR3 = TIM2->CCR4 = 0; } // LCD显示识别结果 void LCD_DisplayResult(uint8_t trash_type, float confidence) { LCD_Clear(); LCD_SetCursor(0, 0); switch(trash_type) { case 0: LCD_WriteString("Recyclable"); break; case 1: LCD_WriteString("Kitchen"); break; case 2: LCD_WriteString("Hazardous"); break; case 3: LCD_WriteString("Other"); break; default: LCD_WriteString("Unknown"); break; } LCD_SetCursor(0, 1); LCD_WriteString("Confidence:"); LCD_WriteFloat(confidence, 2); } int main(void) { uint8_t image_buffer[320*240]; // 图像缓冲区 uint8_t trash_type; float confidence; // 系统初始化 RCC_Configuration(); GPIO_Configuration(); SPI_Configuration(); TIM_Configuration(); // 外设初始化 OV7670_Init(); LCD_Init(); Servo_Init(); AI_Model_Init(); // 显示启动信息 LCD_DisplayResult(4, 0.0f); // 显示Unknown while(1) { // 采集图像 if(OV7670_Capture(image_buffer)) { // AI模型推理 if(AI_Model_Predict(image_buffer, &trash_type, &confidence)) { // 显示识别结果 LCD_DisplayResult(trash_type, confidence); // 控制舵机执行分类动作 Servo_Action(trash_type); } } Delay_ms(2000); // 2秒间隔 } } 总结本AI垃圾分类助手系统通过集成嵌入式AI技术,实现了高效的自动垃圾分类功能。系统利用摄像头采集垃圾图像,并在Raspberry Pi Pico主控芯片上运行轻量级AI模型,快速识别垃圾类型,如可回收、厨余和有害垃圾等,从而提升垃圾分类的准确性和效率。硬件配置方面,系统采用Raspberry Pi Pico作为核心控制器,支持MicroPython环境,便于AI模型的轻量化部署;OV7670摄像头模块负责图像采集,SG90舵机驱动的机械臂执行分类动作,将垃圾推入对应垃圾桶,而1.44寸TFT LCD屏幕则实时显示识别结果,确保用户交互直观便捷。此外,系统具备模型再训练能力,可灵活扩展新的垃圾种类,增强了适应性和可维护性。整体设计兼顾了成本效益和实用性,为智能环保应用提供了可靠的嵌入式解决方案,有助于推动垃圾分类的自动化进程。
  • [技术干货] 基于4G的无人机远程控制系统
    项目开发背景随着无人机技术的快速发展,其在农业监测、环境巡查、物流配送和紧急救援等领域的应用日益广泛。然而,传统无人机多依赖短距离通信方式,如Wi-Fi或无线电,限制了其在远距离或复杂地形下的操作范围与可靠性。这种局限性使得无人机难以在需要大范围覆盖或实时数据传输的场景中发挥最大效能,例如在偏远地区进行长期监测或跨城市物流任务中,通信中断风险较高,影响了整体效率与安全性。为了解决上述挑战,本项目基于4G网络设计了一套无人机远程控制系统。4G网络具有覆盖广、传输稳定和低延迟的特点,能够为无人机提供可靠的长距离通信链路,确保远程控制中心与无人机之间实时交换数据。该系统不仅支持无人机通过4G模块上传GPS定位和飞行状态信息,还能接收来自控制中心的指令,实现精准的远程操控。同时,无人机内置自主飞行功能,如定点悬停和预设航线飞行,进一步增强了其在无人干预下的适应性与实用性。在硬件层面,系统采用高性能STM32F407主控芯片,结合4G DTU模块、多传感器和动力组件,构建了一个高效稳定的飞行平台。这一设计不仅提升了无人机的数据处理与通信能力,还通过大容量电池保障了长时间任务执行。项目开发旨在推动无人机在工业与民用领域的创新应用,为远程监控、灾害响应和智能运输等场景提供更灵活的解决方案,同时降低操作成本与风险。设计实现的功能(1) 无人机通过4G网络与远程控制中心建立长距离通信链路。(2) 远程控制中心可接收无人机回传的GPS定位信息与飞行状态数据。(3) 远程控制中心可通过4G网络向无人机发送飞行控制指令。(4) 无人机具备基本的自主飞行能力,如定点悬停、航线飞行。项目硬件模块组成(1)主控芯片:STM32F407系列(高性能,带DSP指令集)。(2)通信模块:4G DTU(如SIM7600)模块。(3)飞控传感:MPU6050六轴传感器、GPS定位模块、气压计。(4)动力系统:无刷电机、电子调速器(电调)、螺旋桨。(5)电源系统:大容量锂聚合物电池。设计意义该系统通过4G网络实现无人机与远程控制中心的长距离通信,克服了传统遥控方式在距离和障碍物遮挡方面的限制,使得无人机能够在城市、乡村甚至偏远地区进行稳定操作,扩展了应用范围。这种设计特别适用于需要广域覆盖的场景,如边境巡逻、大型基础设施监测和灾害应急响应。远程控制中心能够实时接收无人机回传的GPS定位信息和飞行状态数据,包括位置、高度和姿态等,这为操作人员提供了全面的监控能力,便于及时评估任务进展和无人机健康状况。这种实时数据反馈机制增强了系统的安全性和可靠性,减少了人为误判的风险。通过4G网络向无人机发送飞行控制指令,系统实现了灵活的远程操控,支持动态调整飞行路径或执行紧急命令。这种双向通信能力使得无人机能够适应多变的任务需求,例如在搜救行动中快速响应环境变化,提高了整体任务效率。无人机具备基本的自主飞行能力,如定点悬停和航线飞行,这降低了操作人员的负担,并提升了任务的自动化水平。自主功能结合远程控制,使得系统适用于重复性或精细作业,如农业喷洒、航拍摄影和环境监测,从而优化资源利用并减少人工干预。硬件模块的集成,包括高性能主控芯片、可靠通信组件和精确传感器,确保了系统的稳定运行和长续航能力。这种设计注重实用性和耐用性,为各类行业应用提供了坚实的基础,推动无人机技术在远程控制领域的普及和发展。设计思路该系统设计基于4G网络实现无人机的远程控制,核心思路是利用4G通信模块建立无人机与远程控制中心之间的稳定长距离链路。无人机端采用STM32F407作为主控芯片,负责整体逻辑处理和数据协调,确保系统高效运行。通信模块选用4G DTU(如SIM7600),通过插入SIM卡接入移动网络,实现与控制中心的双向数据传输,包括指令下发和状态回传。无人机通过集成多种传感器实时采集飞行数据,GPS模块提供定位信息,MPU6050六轴传感器监测姿态和加速度,气压计辅助高度测量。这些数据由主控芯片整合后,通过4G网络主动发送至远程控制中心,便于操作人员监控无人机状态。控制中心可解析数据并生成飞行控制指令,如调整航向或速度,再通过4G网络下发至无人机。无人机的自主飞行功能依赖于主控芯片的算法处理,结合传感器反馈实现定点悬停和预设航线飞行。STM32F407的高性能DSP指令集支持快速数据运算,确保飞行控制的实时性和稳定性。动力系统由无刷电机、电调和螺旋桨组成,主控芯片输出PWM信号驱动电调,从而调节电机转速,实现精确的飞行姿态调整。整个系统的电源由大容量锂聚合物电池提供,需合理设计供电电路以确保各模块稳定工作,尤其是4G通信模块和动力系统的高功耗需求。硬件模块之间通过串口或SPI等接口连接,主控芯片统筹调度,实现通信、传感和动力系统的协同运作,最终达成远程控制与自主飞行的平衡。框架图+--------------------------------+ +--------------------------------+ | 无人机系统 | | 远程控制中心 | +--------------------------------+ +--------------------------------+ | | | | | [主控芯片: STM32F407] | | [控制软件] | | | | | | | | +-- [飞控传感器] | | +-- 数据处理与显示 | | | |-- MPU6050六轴传感器 | | | | | | |-- GPS定位模块 | | +-- 指令生成 | | | |-- 气压计 | | | | | | | [通信接口] | | +-- [通信模块: 4G DTU] |<---4G网络--->| 接收: GPS定位、飞行状态数据 | | | | | 发送: 飞行控制指令 | | +-- 4G网络连接 | | | | | | | | [动力系统] | +--------------------------------+ | |-- 无刷电机 | | |-- 电子调速器 (电调) | | |-- 螺旋桨 | | | | [电源系统: 锂聚合物电池] | | (为所有模块供电) | +--------------------------------+ 系统总体设计该系统基于4G网络实现无人机的远程控制,无人机与远程控制中心通过4G通信模块建立稳定的长距离连接,确保数据传输的实时性和可靠性。无人机的核心由STM32F407主控芯片负责整体协调与数据处理,该芯片的高性能特性支持复杂的飞行控制算法和传感器数据融合。硬件模块中,4G DTU模块(如SIM7600)负责网络通信,使无人机能够与远程控制中心进行双向数据交换;飞控传感器包括MPU6050六轴传感器用于姿态检测、GPS模块提供定位信息、气压计辅助高度测量,这些传感器数据被主控芯片采集并处理。动力系统由无刷电机、电子调速器和螺旋桨组成,执行飞行动作;电源系统采用大容量锂聚合物电池,为整个系统提供持续电力。在功能实现上,远程控制中心通过4G网络接收无人机回传的GPS定位信息和飞行状态数据,如位置、速度和姿态,同时可向无人机发送飞行控制指令,例如调整航向或速度。无人机具备基本的自主飞行能力,主控芯片基于传感器输入实现定点悬停和预设航线飞行,确保在无人工干预时保持稳定运行。整体上,系统通过硬件模块的紧密协作和4G网络的广覆盖,实现了远程监控与控制,满足长距离作业需求,同时自主飞行功能提升了系统的灵活性和实用性。系统功能总结编号功能描述(1)无人机通过4G网络与远程控制中心建立长距离通信链路。(2)远程控制中心可接收无人机回传的GPS定位信息与飞行状态数据。(3)远程控制中心可通过4G网络向无人机发送飞行控制指令。(4)无人机具备基本的自主飞行能力,如定点悬停、航线飞行。设计的各个功能模块描述主控芯片模块采用STM32F407系列高性能处理器,具备DSP指令集,负责整体系统的协调与控制。它处理来自传感器的数据,执行飞行控制算法,实现无人机的自主飞行功能,如定点悬停和航线飞行,同时管理通信和数据传输任务。通信模块使用4G DTU设备,例如SIM7600模块,通过4G网络建立与远程控制中心的长距离通信链路。该模块负责发送无人机回传的GPS定位信息和飞行状态数据,并接收来自控制中心的飞行控制指令,确保实时双向通信的稳定性。飞控传感模块集成了MPU6050六轴传感器、GPS定位模块和气压计,用于采集无人机的姿态、位置和高度数据。这些传感器提供准确的飞行状态信息,支持主控芯片进行实时调整,以实现稳定的飞行控制和数据回传功能。动力系统模块包括无刷电机、电子调速器和螺旋桨,负责提供无人机的推力和机动能力。电子调速器根据主控芯片的指令调节电机转速,控制飞行方向和速度,确保无人机能够执行各种飞行任务。电源系统模块采用大容量锂聚合物电池,为所有硬件组件提供可靠的电力供应。它确保系统在长时间飞行中保持稳定运行,支持通信、传感和动力模块的持续工作。上位机代码设计#include <iostream> #include <string> #include <vector> #include <thread> #include <chrono> #include <mutex> #include <atomic> #include <sstream> #include <iomanip> #include <winsock2.h> #include <ws2tcpip.h> #pragma comment(lib, "ws2_32.lib") // 无人机状态数据结构 struct DroneStatus { double latitude; double longitude; double altitude; double pitch; double roll; double yaw; double battery_level; int flight_mode; std::string timestamp; }; // 飞行控制指令 struct FlightCommand { int command_type; // 0:悬停 1:航线飞行 2:返航 3:紧急停止 double target_lat; double target_lng; double target_alt; std::vector<std::pair<double, double>> waypoints; }; class DroneControlSystem { private: SOCKET control_socket; std::atomic<bool> is_connected; std::atomic<bool> is_running; std::thread data_receive_thread; std::thread command_send_thread; std::mutex status_mutex; std::mutex command_mutex; DroneStatus current_status; FlightCommand pending_command; public: DroneControlSystem() : is_connected(false), is_running(false), control_socket(INVALID_SOCKET) { current_status = {0, 0, 0, 0, 0, 0, 100.0, 0, ""}; } ~DroneControlSystem() { disconnect(); } // 初始化网络连接 bool initialize() { WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { std::cerr << "WSAStartup failed" << std::endl; return false; } return true; } // 连接到无人机 bool connectToDrone(const std::string& server_ip, int port) { control_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (control_socket == INVALID_SOCKET) { std::cerr << "Socket creation failed" << std::endl; return false; } sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); inet_pton(AF_INET, server_ip.c_str(), &server_addr.sin_addr); if (connect(control_socket, (sockaddr*)&server_addr, sizeof(server_addr)) == SOCKET_ERROR) { std::cerr << "Connection failed" << std::endl; closesocket(control_socket); control_socket = INVALID_SOCKET; return false; } is_connected = true; is_running = true; // 启动数据接收线程 data_receive_thread = std::thread(&DroneControlSystem::dataReceiveLoop, this); // 启动指令发送线程 command_send_thread = std::thread(&DroneControlSystem::commandSendLoop, this); std::cout << "Connected to drone successfully" << std::endl; return true; } // 断开连接 void disconnect() { is_running = false; is_connected = false; if (data_receive_thread.joinable()) { data_receive_thread.join(); } if (command_send_thread.joinable()) { command_send_thread.join(); } if (control_socket != INVALID_SOCKET) { closesocket(control_socket); control_socket = INVALID_SOCKET; } WSACleanup(); std::cout << "Disconnected from drone" << std::endl; } // 发送飞行控制指令 void sendFlightCommand(const FlightCommand& cmd) { std::lock_guard<std::mutex> lock(command_mutex); pending_command = cmd; } // 获取当前无人机状态 DroneStatus getCurrentStatus() { std::lock_guard<std::mutex> lock(status_mutex); return current_status; } // 定点悬停指令 void hoverAtPosition(double lat, double lng, double alt) { FlightCommand cmd; cmd.command_type = 0; // 悬停 cmd.target_lat = lat; cmd.target_lng = lng; cmd.target_alt = alt; sendFlightCommand(cmd); std::cout << "Hover command sent: Lat=" << lat << ", Lng=" << lng << ", Alt=" << alt << std::endl; } // 航线飞行指令 void flyRoute(const std::vector<std::pair<double, double>>& waypoints, double altitude) { FlightCommand cmd; cmd.command_type = 1; // 航线飞行 cmd.target_alt = altitude; cmd.waypoints = waypoints; sendFlightCommand(cmd); std::cout << "Route flight command sent with " << waypoints.size() << " waypoints" << std::endl; } // 返航指令 void returnToHome() { FlightCommand cmd; cmd.command_type = 2; // 返航 sendFlightCommand(cmd); std::cout << "Return to home command sent" << std::endl; } // 紧急停止指令 void emergencyStop() { FlightCommand cmd; cmd.command_type = 3; // 紧急停止 sendFlightCommand(cmd); std::cout << "Emergency stop command sent" << std::endl; } // 显示无人机状态 void displayStatus() { DroneStatus status = getCurrentStatus(); std::cout << "\n=== Drone Status ===" << std::endl; std::cout << "Position: " << std::fixed << std::setprecision(6) << status.latitude << ", " << status.longitude << std::endl; std::cout << "Altitude: " << status.altitude << " m" << std::endl; std::cout << "Attitude: Pitch=" << status.pitch << "°, Roll=" << status.roll << "°, Yaw=" << status.yaw << "°" << std::endl; std::cout << "Battery: " << status.battery_level << "%" << std::endl; std::cout << "Flight Mode: " << status.flight_mode << std::endl; std::cout << "Timestamp: " << status.timestamp << std::endl; } private: // 数据接收循环 void dataReceiveLoop() { char buffer[1024]; int bytes_received; while (is_running && is_connected) { bytes_received = recv(control_socket, buffer, sizeof(buffer) - 1, 0); if (bytes_received > 0) { buffer[bytes_received] = '\0'; processReceivedData(buffer); } else if (bytes_received == 0) { std::cout << "Connection closed by drone" << std::endl; is_connected = false; break; } else { std::cerr << "Receive error: " << WSAGetLastError() << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(100)); } std::this_thread::sleep_for(std::chrono::milliseconds(50)); } } // 指令发送循环 void commandSendLoop() { while (is_running && is_connected) { std::lock_guard<std::mutex> lock(command_mutex); // 构建指令字符串 std::string command_str = buildCommandString(pending_command); if (!command_str.empty()) { send(control_socket, command_str.c_str(), command_str.length(), 0); // 清空待发送指令 pending_command = FlightCommand(); } std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } // 处理接收到的数据 void processReceivedData(const std::string& data) { // 解析无人机状态数据 // 格式: STATUS,lat,lng,alt,pitch,roll,yaw,battery,mode,timestamp if (data.find("STATUS") == 0) { std::vector<std::string> tokens; std::stringstream ss(data); std::string token; while (std::getline(ss, token, ',')) { tokens.push_back(token); } if (tokens.size() >= 10) { std::lock_guard<std::mutex> lock(status_mutex); current_status.latitude = std::stod(tokens[1]); current_status.longitude = std::stod(tokens[2]); current_status.altitude = std::stod(tokens[3]); current_status.pitch = std::stod(tokens[4]); current_status.roll = std::stod(tokens[5]); current_status.yaw = std::stod(tokens[6]); current_status.battery_level = std::stod(tokens[7]); current_status.flight_mode = std::stoi(tokens[8]); current_status.timestamp = tokens[9]; } } } // 构建指令字符串 std::string buildCommandString(const FlightCommand& cmd) { std::stringstream ss; switch (cmd.command_type) { case 0: // 悬停 ss << "HOVER," << cmd.target_lat << "," << cmd.target_lng << "," << cmd.target_alt; break; case 1: // 航线飞行 ss << "ROUTE," << cmd.target_alt; for (const auto& wp : cmd.waypoints) { ss << "," << wp.first << "," << wp.second; } break; case 2: // 返航 ss << "RTH"; break; case 3: // 紧急停止 ss << "ESTOP"; break; default: return ""; } return ss.str(); } }; // 用户界面类 class UserInterface { private: DroneControlSystem& control_system; public: UserInterface(DroneControlSystem& system) : control_system(system) {} void run() { std::cout << "=== 4G无人机远程控制系统 ===" << std::endl; std::cout << "1. 连接到无人机" << std::endl; std::cout << "2. 显示状态" << std::endl; std::cout << "3. 定点悬停" << std::endl; std::cout << "4. 航线飞行" << std::endl; std::cout << "5. 返航" << std::endl; std::cout << "6. 紧急停止" << std::endl; std::cout << "7. 断开连接" << std::endl; std::cout << "8. 退出" << std::endl; int choice; while (true) { std::cout << "\n请选择操作: "; std::cin >> choice; switch (choice) { case 1: connectToDrone(); break; case 2: control_system.displayStatus(); break; case 3: sendHoverCommand(); break; case 4: sendRouteCommand(); break; case 5: control_system.returnToHome(); break; case 6: control_system.emergencyStop(); break; case 7: control_system.disconnect(); break; case 8: return; default: std::cout << "无效选择" << std::endl; } } } private: void connectToDrone() { std::string ip; int port; std::cout << "输入无人机IP地址: "; std::cin >> ip; std::cout << "输入端口号: "; std::cin >> port; if (control_system.initialize()) { control_system.connectToDrone(ip, port); } } void sendHoverCommand() { double lat, lng, alt; std::cout << "输入目标纬度: "; std::cin >> lat; std::cout << "输入目标经度: "; std::cin >> lng; std::cout << "输入目标高度: "; std::cin >> alt; control_system.hoverAtPosition(lat, lng, alt); } void sendRouteCommand() { int num_points; std::cout << "输入航点数量: "; std::cin >> num_points; std::vector<std::pair<double, double>> waypoints; for (int i = 0; i < num_points; ++i) { double lat, lng; std::cout << "航点 " << i + 1 << " 纬度: "; std::cin >> lat; std::cout << "航点 " << i + 1 << " 经度: "; std::cin >> lng; waypoints.push_back({lat, lng}); } double altitude; std::cout << "输入飞行高度: "; std::cin >> altitude; control_system.flyRoute(waypoints, altitude); } }; int main() { DroneControlSystem control_system; UserInterface ui(control_system); ui.run(); return 0; } 模块代码设计#include "stm32f4xx.h" // MPU6050寄存器定义 #define MPU6050_ADDR 0xD0 #define MPU6050_SMPLRT_DIV 0x19 #define MPU6050_CONFIG 0x1A #define MPU6050_GYRO_CONFIG 0x1B #define MPU6050_ACCEL_CONFIG 0x1C #define MPU6050_ACCEL_XOUT_H 0x3B #define MPU6050_GYRO_XOUT_H 0x43 #define MPU6050_PWR_MGMT_1 0x6B #define MPU6050_WHO_AM_I 0x75 // GPS相关定义 #define GPS_BUFFER_SIZE 256 // 4G模块相关定义 #define AT_OK 0 #define AT_ERROR -1 // 传感器数据结构 typedef struct { int16_t accel_x; int16_t accel_y; int16_t accel_z; int16_t temp; int16_t gyro_x; int16_t gyro_y; int16_t gyro_z; } MPU6050_Data; typedef struct { float latitude; float longitude; float altitude; uint8_t fix_status; } GPS_Data; // 全局变量 volatile MPU6050_Data mpu_data; volatile GPS_Data gps_data; volatile uint8_t gps_buffer[GPS_BUFFER_SIZE]; volatile uint16_t gps_index = 0; // I2C1初始化 void I2C1_Init(void) { // 使能GPIOB和I2C1时钟 RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN; RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; // PB6-I2C1_SCL, PB7-I2C1_SDA GPIOB->MODER |= (2 << 12) | (2 << 14); // 复用功能 GPIOB->OTYPER |= (1 << 6) | (1 << 7); // 开漏输出 GPIOB->OSPEEDR |= (3 << 12) | (3 << 14); // 高速 GPIOB->PUPDR |= (1 << 12) | (1 << 14); // 上拉 GPIOB->AFR[0] |= (4 << 24) | (4 << 28); // AF4 // I2C配置 I2C1->CR1 &= ~I2C_CR1_PE; // 禁用I2C I2C1->CR2 = 42; // 42MHz I2C1->CCR = 210; // 100kHz I2C1->TRISE = 43; // 最大上升时间 I2C1->CR1 |= I2C_CR1_PE; // 使能I2C } // I2C写字节 void I2C_Write(uint8_t dev_addr, uint8_t reg_addr, uint8_t data) { while(I2C1->SR2 & I2C_SR2_BUSY); // 启动条件 I2C1->CR1 |= I2C_CR1_START; while(!(I2C1->SR1 & I2C_SR1_SB)); // 发送设备地址(写) I2C1->DR = dev_addr; while(!(I2C1->SR1 & I2C_SR1_ADDR)); (void)I2C1->SR2; // 发送寄存器地址 I2C1->DR = reg_addr; while(!(I2C1->SR1 & I2C_SR1_TXE)); // 发送数据 I2C1->DR = data; while(!(I2C1->SR1 & I2C_SR1_BTF)); // 停止条件 I2C1->CR1 |= I2C_CR1_STOP; } // I2C读多个字节 void I2C_Read_Multi(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data, uint8_t count) { while(I2C1->SR2 & I2C_SR2_BUSY); // 启动条件 I2C1->CR1 |= I2C_CR1_START; while(!(I2C1->SR1 & I2C_SR1_SB)); // 发送设备地址(写) I2C1->DR = dev_addr; while(!(I2C1->SR1 & I2C_SR1_ADDR)); (void)I2C1->SR2; // 发送寄存器地址 I2C1->DR = reg_addr; while(!(I2C1->SR1 & I2C_SR1_TXE)); // 重新启动 I2C1->CR1 |= I2C_CR1_START; while(!(I2C1->SR1 & I2C_SR1_SB)); // 发送设备地址(读) I2C1->DR = dev_addr | 0x01; while(!(I2C1->SR1 & I2C_SR1_ADDR)); (void)I2C1->SR2; // 读取数据 for(uint8_t i = 0; i < count; i++) { if(i == count - 1) { I2C1->CR1 &= ~I2C_CR1_ACK; // 最后一个字节不发送ACK } while(!(I2C1->SR1 & I2C_SR1_RXNE)); data[i] = I2C1->DR; } // 停止条件 I2C1->CR1 |= I2C_CR1_STOP; I2C1->CR1 |= I2C_CR1_ACK; // 重新使能ACK } // MPU6050初始化 void MPU6050_Init(void) { I2C_Write(MPU6050_ADDR, MPU6050_PWR_MGMT_1, 0x80); // 复位设备 Delay(100); I2C_Write(MPU6050_ADDR, MPU6050_PWR_MGMT_1, 0x00); // 唤醒 I2C_Write(MPU6050_ADDR, MPU6050_SMPLRT_DIV, 0x07); // 采样率1kHz I2C_Write(MPU6050_ADDR, MPU6050_CONFIG, 0x00); // 禁用低通滤波 I2C_Write(MPU6050_ADDR, MPU6050_GYRO_CONFIG, 0x08); // ±500dps I2C_Write(MPU6050_ADDR, MPU6050_ACCEL_CONFIG, 0x10); // ±8g } // 读取MPU6050数据 void MPU6050_ReadData(void) { uint8_t buffer[14]; I2C_Read_Multi(MPU6050_ADDR, MPU6050_ACCEL_XOUT_H, buffer, 14); mpu_data.accel_x = (buffer[0] << 8) | buffer[1]; mpu_data.accel_y = (buffer[2] << 8) | buffer[3]; mpu_data.accel_z = (buffer[4] << 8) | buffer[5]; mpu_data.temp = (buffer[6] << 8) | buffer[7]; mpu_data.gyro_x = (buffer[8] << 8) | buffer[9]; mpu_data.gyro_y = (buffer[10] << 8) | buffer[11]; mpu_data.gyro_z = (buffer[12] << 8) | buffer[13]; } // USART2初始化 (GPS) void USART2_Init(void) { RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; RCC->APB1ENR |= RCC_APB1ENR_USART2EN; // PA2-TX, PA3-RX GPIOA->MODER |= (2 << 4) | (2 << 6); GPIOA->AFR[0] |= (7 << 8) | (7 << 12); USART2->BRR = 42000000 / 9600; // 42MHz, 9600bps USART2->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE | USART_CR1_UE; NVIC_EnableIRQ(USART2_IRQn); } // USART3初始化 (4G模块) void USART3_Init(void) { RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN; RCC->APB1ENR |= RCC_APB1ENR_USART3EN; // PC10-TX, PC11-RX GPIOC->MODER |= (2 << 20) | (2 << 22); GPIOC->AFR[1] |= (7 << 8) | (7 << 12); USART3->BRR = 42000000 / 115200; // 42MHz, 115200bps USART3->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; } // USART发送字符串 void USART_SendString(USART_TypeDef* USARTx, char* str) { while(*str) { while(!(USARTx->SR & USART_SR_TXE)); USARTx->DR = *str++; } } // GPS数据解析 void GPS_ParseData(char* gps_string) { // 简化的NMEA解析,实际需要完整解析GPRMC或GPGGA语句 if(strstr(gps_string, "$GPRMC")) { // 解析经纬度、速度、航向等信息 char* token = strtok(gps_string, ","); uint8_t field = 0; while(token != NULL) { switch(field) { case 3: // 纬度 // 解析纬度 break; case 5: // 经度 // 解析经度 break; case 7: // 速度 // 解析速度 break; case 8: // 航向 // 解析航向 break; } token = strtok(NULL, ","); field++; } } } // 4G模块初始化 void G4_Module_Init(void) { Delay(1000); USART_SendString(USART3, "AT\r\n"); Delay(100); USART_SendString(USART3, "AT+CPIN?\r\n"); Delay(100); USART_SendString(USART3, "AT+CSQ\r\n"); Delay(100); USART_SendString(USART3, "AT+CGATT=1\r\n"); Delay(100); USART_SendString(USART3, "AT+CSTT=\"CMNET\"\r\n"); Delay(100); USART_SendString(USART3, "AT+CIICR\r\n"); Delay(1000); USART_SendString(USART3, "AT+CIFSR\r\n"); } // PWM初始化 (电机控制) void TIM1_PWM_Init(void) { RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; // PA8, PA9, PA10, PA11 - TIM1 CH1, CH2, CH3, CH4 GPIOA->MODER |= (2 << 16) | (2 << 18) | (2 << 20) | (2 << 22); GPIOA->AFR[1] |= (1 << 0) | (1 << 4) | (1 << 8) | (1 << 12); TIM1->PSC = 83; // 84MHz/84 = 1MHz TIM1->ARR = 19999; // 50Hz PWM TIM1->CCR1 = 1000; // 初始占空比 TIM1->CCR2 = 1000; TIM1->CCR3 = 1000; TIM1->CCR4 = 1000; TIM1->CCMR1 = (6 << 4) | (1 << 3) | (6 << 12) | (1 << 11); // PWM模式1 TIM1->CCMR2 = (6 << 4) | (1 << 3) | (6 << 12) | (1 << 11); TIM1->CCER = TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC3E | TIM_CCER_CC4E; TIM1->BDTR |= TIM_BDTR_MOE; TIM1->CR1 = TIM_CR1_CEN; } // 设置电机速度 void Motor_SetSpeed(uint8_t motor, uint16_t speed) { if(speed < 1000) speed = 1000; if(speed > 2000) speed = 2000; switch(motor) { case 0: TIM1->CCR1 = speed; break; case 1: TIM1->CCR2 = speed; break; case 2: TIM1->CCR3 = speed; break; case 3: TIM1->CCR4 = speed; break; } } // 系统时钟初始化 void SystemClock_Config(void) { // 启用HSE RCC->CR |= RCC_CR_HSEON; while(!(RCC->CR & RCC_CR_HSERDY)); // 配置PLL: HSE(8MHz) * 336 / (8 * 2) = 84MHz RCC->PLLCFGR = (8 << 0) | (336 << 6) | (2 << 16) | (1 << 22) | RCC_PLLCFGR_PLLSRC_HSE; // 启用PLL RCC->CR |= RCC_CR_PLLON; while(!(RCC->CR & RCC_CR_PLLRDY)); // 配置闪存延迟 FLASH->ACR = FLASH_ACR_LATENCY_2WS | FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_PRFTEN; // 配置时钟分频 RCC->CFGR |= RCC_CFGR_HPRE_DIV1 | RCC_CFGR_PPRE1_DIV4 | RCC_CFGR_PPRE2_DIV2; // 切换到PLL RCC->CFGR |= RCC_CFGR_SW_PLL; while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); } // 延时函数 void Delay(uint32_t ms) { for(uint32_t i = 0; i < ms * 1000; i++) { __NOP(); } } // USART2中断处理 (GPS数据接收) void USART2_IRQHandler(void) { if(USART2->SR & USART_SR_RXNE) { uint8_t data = USART2->DR; if(gps_index < GPS_BUFFER_SIZE - 1) { gps_buffer[gps_index++] = data; if(data == '\n') { gps_buffer[gps_index] = '\0'; GPS_ParseData((char*)gps_buffer); gps_index = 0; } } else { gps_index = 0; } } } // 主函数 int main(void) { SystemClock_Config(); I2C1_Init(); USART2_Init(); USART3_Init(); TIM1_PWM_Init(); MPU6050_Init(); G4_Module_Init(); // 启动所有电机 for(uint8_t i = 0; i < 4; i++) { Motor_SetSpeed(i, 1000); } while(1) { MPU6050_ReadData(); // 姿态控制算法 (简化的PID) float pitch_error = 0 - (mpu_data.accel_x / 16384.0); // 转换为g float roll_error = 0 - (mpu_data.accel_y / 16384.0); // 简单的电机控制 uint16_t base_speed = 1100; Motor_SetSpeed(0, base_speed + (int)(pitch_error * 100)); Motor_SetSpeed(1, base_speed - (int)(pitch_error * 100)); Motor_SetSpeed(2, base_speed + (int)(roll_error * 100)); Motor_SetSpeed(3, base_speed - (int)(roll_error * 100)); // 发送状态数据到4G网络 char status_msg[128]; sprintf(status_msg, "GPS:%.6f,%.6f|ACC:%.2f,%.2f,%.2f\r\n", gps_data.latitude, gps_data.longitude, mpu_data.accel_x/16384.0, mpu_data.accel_y/16384.0, mpu_data.accel_z/16384.0); USART_SendString(USART3, status_msg); Delay(100); } } 项目核心代码#include "stm32f4xx.h" #include "system_stm32f4xx.h" #include <stdio.h> #include <string.h> // 硬件模块定义 #define GPS_USART USART2 #define DTU_USART USART3 #define MPU6050_I2C I2C1 // 飞行模式定义 typedef enum { MANUAL_MODE = 0, AUTO_HOVER, AUTO_WAYPOINT, RETURN_HOME } FlightMode; // 数据结构定义 typedef struct { float latitude; float longitude; float altitude; float speed; uint8_t satellites; } GPS_Data; typedef struct { float roll; float pitch; float yaw; float accel_x; float accel_y; float accel_z; float gyro_x; float gyro_y; float gyro_z; } IMU_Data; typedef struct { float voltage; float current; uint8_t percentage; } Battery_Data; // 全局变量 volatile GPS_Data gps_data = {0}; volatile IMU_Data imu_data = {0}; volatile Battery_Data battery_data = {0}; volatile FlightMode current_mode = AUTO_HOVER; volatile uint32_t system_tick = 0; volatile uint8_t dtu_connected = 0; // 外部函数声明 extern void SystemClock_Config(void); extern void GPIO_Init(void); extern void USART_Init(void); extern void I2C_Init(void); extern void PWM_Init(void); extern void TIM_Init(void); extern void NVIC_Init(void); extern void GPS_GetData(GPS_Data* data); extern void MPU6050_GetData(IMU_Data* data); extern void Battery_GetData(Battery_Data* data); extern void DTU_SendData(uint8_t* data, uint16_t len); extern uint8_t DTU_ReceiveData(uint8_t* buffer, uint16_t* len); extern void FlightController_Update(void); extern void Motor_SetSpeed(uint8_t motor_id, uint16_t speed); // 系统滴答定时器中断 void SysTick_Handler(void) { system_tick++; } // 主定时器中断 - 用于飞控主循环 void TIM2_IRQHandler(void) { if(TIM2->SR & TIM_SR_UIF) { TIM2->SR &= ~TIM_SR_UIF; // 读取传感器数据 GPS_GetData((GPS_Data*)&gps_data); MPU6050_GetData((IMU_Data*)&imu_data); Battery_GetData((Battery_Data*)&battery_data); // 运行飞控算法 FlightController_Update(); } } // DTU数据接收中断 void USART3_IRQHandler(void) { if(USART3->SR & USART_SR_RXNE) { static uint8_t rx_buffer[256]; static uint16_t rx_index = 0; uint8_t data = USART3->DR; if(data == '\n') { // 解析控制指令 ParseControlCommand(rx_buffer, rx_index); rx_index = 0; } else if(rx_index < sizeof(rx_buffer)-1) { rx_buffer[rx_index++] = data; } } } // 解析控制指令 void ParseControlCommand(uint8_t* cmd, uint16_t len) { if(len < 3) return; // 简单指令解析示例 if(strncmp((char*)cmd, "MODE", 4) == 0) { uint8_t mode = cmd[5] - '0'; if(mode <= RETURN_HOME) { current_mode = (FlightMode)mode; } } else if(strncmp((char*)cmd, "THR", 3) == 0) { // 油门控制指令 } else if(strncmp((char*)cmd, "WP", 2) == 0) { // 航点设置指令 } } // 发送遥测数据 void SendTelemetryData(void) { static uint32_t last_send_time = 0; if((system_tick - last_send_time) > 100) { // 100ms发送一次 char telemetry[256]; int len = snprintf(telemetry, sizeof(telemetry), "GPS:%.6f,%.6f,%.1f|IMU:%.2f,%.2f,%.2f|BAT:%.1f,%d|MODE:%d\n", gps_data.latitude, gps_data.longitude, gps_data.altitude, imu_data.roll, imu_data.pitch, imu_data.yaw, battery_data.voltage, battery_data.percentage, current_mode); if(len > 0) { DTU_SendData((uint8_t*)telemetry, len); } last_send_time = system_tick; } } // 系统状态监测 void SystemMonitor(void) { static uint32_t last_check_time = 0; if((system_tick - last_check_time) > 1000) { // 1s检查一次 // 检查电池电量 if(battery_data.percentage < 20) { // 低电量警告,执行返航 current_mode = RETURN_HOME; } // 检查GPS信号 if(gps_data.satellites < 6) { // GPS信号弱警告 } last_check_time = system_tick; } } // 4G连接状态检查 void CheckDTUConnection(void) { static uint32_t last_check_time = 0; static uint8_t connection_retry = 0; if((system_tick - last_check_time) > 5000) { // 5s检查一次 // 发送心跳包检查连接 uint8_t heartbeat[] = "HEARTBEAT\n"; DTU_SendData(heartbeat, sizeof(heartbeat)-1); if(!dtu_connected && connection_retry < 3) { // 尝试重新连接 connection_retry++; } last_check_time = system_tick; } } // 系统初始化 void System_Init(void) { // 时钟配置 SystemClock_Config(); // 外设初始化 GPIO_Init(); USART_Init(); I2C_Init(); PWM_Init(); TIM_Init(); NVIC_Init(); // 配置系统滴答定时器 SysTick_Config(SystemCoreClock / 1000); // 1ms中断 // 传感器初始化 GPS_Init(); MPU6050_Init(); Battery_Init(); DTU_Init(); // 飞控初始化 FlightController_Init(); } int main(void) { // 系统初始化 System_Init(); // 启动主定时器 - 500Hz飞控频率 TIM2->CR1 |= TIM_CR1_CEN; while(1) { // 发送遥测数据 SendTelemetryData(); // 系统状态监测 SystemMonitor(); // 检查4G连接 CheckDTUConnection(); // 低功耗处理 __WFI(); } } // 简单的延时函数 void Delay_ms(uint32_t ms) { uint32_t start_tick = system_tick; while((system_tick - start_tick) < ms); } 总结本无人机远程控制系统基于4G网络技术,实现了无人机与远程控制中心之间的长距离、可靠通信。该系统通过高效的网络连接,确保了远程监控和控制的实时性与稳定性,适用于各种复杂环境下的应用场景。在功能方面,系统支持无人机通过4G网络与远程控制中心建立持久的通信链路,远程控制中心能够实时接收无人机回传的GPS定位信息和飞行状态数据,并可通过4G网络向无人机发送精确的飞行控制指令。此外,无人机还具备基本的自主飞行能力,如定点悬停和预设航线飞行,进一步提升了系统的自动化水平和操作便捷性。硬件组成上,系统采用STM32F407系列主控芯片,提供高性能计算和DSP指令集支持,确保数据处理的高效性。通信模块选用4G DTU(如SIM7600),保障了网络连接的稳定与高速传输。飞控传感部分包括MPU6050六轴传感器、GPS定位模块和气压计,用于精确感知飞行姿态和位置信息。动力系统由无刷电机、电子调速器和螺旋桨组成,提供可靠的飞行推力,而电源系统则依赖大容量锂聚合物电池,确保系统能够长时间稳定运行。
  • [技术干货] 基于STM32的面粉厂车间安全生产系统
    一、项目开发背景面粉厂作为食品加工行业的重要组成部分,其生产车间环境复杂,涉及大量机械设备运行和粉尘产生,存在较高的安全风险。面粉粉尘具有可燃性,当浓度达到一定阈值时,遇火源可能引发爆炸事故,同时车间温湿度变化和设备异常震动也会影响生产稳定性和人员安全。传统监控方式主要依赖人工巡检和简单仪表,存在响应延迟、数据不全面等问题,难以实现实时预警和高效管理。随着工业自动化与物联网技术的发展,智能监控系统成为提升安全生产水平的关键手段。通过集成传感器、微控制器和无线通信技术,可以实时采集环境参数,及时识别异常状态,并自动触发报警机制,从而有效预防事故的发生。本项目基于STM32单片机设计的面粉厂车间安全生产系统,正是为了应对这些挑战,结合温湿度、粉尘浓度和震动监测等功能,构建一个全面、可靠的监控网络。该系统的开发弥补传统方法的不足,通过高精度传感器和WiFi通信模块,实现数据远程传输与终端APP实时显示,使管理人员能够随时随地掌握车间状况。此外,系统支持阈值远程设置和本地指示,增强了灵活性和智能化程度,有助于降低人为失误,提高应急响应效率。通过这一创新应用,不仅能够保障生产安全,还能推动面粉厂向数字化、智能化转型,为行业可持续发展提供支持。框架图Lexical error on line 26. Unrecognized text. ...e[ESP8266 WiFi模块(ESP-12F)] N -----------------------^框架图层次与流程说明:感知执行层 (最底层)数据采集单元:负责物理世界信号的感知。DHT11:通过单总线数字协议将温湿度数据发送给STM32。GP2Y1051AU0F:输出模拟电压信号,由STM32的ADC模块转换为粉尘浓度数字值。SW-420:输出开关量信号,可通过STM32的GPIO或外部中断(EXTI)检测震动状态。报警执行单元:接收STM32的控制指令,进行声光报警。状态指示单元:直观显示系统运行状态(如电源、联网等)。核心控制层STM32F103RCT6:作为系统的“大脑”。功能:轮询采集所有传感器数据,进行数据处理、阈值比较和逻辑判断。根据结果控制执行单元,并通过UART与WiFi模块进行通信调度。网络通信层ESP8266模块:作为通信网关,通过UART串口与STM32连接,使用AT指令集进行数据传输。WiFi网络:提供无线传输媒介,承载设备与APP之间的TCP/IP通信。应用展示层 (最顶层)Qt终端APP:为用户提供图形化界面。功能:实时显示车间所有环境参数与设备状态;接收并推送报警信息;提供界面供用户远程修改阈值参数,并下发至STM32。二、设计实现的功能(1)温湿度检测功能:采用DHT11温湿度传感器,实时采集车间温湿度数据并显示。(2)粉尘浓度检测功能:采用GP2Y1051AU0F粉尘传感器,检测车间空气中粉尘浓度,超过阈值自动报警。(3)震动检测功能:采用SW-420震动传感器,监测设备运行状态,当震动异常时触发警报。(4)声光报警功能:采用高亮红色LED报警灯与高电平触发有源蜂鸣器组成声光报警单元,实现故障警示。(5)WiFi通信功能:采用ESP8266-WIFI模块(ESP-12F版本),通过UART串口与STM32通信,实现数据上传与远程监控。(6)数据采集与处理功能:由STM32F103RCT6单片机作为主控核心,负责各传感器数据的采集、分析与判断。(7)远程监控与显示功能:终端APP基于Qt(C++)开发,可实时显示车间温湿度、粉尘浓度、震动状态及报警信息。(8)阈值设定与修改功能:APP端支持阈值参数设置,并通过WiFi下发至STM32端实现实时调整。(9)本地运行指示功能:通过LED指示灯显示系统运行与联网状态,便于现场维护与监控。三、项目硬件模块组成(1)主控芯片:STM32F103RCT6,负责系统核心控制、数据处理与逻辑判断。(2)温湿度传感器模块:DHT11,用于检测车间温度与湿度。(3)粉尘传感器模块:GP2Y1051AU0F,用于检测空气中粉尘浓度。(4)震动传感器模块:SW-420,用于监测机械震动状态。(5)WiFi通信模块:ESP8266(ESP-12F),用于无线数据传输与APP通信。(6)报警模块:包含高电平触发有源蜂鸣器与红色LED报警灯,用于声光报警提示。(7)显示与指示模块:LED指示灯,用于显示系统运行与报警状态。(8)电源模块:DC 5V稳压电源模块,为主控及传感器提供稳定电源。(9)数据通信接口:UART串口通信接口,用于STM32与ESP8266之间的数据交互。模块的模块源码已经上传到网盘:https://ccnr8sukk85n.feishu.cn/wiki/QjY8weDYHibqRYkFP2qcA9aGnvb?from=from_copylink四、设计意义本系统的设计旨在提升面粉厂车间的安全生产水平,通过实时监测关键环境参数和设备状态,有效预防因温湿度异常、粉尘浓度超标或机械故障引发的安全事故。面粉厂车间环境中的粉尘具有易燃易爆特性,系统通过粉尘浓度检测与报警功能,能够及时警示工作人员采取措施,降低爆炸风险,保障人员与设备安全。同时,震动监测功能可识别设备异常运行,避免机械故障扩大化,减少生产中断和设备损坏。系统的远程监控与数据上传功能通过WiFi模块实现,使管理人员能够通过终端APP实时查看车间数据,并在异常时接收警报,大大提升了监控的便捷性和响应速度。这种智能化设计减少了人工巡检的依赖,提高了生产管理的效率,并支持阈值参数的灵活调整,使系统能够适应不同工况需求,增强整体运营的适应性。此外,本地声光报警和状态指示功能确保了现场工作人员能够快速识别问题,及时处理异常,进一步强化了车间的应急响应能力。整体上,该系统通过集成传感器、主控芯片和通信模块,实现了对车间环境的全面智能化管理,不仅提升了安全防护水平,还为面粉厂的可持续生产提供了可靠的技术支持。五、设计思路该系统设计以STM32F103RCT6单片机为核心控制器,负责协调各传感器模块的数据采集与处理,实现对面粉厂车间环境的全面监控。通过集成DHT11温湿度传感器、GP2Y1051AU0F粉尘传感器和SW-420震动传感器,系统能够实时获取车间温度、湿度、粉尘浓度及设备震动状态等关键参数。所有采集的数据由STM32进行解析和判断,确保高效可靠的环境监测。数据采集后,系统会与预设阈值进行比对,若温湿度、粉尘浓度或震动频率超出安全范围,STM32将立即触发声光报警单元,包括高电平触发有源蜂鸣器和红色LED报警灯,以警示现场工作人员采取应对措施。报警逻辑基于实时数据分析,确保及时响应异常情况,避免安全生产事故。通信方面,系统采用ESP8266 WiFi模块通过UART串口与STM32连接,实现数据的无线传输。终端APP基于Qt开发,可实时接收并显示车间监测数据,同时在异常时推送警报信息。APP还支持远程修改阈值参数,并通过WiFi下发至STM32,增强系统的灵活性和智能化程度,方便用户根据实际需求调整监控标准。本地运行状态通过LED指示灯实时显示,包括系统正常运行、联网状态及报警触发情况,便于现场维护人员直观掌握系统工作状态。电源模块提供稳定的5V供电,确保各硬件组件可靠运行。整体设计注重实用性与可靠性,通过模块化集成实现了数据采集、处理、报警和远程监控的一体化功能。六、框架图+---------------------------------------------+ | 终端APP (Qt) | | - 实时显示监测数据 | | - 接收警报信息 | | - 设置阈值参数 | +---------------------------------------------+ ^ | WiFi通信 v +---------------------------------------------+ | WiFi模块 (ESP8266) | | (ESP-12F) | +---------------------------------------------+ ^ | UART串口通信 v +---------------------------------------------+ | STM32F103RCT6 | | - 数据采集与处理 | | - 阈值判断与报警控制 | | - 与WiFi模块通信 | +---------------------------------------------+ | | | v v v +----------+ +----------+ +----------+ | 温湿度传感器 | | 粉尘传感器 | | 震动传感器 | | (DHT11) | | (GP2Y1051) | | (SW-420) | +----------+ +----------+ +----------+ | v +---------------------------------------------+ | 报警模块 | | - 声光报警 (蜂鸣器+红色LED) | +---------------------------------------------+ ^ | +---------------------------------------------+ | 电源模块 | | (DC 5V稳压) | +---------------------------------------------+ 七、系统总体设计系统总体设计基于STM32F103RCT6主控芯片为核心,构建一个面粉厂车间安全生产监控系统。系统通过集成多种传感器实时采集车间环境参数,包括温湿度、粉尘浓度和设备震动状态,确保生产环境安全可靠。主控芯片负责数据采集、分析和逻辑判断,当检测到参数超过预设阈值时,自动触发声光报警单元,提醒工作人员及时处理异常情况。硬件架构包括温湿度传感器DHT11、粉尘传感器GP2Y1051AU0F和震动传感器SW-420,这些传感器分别负责监测车间温度、湿度、粉尘浓度和机械震动频率。WiFi通信模块ESP8266通过UART串口与STM32主控连接,实现数据无线传输至终端APP,支持远程监控和数据交互。报警模块由高电平触发有源蜂鸣器和红色LED报警灯组成,用于在异常时发出声光警示。数据采集与处理功能由STM32主控统一管理,实时读取传感器数据并进行阈值比较,判断是否触发报警。系统支持本地和远程阈值参数设置,通过WiFi模块接收APP下发的配置信息,增强系统灵活性和智能化水平。终端APP基于Qt开发,可实时显示车间监测数据、报警状态,并允许用户调整阈值参数。通信机制通过WiFi模块实现STM32与APP之间的稳定数据传输,确保车间环境数据实时上传和远程监控。系统运行状态和报警信息通过本地LED指示灯直观显示,便于现场维护人员快速识别系统状况。电源模块采用DC 5V稳压电源,为整个系统提供稳定电力支持,保证各硬件模块正常运行。八、系统功能总结功能模块功能描述温湿度检测功能采用DHT11传感器实时采集车间温度与湿度数据,并进行分析与显示。粉尘浓度检测功能使用GP2Y1051AU0F传感器检测空气中粉尘浓度,超阈值时自动触发报警机制。震动检测功能通过SW-420震动传感器监测设备运行状态,异常震动时判断机械故障并报警。声光报警功能集成高亮红色LED报警灯和有源蜂鸣器,实现超限或故障时的声光警示。WiFi通信功能采用ESP8266模块通过UART串口与STM32通信,支持数据远程上传和APP交互。数据采集与处理功能STM32F103RCT6主控负责多传感器数据采集、分析与逻辑判断,确保系统稳定运行。远程监控与显示功能基于Qt开发的终端APP实时显示温湿度、粉尘浓度、震动状态及报警信息。阈值设定与修改功能支持APP端远程或本地修改温湿度、粉尘浓度等阈值参数,增强系统灵活性。本地运行指示功能通过LED指示灯实时显示系统运行状态、联网状态及报警情况,便于现场维护。九、设计的各个功能模块描述温湿度检测功能采用DHT11传感器实时采集面粉厂车间的温度和湿度数据,传感器将采集到的参数传输至STM32主控芯片进行处理和显示,确保环境条件处于安全监控范围内。粉尘浓度检测功能通过GP2Y1051AU0F粉尘传感器监测车间空气中的粉尘浓度,当检测值超过预设阈值时,系统会自动触发报警机制,提醒工作人员及时处理潜在安全隐患。震动检测功能使用SW-420震动传感器监测设备运行状态,当检测到异常震动频率或持续高震动时,系统判断可能存在机械故障,并立即启动报警流程以预防设备损坏。声光报警功能由高亮红色LED报警灯和高电平触发有源蜂鸣器组成,当系统检测到温湿度、粉尘浓度或震动参数异常时,自动激活声光警示,为现场人员提供直观的故障提醒。WiFi通信功能集成ESP8266-WIFI模块(ESP-12F版本),通过UART串口与STM32主控芯片进行数据交互,实现车间监测数据的无线上传和终端APP的远程通信,支持实时监控和指令接收。数据采集与处理功能以STM32F103RCT6单片机为核心,负责从各传感器采集温湿度、粉尘浓度和震动数据,并进行实时分析、判断与逻辑控制,确保系统响应准确及时。远程监控与显示功能基于Qt(C++)开发的终端APP,可实时显示车间温湿度、粉尘浓度、震动状态及报警信息,用户可通过APP远程查看数据并在异常时接收警报通知。阈值设定与修改功能允许用户在APP端设置和调整温湿度、粉尘浓度等参数的阈值,这些设置通过WiFi模块下发至STM32端,实现远程参数配置,提升系统的灵活性和适应性。本地运行指示功能通过LED指示灯实时显示系统运行状态和联网情况,例如正常运行时亮绿灯,报警时亮红灯,便于现场维护人员快速识别系统状况并进行处理。十、上位机代码设计11.1 Qt开发入门与环境搭建【1】Qt是什么?Qt 是一个功能强大、跨平台的应用程序开发框架,主要用于创建图形用户界面(GUI)应用程序,但它不仅仅局限于GUI编程。它由挪威的奇趣科技(TrollTech)最初于1991年开发,并在后续的发展历程中经历了多次所有权变更,包括诺基亚和Digia等公司接手,现在Qt属于The Qt Company所有。Qt 主要特点和优势包括:(1)跨平台:Qt 支持多种操作系统,开发者可以使用同一份源代码在不同平台上编译运行,如Windows、Linux、macOS、Android以及各种嵌入式系统(如RTOS),实现“一次编写,到处编译”。(2)C++ 开发:Qt 的核心是基于C++编程语言构建,提供了一套丰富的类库,通过面向对象的设计方式简化了开发过程。(3)图形用户界面:Qt 提供了完整的GUI组件集,包含窗口、按钮、标签、文本框等各种标准控件,以及布局管理器、样式表等功能,使得开发者能够高效地创建美观且功能完善的桌面应用或移动应用界面。(4)工具链完整:Qt 包含一系列集成开发环境(IDE)和辅助工具,例如Qt Creator是一个全能的跨平台IDE,Qt Designer用于可视化拖拽设计UI界面,Qt Linguist支持国际化资源文件的翻译,还有Qt Assistant和大量文档资源方便开发者的使用。(5)非GUI功能丰富:除了GUI功能外,Qt 还提供了众多非图形化功能模块,如网络通信、数据库访问、XML处理、多媒体处理(音频视频)、文件I/O、线程与并发处理、OpenGL和3D图形渲染等。(6)元对象系统:Qt 使用元对象系统(Meta-Object System, MOC)实现了信号与槽机制(Signals and Slots),这是一种高级事件处理机制,允许在不同对象之间安全地进行异步通信。(7)可扩展性与灵活性:Qt 架构高度灵活,支持插件体系结构,开发者可以根据需要自定义组件并轻松地集成到Qt应用中。Qt 以其强大的跨平台能力和全面的功能集合成为许多企业和个人开发者选择用来开发高性能、高稳定性的应用程序的重要工具之一,被广泛应用于各类桌面软件、嵌入式设备、移动应用以及服务器端组件等领域。【2】Qt版本介绍在Qt发行版本中将要涉及两个版本:Qt商业授权和Qt开源授权。(1)Qt商业授权是设计商业软件的开发环境,这些商业软件使用了传统的商业来发布,它包含了一些更新的功能、技术上的支持和大量的解决方案,开发了使用于行业的一些特定的组件,有一些特殊的功能只在商业用户中使用。(2)Qt开源授权是用来开发开源的软件,它提供了一些免费的支持,并遵循QPL协议。开放源代码是免费的软件,不牵涉用户的某些权益。任何人都有使用开源软件和参与它的修改的机会,这就意味着其他的人同样可获得你开发的代码。目前 Qt 的开源授权有两种,一种是 GPL 授权,另一种是 LGPL 授权。【3】Qt开发环境安装Qt的中文官网: https://www.qt.io/zh-cn/QT5.12.6的下载地址:https://download.qt.io/archive/qt/5.12/5.12.6打开下载链接后选择下面的版本进行下载:qt-opensource-windows-x86-5.12.6.exe 13-Nov-2019 07:28 3.7G Details软件安装时断网安装,否则会提示输入账户。如果下载不了,可以在网盘里找到安装包下载: 飞书文档记录的网盘地址:https://ccnr8sukk85n.feishu.cn/wiki/QjY8weDYHibqRYkFP2qcA9aGnvb?from=from_copylink安装的时候,第一个复选框里勾选一个mingw 32编译器即可,其他的不管默认就行,直接点击下一步继续安装。选择MinGW 32-bit 编译器:【4】开发第一个QT程序在QT开发过程中,可以手动编写代码也可以使用UI设计师直接拖拽控件的方式编写界面和布局,在实际的开发过程中一般是两种方式结合使用,提高开发效率。本小节用一个简单的 “Hello QT” 程序介绍一下使用QtCreator新建工程的步骤。(1)打开QtCreator软件,选择New Project,新建一个工程。(2)项目模板选择QT Widgets Application(3)设置项目名称和存放路径注意:QT项目路径和名称不能出现中文字符。(4)编译工具套件选择编译工具套件可以后面自己增加,比如增加Android的。套件是指 Qt 程序从编译链接到运行环境的全部工具和 Qt 类库的集合。(5)设置生成的类信息在类信息设置界面选择基类,目前有三种基类:QMainWindow,QWidget,QDialog。在基类里选择QMainWindow,类名和文件名会根据基类自动修改,一般不需要修改,默认即可。(6)项目管理在项目管理界面可以设置作为子项目,以及加入版本控制系统。这两个功能暂时用不到,都用默认的 <None> ,然后点击 “完成”。(7)创建完成(8) 编辑代码展开main.cpp文件,添加内容如下:#include "mainwindow.h" #include <QApplication> #include <QDebug> #include <QLabel> int main(int argc, char *argv[]) { QApplication a(argc, argv); //MainWindow w; //w.show(); QLabel *label =new QLabel("Hello Qt!"); label->setGeometry(400,100,100,20); label->show(); return a.exec(); } 代码解析:1) #include <QApplication>和 #include <QLabel>是QT的类声明头文件,对于每个QT类都有一个与该类同名的头文件,在这个头文件包含了对该类的定义。 2) main(int argc, char *argv[]) :main函数的标准写法。 3) QApplication a(argc, argv):创建一个QApplication对象,用于管理应用程序的资源,QApplication类的构造函数需要两个参数。 4) QLabel *label =new QLabel("Hello Qt!") :创建QLabel窗口部件,QLabel是一个Qt提供的窗口部件,可以用来显示一行文本。 5) label->setGeometry(400,100,100,20) : 设置控件显示的位置。 6) label->show():使Qlabel创建的窗口可见,就是显示设置的文本。 7) return a.exec():应用程序将控制权传递给QT,让程序进入消息循环。等待可能的菜单,工具条,鼠标等的输入,进行响应。(9)行程序运行程序可以点击左下角的三角形符号或者按下快捷键Ctrl+R。【5】调试输出QT中使用QDebug类输出调试信息。主要用于调试代码,类似于std::cout的替代品,支持QT的数据类型。使用前需要包含<QtDebug>头文件。调试输出的分类qDebug调试信息提示qWarning一般的警告提示qCritical严重错误提示qFatal致命错误提示示例代码:qDebug("调试信息输出"); qWarning("一般警告信息输出"); qCritical("严重错误输出"); qFatal("致命错误输出"); qDebug输出的信息会打印到QT软件下边的输出面板。在上节的HelloQt工程上加上调试输出代码,增加的main.cpp代码如下:#include "mainwindow.h" #include <QApplication> #include <QDebug> int main(int argc, char *argv[]) { QApplication a(argc, argv); //MainWindow w; //w.show(); qDebug()<<"QT调试信息输出"; int data_int=8888; qDebug()<<data_int; float data_float=123.888; qDebug()<<data_float; return a.exec(); } 运行程序,观察输出的调试信息:【6】QT Creator常用的快捷键掌握一些适用的快捷键,可以提高程序开发的效率。(1)F1 键,快速切换光标选中的函数或者类的帮助信息,按一次半屏显示,按下两次全屏显示。(2)F2 键,快速切换到光标选中的函数或者类的源码定义处。(3)F4键,快速在源文件和头文件之间切换。(4)Ctrl(按住)+ Tab,快速切换已打开的文件(5)Ctrl+ I ,缩进光标选中行代码(自动与上层代码对齐)。(6)Ctrl + / ,快速注释或者取消注释光标选中行。(7)快速修改全局变量名鼠标光标选中变量名,按下Ctrl+Shift+R,当变量名称出现红色框表示已经激活全局修改功能。修改一处,整个工程对应变量名称全部会修改。修改完毕之后,光标移开,再按下Ctrl+Shift+R保存修改。(8)快速修改全局函数名快捷方式与变量修改一样按下Ctrl+Shift+R,一处修改整个工程对应的函数名称也会跟着改。选中函数后,按下Ctrl+Shift+R后整个工程的对应的函数名会高亮,并且在软件下方弹出修改框。【7】QT帮助文档Qt 帮助文档太多,难以全部翻译成中文,即使翻译了一部分,翻译花的时间太多,翻译更新的时效性也难以保证,最终还是得看英文帮助,QtCreator 集成了帮助系统,查找非常方便。打开QtCreator,选择菜单栏的最左边的帮助选项,界面如下:(1)查看Qlabel控件的帮助信息:【8】UI设计师使用上节的Hello QT程序使用纯C++代码编写,这一节使用QT界面设计模式实现与上一节Hello QT程序一样的功能。仿照着上节新创建一个工程。双击打开mainwindow.ui文件,进入到UI设计界面。(1)拖一个Label控件到编辑区,双击Label控件可以修改文本内容。(2)运行程序可以点击左下角的三角形符号或者按下快捷键Ctrl+R。(3)UI设计师界面功能介绍【9】按钮控件组QT Creator UI设计师界面的按钮组截图如下:以下是对按钮组控件的一些功能介绍:(1)Push Button按压按钮:最普通的按钮,按(点击)按钮命令计算机执行一些动作,或者回答问题,比如windows开始菜单里的重启,注销,关机等按钮。(2)Tool Button工具按钮:工具按钮通常是一个集合,一般集成在工具栏里。比如打开,保存,复制,粘贴,剪切等常用的操作。(3)Radio Button单选按钮:单选按钮通常是两个以上的形式出现在一块,按钮之间有互斥关系,每次只能选中一个。比如:一个人的性别只能选择一个,不能同时是男性又是女性。(4)Check Box复选框:复选框与单选按钮概念相反,复选框通常表示多个可以同时存在的选项,比如一个人可以同时拥有多个爱好,比如读书、看电影、爬山、游泳等。(5)Command Link Button命令链接按钮:一般用来打开的窗口或者网页链接。(6)Dialog Button Box标准按钮盒:标准按钮盒通常用于对话框程序;比如:常见的确认对话框有 “确定”“取消”等标准按钮,Qt 将这些典型的按钮做成标准按钮盒,并将相应的信号加以封装,方便程序员使用。【10】布局控件组开发一个图形界面应用程序,界面的布局影响到界面的美观。前面的程序中都是使用UI界面拖控件,如果有多个按钮,会出现大小难调整、位置难对齐等问题。Qt 提供的“布局管理“就很好的解决了控件摆放的问题。以下是UI设计师界面的布局相关控件组:功能介绍:(1)Vertical Layout:垂直布局(2)Horizontal Layout:水平布局(3)Grid Layout:网格布局(4)Form Layout:窗体中布局(5)Horizontal Spacers:水平空格,在布局中用来占位。(6)Vertical Spacer:垂直空格,在布局中用来占位。【11】基本布局控件在UI设计界面添加一个布局控件,然后将需要布局的其他控件放入布局控件中即可完成布局,布局控件可以互相嵌套使用。(本节只介绍基本布局控件的使用)以下是4种布局控件的效果:【12】UI设计师的布局功能在UI设计界面的左上角有一排快捷的布局选项,使用时选中两个以上的控件,点击其中一种布局方式就可以切换布局。以下为布局的简单示例图:(1)为布局的选项。(2)控件层次图,可以看到控件的布局摆放层次。如果想要控制某个控件的固定大小,不随着布局改变大小,可以限定最大最小尺寸。选中控件鼠标右键–>大小限定->设置大小。水平布局与垂直布局:水平布局将控件按照水平方式摆放,垂直布局将控件按照垂直方式摆放。鼠标拖动红色布局框上的黑色方点,可以调整布局框的大小。随着布局框的尺寸变化,包含的控件高度不会变化,宽度会随着布局框变化。选中其中一个控件然后鼠标右键>点击大小限定,可以限定控件的最大和最小尺寸。分裂器水平布局与垂直布局:分裂器方式布局,包含控件的高度和宽度都会随着布局框的拉伸而改变。选中其中一个控件然后鼠标右键>点击大小限定,可以限定控件的最大和最小尺寸。窗体中布局与栅格布局:栅格(网格)布局器的基本单元是单元格,而窗体中布局(表单)的基本单元是行。随着布局框的尺寸变化,包含的控件高度不会变化,宽度会随着布局框变化。设置主窗体布局方式:设置主窗体的布局方式后,包含在主窗体内的控件会随着窗体的拉伸自动调整大小。11.2 上位机开发【1】Qt开发环境安装Qt的中文官网: https://www.qt.io/zh-cn/QT5.12.6的下载地址:https://download.qt.io/archive/qt/5.12/5.12.6打开下载链接后选择下面的版本进行下载:如果下载不了,可以在网盘里找到安装包下载: 飞书文档记录的网盘地址:https://ccnr8sukk85n.feishu.cn/wiki/QjY8weDYHibqRYkFP2qcA9aGnvb?from=from_copylink软件安装时断网安装,否则会提示输入账户。安装的时候,第一个复选框里的编译器可以全选,直接点击下一步继续安装。选择编译器: (一定要看清楚了)【2】新建上位机工程前面2讲解了需要用的API接口,接下来就使用Qt设计上位机,设计界面,完成整体上位机的逻辑设计。【1】新建工程【2】设置项目的名称。【3】选择编译系统【4】选择默认继承的类【5】选择编译器【6】点击完成【7】工程创建完成【3】切换编译器在左下角是可以切换编译器的。 可以选择用什么样的编译器编译程序。目前新建工程的时候选择了2种编译器。 一种是mingw32这个编译Windows下运行的程序。 一种是Android编译器,可以生成Android手机APP。不过要注意:Android的编译器需要配置一些环境才可以正常使用,这个大家可以网上找找教程配置一下就行了。windows的编译器就没有这么麻烦,安装好Qt就可以编译使用。下面我这里就选择的 mingw32这个编译器,编译Windows下运行的程序。【4】编译测试功能创建完毕之后,编译测试一下功能是否OK。点击左下角的绿色三角形按钮。正常运行就可以看到弹出一个白色的框框。这就表示工程环境没有问题了。 接下来就可以放心的设计界面了。【5】设计UI界面与工程配置【1】打开UI文件打开默认的界面如下:【2】开始设计界面根据自己需求设计界面。【7】编译Windows上位机点击软件左下角的绿色三角形按钮进行编译运行。【8】配置Android环境如果想编译Android手机APP,必须要先自己配置好自己的Android环境。(搭建环境的过程可以自行百度搜索学习)然后才可以进行下面的步骤。【1】选择Android编译器选择编译器。切换编译器。【2】创建Android配置文件创建完成。11.3 上位机代码#include <QtWidgets> #include <QTcpSocket> #include <QTimer> class SafetyMonitor : public QWidget { Q_OBJECT public: SafetyMonitor(QWidget *parent = nullptr) : QWidget(parent) { setupUI(); setupConnections(); setupNetwork(); } private slots: void connectToDevice(); void disconnectFromDevice(); void sendThresholds(); void readData(); void connectionError(); void updateDisplay(); private: void setupUI(); void setupConnections(); void setupNetwork(); void parseSensorData(const QString &data); // UI组件 QLabel *tempLabel, *humiLabel, *dustLabel, *vibLabel; QLCDNumber *tempLCD, *humiLCD, *dustLCD; QLabel *vibStatus, *alarmStatus; QDoubleSpinBox *tempThreshold, *humiThreshold, *dustThreshold; QPushButton *connectBtn, *disconnectBtn, *setThresholdBtn; QLineEdit *ipEdit, *portEdit; QTextEdit *logText; QTimer *updateTimer; // 网络组件 QTcpSocket *tcpSocket; // 数据存储 double currentTemp, currentHumi, currentDust; bool currentVib, currentAlarm; }; void SafetyMonitor::setupUI() { // 主布局 QVBoxLayout *mainLayout = new QVBoxLayout(this); // 连接设置区域 QGroupBox *connGroup = new QGroupBox("设备连接"); QHBoxLayout *connLayout = new QHBoxLayout(connGroup); ipEdit = new QLineEdit("192.168.1.100"); portEdit = new QLineEdit("8080"); connectBtn = new QPushButton("连接"); disconnectBtn = new QPushButton("断开"); disconnectBtn->setEnabled(false); connLayout->addWidget(new QLabel("IP:")); connLayout->addWidget(ipEdit); connLayout->addWidget(new QLabel("端口:")); connLayout->addWidget(portEdit); connLayout->addWidget(connectBtn); connLayout->addWidget(disconnectBtn); // 数据显示区域 QGroupBox *dataGroup = new QGroupBox("实时监测数据"); QGridLayout *dataLayout = new QGridLayout(dataGroup); tempLabel = new QLabel("温度(°C):"); humiLabel = new QLabel("湿度(%):"); dustLabel = new QLabel("粉尘(mg/m3):"); vibLabel = new QLabel("震动状态:"); tempLCD = new QLCDNumber(); humiLCD = new QLCDNumber(); dustLCD = new QLCDNumber(); tempLCD->setSegmentStyle(QLCDNumber::Filled); humiLCD->setSegmentStyle(QLCDNumber::Filled); dustLCD->setSegmentStyle(QLCDNumber::Filled); vibStatus = new QLabel("正常"); alarmStatus = new QLabel("无报警"); alarmStatus->setStyleSheet("QLabel { background-color: green; color: white; padding: 5px; }"); dataLayout->addWidget(tempLabel, 0, 0); dataLayout->addWidget(tempLCD, 0, 1); dataLayout->addWidget(humiLabel, 1, 0); dataLayout->addWidget(humiLCD, 1, 1); dataLayout->addWidget(dustLabel, 2, 0); dataLayout->addWidget(dustLCD, 2, 1); dataLayout->addWidget(vibLabel, 3, 0); dataLayout->addWidget(vibStatus, 3, 1); dataLayout->addWidget(new QLabel("报警状态:"), 4, 0); dataLayout->addWidget(alarmStatus, 4, 1); // 阈值设置区域 QGroupBox *thresholdGroup = new QGroupBox("报警阈值设置"); QGridLayout *thresholdLayout = new QGridLayout(thresholdGroup); tempThreshold = new QDoubleSpinBox(); humiThreshold = new QDoubleSpinBox(); dustThreshold = new QDoubleSpinBox(); tempThreshold->setRange(0, 100); humiThreshold->setRange(0, 100); dustThreshold->setRange(0, 1.0); tempThreshold->setValue(35.0); humiThreshold->setValue(80.0); dustThreshold->setValue(0.08); setThresholdBtn = new QPushButton("设置阈值"); thresholdLayout->addWidget(new QLabel("温度阈值:"), 0, 0); thresholdLayout->addWidget(tempThreshold, 0, 1); thresholdLayout->addWidget(new QLabel("湿度阈值:"), 1, 0); thresholdLayout->addWidget(humiThreshold, 1, 1); thresholdLayout->addWidget(new QLabel("粉尘阈值:"), 2, 0); thresholdLayout->addWidget(dustThreshold, 2, 1); thresholdLayout->addWidget(setThresholdBtn, 3, 0, 1, 2); // 日志区域 QGroupBox *logGroup = new QGroupBox("系统日志"); QVBoxLayout *logLayout = new QVBoxLayout(logGroup); logText = new QTextEdit(); logText->setReadOnly(true); logLayout->addWidget(logText); // 组合所有区域 mainLayout->addWidget(connGroup); mainLayout->addWidget(dataGroup); mainLayout->addWidget(thresholdGroup); mainLayout->addWidget(logGroup); setWindowTitle("面粉厂安全生产监控系统"); resize(600, 800); } void SafetyMonitor::setupConnections() { connect(connectBtn, &QPushButton::clicked, this, &SafetyMonitor::connectToDevice); connect(disconnectBtn, &QPushButton::clicked, this, &SafetyMonitor::disconnectFromDevice); connect(setThresholdBtn, &QPushButton::clicked, this, &SafetyMonitor::sendThresholds); } void SafetyMonitor::setupNetwork() { tcpSocket = new QTcpSocket(this); connect(tcpSocket, &QTcpSocket::readyRead, this, &SafetyMonitor::readData); connect(tcpSocket, &QTcpSocket::errorOccurred, this, &SafetyMonitor::connectionError); updateTimer = new QTimer(this); connect(updateTimer, &QTimer::timeout, this, &SafetyMonitor::updateDisplay); } void SafetyMonitor::connectToDevice() { tcpSocket->connectToHost(ipEdit->text(), portEdit->text().toInt()); if(tcpSocket->waitForConnected(3000)) { logText->append("连接设备成功"); connectBtn->setEnabled(false); disconnectBtn->setEnabled(true); setThresholdBtn->setEnabled(true); updateTimer->start(1000); } else { logText->append("连接设备失败"); } } void SafetyMonitor::disconnectFromDevice() { tcpSocket->disconnectFromHost(); connectBtn->setEnabled(true); disconnectBtn->setEnabled(false); setThresholdBtn->setEnabled(false); updateTimer->stop(); logText->append("断开设备连接"); } void SafetyMonitor::sendThresholds() { if(tcpSocket->state() == QAbstractSocket::ConnectedState) { QString command = QString("SET_THRESHOLD:TEMP:%1,HUMI:%2,DUST:%3") .arg(tempThreshold->value()) .arg(humiThreshold->value()) .arg(dustThreshold->value()); tcpSocket->write(command.toUtf8()); logText->append("发送阈值设置: " + command); } } void SafetyMonitor::readData() { QByteArray data = tcpSocket->readAll(); QString sensorData = QString::fromUtf8(data); parseSensorData(sensorData); logText->append("接收数据: " + sensorData); } void SafetyMonitor::parseSensorData(const QString &data) { QStringList parts = data.split(','); for(const QString &part : parts) { if(part.startsWith("TEMP:")) { currentTemp = part.mid(5).toDouble(); } else if(part.startsWith("HUMI:")) { currentHumi = part.mid(5).toDouble(); } else if(part.startsWith("DUST:")) { currentDust = part.mid(5).toDouble(); } else if(part.startsWith("VIB:")) { currentVib = (part.mid(4).toInt() == 1); } else if(part.startsWith("ALARM:")) { currentAlarm = (part.mid(6).toInt() == 1); } } } void SafetyMonitor::updateDisplay() { tempLCD->display(currentTemp); humiLCD->display(currentHumi); dustLCD->display(currentDust); vibStatus->setText(currentVib ? "异常" : "正常"); vibStatus->setStyleSheet(currentVib ? "QLabel { color: red; font-weight: bold; }" : "QLabel { color: green; }"); if(currentAlarm) { alarmStatus->setText("报警中"); alarmStatus->setStyleSheet("QLabel { background-color: red; color: white; padding: 5px; }"); QApplication::beep(); } else { alarmStatus->setText("正常"); alarmStatus->setStyleSheet("QLabel { background-color: green; color: white; padding: 5px; }"); } } void SafetyMonitor::connectionError() { logText->append("连接错误: " + tcpSocket->errorString()); disconnectFromDevice(); } int main(int argc, char *argv[]) { QApplication app(argc, argv); SafetyMonitor monitor; monitor.show(); return app.exec(); } #include "main.moc" 十一、STM32模块代码设计当前项目使用的相关软件工具、模块源码已经上传到网盘:https://ccnr8sukk85n.feishu.cn/wiki/QjY8weDYHibqRYkFP2qcA9aGnvb?from=from_copylink#include "stm32f10x.h" // 引脚定义 #define DHT11_PIN GPIO_Pin_0 #define DHT11_PORT GPIOC #define DUST_ADC ADC1 #define DUST_PIN GPIO_Pin_1 #define DUST_PORT GPIOA #define DUST_ADC_CH ADC_Channel_1 #define VIBRATION_PIN GPIO_Pin_1 #define VIBRATION_PORT GPIOC #define BUZZER_PIN GPIO_Pin_2 #define BUZZER_PORT GPIOC #define ALARM_LED_PIN GPIO_Pin_3 #define ALARM_LED_PORT GPIOC #define STATUS_LED_PIN GPIO_Pin_4 #define STATUS_LED_PORT GPIOC // WiFi模块串口 #define WIFI_USART USART1 #define WIFI_USART_IRQ USART1_IRQn // 阈值定义 volatile float temp_threshold = 35.0; volatile float humi_threshold = 80.0; volatile float dust_threshold = 100.0; // 传感器数据结构 typedef struct { float temperature; float humidity; float dust_density; uint8_t vibration_state; } SensorData; SensorData sensor_data; // 系统状态 volatile uint8_t system_alarm = 0; volatile uint8_t wifi_connected = 0; // 延时函数 void delay_ms(uint32_t ms) { volatile uint32_t i, j; for(i = 0; i < ms; i++) for(j = 0; j < 7200; j++); } void delay_us(uint32_t us) { volatile uint32_t i; for(i = 0; i < us * 8; i++); } // GPIO初始化 void GPIO_Init_Config(void) { RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPCEN; // DHT11 - 推挽输出/浮空输入 GPIOC->CRL &= ~(0xF << 0); GPIOC->CRL |= (0x3 << 0); // 粉尘传感器ADC引脚 - 模拟输入 GPIOA->CRL &= ~(0xF << 4); GPIOA->CRL |= (0x0 << 4); // 震动传感器 - 浮空输入 GPIOC->CRL &= ~(0xF << 4); GPIOC->CRL |= (0x4 << 4); // 蜂鸣器 - 推挽输出 GPIOC->CRL &= ~(0xF << 8); GPIOC->CRL |= (0x3 << 8); // 报警LED - 推挽输出 GPIOC->CRL &= ~(0xF << 12); GPIOC->CRL |= (0x3 << 12); // 状态LED - 推挽输出 GPIOC->CRL &= ~(0xF << 16); GPIOC->CRL |= (0x3 << 16); } // USART1初始化 - WiFi通信 void USART1_Init(void) { RCC->APB2ENR |= RCC_APB2ENR_USART1EN; // PA9-TX 推挽复用输出, PA10-RX 浮空输入 GPIOA->CRH &= ~(0xFF << 4); GPIOA->CRH |= (0xB << 4) | (0x4 << 8); USART1->BRR = 72000000 / 115200; USART1->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; USART1->CR1 |= USART_CR1_RXNEIE; NVIC_EnableIRQ(WIFI_USART_IRQ); NVIC_SetPriority(WIFI_USART_IRQ, 0); } // ADC初始化 - 粉尘传感器 void ADC1_Init(void) { RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; ADC1->SQR1 = 0; ADC1->SQR2 = 0; ADC1->SQR3 = DUST_ADC_CH; ADC1->SMPR2 = 0; ADC1->SMPR2 |= (0x7 << (DUST_ADC_CH * 3)); ADC1->CR1 = 0; ADC1->CR2 = ADC_CR2_ADON | ADC_CR2_CONT; // ADC校准 ADC1->CR2 |= ADC_CR2_RSTCAL; while(ADC1->CR2 & ADC_CR2_RSTCAL); ADC1->CR2 |= ADC_CR2_CAL; while(ADC1->CR2 & ADC_CR2_CAL); delay_ms(1); ADC1->CR2 |= ADC_CR2_ADON; } // DHT11温湿度传感器驱动 uint8_t DHT11_Read_Data(float *temperature, float *humidity) { uint8_t data[5] = {0}; uint8_t i, j; // 主机发送开始信号 DHT11_PORT->BRR = DHT11_PIN; // 拉低 delay_ms(18); DHT11_PORT->BSRR = DHT11_PIN; // 释放 delay_us(40); // 配置为输入模式 GPIOC->CRL &= ~(0xF << 0); GPIOC->CRL |= (0x4 << 0); // 等待DHT11响应 if((DHT11_PORT->IDR & DHT11_PIN)) return 0; delay_us(80); if(!(DHT11_PORT->IDR & DHT11_PIN)) return 0; delay_us(80); // 读取40位数据 for(i = 0; i < 5; i++) { for(j = 0; j < 8; j++) { while(!(DHT11_PORT->IDR & DHT11_PIN)); delay_us(30); if(DHT11_PORT->IDR & DHT11_PIN) { data[i] |= (1 << (7 - j)); while(DHT11_PORT->IDR & DHT11_PIN); } } } // 校验数据 if(data[4] == (data[0] + data[1] + data[2] + data[3])) { *humidity = data[0] + data[1] * 0.1; *temperature = data[2] + data[3] * 0.1; // 恢复输出模式 GPIOC->CRL &= ~(0xF << 0); GPIOC->CRL |= (0x3 << 0); return 1; } // 恢复输出模式 GPIOC->CRL &= ~(0xF << 0); GPIOC->CRL |= (0x3 << 0); return 0; } // 粉尘浓度读取 float Dust_Sensor_Read(void) { uint32_t adc_value = 0; float voltage, density; ADC1->CR2 |= ADC_CR2_ADON; delay_ms(10); // 读取ADC值 for(uint8_t i = 0; i < 10; i++) { ADC1->CR2 |= ADC_CR2_SWSTART; while(!(ADC1->SR & ADC_SR_EOC)); adc_value += ADC1->DR; delay_ms(5); } adc_value /= 10; voltage = (adc_value * 3.3) / 4096.0; // 转换为粉尘浓度 (ug/m3) // 需要根据传感器特性曲线校准 density = (voltage - 0.6) * 200.0; if(density < 0) density = 0; return density; } // 震动传感器读取 uint8_t Vibration_Sensor_Read(void) { return (VIBRATION_PORT->IDR & VIBRATION_PIN) ? 1 : 0; } // 蜂鸣器控制 void Buzzer_Control(uint8_t state) { if(state) { BUZZER_PORT->BSRR = BUZZER_PIN; } else { BUZZER_PORT->BRR = BUZZER_PIN; } } // LED控制 void LED_Control(uint8_t led, uint8_t state) { if(led == 0) { // 报警LED if(state) { ALARM_LED_PORT->BSRR = ALARM_LED_PIN; } else { ALARM_LED_PORT->BRR = ALARM_LED_PIN; } } else { // 状态LED if(state) { STATUS_LED_PORT->BSRR = STATUS_LED_PIN; } else { STATUS_LED_PORT->BRR = STATUS_LED_PIN; } } } // 数据打包发送 void Send_Sensor_Data(void) { char buffer[128]; sprintf(buffer, "TEMP:%.1f,HUMI:%.1f,DUST:%.1f,VIB:%d,ALARM:%d\r\n", sensor_data.temperature, sensor_data.humidity, sensor_data.dust_density, sensor_data.vibration_state, system_alarm); for(uint8_t i = 0; buffer[i] != '\0'; i++) { USART1->DR = buffer[i]; while(!(USART1->SR & USART_SR_TXE)); } } // 报警检测 void Alarm_Check(void) { uint8_t alarm = 0; if(sensor_data.temperature > temp_threshold) alarm = 1; if(sensor_data.humidity > humi_threshold) alarm = 1; if(sensor_data.dust_density > dust_threshold) alarm = 1; if(sensor_data.vibration_state) alarm = 1; if(alarm && !system_alarm) { system_alarm = 1; LED_Control(0, 1); // 打开报警LED Buzzer_Control(1); // 打开蜂鸣器 } else if(!alarm && system_alarm) { system_alarm = 0; LED_Control(0, 0); // 关闭报警LED Buzzer_Control(0); // 关闭蜂鸣器 } } // WiFi数据接收处理 void USART1_IRQHandler(void) { if(USART1->SR & USART_SR_RXNE) { uint8_t data = USART1->DR; static char cmd_buffer[32]; static uint8_t cmd_index = 0; if(data == '\r' || data == '\n') { cmd_buffer[cmd_index] = '\0'; // 解析阈值设置命令 if(strncmp(cmd_buffer, "TEMP:", 5) == 0) { temp_threshold = atof(cmd_buffer + 5); } else if(strncmp(cmd_buffer, "HUMI:", 5) == 0) { humi_threshold = atof(cmd_buffer + 5); } else if(strncmp(cmd_buffer, "DUST:", 5) == 0) { dust_threshold = atof(cmd_buffer + 5); } else if(strcmp(cmd_buffer, "CONNECTED") == 0) { wifi_connected = 1; LED_Control(1, 1); } else if(strcmp(cmd_buffer, "DISCONNECTED") == 0) { wifi_connected = 0; LED_Control(1, 0); } cmd_index = 0; } else if(cmd_index < 31) { cmd_buffer[cmd_index++] = data; } } } // 系统初始化 void System_Init(void) { // 时钟配置 RCC->CFGR |= RCC_CFGR_HPRE_DIV1; RCC->CFGR |= RCC_CFGR_PPRE2_DIV1; RCC->CFGR |= RCC_CFGR_PPRE1_DIV2; FLASH->ACR |= FLASH_ACR_LATENCY_2; RCC->CR |= RCC_CR_HSEON; while(!(RCC->CR & RCC_CR_HSERDY)); RCC->CFGR |= RCC_CFGR_PLLSRC; RCC->CFGR |= RCC_CFGR_PLLMULL9; RCC->CR |= RCC_CR_PLLON; while(!(RCC->CR & RCC_CR_PLLRDY)); RCC->CFGR |= RCC_CFGR_SW_PLL; while(!(RCC->CFGR & RCC_CFGR_SWS_PLL)); SystemCoreClock = 72000000; GPIO_Init_Config(); USART1_Init(); ADC1_Init(); // 初始化状态 LED_Control(0, 0); LED_Control(1, 0); Buzzer_Control(0); } // 主函数 int main(void) { System_Init(); uint32_t last_send_time = 0; uint32_t last_dht11_time = 0; uint32_t last_vibration_time = 0; uint8_t vibration_count = 0; while(1) { uint32_t current_time = SysTick->VAL; // DHT11数据采集 (每2秒) if(current_time - last_dht11_time > 2000) { if(DHT11_Read_Data(&sensor_data.temperature, &sensor_data.humidity)) { // 数据读取成功 } last_dht11_time = current_time; } // 粉尘浓度采集 (持续) sensor_data.dust_density = Dust_Sensor_Read(); // 震动检测 (防抖处理) if(Vibration_Sensor_Read()) { vibration_count++; if(vibration_count > 5) { sensor_data.vibration_state = 1; last_vibration_time = current_time; } } else { vibration_count = 0; if(current_time - last_vibration_time > 5000) { sensor_data.vibration_state = 0; } } // 报警检测 Alarm_Check(); // 数据发送 (每1秒) if(current_time - last_send_time > 1000) { if(wifi_connected) { Send_Sensor_Data(); } last_send_time = current_time; // 状态LED闪烁 static uint8_t led_state = 0; LED_Control(1, led_state); led_state = !led_state; } delay_ms(100); } } 十二、STM32项目核心代码当前项目使用的相关软件工具、模块源码已经上传到网盘:https://ccnr8sukk85n.feishu.cn/wiki/QjY8weDYHibqRYkFP2qcA9aGnvb?from=from_copylink#include "stm32f10x.h" // 传感器数据结构 typedef struct { float temperature; float humidity; float dust_concentration; uint8_t vibration_state; } SensorData; // 报警阈值结构 typedef struct { float temp_threshold; float hum_threshold; float dust_threshold; } ThresholdData; // 全局变量 SensorData sensor_data = {0}; ThresholdData thresholds = {35.0, 80.0, 50.0}; // 默认阈值 uint8_t alarm_flag = 0; uint8_t wifi_connected = 0; // 函数声明 void System_Init(void); void GPIO_Init(void); void UART_Init(void); void TIM_Init(void); void ADC_Init(void); void Read_Sensors(void); void Check_Alarm(void); void Send_Data_To_WIFI(void); void Process_WIFI_Command(void); void LED_Display_Status(void); int main(void) { // 系统初始化 System_Init(); GPIO_Init(); UART_Init(); TIM_Init(); ADC_Init(); // 主循环 while(1) { // 读取传感器数据 Read_Sensors(); // 检查报警条件 Check_Alarm(); // 发送数据到WiFi模块 Send_Data_To_WIFI(); // 处理WiFi命令 Process_WIFI_Command(); // LED状态显示 LED_Display_Status(); // 延时 for(volatile int i = 0; i < 1000000; i++); } } // 系统初始化 void System_Init(void) { // 开启时钟 RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_AFIOEN | RCC_APB2ENR_USART1EN | RCC_APB2ENR_ADC1EN; RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // 系统时钟配置 SystemInit(); } // GPIO初始化 void GPIO_Init(void) { // PA2, PA3 - USART2 TX, RX (连接ESP8266) GPIOA->CRL &= ~(GPIO_CRL_CNF2 | GPIO_CRL_CNF3 | GPIO_CRL_MODE2 | GPIO_CRL_MODE3); GPIOA->CRL |= GPIO_CRL_CNF2_1 | GPIO_CRL_MODE2_0 | // PA2: 复用推挽输出 GPIO_CRL_CNF3_0 | GPIO_CRL_MODE3_0; // PA3: 浮空输入 // PC13 - 系统运行指示灯 GPIOC->CRH &= ~(GPIO_CRH_CNF13 | GPIO_CRH_MODE13); GPIOC->CRH |= GPIO_CRH_MODE13_0; // 推挽输出, 2MHz // PC14 - 报警指示灯 GPIOC->CRH &= ~(GPIO_CRH_CNF14 | GPIO_CRH_MODE14); GPIOC->CRH |= GPIO_CRH_MODE14_0; // 推挽输出, 2MHz // PB8 - 蜂鸣器控制 GPIOB->CRH &= ~(GPIO_CRH_CNF8 | GPIO_CRH_MODE8); GPIOB->CRH |= GPIO_CRH_MODE8_0; // 推挽输出, 2MHz // PA0 - 震动传感器输入 GPIOA->CRL &= ~(GPIO_CRL_CNF0 | GPIO_CRL_MODE0); GPIOA->CRL |= GPIO_CRL_CNF0_0; // 浮空输入 // PA1 - 粉尘传感器ADC输入 GPIOA->CRL &= ~(GPIO_CRL_CNF1 | GPIO_CRL_MODE1); GPIOA->CRL |= GPIO_CRL_CNF1_1; // 模拟输入 } // UART初始化 void UART_Init(void) { // USART2初始化 - 连接ESP8266 USART2->BRR = 0x1D4C; // 9600波特率 @72MHz USART2->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; USART2->CR1 |= USART_CR1_RXNEIE; // 使能接收中断 // 配置NVIC NVIC_EnableIRQ(USART2_IRQn); NVIC_SetPriority(USART2_IRQn, 0); } // 定时器初始化 void TIM_Init(void) { // TIM2用于定时读取传感器 TIM2->PSC = 7200 - 1; // 10kHz TIM2->ARR = 10000 - 1; // 1秒 TIM2->DIER = TIM_DIER_UIE; // 使能更新中断 TIM2->CR1 = TIM_CR1_CEN; // 启动定时器 // 配置NVIC NVIC_EnableIRQ(TIM2_IRQn); NVIC_SetPriority(TIM2_IRQn, 1); } // ADC初始化 void ADC_Init(void) { // ADC1初始化 ADC1->SQR1 = 0; // 1个转换 ADC1->SQR3 = 1; // 通道1 (PA1) ADC1->SMPR2 = ADC_SMPR2_SMP1_2 | ADC_SMPR2_SMP1_1 | ADC_SMPR2_SMP1_0; // 239.5周期 ADC1->CR2 = ADC_CR2_ADON | ADC_CR2_CONT; // 开启ADC, 连续转换 } // 读取传感器数据 void Read_Sensors(void) { // 读取温湿度 (DHT11) // 这里调用DHT11读取函数 // sensor_data.temperature = DHT11_Read_Temperature(); // sensor_data.humidity = DHT11_Read_Humidity(); // 读取粉尘浓度 (GP2Y1051AU0F) ADC1->CR2 |= ADC_CR2_SWSTART; while(!(ADC1->SR & ADC_SR_EOC)); uint16_t adc_value = ADC1->DR; sensor_data.dust_concentration = (adc_value * 3.3 / 4096) * 100; // 转换为浓度值 // 读取震动状态 (SW-420) sensor_data.vibration_state = (GPIOA->IDR & GPIO_IDR_IDR0) ? 1 : 0; } // 检查报警条件 void Check_Alarm(void) { alarm_flag = 0; // 温度报警 if(sensor_data.temperature > thresholds.temp_threshold) alarm_flag |= 0x01; // 湿度报警 if(sensor_data.humidity > thresholds.hum_threshold) alarm_flag |= 0x02; // 粉尘浓度报警 if(sensor_data.dust_concentration > thresholds.dust_threshold) alarm_flag |= 0x04; // 震动报警 if(sensor_data.vibration_state) alarm_flag |= 0x08; // 控制报警设备 if(alarm_flag) { GPIOC->BSRR = GPIO_BSRR_BR14; // 报警灯亮 GPIOB->BSRR = GPIO_BSRR_BS8; // 蜂鸣器响 } else { GPIOC->BSRR = GPIO_BSRR_BS14; // 报警灯灭 GPIOB->BSRR = GPIO_BSRR_BR8; // 蜂鸣器关 } } // 发送数据到WiFi模块 void Send_Data_To_WIFI(void) { if(wifi_connected) { // 构造JSON格式数据 char buffer[128]; sprintf(buffer, "{\"temp\":%.1f,\"hum\":%.1f,\"dust\":%.1f,\"vib\":%d,\"alarm\":%d}", sensor_data.temperature, sensor_data.humidity, sensor_data.dust_concentration, sensor_data.vibration_state, alarm_flag); // 发送数据 for(int i = 0; buffer[i] != '\0'; i++) { USART2->DR = buffer[i]; while(!(USART2->SR & USART_SR_TXE)); } USART2->DR = '\n'; while(!(USART2->SR & USART_SR_TXE)); } } // 处理WiFi命令 void Process_WIFI_Command(void) { // 处理来自APP的阈值设置命令 // 实际实现中需要解析JSON命令 } // LED状态显示 void LED_Display_Status(void) { static uint32_t blink_counter = 0; blink_counter++; // 系统运行指示灯闪烁 if(blink_counter % 2 == 0) GPIOC->BSRR = GPIO_BSRR_BS13; // 亮 else GPIOC->BSRR = GPIO_BSRR_BR13; // 灭 } // USART2中断服务函数 void USART2_IRQHandler(void) { if(USART2->SR & USART_SR_RXNE) { uint8_t data = USART2->DR; // 处理WiFi模块响应 if(data == 'O' || data == 'K') // 简化处理 { wifi_connected = 1; } // 这里可以添加更多命令解析逻辑 } } // TIM2中断服务函数 - 定时读取传感器 void TIM2_IRQHandler(void) { if(TIM2->SR & TIM_SR_UIF) { TIM2->SR &= ~TIM_SR_UIF; // 设置标志触发主循环读取传感器 // 实际实现中可以使用标志位或直接调用Read_Sensors() } } 十三、总结本项目成功设计并实现了一个基于STM32的面粉厂车间安全生产系统,旨在通过智能化手段保障车间环境的安全运行。系统集成了温湿度、粉尘浓度和震动监测等多种传感器,能够实时采集关键参数,并在异常时自动触发声光报警,有效预防潜在风险。系统以STM32F103RCT6为主控核心,结合DHT11、GP2Y1051AU0F和SW-420等传感器模块,实现了数据的准确采集与处理。通过ESP8266 WiFi模块,系统支持远程通信,使终端APP能够实时显示数据并接收警报,同时允许用户灵活设置阈值参数,增强了系统的适应性和智能化程度。整体设计注重实用性与可靠性,通过本地LED指示灯和声光报警单元提供直观的状态反馈,确保了现场维护的便捷性。该系统不仅提升了面粉厂车间的安全管理水平,还为类似工业环境提供了可扩展的解决方案,具有广泛的应用前景。
  • [技术干货] 基于STM32设计的水资源监测系统
    一、前言1.1 项目开发背景水资源作为人类赖以生存和发展的基础性自然资源,其质量直接关系到生态环境的可持续发展和人民群众的生命健康。随着工业化与城镇化的快速推进,水体污染问题日益严重,水资源的保护与科学管理已成为全球关注的重要议题。为了实现对水环境的高效监测与管理,亟需构建一套集成化、智能化的水资源环境监测系统,实现水质数据的实时采集、分析、传输与展示,为环境治理提供数据支撑与决策依据。传统的水质监测方法多以人工采样与实验室分析为主,周期长、效率低,难以及时反映动态变化的水质状况,无法满足当前水环境实时监控的需求。借助嵌入式技术、物联网通信技术及传感器技术的发展,构建基于STM32嵌入式平台的智能水环境监测系统成为一种可行而有效的方案。该系统通过多种水质传感器实现对水温、pH值、溶解氧、电导率、水位等参数的连续自动监测,并通过无线通信方式将数据实时上传至物联网平台,实现远程监控与报警。本项目基于STM32F103C8T6单片机作为主控核心,结合DS18B20、pH传感器、溶解氧传感器、TDS电导率传感器、水位传感器等多种模块构建多参数水质检测系统,通过IIC接口实现本地OLED显示,结合Air780E 4G通信模块实现远程数据上传。项目采用MQTT协议接入华为云物联网平台,同时配套开发基于Qt(C++)的Android手机APP和Windows上位机,用于数据可视化展示和历史数据分析,提升系统的可用性与智能化水平。系统还集成了高电平触发蜂鸣器,在监测数据出现异常时可实现现场声光报警,有效提升应急响应能力。该系统具有部署灵活、运行稳定、实时性强等优点,广泛适用于河流、湖泊、水库、水产养殖、污水处理等多种场景,对于推动水环境保护信息化、智能化具有重要现实意义和应用价值。1.2 设计实现的功能(1)水温检测功能:通过DS18B20防水温度传感器对水体温度进行实时监测,并将数据传送至主控芯片处理。(2)pH值检测功能:采用模拟量输出的pH传感器对水体酸碱度进行检测,经ADC采集并通过算法转换为实际pH值。(3)溶解氧检测功能:采用485接口输出的溶解氧传感器,通过485转串口模块与STM32通信,实时获取水中溶解氧浓度。(4)水位检测功能:使用电阻式模拟量输出的水位传感器,实现水体液位的实时监测,并通过ADC接口采集。(5)TDS电导率检测功能:通过TDS传感器采集水体的电导率信息,反映水中溶解固体物的含量。(6)OLED本地显示功能:通过0.96寸IIC接口OLED屏,实时显示采集的水温、pH值、溶解氧、水位、电导率等关键参数,便于现场查看。(7)数据上云功能:利用Air780E 4G模块,通过MQTT协议将采集的所有水质参数上传至华为云IoT物联网平台,实现远程数据可视化管理。(8)远程APP与上位机显示功能:基于Qt(C++)开发Android手机APP与Windows上位机,接收云端数据并以图表方式进行可视化展示,支持实时查看与历史数据查询。(9)历史数据记录与折线图查看功能:Android APP端支持每日历史数据的存储和查询功能,用户可通过折线图形式查看各项水质指标的变化趋势。(10)异常报警功能:当系统监测到水质参数异常(如超出设定阈值)时,触发高电平蜂鸣器进行现场声响报警,提示相关人员及时处理。1.3 项目硬件模块组成(1)STM32F103C8T6主控模块:作为系统的核心控制单元,负责采集传感器数据、数据处理、本地显示和数据上传等任务。(2)DS18B20水温传感器模块:用于实时采集水体温度数据,具有防水封装,通信方式为单总线接口。(3)pH值检测传感器模块:输出模拟电压信号,通过STM32的ADC采样接口读取,结合公式算法换算出实际pH值。(4)溶解氧传感器模块:采用RS485接口输出,通过485转TTL串口模块连接STM32,实现对水体溶解氧浓度的读取。(5)水位检测模块:使用电阻式模拟量输出的水位传感器,通过STM32的ADC接口获取水位高度信息。(6)TDS电导率传感器模块:模拟量输出,检测水中溶解固体的电导率值,通过ADC采集处理。(7)OLED显示模块:采用0.96寸IIC通信接口,用于实时显示采集到的水温、pH值、溶解氧、水位、电导率等数据。(8)Air780E 4G通信模块:负责将处理后的水质数据通过MQTT协议上传至华为云IoT物联网平台,实现远程数据通信。(9)蜂鸣器模块:采用高电平触发方式,在检测数据异常时发出声响警报,用于本地故障提示。(10)485转串口通信模块:用于将溶解氧传感器的RS485信号转换为STM32可识别的TTL串口信号,实现串口数据通信。1.4 设计思路本项目设计一套基于STM32的水资源环境监测系统,实现多项水质参数的实时监测、本地显示、远程上传和异常报警等功能。系统整体设计思路遵循“模块化、集成化、智能化”的原则,围绕数据采集、数据处理、数据传输与数据展示四个核心环节进行系统开发。在数据采集部分,系统选用多种水质传感器模块,分别用于采集水温、pH值、溶解氧、水位和电导率等核心水质指标。其中,温度传感器DS18B20通过单总线通信方式获取数据;pH值和TDS电导率采用模拟量输出方式,STM32通过ADC模块进行电压采样并结合算法转换为实际值;水位传感器同样输出模拟信号,方便读取液位变化;溶解氧传感器则通过RS485通信输出,经过485转串口模块后接入STM32的串口读取。通过合理搭配通信协议和接口资源,实现了多参数、多通道的水质数据采集。在数据处理与显示方面,STM32F103C8T6主控芯片作为系统核心,负责各项传感器数据的采集调度与数值处理。处理后的数据通过IIC接口发送至0.96寸OLED显示屏,便于现场查看当前水质状态。系统设计时充分考虑资源优化,确保多个模块协同工作,避免通信冲突和资源浪费。在数据传输部分,系统集成了Air780E 4G模块,实现远程无线通信。选用轻量级的MQTT协议,将处理后的数据通过4G网络上传至华为云物联网平台,确保数据上传的稳定性和实时性。MQTT协议具有发布/订阅机制,能够降低网络带宽消耗,提高通信效率,特别适合物联网设备的低功耗、低带宽应用场景。为实现远程数据展示与分析,系统配套开发Android手机APP与Windows上位机软件,采用Qt(C++)设计,支持从华为云物联网平台接收数据并进行可视化展示。APP端还支持历史数据存储与查看功能,用户可按日期查询历史记录,并以折线图形式分析水质变化趋势。通过跨平台设计,实现了移动端与PC端的无缝对接。系统具备异常报警功能,当监测到某项参数超过预设阈值时,STM32将自动驱动蜂鸣器报警,实现现场预警,增强系统的安全性和响应能力。整体系统硬件采用模块化结构设计,便于后期扩展与维护,同时提升了项目的稳定性和实用性。1.5 系统功能总结功能模块实现内容硬件支持通信方式水温检测实时监测水体温度DS18B20温度传感器单总线pH值检测检测水体酸碱度,转换为实际pH值显示模拟pH传感器模拟量 → ADC溶解氧检测检测水中溶解氧浓度溶解氧传感器 + 485转串口模块RS485 → TTL串口水位检测实时监测水位高度电阻式水位传感器模拟量 → ADCTDS电导率检测检测水体中溶解固体含量的电导率TDS电导率传感器模拟量 → ADC本地显示实时显示各项水质数据(温度、pH、溶解氧、水位、电导率)0.96寸OLED显示屏(IIC接口)IIC通信数据上传将采集数据上传至华为云IoT平台Air780E 4G模块MQTT协议(4G网络)远程数据显示在Android APP和Windows上位机端查看实时及历史数据,支持折线图显示Qt开发APP与上位机MQTT订阅(云端同步)历史数据管理APP端支持每日数据存储与查询,图形化展示水质变化Android手机APP本地+云端数据同步异常报警当数据异常超阈值,蜂鸣器报警提醒高电平触发蜂鸣器IO控制1.6 开发工具的选择【1】设备端开发STM32的编程语言选择C语言,C语言执行效率高,大学里主学的C语言,C语言编译出来的可执行文件最接近于机器码,汇编语言执行效率最高,但是汇编的移植性比较差,目前在一些操作系统内核里还有一些低配的单片机使用的较多,平常的单片机编程还是以C语言为主。C语言的执行效率仅次于汇编,语法理解、代码通用性强,也支持跨平台,在嵌入式底层、单片机编程里用的非常多,当前的设计就是采用C语言开发。开发工具选择Keil,keil是一家世界领先的嵌入式微控制器软件开发商,在2015年,keil被ARM公司收购。因为当前芯片选择的是STM32F103系列,STMF103是属于ARM公司的芯片构架、Cortex-M3内核系列的芯片,所以使用Kile来开发STM32是有先天优势的,而keil在各大高校使用的也非常多,很多教科书里都是以keil来教学,开发51单片机、STM32单片机等等。目前作为MCU芯片开发的软件也不只是keil一家独大,IAR在MCU微处理器开发领域里也使用的非常多,IAR扩展性更强,也支持STM32开发,也支持其他芯片,比如:CC2530,51单片机的开发。从软件的使用上来讲,IAR比keil更加简洁,功能相对少一些。如果之前使用过keil,而且使用频率较多,已经习惯再使用IAR是有点不适应界面的。【2】上位机开发上位机的开发选择Qt框架,编程语言采用C++;Qt是一个1991年由Qt Company开发的跨平台C++图形用户界面应用程序开发框架。它既可以开发GUI程序,也可用于开发非GUI程序,比如控制台工具和服务器。Qt是面向对象的框架,使用特殊的代码生成扩展(称为元对象编译器(Meta Object Compiler, moc))以及一些宏,Qt很容易扩展,并且允许真正地组件编程。Qt能轻松创建具有原生C++性能的连接设备、用户界面(UI)和应用程序。它功能强大且结构紧凑,拥有直观的工具和库。1.7 数据交互字段在将本项目采集的数据上传至 华为云 IoT 物联网平台 时,需要为设备模型(即产品模型)定义一组字段属性(属性服务),用于描述设备采集到的各种数据。这些属性应能完整表达你系统的功能。在华为云 IoT 平台定义以下字段属性:字段名称(标识符)数据类型单位描述示例值waterTempfloat℃水温22.5phValuefloat无PH值7.21dissolvedOxygenfloatmg/L溶解氧浓度6.85waterLevelfloatcm当前水位高度13.2tdsValuefloatppmTDS电导率(溶解性总固体)580uploadTimestring时间戳数据上传时间2025-06-05T12:30:00alarmStatusint无异常报警状态(0正常,1异常)0说明:数据类型需与华为云平台支持的类型一致,如 int、float、string、boolean。alarmStatus 可用于报警提示功能,如当PH超标、TDS过高等时上传 1。uploadTime 可以作为历史数据存储和查询依据。MQTT 上传格式(JSON 结构):{ "waterTemp": 22.5, "phValue": 7.21, "dissolvedOxygen": 6.85, "waterLevel": 13.2, "tdsValue": 580, "alarmStatus": 0, "uploadTime": "2025-06-05T12:30:00" } 二、硬件介绍2.1 STM32F102C8T6STM32F103C8T6 是 STMicroelectronics 公司推出的一款基于 ARM Cortex-M3 内核的 32 位高性能低功耗微控制器,属于 STM32F1 系列中的主流型产品。该芯片具有较高的性价比和稳定的性能,被广泛应用于嵌入式控制系统、物联网设备、传感器采集、工业自动化等领域。STM32F103C8T6 运行主频为 72MHz,具有 64KB 的 Flash 程序存储器和 20KB 的 SRAM,满足中小型嵌入式项目的代码和数据存储需求。它集成丰富的外设接口,包括多达 37 个通用 I/O 端口、3 个通用定时器、1 个高级定时器、多个串口(USART)、SPI 接口、I²C 接口以及 12 位 ADC 模块(多达 10 个通道),能够支持多种模拟和数字信号输入输出。该芯片封装为 LQFP-48(48 引脚封装),方便用户在 PCB 设计和元件焊接过程中操作。它支持 2.0V~3.6V 的供电电压,具有较强的抗干扰能力和低功耗特性,适合在复杂或要求较高的工作环境中运行。在开发方面,STM32F103C8T6 支持多种开发工具,如 Keil、IAR、STM32CubeIDE 等,开发者既可以使用标准外设库(SPL)或硬件抽象库(HAL)开发,也可以直接基于寄存器编程方式进行底层控制,便于系统的精细调试与资源优化。芯片支持通过 ISP 方式(串口)下载程序,用户可以方便地进行程序更新和调试。STM32F103C8T6 是一款性能稳定、资源丰富、接口齐全的通用型微控制器,非常适合应用在多传感器数据采集、通信控制和实时处理等嵌入式系统设计中,如本项目中的水资源环境监测系统。2.2 Air780eAir780E 是移远通信(Quectel)推出的一款高性能、低功耗、功能丰富的 4G 全网通通信模块,适用于物联网领域中对数据传输速率、可靠性和远程接入能力有较高要求的应用场景。该模块支持 LTE CAT1 通信标准,可实现较快的数据传输速度与稳定的网络连接能力,广泛应用于智能终端、远程监控、工业物联、智慧农业等系统中。Air780E 支持中国三大运营商(移动、联通、电信)的 4G 网络,具备良好的网络兼容性和通信覆盖能力。模块内部集成了 TCP/IP 协议栈,并支持常用的通信协议如 MQTT、HTTP、FTP、UDP、TCP 等,用户可以通过串口 AT 指令方式快速配置模块,实现云平台的数据上传与下发控制。对于本项目,Air780E 通过 MQTT 协议将采集的水质数据上传到华为云物联网平台,起到了关键的数据传输桥梁作用。硬件接口方面,Air780E 模块提供标准的 UART 接口,方便与主控芯片(如 STM32)通信,支持常见的波特率配置,并且具有多路 GPIO、SIM 卡接口、天线接口、电源管理等功能。模块支持主控通过串口发送 AT 指令来进行拨号上网、参数设置、数据发送、断线重连等操作,操作、上手快速,能够显著降低开发难度。在功耗控制方面,Air780E 具备多种省电模式,如飞行模式、睡眠模式和掉电模式,特别适合电池供电类的嵌入式应用场景。模块体积小巧,集成度高,封装方式适合贴片安装,可方便集成在各种尺寸受限的设备中。Air780E 是一款集高可靠性、易用性和网络兼容性于一体的 4G 通信模块,特别适合用于需要远程数据传输、接入云平台的嵌入式系统。在本水资源环境监测项目中,它有效实现了传感器数据从本地终端到云端的远距离传输,是系统远程监控功能实现的关键组成部分。2.3 TDS传感器TDS电导率检测传感器是一种用于检测水中溶解性总固体(Total Dissolved Solids)含量的传感器,广泛应用于水质检测、饮用水处理、水产养殖、农业灌溉等场景。TDS 的单位通常为 ppm(每百万分之一),代表每升水中含有多少毫克的可溶性物质,如矿物质、盐类、金属离子等。该传感器的工作原理基于电导率的测量,即通过检测水体对电流的导通能力,间接判断水中溶解固体的浓度。水中含有的离子越多,导电能力越强,对应的TDS值也越高。传感器内部通常包含两个电极,当电极浸入水中后,电解质会使电流形成通路,控制模块通过测量电导值并结合算法,将其转换为实际的TDS数值。TDS传感器通常输出为模拟电压信号,便于与单片机的ADC模块(如STM32的ADC)进行对接。传感器输出的电压与TDS值成正比,经过采样与算法换算后即可获得相应的ppm浓度值。在应用中,为了获得更准确的测量结果,常配合温度传感器(如DS18B20)进行温度补偿,以消除不同水温对电导率造成的影响。TDS传感器一般采用防水探头,外壳材质耐腐蚀,适合长期浸入水中使用。控制模块则具备电压调节、滤波放大、信号校准等功能,提高输出信号的稳定性和抗干扰能力。在本水资源环境监测系统中,TDS电导率传感器用于实时监测水体中溶解物的浓度,反映水质纯净程度,为判断水质优劣提供关键依据。通过STM32的ADC接口读取其输出的模拟信号,并结合补偿算法计算出当前水体的TDS值,再通过OLED本地显示及4G模块上传至物联网平台,为远程水质管理与预警提供数据支持。2.4 溶解氧传感器溶解氧检测传感器是一种用于测量水体中溶解氧(Dissolved Oxygen, DO)浓度的传感器,广泛应用于水质监测、水产养殖、环保工程、污水处理等领域。溶解氧是水中生物呼吸和水体自净能力的重要指标,过高或过低都会影响水体生态系统的稳定性。常见的溶解氧传感器类型主要包括电化学式(极谱型、盖尔文型)和光学式。电化学式传感器工作原理基于氧分子通过膜扩散进入电极系统发生还原反应,产生电流信号,该信号与溶解氧浓度成正比。光学式则通过荧光猝灭原理测量氧气浓度,具有更高的稳定性和更长的使用寿命,但成本相对较高。在本系统中使用的溶解氧传感器为 RS-485 接口输出型,具有抗干扰能力强、传输距离远、通信稳定等优点。通过与485转串口模块配合,STM32单片机可以通过串口协议(如Modbus-RTU)与传感器进行通信,实时读取水中溶解氧的数值。该传感器通常支持标准指令查询,返回的数据格式规范,便于主控程序解析。传感器通常采用防水封装,能够长期浸入水中运行,且部分型号支持自动温度补偿,进一步提高测量的准确性。部分高端型号还具备校准功能,用户可以根据实际水质环境定期校准以保持精度。在本水资源环境监测系统中,溶解氧传感器的作用是监控水体中氧含量水平,从而判断是否存在水质恶化、生物缺氧等异常情况。通过 STM32 读取溶解氧值后,不仅可在 OLED 显示屏上实时查看,还可以通过 4G 模块上传至云平台,便于远程监测和历史数据分析,为水质保护和应急响应提供依据。三、部署华为云物联网平台华为云官网: https://www.huaweicloud.com/打开官网,搜索物联网,就能快速找到 设备接入IoTDA。3.1 物联网平台介绍华为云物联网平台(IoT 设备接入云服务)提供海量设备的接入和管理能力,将物理设备联接到云,支撑设备数据采集上云和云端下发命令给设备进行远程控制,配合华为云其他产品,帮助我们快速构筑物联网解决方案。使用物联网平台构建一个完整的物联网解决方案主要包括3部分:物联网平台、业务应用和设备。物联网平台作为连接业务应用和设备的中间层,屏蔽了各种复杂的设备接口,实现设备的快速接入;同时提供强大的开放能力,支撑行业用户构建各种物联网解决方案。设备可以通过固网、2G/3G/4G/5G、NB-IoT、Wifi等多种网络接入物联网平台,并使用LWM2M/CoAP、MQTT、HTTPS协议将业务数据上报到平台,平台也可以将控制命令下发给设备。业务应用通过调用物联网平台提供的API,实现设备数据采集、命令下发、设备管理等业务场景。3.2 开通物联网服务地址: https://www.huaweicloud.com/product/iothub.html开通免费单元。点击立即创建。正在创建标准版实例,需要等待片刻。创建完成之后,点击详情。 可以看到标准版实例的设备接入端口和地址。下面框起来的就是端口号和域名点击实例名称,可以查看当前免费单元的配置情况。开通之后,点击接入信息,也能查看接入信息。 我们当前设备准备采用MQTT协议接入华为云平台,这里可以看到MQTT协议的地址和端口号等信息。总结:端口号: MQTT (1883)| MQTTS (8883) 接入地址: dab1a1f2c6.st1.iotda-device.cn-north-4.myhuaweicloud.com根据域名地址得到IP地址信息:打开Windows电脑的命令行控制台终端,使用ping 命令。ping一下即可。Microsoft Windows [版本 10.0.19045.5011] (c) Microsoft Corporation。保留所有权利。 C:\Users\Lenovo>ping dab1a1f2c6.st1.iotda-device.cn-north-4.myhuaweicloud.com 正在 Ping dab1a1f2c6.st1.iotda-device.cn-north-4.myhuaweicloud.com [117.78.5.125] 具有 32 字节的数据: 来自 117.78.5.125 的回复: 字节=32 时间=37ms TTL=44 来自 117.78.5.125 的回复: 字节=32 时间=37ms TTL=44 来自 117.78.5.125 的回复: 字节=32 时间=37ms TTL=44 来自 117.78.5.125 的回复: 字节=32 时间=37ms TTL=44 117.78.5.125 的 Ping 统计信息: 数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失), 往返行程的估计时间(以毫秒为单位): 最短 = 37ms,最长 = 37ms,平均 = 37ms C:\Users\Lenovo> MQTT协议接入端口号有两个,1883是非加密端口,8883是证书加密端口,单片机无法加载证书,所以使用1883端口合适。3.3 创建产品链接:https://console.huaweicloud.com/iotdm/?region=cn-north-4#/dm-dev/all-product?instanceId=03c5c68c-e588-458c-90c3-9e4c640be7af(1)创建产品(2)填写产品信息根据自己产品名字填写,下面的设备类型选择自定义类型。(3)产品创建成功创建完成之后点击查看详情。(4)添加自定义模型产品创建完成之后,点击进入产品详情页面,翻到最下面可以看到模型定义。模型来说: 就是存放设备上传到云平台的数据。你可以根据自己的产品进行创建。比如:在将本项目采集的数据上传至 华为云 IoT 物联网平台 时,需要为设备模型(即产品模型)定义一组字段属性(属性服务),用于描述设备采集到的各种数据。这些属性应能完整表达你系统的功能。根据项目功能,在华为云 IoT 平台定义以下字段属性:字段名称(标识符)数据类型单位描述示例值waterTempfloat℃水温22.5phValuefloat无PH值7.21dissolvedOxygenfloatmg/L溶解氧浓度6.85waterLevelfloatcm当前水位高度13.2tdsValuefloatppmTDS电导率(溶解性总固体)580uploadTimestring时间戳数据上传时间2025-06-05T12:30:00alarmStatusint无异常报警状态(0正常,1异常)0说明:数据类型需与华为云平台支持的类型一致,如 int、float、string、boolean。alarmStatus 可用于报警提示功能,如当PH超标、TDS过高等时上传 1。uploadTime 可以作为历史数据存储和查询依据。MQTT 上传格式(JSON 结构):{ "waterTemp": 22.5, "phValue": 7.21, "dissolvedOxygen": 6.85, "waterLevel": 13.2, "tdsValue": 580, "alarmStatus": 0, "uploadTime": "2025-06-05T12:30:00" } 在华为云 IoT 平台创建产品后,可以在 产品模型 > 服务 > 属性 中添加上述字段,并配置设备上传的 Topic。STM32 端通过 AT 指令控制 4G 模块(Air780E)连接 MQTT Broker,将这些字段打包为 JSON 并上报即可。先点击自定义模型。再创建一个服务ID。接着点击新增属性。3.4 添加设备产品是属于上层的抽象模型,接下来在产品模型下添加实际的设备。添加的设备最终需要与真实的设备关联在一起,完成数据交互。(1)注册设备(2)根据自己的设备填写(3)保存设备信息创建完毕之后,点击保存并关闭,得到创建的设备密匙信息。该信息在后续生成MQTT三元组的时候需要使用。(4)设备创建完成(5)设备详情3.5 MQTT协议主题订阅与发布(1)MQTT协议介绍当前的设备是采用MQTT协议与华为云平台进行通信。MQTT是一个物联网传输协议,它被设计用于轻量级的发布/订阅式消息传输,旨在为低带宽和不稳定的网络环境中的物联网设备提供可靠的网络服务。MQTT是专门针对物联网开发的轻量级传输协议。MQTT协议针对低带宽网络,低计算能力的设备,做了特殊的优化,使得其能适应各种物联网应用场景。目前MQTT拥有各种平台和设备上的客户端,已经形成了初步的生态系统。MQTT是一种消息队列协议,使用发布/订阅消息模式,提供一对多的消息发布,解除应用程序耦合,相对于其他协议,开发更;MQTT协议是工作在TCP/IP协议上;由TCP/IP协议提供稳定的网络连接;所以,只要具备TCP协议栈的网络设备都可以使用MQTT协议。 本次设备采用的ESP8266就具备TCP协议栈,能够建立TCP连接,所以,配合STM32代码里封装的MQTT协议,就可以与华为云平台完成通信。华为云的MQTT协议接入帮助文档在这里: https://support.huaweicloud.com/devg-iothub/iot_02_2200.html业务流程:(2)华为云平台MQTT协议使用限制描述限制支持的MQTT协议版本3.1.1与标准MQTT协议的区别支持Qos 0和Qos 1支持Topic自定义不支持QoS2不支持will、retain msgMQTTS支持的安全等级采用TCP通道基础 + TLS协议(最高TLSv1.3版本)单帐号每秒最大MQTT连接请求数无限制单个设备每分钟支持的最大MQTT连接数1单个MQTT连接每秒的吞吐量,即带宽,包含直连设备和网关3KB/sMQTT单个发布消息最大长度,超过此大小的发布请求将被直接拒绝1MBMQTT连接心跳时间建议值心跳时间限定为30至1200秒,推荐设置为120秒产品是否支持自定义Topic支持消息发布与订阅设备只能对自己的Topic进行消息发布与订阅每个订阅请求的最大订阅数无限制(3)主题订阅格式帮助文档地址:https://support.huaweicloud.com/devg-iothub/iot_02_2200.html对于设备而言,一般会订阅平台下发消息给设备 这个主题。设备想接收平台下发的消息,就需要订阅平台下发消息给设备 的主题,订阅后,平台下发消息给设备,设备就会收到消息。如果设备想要知道平台下发的消息,需要订阅上面图片里标注的主题。以当前设备为例,最终订阅主题的格式如下: $oc/devices/{device_id}/sys/messages/down 最终的格式: $oc/devices/663cb18871d845632a0912e7_dev1/sys/messages/down(4)主题发布格式对于设备来说,主题发布表示向云平台上传数据,将最新的传感器数据,设备状态上传到云平台。这个操作称为:属性上报。帮助文档地址:https://support.huaweicloud.com/usermanual-iothub/iot_06_v5_3010.html根据帮助文档的介绍, 当前设备发布主题,上报属性的格式总结如下:发布的主题格式: $oc/devices/{device_id}/sys/properties/report 最终的格式: $oc/devices/663cb18871d845632a0912e7_dev1/sys/properties/report 发布主题时,需要上传数据,这个数据格式是JSON格式。 上传的JSON数据格式如下: { "services": [ { "service_id": <填服务ID>, "properties": { "<填属性名称1>": <填属性值>, "<填属性名称2>": <填属性值>, .......... } } ] } 根据JSON格式,一次可以上传多个属性字段。 这个JSON格式里的,服务ID,属性字段名称,属性值类型,在前面创建产品的时候就已经介绍了,不记得可以翻到前面去查看。 根据这个格式,组合一次上传的属性数据: {"services": [{"service_id": "stm32","properties":{"你的字段名字1":30,"你的字段名字2":10,"你的字段名字3":1,"你的字段名字4":0}}]} 3.6 MQTT三元组MQTT协议登录需要填用户ID,设备ID,设备密码等信息,就像我们平时登录QQ,微信一样要输入账号密码才能登录。MQTT协议登录的这3个参数,一般称为MQTT三元组。接下来介绍,华为云平台的MQTT三元组参数如何得到。(1)MQTT服务器地址要登录MQTT服务器,首先记得先知道服务器的地址是多少,端口是多少。帮助文档地址:https://console.huaweicloud.com/iotdm/?region=cn-north-4#/dm-portal/homeMQTT协议的端口支持1883和8883,它们的区别是:8883 是加密端口更加安全。但是单片机上使用比较困难,所以当前的设备是采用1883端口进连接的。根据上面的域名和端口号,得到下面的IP地址和端口号信息: 如果设备支持填写域名可以直接填域名,不支持就直接填写IP地址。 (IP地址就是域名解析得到的)华为云的MQTT服务器地址:117.78.5.125 华为云的MQTT端口号:1883 如何得到IP地址?如何域名转IP? 打开Windows的命令行输入以下命令。ping ad635970a1.st1.iotda-device.cn-north-4.myhuaweicloud.com(2)生成MQTT三元组华为云提供了一个在线工具,用来生成MQTT鉴权三元组: https://iot-tool.obs-website.cn-north-4.myhuaweicloud.com/打开这个工具,填入设备的信息(也就是刚才创建完设备之后保存的信息),点击生成,就可以得到MQTT的登录信息了。下面是打开的页面:填入设备的信息: (上面两行就是设备创建完成之后保存得到的)直接得到三元组信息。得到三元组之后,设备端通过MQTT协议登录鉴权的时候,填入参数即可。ClientId 663cb18871d845632a0912e7_dev1_0_0_2024050911 Username 663cb18871d845632a0912e7_dev1 Password 71b82deae83e80f04c4269b5bbce3b2fc7c13f610948fe210ce18650909ac2373.7 模拟设备登录测试经过上面的步骤介绍,已经创建了产品,设备,数据模型,得到MQTT登录信息。 接下来就用MQTT客户端软件模拟真实的设备来登录平台。测试与服务器通信是否正常。MQTT软件下载地址【免费】: https://download.csdn.net/download/xiaolong1126626497/89928772(1)填入登录信息打开MQTT客户端软件,对号填入相关信息(就是上面的文本介绍)。然后,点击登录,订阅主题,发布主题。(2)打开网页查看完成上面的操作之后,打开华为云网页后台,可以看到设备已经在线了。点击详情页面,可以看到上传的数据:到此,云平台的部署已经完成,设备已经可以正常上传数据了。(3)MQTT登录测试参数总结MQTT服务器: 117.78.5.125 MQTT端口号: 183 //物联网服务器的设备信息 #define MQTT_ClientID "663cb18871d845632a0912e7_dev1_0_0_2024050911" #define MQTT_UserName "663cb18871d845632a0912e7_dev1" #define MQTT_PassWord "71b82deae83e80f04c4269b5bbce3b2fc7c13f610948fe210ce18650909ac237" //订阅与发布的主题 #define SET_TOPIC "$oc/devices/663cb18871d845632a0912e7_dev1/sys/messages/down" //订阅 #define POST_TOPIC "$oc/devices/663cb18871d845632a0912e7_dev1/sys/properties/report" //发布 发布的数据: {"services": [{"service_id": "stm32","properties":{"你的字段名字1":30,"你的字段名字2":10,"你的字段名字3":1,"你的字段名字4":0}}]} 3.8 创建IAM账户创建一个IAM账户,因为接下来开发上位机,需要使用云平台的API接口,这些接口都需要token进行鉴权。来说,就是身份的认证。 调用接口获取Token时,就需要填写IAM账号信息。所以,接下来演示一下过程。地址: https://console.huaweicloud.com/iam/?region=cn-north-4#/iam/users**【1】获取项目凭证 ** 点击左上角用户名,选择下拉菜单里的我的凭证项目凭证:28add376c01e4a61ac8b621c714bf459【2】创建IAM用户鼠标放在左上角头像上,在下拉菜单里选择统一身份认证。点击左上角创建用户。创建成功:【3】创建完成用户信息如下:主用户名 l19504562721 IAM用户 ds_abc 密码 DS12345678 3.9 获取影子数据帮助文档:https://support.huaweicloud.com/api-iothub/iot_06_v5_0079.html设备影子介绍:设备影子是一个用于存储和检索设备当前状态信息的JSON文档。 每个设备有且只有一个设备影子,由设备ID唯一标识 设备影子仅保存最近一次设备的上报数据和预期数据 无论该设备是否在线,都可以通过该影子获取和设置设备的属性来说:设备影子就是保存,设备最新上传的一次数据。我们设计的软件里,如果想要获取设备的最新状态信息,就采用设备影子接口。如果对接口不熟悉,可以先进行在线调试:https://apiexplorer.developer.huaweicloud.com/apiexplorer/doc?product=IoTDA&api=ShowDeviceShadow在线调试接口,可以请求影子接口,了解请求,与返回的数据格式。调试完成看右下角的响应体,就是返回的影子数据。设备影子接口返回的数据如下:{ "device_id": "663cb18871d845632a0912e7_dev1", "shadow": [ { "service_id": "stm32", "desired": { "properties": null, "event_time": null }, "reported": { "properties": { "DHT11_T": 18, "DHT11_H": 90, "BH1750": 38, "MQ135": 70 }, "event_time": "20240509T113448Z" }, "version": 3 } ] } 调试成功之后,可以得到访问影子数据的真实链接,接下来的代码开发中,就采用Qt写代码访问此链接,获取影子数据,完成上位机开发。链接如下:https://ad635970a1.st1.iotda-app.cn-north-4.myhuaweicloud.com:443/v5/iot/28add376c01e4a61ac8b621c714bf459/devices/663cb18871d845632a0912e7_dev1/shadow3.10 访问接口的代码实现(1)配置 Qt 项目在 Qt 项目的 .pro 文件中,加入对 libcurl 的支持:QT += core CONFIG += console CONFIG -= app_bundle INCLUDEPATH += /usr/include/curl # 根据你的系统设置 libcurl 的路径 LIBS += -lcurl # 链接 libcurl 库 SOURCES += main.cpp(2)代码实现main.cpp 文件中实现代码如下:#include <QCoreApplication> #include <curl/curl.h> #include <QDebug> #include <QString> #include <QByteArray> // 回调函数,处理libcurl下载数据 size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) { size_t totalSize = size * nmemb; QByteArray *response = static_cast<QByteArray *>(userp); response->append(static_cast<char *>(contents), totalSize); return totalSize; } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // 初始化libcurl CURL *curl; CURLcode res; QByteArray responseData; // 用于存储响应数据 curl_global_init(CURL_GLOBAL_DEFAULT); curl = curl_easy_init(); if (curl) { // 设置访问URL const QString url = "https://ad635970a1.st1.iotda-app.cn-north-4.myhuaweicloud.com:443/v5/iot/28add376c01e4a61ac8b621c714bf459/devices/663cb18871d845632a0912e7_dev1/shadow"; // 设置HTTP请求头 struct curl_slist *headers = NULL; headers = curl_slist_append(headers, "Authorization: Bearer <Your_Access_Token>"); // 这里需要替换为你的实际 token curl_easy_setopt(curl, CURLOPT_URL, url.toStdString().c_str()); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &responseData); // 发起GET请求 res = curl_easy_perform(curl); if (res != CURLE_OK) { qDebug() << "Curl request failed:" << curl_easy_strerror(res); } else { qDebug() << "Response data:" << responseData; } // 清理 curl_easy_cleanup(curl); curl_slist_free_all(headers); } curl_global_cleanup(); return a.exec(); } 3.11 数据解析代码在 Qt 中使用 CJSON (一个用于解析 JSON 数据的轻量级 C 库) 来解析返回的 JSON 数据。(1)配置 Qt 项目在 Qt 项目的 .pro 文件中,确保包括了 CJSON 的头文件,并链接 CJSON 的源代码。QT += core CONFIG += console CONFIG -= app_bundle SOURCES += main.cpp \ cJSON.c # 将 cJSON.c 文件添加到你的项目中 INCLUDEPATH += path/to/cjson/ # 添加 CJSON 头文件的路径 LIBS += -lcurl # 链接 libcurl 库(2)解析 JSON 数据的完整代码在 main.cpp 中,以下代码展示了如何解析你提供的 JSON 数据。#include <QCoreApplication> #include <curl/curl.h> #include <QDebug> #include <QString> #include <QByteArray> #include "cJSON.h" // 回调函数,处理libcurl下载数据 size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) { size_t totalSize = size * nmemb; QByteArray *response = static_cast<QByteArray *>(userp); response->append(static_cast<char *>(contents), totalSize); return totalSize; } // 解析 JSON 数据 void parseJson(const QByteArray &data) { // 将 QByteArray 转换为 char* const char* jsonData = data.constData(); // 解析 JSON cJSON *root = cJSON_Parse(jsonData); if (root == NULL) { qDebug() << "Error parsing JSON."; return; } // 解析 "device_id" cJSON *deviceId = cJSON_GetObjectItemCaseSensitive(root, "device_id"); if (cJSON_IsString(deviceId) && (deviceId->valuestring != NULL)) { qDebug() << "Device ID:" << deviceId->valuestring; } // 解析 "shadow" 数组 cJSON *shadow = cJSON_GetObjectItemCaseSensitive(root, "shadow"); if (cJSON_IsArray(shadow)) { cJSON *shadowItem = NULL; cJSON_ArrayForEach(shadowItem, shadow) { // 解析每个 shadow 项目 cJSON *serviceId = cJSON_GetObjectItemCaseSensitive(shadowItem, "service_id"); if (cJSON_IsString(serviceId) && (serviceId->valuestring != NULL)) { qDebug() << "Service ID:" << serviceId->valuestring; } // 解析 "reported" 对象 cJSON *reported = cJSON_GetObjectItemCaseSensitive(shadowItem, "reported"); if (cJSON_IsObject(reported)) { // 解析 "properties" 对象 cJSON *properties = cJSON_GetObjectItemCaseSensitive(reported, "properties"); if (cJSON_IsObject(properties)) { cJSON *data1 = cJSON_GetObjectItemCaseSensitive(properties, "data1"); if (cJSON_IsNumber(data1)) { qDebug() << "data1:" << data1->valueint; } cJSON *data2 = cJSON_GetObjectItemCaseSensitive(properties, "data2"); if (cJSON_IsNumber(data2)) { qDebug() << "data2:" << data2->valueint; } cJSON *data3 = cJSON_GetObjectItemCaseSensitive(properties, "data3"); if (cJSON_IsNumber(data3)) { qDebug() << "data3:" << data3->valueint; } cJSON *data4 = cJSON_GetObjectItemCaseSensitive(properties, "data4"); if (cJSON_IsNumber(data4)) { qDebug() << "data4:" << data4->valueint; } } // 解析 "event_time" cJSON *eventTime = cJSON_GetObjectItemCaseSensitive(reported, "event_time"); if (cJSON_IsString(eventTime) && (eventTime->valuestring != NULL)) { qDebug() << "Event Time:" << eventTime->valuestring; } } // 解析 version cJSON *version = cJSON_GetObjectItemCaseSensitive(shadowItem, "version"); if (cJSON_IsNumber(version)) { qDebug() << "Version:" << version->valueint; } } } // 释放 JSON 对象 cJSON_Delete(root); } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // 模拟获取到的 JSON 数据 QByteArray jsonData = R"( { "device_id": "663cb18871d845632a0912e7_dev1", "shadow": [ { "service_id": "stm32", "desired": { "properties": null, "event_time": null }, "reported": { "properties": { "data1": 18, "data2": 90, "data3": 38, "data4": 70 }, "event_time": "20240509T113448Z" }, "version": 3 } ] })"; // 调用解析函数 parseJson(jsonData); return a.exec(); } 四、STM32设备端代码设计下面给出的 STM32F103C8T6 主控的 main.c 代码框架,各子模块(PH传感器采集、DS18B20水温读取、485串口溶解氧读取、水位传感器ADC采集、TDS传感器ADC采集、OLED显示、4G模块MQTT通信、蜂鸣器报警)均有对应的驱动和接口函数。设计思路(整体代码)STM32主程序设计采用基于寄存器的编程方式,代码结构清晰,模块化管理。系统启动后,初始化所有硬件外设和通信接口(ADC、UART、I2C、GPIO等),然后进入主循环。主循环中依次进行各传感器数据采集,通过ADC读取模拟传感器电压,串口通信读取溶解氧传感器数据,数字接口读取DS18B20温度数据。采集到的原始数据经过校准和算法转换,得到实际物理值。随后将所有传感器数据更新到OLED屏幕,实现本地实时显示。同时,系统根据预设阈值判断是否产生报警,控制蜂鸣器提示。周期性地将完整数据打包成JSON格式,通过UART驱动的4G模块发送MQTT消息上传到华为云物联网平台。系统通过定时器或延时函数控制采样和上传频率,确保实时性和稳定性。异常处理包括传感器故障检测和通信错误重试机制,保障系统可靠运行。main.c 示例代码#include "stm32f10x.h" #include "adc.h" #include "usart.h" #include "i2c.h" #include "oled.h" #include "ds18b20.h" #include "ph_sensor.h" #include "do_sensor.h" #include "tds_sensor.h" #include "water_level_sensor.h" #include "buzzer.h" #include "mqtt_4g.h" #include "json_utils.h" // 自定义JSON封装函数 // 采样与上传周期 (单位: ms) #define SAMPLE_INTERVAL 5000 #define UPLOAD_INTERVAL 60000 // 传感器数据结构体 typedef struct { float waterTemp; float phValue; float dissolvedOxygen; float waterLevel; float tdsValue; uint8_t alarmStatus; } WaterData_t; static WaterData_t g_waterData; void SystemClock_Config(void); void Periph_Init(void); void Delay_ms(uint32_t ms); uint8_t Check_Alarm(const WaterData_t *data); int main(void) { SystemClock_Config(); Periph_Init(); uint32_t lastSampleTime = 0; uint32_t lastUploadTime = 0; while(1) { uint32_t currentTime = GetSysTick_ms(); // 定时采样 if(currentTime - lastSampleTime >= SAMPLE_INTERVAL) { lastSampleTime = currentTime; // 读取各传感器数据 g_waterData.waterTemp = DS18B20_ReadTemperature(); g_waterData.phValue = PH_GetValue(); // 模拟量转换并校准 g_waterData.dissolvedOxygen = DO_ReadValue(); // 485串口读取 g_waterData.waterLevel = WaterLevel_ReadADC();// ADC读取 g_waterData.tdsValue = TDS_ReadValue(); // ADC读取并算法转换 // 判断报警状态 g_waterData.alarmStatus = Check_Alarm(&g_waterData); // OLED显示更新 OLED_Clear(); OLED_ShowString(0, 0, "Water Temp:"); OLED_ShowFloat(80, 0, g_waterData.waterTemp, 1); OLED_ShowString(0, 2, "PH Value:"); OLED_ShowFloat(80, 2, g_waterData.phValue, 2); OLED_ShowString(0, 4, "DO mg/L:"); OLED_ShowFloat(80, 4, g_waterData.dissolvedOxygen, 2); OLED_ShowString(0, 6, "Water Level:"); OLED_ShowFloat(80, 6, g_waterData.waterLevel, 1); OLED_ShowString(0, 8, "TDS ppm:"); OLED_ShowFloat(80, 8, g_waterData.tdsValue, 0); if(g_waterData.alarmStatus) { Buzzer_On(); } else { Buzzer_Off(); } } // 定时上传 if(currentTime - lastUploadTime >= UPLOAD_INTERVAL) { lastUploadTime = currentTime; // 组装JSON数据 char jsonData[256]; Json_CreateWaterDataJson(&g_waterData, jsonData, sizeof(jsonData)); // 通过4G模块上传 MQTT_4G_Publish(jsonData); } } } // 报警判断逻辑示例 uint8_t Check_Alarm(const WaterData_t *data) { if(data->phValue < 6.5f || data->phValue > 8.5f) return 1; if(data->dissolvedOxygen < 4.0f) return 1; if(data->tdsValue > 1000) return 1; // 根据需要扩展报警条件 return 0; } // 初始化所有外设 void Periph_Init(void) { ADC_Init_Config(); USART1_Init(115200); // 4G模块串口 USART2_Init(9600); // 485串口(溶解氧) I2C_Init(); OLED_Init(); DS18B20_Init(); PH_Sensor_Init(); DO_Sensor_Init(); TDS_Sensor_Init(); WaterLevel_Sensor_Init(); Buzzer_Init(); MQTT_4G_Init(); } // 系统时钟配置及systick定时器配置,具体由你实现 void SystemClock_Config(void) { // 省略具体时钟初始化代码 } // 获取系统运行时间(毫秒) uint32_t GetSysTick_ms(void) { // systick中断已配置,每1ms递增一个计数 extern volatile uint32_t sysTickCounter; return sysTickCounter; } 五、上位机开发为了方便查看设备上传的数据,接下来利用Qt开发一款Android手机APP 和 Windows上位机。使用华为云平台提供的API接口获取设备上传的数据,进行可视化显示,以及远程控制设备。5.1 Qt开发环境安装Qt的中文官网: https://www.qt.io/zh-cn/QT5.12.6的下载地址:https://download.qt.io/archive/qt/5.12/5.12.6打开下载链接后选择下面的版本进行下载:如果下载不了,可以在网盘里找到安装包下载: https://ccnr8sukk85n.feishu.cn/wiki/QjY8weDYHibqRYkFP2qcA9aGnvb?from=from_copylink软件安装时断网安装,否则会提示输入账户。安装的时候,第一个复选框里的编译器可以全选,直接点击下一步继续安装。选择编译器: (一定要看清楚了)前面2讲解了需要用的API接口,接下来就使用Qt设计上位机,设计界面,完成整体上位机的逻辑设计。【1】新建工程【2】设置项目的名称。【3】选择编译系统【4】选择默认继承的类【5】选择编译器【6】点击完成【7】工程创建完成5.3 切换编译器在左下角是可以切换编译器的。 可以选择用什么样的编译器编译程序。目前新建工程的时候选择了2种编译器。 一种是mingw32这个编译Windows下运行的程序。 一种是Android编译器,可以生成Android手机APP。不过要注意:Android的编译器需要配置一些环境才可以正常使用,这个大家可以看下面的教程配置一下就行了。Android环境搭建的博客链接: https://blog.csdn.net/xiaolong1126626497/article/details/117254453windows的编译器就没有这么麻烦,安装好Qt就可以编译使用。下面我这里就选择的 mingw32这个编译器,编译Windows下运行的程序。5.4 编译测试功能创建完毕之后,编译测试一下功能是否OK。点击左下角的绿色三角形按钮。正常运行就可以看到弹出一个白色的框框。这就表示工程环境没有问题了。 接下来就可以放心的设计界面了。5.5 设计UI界面与工程配置【1】打开UI文件打开默认的界面如下:【2】开始设计界面根据自己需求设计界面。5.5 编译Windows上位机点击软件左下角的绿色三角形按钮进行编译运行。5.6 配置Android环境如果想编译Android手机APP,必须要先自己配置好自己的Android环境。(搭建环境的过程可以自行百度搜索学习)然后才可以进行下面的步骤。【1】选择Android编译器选择编译器。切换编译器。【2】创建Android配置文件创建完成。【3】配置Android图标与名称【3】编译Android上位机Qt本身是跨平台的,直接选择Android的编译器,就可以将程序编译到Android平台。然后点击构建。成功之后,在目录下可以看到生成的apk文件,也就是Android手机的安装包,电脑端使用QQ发送给手机QQ,手机登录QQ接收,就能直接安装。生成的apk的目录在哪里呢? 编译完成之后,在控制台会输出APK文件的路径。知道目录在哪里之后,在Windows的文件资源管理器里,找到路径,具体看下图,找到生成的apk文件。 -- File: D:/QtProject/build-265_AgritechIoTManager-Android_for_arm64_v8a_Clang_Qt_5_12_6_for_Android_ARM64_v8a-Release/android-build//build/outputs/apk/debug/android-build-debug.apk 六、总结本项目基于STM32F103C8T6主控芯片,设计实现了一个集水温、PH值、溶解氧、水位和TDS电导率多参数检测于一体的水资源环境监测系统。系统通过高精度传感器采集关键水质指标,利用OLED显示屏实现本地数据实时展示,保证现场监测的直观性与便捷性。结合Air780E 4G模块和MQTT协议,系统将采集的数据稳定可靠地上传至华为云物联网平台,支持远程监控与历史数据查询,极大提升了数据管理的智能化和自动化水平。此外,系统设计中充分考虑了异常报警功能,能够及时发现并反馈水质异常,保障环境安全。Android手机APP和Windows上位机软件的双平台设计,实现了多终端的远程数据访问与管理,满足不同用户的使用需求。整体方案采用寄存器级STM32编程,提升了系统的稳定性和响应速度,兼顾硬件性能与软件效率。本项目的完成不仅验证了基于STM32的多参数水质监测系统的可行性和实用性,也为后续环境监测设备的智能化升级提供了可靠的技术参考,具有较高的应用推广价值和社会意义。
  • [技术干货] 2025年10月嵌入式项目开发专题总汇
    随着嵌入式技术与物联网应用的深度融合,基于高性能、低功耗的STM32微控制器的智能系统设计,正日益成为推动行业智能化转型的重要技术路径。从工业现场到农业生产,从文化保护到健康管理,STM32凭借其丰富的外设资源与出色的实时处理能力,为复杂场景下的监测与控制任务提供了稳定而灵活的解决方案。本文所汇集的项目,涵盖了电竞训练、工业诊断、古建筑监测、生产安全、环境控制以及公共健康等多个前沿领域,充分展现了STM32在跨行业应用中的广泛适应性与创新潜力。每个系统均以STM32为核心,整合传感器网络、通信模块与智能算法,实现对特定场景的精准感知、数据分析和自动控制,体现了硬件设计与软件逻辑的紧密结合。通过这些案例,我们不仅能够深入理解STM32在实际项目中的开发流程与关键技术实现,也可洞察嵌入式系统如何赋能传统行业,推动其向数字化、智能化方向演进。以下将逐一介绍这些具有代表性的STM32应用系统设计,展现其功能架构、技术特色与应用价值。1. 基于STM32设计的电竞选手训练辅助系统设计(https://bbs.huaweicloud.com/forum/thread-0263196309945715036-1-1.html)该系统旨在通过科技手段量化并提升电竞选手的训练效率与竞技状态。系统利用STM32作为主控,连接心率传感器、肌电传感器或反应速度测试模块,实时采集选手在训练过程中的生理与反应数据。通过对按键操作频率、应激反应时间等关键指标的分析,系统可生成训练报告,帮助选手和教练科学地评估表现、调整训练方案,并预防过度疲劳,从而实现数据驱动的科学化训练管理。2. 基于STM32设计的工业设备噪声故障诊断系统(https://bbs.huaweicloud.com/forum/thread-02107196309988373024-1-1.html)本系统专注于工业设备的预测性维护。通过高精度麦克风传感器阵列采集设备运行时发出的噪声信号,经由STM32进行实时信号处理(如FFT频谱分析)。系统能有效识别出与设备健康状态相关的特征频率,一旦分析出与典型故障(如轴承磨损、部件松动)匹配的噪声模式,便会立即发出预警。这实现了从“事后维修”到“事前预警”的转变,极大降低了非计划停机风险与维护成本。3. 基于STM32设计的古建筑微振动监测系统(https://bbs.huaweicloud.com/forum/thread-0263196310399762037-1-1.html)针对古建筑保护的特殊需求,该系统用于持续监测由周边交通、工程施工等引起的微弱振动。STM32连接高灵敏度的振动传感器,以极低的功耗长时间工作,精确记录振动数据。通过设定安全的振动阈值,当监测数据超过限值时系统可自动报警,为文物保护单位提供科学、客观的数据支持,以便及时采取保护措施,防范结构性损伤于未然。4. 基于STM32设计的智能车间生产安全监控系统(https://bbs.huaweicloud.com/forum/thread-0215196310436428023-1-1.html)此系统构建了一个集成的车间安全防护网络。STM32作为控制核心,可接入多种传感器,如热释电红外传感器用于识别人员闯入危险区域,温湿度、烟雾传感器用于监测环境异常,同时可连接光电开关等装置实现设备联锁控制。一旦发现安全隐患,系统将立即触发声光报警并可通过网络通知管理人员,有效保障人员与设备安全,符合现代智能工厂的安全管理标准。5. 基于STM32设计的智能防疫消毒门禁系统(https://bbs.huaweicloud.com/forum/thread-0254196310472965028-1-1.html)结合公共卫生需求,该系统实现了身份识别与自动消毒的一体化功能。人员通过门禁(如刷卡、人脸识别)时,STM32控制超声波雾化或喷雾装置,对通行区域进行局部自动消毒。同时,可集成非接触式体温检测模块,对异常体温进行告警记录。该系统特别适用于医院、实验室等场所,在保障通勤效率的同时,提升了环境消毒的自动化与智能化水平。6. 基于STM32设计的智能农业虫情监测系统设计https://bbs.huaweicloud.com/forum/thread-0259196310502633031-1-1.html)该系统革新了传统农业的虫情测报方式。在STM32的控制下,系统利用昆虫趋光特性,通过特定光源诱集害虫,并利用高清摄像头定时拍摄虫盘图像。图像数据通过无线网络上传至服务器,借助AI图像识别技术自动计数并识别害虫种类与数量,从而精准评估虫情发生程度,为农户提供精准施药指导,减少农药滥用,实现绿色防控。7. 基于STM32设计的智能实验室离心机监控系统(https://bbs.huaweicloud.com/forum/thread-0254196310531282029-1-1.html)为确保实验室离心机安全、规范运行,本系统增加了全面的状态监控功能。STM32实时监测离心机的盖门状态、转速、运行时间与转子温度等关键参数。通过陀螺仪监测整机振动,若出现严重不平衡或盖门未紧闭等情况,系统将强制减速或停止运行并报警。所有运行数据可被记录与追溯,极大地提升了实验室设备的安全管理水平。8. 基于STM32设计的智能图书馆书籍定位系统设计(https://bbs.huaweicloud.com/forum/thread-0231196310558120031-1-1.html)该系统致力于解决图书馆“找书难”的问题。通过在每本书中嵌入低成本RFID标签,并在书架上部署由STM32控制的RFID读取器网络,系统可以实时、精准地定位每一本书所在的具体架位。读者通过手机APP或查询终端输入书名,即可获得精确到特定书架的导航路径,大幅提升了图书的查找效率与馆藏管理的智能化水平。9. 基于STM32设计的智能温室二氧化碳施肥系统(https://bbs.huaweicloud.com/forum/thread-0282196310586152016-1-1.html)二氧化碳是植物光合作用的关键原料。本系统通过STM32控制高精度CO2浓度传感器,持续监测温室内的气体环境。当浓度低于作物生长最佳阈值时,STM32自动开启CO2气源阀门或发生装置,进行精准补充;当浓度达到设定上限时则自动关闭。这种闭环控制实现了CO2浓度的按需供给,为农作物创造了最优的生长条件,有效促进增产。10. 基于STM32设计的智能游泳池水质监测系统(https://bbs.huaweicloud.com/forum/thread-0239196310618796022-1-1.html)该系统实现了对游泳池水质的自动化、多参数监测。STM32同步采集接入的pH值传感器、余氯(ORP)传感器、浊度传感器等的数据,实时显示水质状况。当某项指标超出安全范围时,系统可发出警报,并能够自动或提示管理人员启动投药泵、循环泵等设备,从而维持水质的清洁、安全与稳定,降低人工检测成本,保障游泳者的健康。
  • [技术干货] 基于STM32设计的智能游泳池水质监测系统
    项目开发背景游泳池作为公共休闲和健身场所,其水质安全直接关系到使用者的健康与舒适。水质参数如pH值、余氯浓度和浊度是衡量水质的关键指标,若控制不当,可能导致细菌滋生、皮肤刺激或其他健康风险,因此对游泳池水质进行持续、准确的监测至关重要。传统的游泳池水质管理多依赖人工定期检测和手动调节,这种方式效率低下且容易因人为误差或延迟响应导致水质波动。尤其在高峰期或大型泳池中,实时监控和快速调整变得困难,增加了运营成本和安全隐患,亟需引入自动化、智能化的解决方案。随着物联网和嵌入式技术的快速发展,基于微控制器的智能监测系统成为提升水质管理效率的有效途径。本系统采用STM32F103C8T6作为核心控制器,结合高精度传感器和Wi-Fi通信模块,能够实时采集和分析水质数据,并通过自动控制机制调节相关设备,同时利用云平台和上位机实现远程监控与数据记录,为游泳池运营提供可靠的技术支持。该项目的开发旨在构建一套高效、低功耗的智能水质监测系统,通过实时预警和趋势分析,帮助管理人员及时应对水质异常,降低维护成本,并提升用户体验,为现代游泳池的智能化管理提供实用参考。设计实现的功能(1)实时监测游泳池水pH值、余氯浓度和浊度(2)自动控制水质调节设备运行(3)水质数据异常预警和记录(4)QT上位机显示水质参数变化趋势和设备状态项目硬件模块组成(1)STM32F103C8T6最小系统核心板作为主控制器(2)PH-4502C pH传感器监测酸碱度(3)余氯传感器模块监测氯含量(4)浊度传感器监测水质清澈度(5)ESP8266-01S Wi-Fi模块连接华为云平台(6)洞洞板焊接传感器接口电路,杜邦线连接各模块设计意义该智能游泳池水质监测系统的设计意义在于实现对游泳池水质的实时、精确监测,通过集成pH值、余氯浓度和浊度传感器,系统能够持续采集关键水质参数,确保水质始终处于安全范围内。这种自动化监测减少了人工检测的频率和误差,提高了监控的可靠性和效率。系统通过自动控制水质调节设备运行,能够在检测到参数异常时及时调整,如添加消毒剂或调节酸碱度,从而保障游泳者的健康与安全。避免因水质问题导致的皮肤刺激、眼部不适或更严重的健康风险,提升游泳池的使用体验和卫生标准。水质数据异常预警和记录功能使得管理人员能够快速响应潜在问题,历史数据记录有助于分析水质变化趋势,为长期维护和优化提供数据支持。这降低了运营成本,延长了设备寿命,并确保了合规性。通过ESP8266 Wi-Fi模块连接华为云平台和QT上位机显示,实现了远程监控和数据可视化,管理人员可以随时随地查看水质参数变化趋势和设备状态,便于及时决策和干预,增强了系统的智能化和便捷性。设计思路该系统以STM32F103C8T6微控制器为核心,负责协调整个监测系统的运行。通过其内置的ADC模块实时采集pH传感器、余氯传感器和浊度传感器的模拟信号,并将这些数据转换为数字值进行处理。STM32还负责控制水质调节设备的开关,例如根据监测结果自动启动加氯泵或pH调节剂投放装置,以维持水质参数在预设的安全范围内。传感器数据采集部分使用PH-4502C pH传感器监测水的酸碱度,余氯传感器模块检测氯离子浓度,浊度传感器评估水的清澈程度。这些传感器通过洞洞板焊接的接口电路连接到STM32的相应引脚,使用杜邦线进行物理连接,确保信号稳定传输和简化硬件布局。数据处理与异常预警功能由STM32实时执行,它将采集到的pH值、余氯浓度和浊度数据与预设阈值进行比较。如果任何参数超出正常范围,系统会立即触发预警机制,例如通过内部标志或连接的外设发出警报,同时将异常事件记录到STM32的存储器中,便于后续分析和追溯。自动控制机制基于监测数据动态调整水质调节设备。例如,当余氯浓度低于设定值时,STM32会输出控制信号启动加氯设备;当pH值偏离理想范围时,则调节酸碱度投放装置的运行。这种闭环控制逻辑通过STM32的GPIO和定时器功能实现,确保水质持续保持在健康水平。通信部分通过ESP8266-01S Wi-Fi模块将STM32连接到华为云平台,实现数据的远程传输和存储。水质参数、设备状态及异常记录定期上传至云平台,用户可以通过网络实时监控系统运行情况,并利用云服务进行数据备份和远程管理。QT上位机应用程序作为人机交互界面,从云平台或直接通过通信接口接收数据,以图形化方式显示水质参数的历史变化趋势和设备当前状态。这提供了直观的监控体验,帮助用户快速识别水质问题并做出相应决策。硬件组装方面,所有模块包括STM32核心板、传感器和Wi-Fi模块均在洞洞板上进行焊接和固定,传感器接口电路设计考虑了信号匹配和抗干扰能力。杜邦线用于灵活连接各组件,简化了系统的布线和维护过程,确保整体结构的可靠性和易扩展性。框架图+-------------------------+ | 传感器层 | | +----------+----------+ | | | pH传感器 | 余氯传感器| | | +----------+----------+ | | +----------+ | | | 浊度传感器| | | +----------+ | +-------------------------+ | v +-------------------------+ | 控制层 | | +---------------------+ | | | STM32F103C8T6 | | | | 主控制器 | | | | - 数据采集 | | | | - 数据处理 | | | | - 控制逻辑 | | | +---------------------+ | +-------------------------+ | |-----------------> +-------------------------+ | | 执行层 | | | +---------------------+ | | | | 水质调节设备 | | | | | (加氯机、pH调节等) | | | | +---------------------+ | | +-------------------------+ | v +-------------------------+ | 通信层 | | +---------------------+ | | | ESP8266 Wi-Fi模块 | | | | 连接华为云平台 | | | +---------------------+ | +-------------------------+ | v +-------------------------+ | 云平台层 | | +---------------------+ | | | 华为云 | | | | - 数据存储 | | | | - 异常预警 | | | | - 记录日志 | | | +---------------------+ | +-------------------------+ | v +-------------------------+ | 应用层 | | +---------------------+ | | | QT上位机 | | | | - 显示水质参数 | | | | - 显示趋势 | | | | - 设备状态 | | | +---------------------+ | +-------------------------+ 系统总体设计本系统以STM32F103C8T6最小系统核心板作为主控制器,负责整体协调和数据处理。该系统通过集成PH-4502C pH传感器、余氯传感器模块和浊度传感器,实时采集游泳池水的关键参数,包括酸碱度、氯含量和清澈度。这些传感器通过洞洞板焊接的接口电路和杜邦线连接到主控制器,确保稳定可靠的数据传输。系统通过STM32主控制器对采集的传感器数据进行实时分析和处理,并根据预设阈值自动控制水质调节设备的运行。例如,当pH值或余氯浓度超出正常范围时,控制器会触发相应设备进行调节,以维持水质的平衡状态。同时,系统具备数据异常预警功能,能够及时检测到水质参数异常并记录相关事件,便于后续查询和分析。为了远程监控和数据存储,系统集成了ESP8266-01S Wi-Fi模块,该模块将处理后的水质数据上传至华为云平台。通过云平台,用户可以实时访问水质信息,并实现历史数据的备份和查询。此外,系统还支持QT上位机显示界面,该界面直观展示水质参数的变化趋势和设备运行状态,帮助用户全面了解游泳池水质的动态情况。整个设计注重实用性和可靠性,通过硬件组件的合理布局和软件算法的优化,确保系统能够长期稳定运行。自动控制机制和预警功能进一步提升了系统的智能化水平,有效保障游泳池水质的卫生与安全。系统功能总结功能描述实时监测水质参数使用pH传感器、余氯传感器和浊度传感器,实时监测游泳池水的pH值、余氯浓度和浊度自动控制水质调节设备基于监测数据,自动控制水质调节设备的运行,以维持水质标准水质数据异常预警和记录当水质参数异常时,系统发出预警,并记录异常数据用于后续分析QT上位机显示通过Wi-Fi模块连接云平台,在QT开发的上位机上显示水质参数变化趋势和设备状态设计的各个功能模块描述STM32F103C8T6最小系统核心板作为主控制器,负责协调整个系统的运行,包括采集传感器数据、处理水质参数、执行自动控制逻辑、管理通信连接以及实现预警和记录功能。它通过内置的ADC和GPIO接口与各模块交互,确保实时性和稳定性。PH-4502C pH传感器模块用于监测游泳池水的酸碱度,其输出模拟信号经过STM32的ADC转换后得到pH值数据。该模块通过洞洞板焊接的接口电路与主控制器连接,确保信号传输的准确性和抗干扰能力。余氯传感器模块专门检测水中的余氯浓度,通过电化学原理将氯含量转换为电信号,由STM32进行采集和处理。该模块与主控制器通过杜邦线连接,实现实时数据更新,帮助维持水质安全。浊度传感器模块测量水质的清澈度,利用光学原理检测悬浮颗粒物,输出信号经STM32解析后得到浊度数值。该模块集成在接口电路中,通过杜邦线与主控制器相连,提供可靠的水质评估数据。ESP8266-01S Wi-Fi模块实现无线通信功能,连接至华为云平台,使得水质数据能够实时上传和远程访问。STM32通过串口与Wi-Fi模块通信,确保数据稳定传输,并支持云端指令接收和设备状态同步。自动控制功能由STM32通过输出引脚驱动外部水质调节设备,如加药泵或过滤器,根据监测到的pH、余氯和浊度数据自动调整设备运行状态。这一过程基于预设阈值,实现智能化水质维护。水质数据异常预警和记录功能由STM32实现,当检测到pH、余氯或浊度参数超出安全范围时,系统会触发本地预警并通过Wi-Fi模块上报至云平台,同时将异常事件记录在存储器中,便于后续分析和追溯。QT上位机显示系统通过从华为云平台获取数据,实时展示水质参数的变化趋势和设备运行状态。该界面以图表和数值形式呈现信息,为用户提供直观的监控体验,并支持历史数据查询和报警通知。上位机代码设计// main.cpp #include <QApplication> #include <QMainWindow> #include <QVBoxLayout> #include <QHBoxLayout> #include <QGridLayout> #include <QLabel> #include <QPushButton> #include <QTextEdit> #include <QTabWidget> #include <QChartView> #include <QLineSeries> #include <QDateTimeAxis> #include <QValueAxis> #include <QTimer> #include <QDateTime> #include <QMessageBox> #include <QFile> #include <QTextStream> #include <QtMqtt/QMqttClient> #include <QJsonDocument> #include <QJsonObject> QT_CHARTS_USE_NAMESPACE class WaterQualityMonitor : public QMainWindow { Q_OBJECT public: WaterQualityMonitor(QWidget *parent = nullptr) : QMainWindow(parent) { setupUI(); setupMQTT(); setupCharts(); setupTimer(); } private slots: void onConnected(); void onMessageReceived(const QByteArray &message, const QMqttTopicName &topic); void updateRealtimeData(); void saveDataToFile(); void checkAlarms(); private: void setupUI(); void setupMQTT(); void setupCharts(); void setupTimer(); // MQTT客户端 QMqttClient *m_client; // UI组件 QTabWidget *m_tabWidget; // 实时数据显示 QLabel *m_phValueLabel; QLabel *m_chlorineValueLabel; QLabel *m_turbidityValueLabel; QLabel *m_statusLabel; QTextEdit *m_logTextEdit; // 图表 QChart *m_phChart; QChart *m_chlorineChart; QChart *m_turbidityChart; QLineSeries *m_phSeries; QLineSeries *m_chlorineSeries; QLineSeries *m_turbiditySeries; // 数据存储 QVector<QPointF> m_phData; QVector<QPointF> m_chlorineData; QVector<QPointF> m_turbidityData; // 定时器 QTimer *m_updateTimer; QTimer *m_saveTimer; // 当前数据 double m_currentPH; double m_currentChlorine; double m_currentTurbidity; }; void WaterQualityMonitor::setupUI() { setWindowTitle("智能游泳池水质监测系统"); setMinimumSize(1200, 800); // 创建主窗口部件 QWidget *centralWidget = new QWidget(this); setCentralWidget(centralWidget); // 创建标签页 m_tabWidget = new QTabWidget(centralWidget); // 实时监控页面 QWidget *realtimeTab = new QWidget(); QVBoxLayout *realtimeLayout = new QVBoxLayout(realtimeTab); // 数据显示区域 QGridLayout *dataLayout = new QGridLayout(); dataLayout->addWidget(new QLabel("pH值:"), 0, 0); m_phValueLabel = new QLabel("--"); m_phValueLabel->setStyleSheet("font-size: 24pt; color: blue;"); dataLayout->addWidget(m_phValueLabel, 0, 1); dataLayout->addWidget(new QLabel("余氯浓度(mg/L):"), 1, 0); m_chlorineValueLabel = new QLabel("--"); m_chlorineValueLabel->setStyleSheet("font-size: 24pt; color: green;"); dataLayout->addWidget(m_chlorineValueLabel, 1, 1); dataLayout->addWidget(new QLabel("浊度(NTU):"), 2, 0); m_turbidityValueLabel = new QLabel("--"); m_turbidityValueLabel->setStyleSheet("font-size: 24pt; color: orange;"); dataLayout->addWidget(m_turbidityValueLabel, 2, 1); dataLayout->addWidget(new QLabel("系统状态:"), 3, 0); m_statusLabel = new QLabel("等待连接..."); m_statusLabel->setStyleSheet("font-size: 16pt; color: gray;"); dataLayout->addWidget(m_statusLabel, 3, 1); realtimeLayout->addLayout(dataLayout); // 日志区域 realtimeLayout->addWidget(new QLabel("系统日志:")); m_logTextEdit = new QTextEdit(); m_logTextEdit->setMaximumHeight(200); realtimeLayout->addWidget(m_logTextEdit); // 趋势图表页面 QWidget *chartTab = new QWidget(); QVBoxLayout *chartLayout = new QVBoxLayout(chartTab); // pH值图表 QChartView *phChartView = new QChartView(); phChartView->setRenderHint(QPainter::Antialiasing); chartLayout->addWidget(new QLabel("pH值变化趋势:")); chartLayout->addWidget(phChartView); // 余氯浓度图表 QChartView *chlorineChartView = new QChartView(); chlorineChartView->setRenderHint(QPainter::Antialiasing); chartLayout->addWidget(new QLabel("余氯浓度变化趋势:")); chartLayout->addWidget(chlorineChartView); // 浊度图表 QChartView *turbidityChartView = new QChartView(); turbidityChartView->setRenderHint(QPainter::Antialiasing); chartLayout->addWidget(new QLabel("浊度变化趋势:")); chartLayout->addWidget(turbidityChartView); m_tabWidget->addTab(realtimeTab, "实时监控"); m_tabWidget->addTab(chartTab, "趋势分析"); QVBoxLayout *mainLayout = new QVBoxLayout(centralWidget); mainLayout->addWidget(m_tabWidget); } void WaterQualityMonitor::setupMQTT() { m_client = new QMqttClient(this); m_client->setHostname("your_huawei_cloud_address"); // 替换为实际华为云地址 m_client->setPort(1883); m_client->setUsername("your_username"); // 替换为实际用户名 m_client->setPassword("your_password"); // 替换为实际密码 connect(m_client, &QMqttClient::connected, this, &WaterQualityMonitor::onConnected); connect(m_client, &QMqttClient::messageReceived, this, &WaterQualityMonitor::onMessageReceived); // 连接MQTT服务器 m_client->connectToHost(); } void WaterQualityMonitor::setupCharts() { // pH值图表设置 m_phChart = new QChart(); m_phSeries = new QLineSeries(); m_phChart->addSeries(m_phSeries); m_phChart->setTitle("pH值监测"); m_phChart->createDefaultAxes(); QDateTimeAxis *axisX = new QDateTimeAxis(); axisX->setTitleText("时间"); axisX->setFormat("hh:mm:ss"); m_phChart->addAxis(axisX, Qt::AlignBottom); m_phSeries->attachAxis(axisX); QValueAxis *axisY = new QValueAxis(); axisY->setTitleText("pH值"); axisY->setRange(6.0, 8.5); m_phChart->addAxis(axisY, Qt::AlignLeft); m_phSeries->attachAxis(axisY); // 余氯浓度图表设置 m_chlorineChart = new QChart(); m_chlorineSeries = new QLineSeries(); m_chlorineChart->addSeries(m_chlorineSeries); m_chlorineChart->setTitle("余氯浓度监测"); m_chlorineChart->createDefaultAxes(); QDateTimeAxis *axisX2 = new QDateTimeAxis(); axisX2->setTitleText("时间"); axisX2->setFormat("hh:mm:ss"); m_chlorineChart->addAxis(axisX2, Qt::AlignBottom); m_chlorineSeries->attachAxis(axisX2); QValueAxis *axisY2 = new QValueAxis(); axisY2->setTitleText("余氯浓度(mg/L)"); axisY2->setRange(0.0, 2.0); m_chlorineChart->addAxis(axisY2, Qt::AlignLeft); m_chlorineSeries->attachAxis(axisY2); // 浊度图表设置 m_turbidityChart = new QChart(); m_turbiditySeries = new QLineSeries(); m_turbidityChart->addSeries(m_turbiditySeries); m_turbidityChart->setTitle("浊度监测"); m_turbidityChart->createDefaultAxes(); QDateTimeAxis *axisX3 = new QDateTimeAxis(); axisX3->setTitleText("时间"); axisX3->setFormat("hh:mm:ss"); m_turbidityChart->addAxis(axisX3, Qt::AlignBottom); m_turbiditySeries->attachAxis(axisX3); QValueAxis *axisY3 = new QValueAxis(); axisY3->setTitleText("浊度(NTU)"); axisY3->setRange(0.0, 10.0); m_turbidityChart->addAxis(axisY3, Qt::AlignLeft); m_turbiditySeries->attachAxis(axisY3); // 设置图表视图 QList<QChartView*> chartViews = findChildren<QChartView*>(); if (chartViews.size() >= 3) { chartViews[0]->setChart(m_phChart); chartViews[1]->setChart(m_chlorineChart); chartViews[2]->setChart(m_turbidityChart); } } void WaterQualityMonitor::setupTimer() { m_updateTimer = new QTimer(this); connect(m_updateTimer, &QTimer::timeout, this, &WaterQualityMonitor::updateRealtimeData); m_updateTimer->start(1000); // 1秒更新一次 m_saveTimer = new QTimer(this); connect(m_saveTimer, &QTimer::timeout, this, &WaterQualityMonitor::saveDataToFile); m_saveTimer->start(60000); // 1分钟保存一次数据 } void WaterQualityMonitor::onConnected() { m_statusLabel->setText("已连接到云平台"); m_statusLabel->setStyleSheet("color: green;"); // 订阅主题 m_client->subscribe(QMqttTopicFilter("pool/water/quality")); m_logTextEdit->append(QDateTime::currentDateTime().toString() + " - 成功连接到云平台"); } void WaterQualityMonitor::onMessageReceived(const QByteArray &message, const QMqttTopicName &topic) { QJsonDocument doc = QJsonDocument::fromJson(message); if (doc.isObject()) { QJsonObject obj = doc.object(); m_currentPH = obj["pH"].toDouble(); m_currentChlorine = obj["chlorine"].toDouble(); m_currentTurbidity = obj["turbidity"].toDouble(); // 更新实时数据显示 m_phValueLabel->setText(QString::number(m_currentPH, 'f', 2)); m_chlorineValueLabel->setText(QString::number(m_currentChlorine, 'f', 2)); m_turbidityValueLabel->setText(QString::number(m_currentTurbidity, 'f', 2)); // 添加到数据序列 qint64 currentTime = QDateTime::currentDateTime().toMSecsSinceEpoch(); m_phData.append(QPointF(currentTime, m_currentPH)); m_chlorineData.append(QPointF(currentTime, m_currentChlorine)); m_turbidityData.append(QPointF(currentTime, m_currentTurbidity)); // 检查警报 checkAlarms(); // 限制数据点数量 if (m_phData.size() > 100) { m_phData.removeFirst(); m_chlorineData.removeFirst(); m_turbidityData.removeFirst(); } } } void WaterQualityMonitor::updateRealtimeData() { // 更新图表数据 m_phSeries->replace(m_phData); m_chlorineSeries->replace(m_chlorineData); m_turbiditySeries->replace(m_turbidityData); } void WaterQualityMonitor::saveDataToFile() { QFile file("water_quality_data.csv"); if (file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) { QTextStream stream(&file); QDateTime currentTime = QDateTime::currentDateTime(); stream << currentTime.toString("yyyy-MM-dd hh:mm:ss") << "," << m_currentPH << "," << m_currentChlorine << "," << m_currentTurbidity << "\n"; file.close(); } } void WaterQualityMonitor::checkAlarms() { QString alarmMessage; // pH值警报 (正常范围: 7.2-7.8) if (m_currentPH < 7.2 || m_currentPH > 7.8) { alarmMessage = QString("警告: pH值异常! 当前值: %1 (正常范围: 7.2-7.8)").arg(m_currentPH); m_logTextEdit->append(QDateTime::currentDateTime().toString() + " - " + alarmMessage); } // 余氯浓度警报 (正常范围: 0.3-1.0 mg/L) if (m_currentChlorine < 0.3 || m_currentChlorine > 1.0) { alarmMessage = QString("警告: 余氯浓度异常! 当前值: %1 mg/L (正常范围: 0.3-1.0 mg/L)").arg(m_currentChlorine); m_logTextEdit->append(QDateTime::currentDateTime().toString() + " - " + alarmMessage); } // 浊度警报 (正常范围: < 1 NTU) if (m_currentTurbidity > 1.0) { alarmMessage = QString("警告: 浊度异常! 当前值: %1 NTU (正常值: < 1 NTU)").arg(m_currentTurbidity); m_logTextEdit->append(QDateTime::currentDateTime().toString() + " - " + alarmMessage); } } int main(int argc, char *argv[]) { QApplication app(argc, argv); WaterQualityMonitor monitor; monitor.show(); return app.exec(); } #include "main.moc" # WaterQualityMonitor.pro QT += core gui charts network mqtt greaterThan(QT_MAJOR_VERSION, 4): QT += widgets CONFIG += c++11 TARGET = WaterQualityMonitor TEMPLATE = app SOURCES += \ main.cpp HEADERS += # 发布版本配置 CONFIG(release, debug|release) { DEFINES += QT_NO_DEBUG_OUTPUT } # 设置编译选项 win32: { QMAKE_CXXFLAGS += -O2 QMAKE_LFLAGS += -static } # CMakeLists.txt (可选,用于CMake构建) cmake_minimum_required(VERSION 3.16) project(WaterQualityMonitor) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(Qt6 REQUIRED COMPONENTS Core Widgets Charts Network Mqtt) qt_standard_project_setup() qt_add_executable(WaterQualityMonitor main.cpp ) qt_add_resources(WaterQualityMonitor "resources" PREFIX "/" ) target_link_libraries(WaterQualityMonitor Qt6::Core Qt6::Widgets Qt6::Charts Qt6::Network Qt6::Mqtt) 这个上位机程序提供了以下功能:实时监控界面:显示pH值、余氯浓度、浊度的实时数据系统状态指示系统日志显示趋势分析界面:三个独立的图表分别显示pH值、余氯浓度、浊度的历史变化趋势支持时间轴显示数据管理:自动保存数据到CSV文件实时数据更新和图表刷新报警功能:水质参数异常自动报警报警信息记录到日志云平台通信:通过MQTT协议连接华为云平台实时接收STM32发送的水质数据使用时需要:配置正确的华为云MQTT服务器地址、端口、用户名和密码确保网络连接正常根据需要调整水质参数的正常范围阈值模块代码设计#include "stm32f10x.h" // 传感器引脚定义 #define PH_SENSOR_PIN GPIO_Pin_0 // PA0 - pH传感器 #define CL_SENSOR_PIN GPIO_Pin_1 // PA1 - 余氯传感器 #define TURB_SENSOR_PIN GPIO_Pin_2 // PA2 - 浊度传感器 #define RELAY_PH_UP GPIO_Pin_0 // PB0 - pH上调继电器 #define RELAY_PH_DOWN GPIO_Pin_1 // PB1 - pH下调继电器 #define RELAY_CHLORINE GPIO_Pin_2 // PB2 - 加氯继电器 #define RELAY_FILTER GPIO_Pin_3 // PB3 - 过滤泵继电器 // 传感器数据结构 typedef struct { float pH_value; float chlorine_value; float turbidity_value; uint8_t ph_status; uint8_t chlorine_status; uint8_t turbidity_status; } SensorData; SensorData water_data; uint8_t wifi_connected = 0; // ADC初始化 void ADC_Configuration(void) { RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; // 使能ADC1时钟 RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; // 使能GPIOA时钟 // 配置PA0, PA1, PA2为模拟输入 GPIOA->CRL &= ~(GPIO_CRL_CNF0 | GPIO_CRL_MODE0 | GPIO_CRL_CNF1 | GPIO_CRL_MODE1 | GPIO_CRL_CNF2 | GPIO_CRL_MODE2); GPIOA->CRL |= (GPIO_CRL_CNF0_0 | GPIO_CRL_CNF1_0 | GPIO_CRL_CNF2_0); // ADC配置 ADC1->CR2 = 0; ADC1->CR2 |= ADC_CR2_ADON; // 开启ADC ADC1->CR2 |= ADC_CR2_CAL; // 开始校准 while(ADC1->CR2 & ADC_CR2_CAL); // 等待校准完成 ADC1->SQR1 = 0; // 1个转换 ADC1->SQR3 = 0; // 通道0 ADC1->SMPR2 = ADC_SMPR2_SMP0_2 | // 通道0采样时间239.5周期 ADC_SMPR2_SMP1_2 | // 通道1采样时间239.5周期 ADC_SMPR2_SMP2_2; // 通道2采样时间239.5周期 } // 读取ADC值 uint16_t ADC_Read(uint8_t channel) { ADC1->SQR3 = channel; // 设置转换通道 ADC1->CR2 |= ADC_CR2_ADON; // 开始转换 while(!(ADC1->SR & ADC_SR_EOC)); // 等待转换完成 return ADC1->DR; // 返回转换结果 } // pH传感器读取和校准 float Read_pH_Sensor(void) { uint32_t sum = 0; uint16_t adc_value; float voltage, pH_value; // 读取16次取平均 for(int i = 0; i < 16; i++) { adc_value = ADC_Read(0); // 通道0 - pH传感器 sum += adc_value; for(int j = 0; j < 1000; j++); // 短暂延时 } adc_value = sum >> 4; // 求平均值 voltage = (float)adc_value * 3.3 / 4096.0; // 转换为电压 // pH值计算 (PH-4502C特性曲线) // 实际应用中需要根据传感器手册进行校准 pH_value = 3.5 * voltage + 2.0; // 简化计算公式 return pH_value; } // 余氯传感器读取 float Read_Chlorine_Sensor(void) { uint32_t sum = 0; uint16_t adc_value; float voltage, chlorine_value; for(int i = 0; i < 16; i++) { adc_value = ADC_Read(1); // 通道1 - 余氯传感器 sum += adc_value; for(int j = 0; j < 1000; j++); } adc_value = sum >> 4; voltage = (float)adc_value * 3.3 / 4096.0; // 余氯浓度计算 (mg/L) chlorine_value = voltage * 2.0; // 简化计算公式 return chlorine_value; } // 浊度传感器读取 float Read_Turbidity_Sensor(void) { uint32_t sum = 0; uint16_t adc_value; float voltage, turbidity_value; for(int i = 0; i < 16; i++) { adc_value = ADC_Read(2); // 通道2 - 浊度传感器 sum += adc_value; for(int j = 0; j < 1000; j++); } adc_value = sum >> 4; voltage = (float)adc_value * 3.3 / 4096.0; // 浊度计算 (NTU) turbidity_value = (3.0 - voltage) * 500.0; // 简化计算公式 return turbidity_value; } // GPIO初始化 void GPIO_Configuration(void) { RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; // 使能GPIOB时钟 // 配置PB0-PB3为推挽输出 (继电器控制) GPIOB->CRL &= ~(GPIO_CRL_CNF0 | GPIO_CRL_MODE0 | GPIO_CRL_CNF1 | GPIO_CRL_MODE1 | GPIO_CRL_CNF2 | GPIO_CRL_MODE2 | GPIO_CRL_CNF3 | GPIO_CRL_MODE3); GPIOB->CRL |= (GPIO_CRL_MODE0_0 | GPIO_CRL_MODE0_1 | GPIO_CRL_MODE1_0 | GPIO_CRL_MODE1_1 | GPIO_CRL_MODE2_0 | GPIO_CRL_MODE2_1 | GPIO_CRL_MODE3_0 | GPIO_CRL_MODE3_1); } // USART2初始化 (ESP8266通信) void USART2_Configuration(void) { RCC->APB1ENR |= RCC_APB1ENR_USART2EN; // 使能USART2时钟 RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; // 使能GPIOA时钟 // 配置PA2为推挽输出 (TX) GPIOA->CRL &= ~(GPIO_CRL_CNF2 | GPIO_CRL_MODE2); GPIOA->CRL |= GPIO_CRL_CNF2_1 | GPIO_CRL_MODE2_0 | GPIO_CRL_MODE2_1; // 配置PA3为浮空输入 (RX) GPIOA->CRL &= ~(GPIO_CRL_CNF3 | GPIO_CRL_MODE3); GPIOA->CRL |= GPIO_CRL_CNF3_0; // 波特率设置 115200 USART2->BRR = 72000000 / 115200; // 使能发送和接收 USART2->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; } // USART发送字符 void USART2_SendChar(char ch) { while(!(USART2->SR & USART_SR_TXE)); // 等待发送缓冲区空 USART2->DR = ch; } // USART发送字符串 void USART2_SendString(char *str) { while(*str) { USART2_SendChar(*str++); } } // 继电器控制函数 void Control_Relay(uint8_t relay, uint8_t state) { if(state) { GPIOB->BSRR = (1 << relay); // 置位,打开继电器 } else { GPIOB->BRR = (1 << relay); // 复位,关闭继电器 } } // 水质自动控制 void Water_Quality_Control(void) { // pH控制 (理想范围: 7.2-7.8) if(water_data.pH_value < 7.2) { Control_Relay(0, 1); // 打开pH上调 Control_Relay(1, 0); // 关闭pH下调 water_data.ph_status = 1; // pH偏低 } else if(water_data.pH_value > 7.8) { Control_Relay(0, 0); // 关闭pH上调 Control_Relay(1, 1); // 打开pH下调 water_data.ph_status = 2; // pH偏高 } else { Control_Relay(0, 0); // 关闭pH上调 Control_Relay(1, 0); // 关闭pH下调 water_data.ph_status = 0; // pH正常 } // 余氯控制 (理想范围: 1.0-3.0 mg/L) if(water_data.chlorine_value < 1.0) { Control_Relay(2, 1); // 打开加氯 water_data.chlorine_status = 1; // 余氯偏低 } else if(water_data.chlorine_value > 3.0) { Control_Relay(2, 0); // 关闭加氯 water_data.chlorine_status = 2; // 余氯偏高 } else { Control_Relay(2, 0); // 关闭加氯 water_data.chlorine_status = 0; // 余氯正常 } // 浊度控制 (理想范围: 0-5 NTU) if(water_data.turbidity_value > 5.0) { Control_Relay(3, 1); // 打开过滤泵 water_data.turbidity_status = 1; // 浊度偏高 } else { Control_Relay(3, 0); // 关闭过滤泵 water_data.turbidity_status = 0; // 浊度正常 } } // ESP8266发送数据到云平台 void Send_Data_To_Cloud(void) { char buffer[128]; // 构造JSON格式数据 sprintf(buffer, "{\"pH\":%.2f,\"chlorine\":%.2f,\"turbidity\":%.2f,\"status\":%d}", water_data.pH_value, water_data.chlorine_value, water_data.turbidity_value, wifi_connected); USART2_SendString("AT+CIPSEND=0,"); USART2_SendString(buffer); USART2_SendString("\r\n"); } // 系统时钟初始化 void RCC_Configuration(void) { // 启用HSE RCC->CR |= RCC_CR_HSEON; while(!(RCC->CR & RCC_CR_HSERDY)); // 配置PLL RCC->CFGR |= RCC_CFGR_PLLSRC; // PLL源选择HSE RCC->CFGR |= RCC_CFGR_PLLMULL9; // PLL倍频9倍 (8MHz * 9 = 72MHz) // 启用PLL RCC->CR |= RCC_CR_PLLON; while(!(RCC->CR & RCC_CR_PLLRDY)); // 配置Flash预取指 FLASH->ACR |= FLASH_ACR_PRFTBE; FLASH->ACR |= FLASH_ACR_LATENCY_2; // 切换系统时钟到PLL RCC->CFGR |= RCC_CFGR_SW_PLL; while(!(RCC->CFGR & RCC_CFGR_SWS_PLL)); } // 延时函数 void Delay(uint32_t count) { for(uint32_t i = 0; i < count; i++); } // 系统初始化 void System_Init(void) { RCC_Configuration(); GPIO_Configuration(); ADC_Configuration(); USART2_Configuration(); // 初始化所有继电器为关闭状态 Control_Relay(0, 0); Control_Relay(1, 0); Control_Relay(2, 0); Control_Relay(3, 0); } int main(void) { System_Init(); while(1) { // 读取传感器数据 water_data.pH_value = Read_pH_Sensor(); water_data.chlorine_value = Read_Chlorine_Sensor(); water_data.turbidity_value = Read_Turbidity_Sensor(); // 水质自动控制 Water_Quality_Control(); // 发送数据到云平台 if(wifi_connected) { Send_Data_To_Cloud(); } // 延时5秒 Delay(5000000); } } // ESP8266接收中断处理 void USART2_IRQHandler(void) { if(USART2->SR & USART_SR_RXNE) { uint8_t data = USART2->DR; // 处理Wi-Fi模块响应 if(data == 'O' || data == 'K') { wifi_connected = 1; } } } 项目核心代码#include "stm32f10x.h" // 传感器数据结构体 typedef struct { float ph_value; float chlorine_value; float turbidity_value; } SensorData; // 设备控制结构体 typedef struct { uint8_t ph_control; uint8_t chlorine_control; uint8_t filter_control; } DeviceControl; // 全局变量 SensorData current_data; DeviceControl device_status; uint32_t system_tick = 0; // 函数声明 void System_Init(void); void GPIO_Init(void); void ADC_Init(void); void USART_Init(void); void TIM_Init(void); void ESP8266_SendData(SensorData data); void Device_Control(SensorData data); void Alert_Check(SensorData data); float Read_PH_Sensor(void); float Read_Chlorine_Sensor(void); float Read_Turbidity_Sensor(void); int main(void) { // 系统初始化 System_Init(); while(1) { // 每1秒执行一次主循环 if(system_tick >= 1000) { system_tick = 0; // 读取传感器数据 current_data.ph_value = Read_PH_Sensor(); current_data.chlorine_value = Read_Chlorine_Sensor(); current_data.turbidity_value = Read_Turbidity_Sensor(); // 发送数据到云平台 ESP8266_SendData(current_data); // 设备控制 Device_Control(current_data); // 异常预警检查 Alert_Check(current_data); } } } // 系统初始化 void System_Init(void) { // 设置系统时钟 RCC->CR |= 0x00010000; // HSEON while(!(RCC->CR & 0x00020000)); // 等待HSE就绪 RCC->CFGR = 0x00010000; // PLL输入时钟源为HSE RCC->CFGR |= 0x001C0000; // PLL倍频系数9 RCC->CR |= 0x01000000; // PLLON while(!(RCC->CR & 0x02000000)); // 等待PLL就绪 FLASH->ACR |= 0x12; // Flash等待状态 RCC->CFGR |= 0x00000002; // PLL作为系统时钟 while((RCC->CFGR & 0x08) != 0x08); // 等待切换完成 // 初始化各外设 GPIO_Init(); ADC_Init(); USART_Init(); TIM_Init(); } // GPIO初始化 void GPIO_Init(void) { // 使能GPIO时钟 RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN; // PA0, PA1, PA2 作为模拟输入 (pH, 余氯, 浊度传感器) GPIOA->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0 | GPIO_CRL_MODE1 | GPIO_CRL_CNF1 | GPIO_CRL_MODE2 | GPIO_CRL_CNF2); GPIOA->CRL |= (GPIO_CRL_CNF0_0 | GPIO_CRL_CNF1_0 | GPIO_CRL_CNF2_0); // PC13, PC14, PC15 作为输出 (设备控制) GPIOC->CRH &= ~(GPIO_CRH_MODE13 | GPIO_CRH_CNF13 | GPIO_CRH_MODE14 | GPIO_CRH_CNF14 | GPIO_CRH_MODE15 | GPIO_CRH_CNF15); GPIOC->CRH |= (GPIO_CRH_MODE13_0 | GPIO_CRH_MODE14_0 | GPIO_CRH_MODE15_0); // PA9(TX), PA10(RX) 作为USART1 GPIOA->CRH &= ~(GPIO_CRH_MODE9 | GPIO_CRH_CNF9 | GPIO_CRH_MODE10 | GPIO_CRH_CNF10); GPIOA->CRH |= (GPIO_CRH_MODE9_1 | GPIO_CRH_CNF9_1 | // PA9: 输出50MHz, 复用推挽 GPIO_CRH_CNF10_0); // PA10: 浮空输入 } // ADC初始化 void ADC_Init(void) { // 使能ADC1时钟 RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; // ADC校准 ADC1->CR2 |= ADC_CR2_ADON; delay_ms(1); ADC1->CR2 |= ADC_CR2_CAL; while(ADC1->CR2 & ADC_CR2_CAL); // 配置ADC ADC1->CR1 = 0; ADC1->CR2 = ADC_CR2_ADON | ADC_CR2_CONT; // 连续转换模式 ADC1->SMPR2 = (ADC_SMPR2_SMP0_2 | ADC_SMPR2_SMP0_1 | // 通道0采样时间239.5周期 ADC_SMPR2_SMP1_2 | ADC_SMPR2_SMP1_1 | // 通道1采样时间239.5周期 ADC_SMPR2_SMP2_2 | ADC_SMPR2_SMP2_1); // 通道2采样时间239.5周期 } // USART初始化 (用于ESP8266通信) void USART_Init(void) { // 使能USART1时钟 RCC->APB2ENR |= RCC_APB2ENR_USART1EN; // 配置波特率 115200 USART1->BRR = 390; // 72MHz / 115200 = 625, 625/16=39.0625 // 配置控制寄存器 USART1->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; // 使能USART, 发送, 接收 USART1->CR2 = 0; USART1->CR3 = 0; } // 定时器初始化 void TIM_Init(void) { // 使能TIM2时钟 RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // 配置TIM2 1ms中断 TIM2->PSC = 7200 - 1; // 72MHz/7200 = 10kHz TIM2->ARR = 10 - 1; // 10kHz/10 = 1kHz = 1ms TIM2->DIER = TIM_DIER_UIE; // 使能更新中断 TIM2->CR1 = TIM_CR1_CEN; // 使能定时器 // 配置NVIC NVIC->ISER[0] |= (1 << TIM2_IRQn); NVIC->IP[TIM2_IRQn] = 0x10; } // 读取pH传感器 float Read_PH_Sensor(void) { ADC1->SQR3 = 0; // 通道0 ADC1->CR2 |= ADC_CR2_SWSTART; while(!(ADC1->SR & ADC_SR_EOC)); uint16_t adc_value = ADC1->DR; return (adc_value * 3.3f / 4096.0f) * 2.0f + 4.0f; // 转换为pH值 } // 读取余氯传感器 float Read_Chlorine_Sensor(void) { ADC1->SQR3 = 1; // 通道1 ADC1->CR2 |= ADC_CR2_SWSTART; while(!(ADC1->SR & ADC_SR_EOC)); uint16_t adc_value = ADC1->DR; return (adc_value * 3.3f / 4096.0f) * 5.0f; // 转换为余氯浓度(mg/L) } // 读取浊度传感器 float Read_Turbidity_Sensor(void) { ADC1->SQR3 = 2; // 通道2 ADC1->CR2 |= ADC_CR2_SWSTART; while(!(ADC1->SR & ADC_SR_EOC)); uint16_t adc_value = ADC1->DR; return (adc_value * 3.3f / 4096.0f) * 100.0f; // 转换为浊度值(NTU) } // 发送数据到ESP8266 void ESP8266_SendData(SensorData data) { char buffer[128]; sprintf(buffer, "{\"pH\":%.2f,\"chlorine\":%.2f,\"turbidity\":%.2f}\r\n", data.ph_value, data.chlorine_value, data.turbidity_value); for(int i = 0; buffer[i] != '\0'; i++) { USART1->DR = buffer[i]; while(!(USART1->SR & USART_SR_TXE)); } } // 设备控制 void Device_Control(SensorData data) { // pH控制 (目标范围7.2-7.6) if(data.ph_value < 7.2f) { GPIOC->BSRR = GPIO_BSRR_BS13; // 开启pH调节设备 device_status.ph_control = 1; } else if(data.ph_value > 7.6f) { GPIOC->BSRR = GPIO_BSRR_BR13; // 关闭pH调节设备 device_status.ph_control = 0; } // 余氯控制 (目标范围1.0-3.0 mg/L) if(data.chlorine_value < 1.0f) { GPIOC->BSRR = GPIO_BSRR_BS14; // 开启加氯设备 device_status.chlorine_control = 1; } else if(data.chlorine_value > 3.0f) { GPIOC->BSRR = GPIO_BSRR_BR14; // 关闭加氯设备 device_status.chlorine_control = 0; } // 过滤控制 (浊度>5时开启) if(data.turbidity_value > 5.0f) { GPIOC->BSRR = GPIO_BSRR_BS15; // 开启过滤设备 device_status.filter_control = 1; } else { GPIOC->BSRR = GPIO_BSRR_BR15; // 关闭过滤设备 device_status.filter_control = 0; } } // 异常预警检查 void Alert_Check(SensorData data) { if(data.ph_value < 6.8f || data.ph_value > 8.0f || data.chlorine_value < 0.5f || data.chlorine_value > 5.0f || data.turbidity_value > 10.0f) { // 发送预警信息 char alert_msg[64]; sprintf(alert_msg, "ALERT: pH=%.2f, Cl=%.2f, Turb=%.2f\r\n", data.ph_value, data.chlorine_value, data.turbidity_value); for(int i = 0; alert_msg[i] != '\0'; i++) { USART1->DR = alert_msg[i]; while(!(USART1->SR & USART_SR_TXE)); } } } // TIM2中断服务函数 void TIM2_IRQHandler(void) { if(TIM2->SR & TIM_SR_UIF) { TIM2->SR &= ~TIM_SR_UIF; system_tick++; } } // 简单延时函数 void delay_ms(uint32_t ms) { for(uint32_t i = 0; i < ms * 1000; i++) { __NOP(); } } 总结该系统基于STM32F103C8T6微控制器设计,实现了对游泳池水质的智能化监测与管理。通过集成pH、余氯和浊度传感器,系统能够实时采集关键水质参数,并自动控制调节设备运行,确保水质始终处于安全范围内。硬件部分采用模块化结构,包括STM32核心板、PH-4502C传感器、余氯模块和浊度传感器,通过洞洞板电路和杜邦线可靠连接。ESP8266-01S Wi-Fi模块实现了与华为云平台的数据交互,为远程监控提供了基础支持。系统具备数据异常预警和记录功能,当检测到参数超标时能及时触发警报并存储历史数据。结合QT开发的上位机界面,用户可直观查看水质趋势图和设备状态,提升了管理效率和系统可靠性。整体设计兼顾实用性与扩展性,为游泳池水质维护提供了一套低成本、高精度的自动化解决方案,未来可通过软件升级进一步优化数据分析能力。
  • [技术干货] 基于STM32设计的智能温室二氧化碳施肥系统
    项目开发背景随着全球人口持续增长和农业资源日益紧张,高效、可持续的农业生产方式成为解决粮食安全问题的关键。温室农业作为一种可控环境农业技术,能够通过优化作物生长条件来提高产量和品质,尤其在现代城市化进程中,其重要性日益凸显。然而,传统温室管理多依赖人工经验,难以实现精准调控,导致资源浪费和效率低下,特别是在二氧化碳施肥方面,不当的控制可能引发环境问题或作物生长不均。二氧化碳是植物光合作用的重要原料,在温室环境中,适当增加CO2浓度可以显著提升作物的光合效率,促进生长并增加产量。研究表明,在光照充足条件下,将CO2浓度维持在适宜范围(如800-1200 ppm)可使作物产量提高20%-30%。但传统CO2施肥方法往往基于粗略估算,缺乏实时监测和自动调节,容易造成浓度波动,影响作物健康,同时也增加了能源和成本消耗。为克服这些局限性,智能温室控制系统应运而生,它结合传感器技术、微控制器和物联网,实现对环境参数的精准监测与自动控制。本项目基于STM32微控制器设计智能CO2施肥系统,旨在通过集成CO2、温湿度和光照传感器,实时采集数据,并利用继电器自动调节CO2发生装置,确保施肥过程精准高效。同时,系统通过Wi-Fi模块连接华为云平台,实现数据远程传输和分析,辅以QT上位机进行可视化显示,帮助用户优化作物生长环境。该系统的开发不仅提升了温室管理的自动化水平,还通过数据驱动决策降低了人为误差,为现代智慧农业提供了可行的技术解决方案。它有望在资源节约、产量提升和环境保护方面发挥积极作用,推动农业向智能化、可持续化方向发展。设计实现的功能(1)实时监测温室CO2浓度(使用SGP30传感器)(2)实时监测温室温度(使用DHT22传感器)(3)实时监测温室湿度(使用DHT22传感器)(4)实时监测温室光照强度(使用GY-30传感器)(5)自动控制CO2发生装置进行精准施肥(使用继电器模块)(6)通过Wi-Fi将环境参数传输到华为云(使用ESP8266模块)(7)作物生长环境优化控制(通过STM32主控制器基于传感器数据实现)(8)QT上位机显示环境参数和施肥效果分析(通过云数据传输实现)项目硬件模块组成(1)STM32F103C8T6最小系统核心板作为主控制器(2)SGP30 CO2传感器监测二氧化碳浓度(3)DHT22温湿度传感器监测环境参数(4)GY-30光照传感器监测光照强度(5)继电器模块控制CO2发生装置(6)ESP8266-01S Wi-Fi模块实现华为云通信设计意义该系统通过实时监测温室内的二氧化碳浓度、温度和光照强度,并结合自动控制CO2发生装置,实现了对作物生长环境的精准调控。这种设计能够有效提升光合作用效率,促进作物健康生长,从而提高温室农业的生产力和经济效益。通过集成多种传感器和控制器,系统优化了资源利用,减少了人工干预和能源浪费。自动化的环境控制不仅降低了运营成本,还确保了作物在不同生长阶段获得适宜的条件,增强了温室管理的稳定性和可靠性。借助ESP8266 Wi-Fi模块与华为云的通信功能,系统支持远程数据监控和上位机显示,为用户提供实时的环境参数和施肥效果分析。这有助于农业管理者基于数据做出科学决策,及时调整控制策略,提升整体管理效率。该设计体现了现代农业向智能化和精准化发展的趋势,具有广泛的应用价值。它能够推动温室种植技术的创新,促进农业可持续发展,为应对粮食安全和环境挑战提供实用解决方案。设计思路本系统以STM32F103C8T6最小系统核心板作为主控制器,负责协调整个智能温室二氧化碳施肥系统的运行。系统通过集成多种传感器实时采集温室环境参数,其中SGP30 CO2传感器用于监测二氧化碳浓度,DHT22温湿度传感器获取环境温度数据,GY-30光照传感器检测光照强度。这些传感器数据通过I2C或GPIO接口传输到STM32控制器,进行实时处理和校准,确保监测的准确性和稳定性。在自动控制方面,STM32根据预设的CO2浓度阈值和作物生长需求,通过继电器模块精准控制CO2发生装置的开启与关闭。当监测到的CO2浓度低于设定值时,控制器会触发继电器启动施肥装置;反之则关闭,以实现二氧化碳的精准补充,避免浪费或过度施肥。这一过程完全自动化,无需人工干预,提高了温室管理的效率。作物生长环境优化控制主要通过分析采集的温度和光照数据来实现。STM32会综合环境参数,例如在光照充足时适当提高CO2施肥目标,或在温度过高时调整控制策略,以模拟理想生长条件。这种优化基于简单的反馈机制,确保作物处于适宜的CO2、温度和光照平衡状态,促进健康生长。系统通过ESP8266-01S Wi-Fi模块将处理后的环境数据和施肥状态上传至华为云平台,实现远程数据传输和存储。QT上位机应用程序从云平台获取这些数据,以图形化界面实时显示温室CO2浓度、温度、光照强度等参数,并提供历史趋势分析和施肥效果评估,帮助用户直观了解环境变化和控制系统性能。框架图+-------------------+ +-------------------+ +-------------------+ | | | | | | | SGP30 CO2传感器 |----->| | | | | | | | | | +-------------------+ | | | | | STM32F103C8T6 |----->| 继电器模块 |-----> CO2发生装置 +-------------------+ | 主控制器 | | | | | | | | | | DHT22温湿度传感器 |----->| | | | | | | | | | +-------------------+ | | +-------------------+ | | +-------------------+ | | +-------------------+ | | | | | | | GY-30光照传感器 |----->| |----->| ESP8266 Wi-Fi |-----> 华为云 ----> QT上位机 | | | | | | +-------------------+ +-------------------+ +-------------------+ 系统总体设计本系统基于STM32F103C8T6最小系统核心板作为主控制器,实现智能温室二氧化碳施肥的自动化管理。系统通过集成多种传感器和执行器,实时采集温室环境数据并控制CO2施肥过程,同时借助无线通信和上位机界面进行数据展示与分析,确保作物生长环境的优化。系统采用SGP30传感器监测二氧化碳浓度,DHT22传感器采集温度和湿度数据,GY-30传感器检测光照强度。STM32控制器定期轮询这些传感器,获取实时环境参数,并进行数据滤波和校准处理,以保证监测的准确性和稳定性。控制逻辑基于预设的环境阈值,STM32根据采集的CO2浓度、温度和光照数据,自动判断是否需要启动施肥。当CO2浓度低于设定值时,控制器通过继电器模块驱动CO2发生装置开启,进行精准施肥;同时,系统综合考虑温度和光照等因素,动态调整控制策略,以优化作物生长条件。通信部分通过ESP8266-01S Wi-Fi模块实现,系统将环境参数和施肥状态数据上传至华为云平台。这使得数据能够远程存储和访问,为上位机提供实时数据源,并支持后续的数据分析和历史记录查询。QT开发的上位机应用程序从华为云获取数据,以图形化界面实时显示温室环境参数,如CO2浓度、温度、湿度和光照强度,同时提供施肥效果分析功能,帮助用户直观评估系统性能并做出决策优化。系统功能总结功能模块功能描述环境参数监测实时监测温室内的CO2浓度、温度和光照强度,使用SGP30、DHT22和GY-30传感器采集数据CO2施肥控制自动控制CO2发生装置进行精准施肥,基于环境数据通过继电器模块实现开关控制环境优化控制综合CO2浓度、温度、光照等参数,动态调整环境条件以优化作物生长数据通信与显示通过ESP8266 Wi-Fi模块上传数据至华为云,QT上位机实时显示环境参数和施肥效果分析设计的各个功能模块描述STM32F103C8T6最小系统核心板作为主控制器,负责协调整个系统的运行,采集传感器数据并进行处理,根据预设逻辑自动控制CO2施肥操作,确保温室环境的稳定和优化。它通过集成各模块实现实时监测和控制功能,是整个系统的核心处理单元。SGP30 CO2传感器用于实时监测温室内的二氧化碳浓度,提供高精度的CO2数据,帮助系统判断是否需要启动施肥。DHT22温湿度传感器检测环境的温度和湿度参数,为环境优化提供全面的气候数据支持。GY-30光照传感器监测光照强度,辅助分析作物生长条件,确保光照因素得到充分考虑。继电器模块与CO2发生装置连接,根据主控制器的指令自动控制施肥设备的开启和关闭,实现精准的CO2施肥。当CO2浓度低于设定阈值时,系统会通过继电器触发CO2发生装置,以维持适宜的浓度水平,从而优化作物生长环境。ESP8266-01S Wi-Fi模块实现与华为云的通信功能,将采集的环境参数如CO2浓度、温度、湿度和光照强度上传至云平台,同时支持从云端接收指令或配置数据,增强系统的远程监控和互联能力。QT上位机软件用于显示温室的环境参数和施肥效果分析,提供直观的图形界面展示实时数据、历史趋势和施肥操作记录,帮助用户进行环境评估和决策优化。上位机代码设计// main.cpp #include <QApplication> #include <QMainWindow> #include <QVBoxLayout> #include <QHBoxLayout> #include <QGridLayout> #include <QLabel> #include <QPushButton> #include <QTextEdit> #include <QTabWidget> #include <QChartView> #include <QLineSeries> #include <QDateTimeAxis> #include <QValueAxis> #include <QTimer> #include <QMessageBox> #include <QGroupBox> #include <QSpinBox> #include <QDoubleSpinBox> #include <QProgressBar> #include <QTableWidget> #include <QHeaderView> #include <QtMqtt/QMqttClient> #include <QDateTime> #include <QtCharts> QT_CHARTS_USE_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) { setupUI(); setupMQTT(); setupCharts(); setupConnections(); } private slots: void onConnectButtonClicked() { if (mqttClient->state() == QMqttClient::Disconnected) { mqttClient->setHostname("你的华为云地址"); mqttClient->setPort(1883); mqttClient->setUsername("设备ID"); mqttClient->setPassword("设备密钥"); mqttClient->connectToHost(); } else { mqttClient->disconnectFromHost(); } } void onStateChanged(QMqttClient::ClientState state) { if (state == QMqttClient::Connected) { statusLabel->setText("已连接到华为云"); connectButton->setText("断开连接"); // 订阅主题 mqttClient->subscribe("sensor/data", 0); mqttClient->subscribe("control/status", 0); } else { statusLabel->setText("未连接"); connectButton->setText("连接"); } } void onMessageReceived(const QByteArray &message, const QMqttTopicName &topic) { QString msg = QString::fromUtf8(message); if (topic.name() == "sensor/data") { processSensorData(msg); } else if (topic.name() == "control/status") { controlStatusText->append(QDateTime::currentDateTime().toString() + " - " + msg); } } void onControlButtonClicked() { if (mqttClient->state() == QMqttClient::Connected) { QString command = QString("{\"target_co2\":%1,\"mode\":\"%2\"}") .arg(targetCO2SpinBox->value()) .arg(autoModeCheckBox->isChecked() ? "auto" : "manual"); mqttClient->publish("control/command", command.toUtf8(), 0, false); } } void updateCharts() { // 更新图表数据 QDateTime now = QDateTime::currentDateTime(); // CO2浓度图表 co2Series->append(now.toMSecsSinceEpoch(), currentCO2); if (co2Series->count() > 100) { co2Series->remove(0); } // 温度图表 tempSeries->append(now.toMSecsSinceEpoch(), currentTemp); if (tempSeries->count() > 100) { tempSeries->remove(0); } // 光照图表 lightSeries->append(now.toMSecsSinceEpoch(), currentLight); if (lightSeries->count() > 100) { lightSeries->remove(0); } // 更新X轴范围 QDateTime minTime = now.addSecs(-300); // 显示最近5分钟数据 co2Chart->axisX()->setMin(minTime); co2Chart->axisX()->setMax(now); } private: void setupUI() { setWindowTitle("智能温室CO2施肥监控系统"); setMinimumSize(1200, 800); QWidget *centralWidget = new QWidget(this); setCentralWidget(centralWidget); QVBoxLayout *mainLayout = new QVBoxLayout(centralWidget); // 状态栏 QHBoxLayout *statusLayout = new QHBoxLayout(); statusLabel = new QLabel("未连接"); connectButton = new QPushButton("连接"); statusLayout->addWidget(statusLabel); statusLayout->addStretch(); statusLayout->addWidget(connectButton); // 标签页 QTabWidget *tabWidget = new QTabWidget(); // 实时监控标签页 QWidget *monitorTab = createMonitorTab(); tabWidget->addTab(monitorTab, "实时监控"); // 数据分析标签页 QWidget *analysisTab = createAnalysisTab(); tabWidget->addTab(analysisTab, "数据分析"); // 控制设置标签页 QWidget *controlTab = createControlTab(); tabWidget->addTab(controlTab, "控制设置"); mainLayout->addLayout(statusLayout); mainLayout->addWidget(tabWidget); } QWidget* createMonitorTab() { QWidget *tab = new QWidget(); QHBoxLayout *layout = new QHBoxLayout(tab); // 左侧:传感器数据显示 QGroupBox *sensorGroup = new QGroupBox("传感器数据"); QGridLayout *sensorLayout = new QGridLayout(sensorGroup); sensorLayout->addWidget(new QLabel("CO2浓度:"), 0, 0); co2Label = new QLabel("-- ppm"); sensorLayout->addWidget(co2Label, 0, 1); sensorLayout->addWidget(new QLabel("温度:"), 1, 0); tempLabel = new QLabel("-- °C"); sensorLayout->addWidget(tempLabel, 1, 1); sensorLayout->addWidget(new QLabel("湿度:"), 2, 0); humidityLabel = new QLabel("-- %"); sensorLayout->addWidget(humidityLabel, 2, 1); sensorLayout->addWidget(new QLabel("光照强度:"), 3, 0); lightLabel = new QLabel("-- Lux"); sensorLayout->addWidget(lightLabel, 3, 1); sensorLayout->addWidget(new QLabel("设备状态:"), 4, 0); deviceStatusLabel = new QLabel("离线"); sensorLayout->addWidget(deviceStatusLabel, 4, 1); // 右侧:图表显示 QTabWidget *chartTabWidget = new QTabWidget(); QWidget *co2ChartWidget = new QWidget(); QVBoxLayout *co2ChartLayout = new QVBoxLayout(co2ChartWidget); co2ChartView = new QChartView(); co2ChartLayout->addWidget(co2ChartView); chartTabWidget->addTab(co2ChartWidget, "CO2浓度"); QWidget *tempChartWidget = new QWidget(); QVBoxLayout *tempChartLayout = new QVBoxLayout(tempChartWidget); tempChartView = new QChartView(); tempChartLayout->addWidget(tempChartView); chartTabWidget->addTab(tempChartWidget, "温度"); QWidget *lightChartWidget = new QWidget(); QVBoxLayout *lightChartLayout = new QVBoxLayout(lightChartWidget); lightChartView = new QChartView(); lightChartLayout->addWidget(lightChartView); chartTabWidget->addTab(lightChartWidget, "光照强度"); layout->addWidget(sensorGroup, 1); layout->addWidget(chartTabWidget, 3); return tab; } QWidget* createAnalysisTab() { QWidget *tab = new QWidget(); QVBoxLayout *layout = new QVBoxLayout(tab); // 数据表格 dataTable = new QTableWidget(); dataTable->setColumnCount(5); dataTable->setHorizontalHeaderLabels({"时间", "CO2浓度", "温度", "湿度", "光照强度"}); dataTable->horizontalHeader()->setStretchLastSection(true); // 分析结果 QGroupBox *analysisGroup = new QGroupBox("施肥效果分析"); QVBoxLayout *analysisLayout = new QVBoxLayout(analysisGroup); analysisText = new QTextEdit(); analysisText->setReadOnly(true); analysisLayout->addWidget(analysisText); layout->addWidget(dataTable, 2); layout->addWidget(analysisGroup, 1); return tab; } QWidget* createControlTab() { QWidget *tab = new QWidget(); QVBoxLayout *layout = new QVBoxLayout(tab); // 控制设置 QGroupBox *controlGroup = new QGroupBox("控制参数设置"); QGridLayout *controlLayout = new QGridLayout(controlGroup); controlLayout->addWidget(new QLabel("目标CO2浓度:"), 0, 0); targetCO2SpinBox = new QSpinBox(); targetCO2SpinBox->setRange(400, 2000); targetCO2SpinBox->setValue(800); targetCO2SpinBox->setSuffix(" ppm"); controlLayout->addWidget(targetCO2SpinBox, 0, 1); controlLayout->addWidget(new QLabel("控制模式:"), 1, 0); autoModeCheckBox = new QCheckBox("自动模式"); autoModeCheckBox->setChecked(true); controlLayout->addWidget(autoModeCheckBox, 1, 1); controlButton = new QPushButton("发送控制指令"); controlLayout->addWidget(controlButton, 2, 0, 1, 2); // 控制状态日志 QGroupBox *logGroup = new QGroupBox("控制状态日志"); QVBoxLayout *logLayout = new QVBoxLayout(logGroup); controlStatusText = new QTextEdit(); controlStatusText->setReadOnly(true); logLayout->addWidget(controlStatusText); layout->addWidget(controlGroup); layout->addWidget(logGroup); return tab; } void setupMQTT() { mqttClient = new QMqttClient(this); mqttClient->setClientId(QString("QT_CLIENT_%1").arg(QDateTime::currentSecsSinceEpoch())); } void setupCharts() { // CO2浓度图表 co2Chart = new QChart(); co2Series = new QLineSeries(); co2Chart->addSeries(co2Series); co2Chart->setTitle("CO2浓度实时监测"); QDateTimeAxis *axisX = new QDateTimeAxis(); axisX->setFormat("hh:mm:ss"); axisX->setTitleText("时间"); co2Chart->addAxis(axisX, Qt::AlignBottom); co2Series->attachAxis(axisX); QValueAxis *axisY = new QValueAxis(); axisY->setRange(400, 2000); axisY->setTitleText("CO2浓度 (ppm)"); co2Chart->addAxis(axisY, Qt::AlignLeft); co2Series->attachAxis(axisY); co2ChartView->setChart(co2Chart); // 温度图表 tempChart = new QChart(); tempSeries = new QLineSeries(); tempChart->addSeries(tempSeries); tempChart->setTitle("温度实时监测"); // 光照强度图表 lightChart = new QChart(); lightSeries = new QLineSeries(); lightChart->addSeries(lightSeries); lightChart->setTitle("光照强度实时监测"); } void setupConnections() { connect(connectButton, &QPushButton::clicked, this, &MainWindow::onConnectButtonClicked); connect(mqttClient, &QMqttClient::stateChanged, this, &MainWindow::onStateChanged); connect(mqttClient, &QMqttClient::messageReceived, this, &MainWindow::onMessageReceived); connect(controlButton, &QPushButton::clicked, this, &MainWindow::onControlButtonClicked); // 定时更新图表 QTimer *chartTimer = new QTimer(this); connect(chartTimer, &QTimer::timeout, this, &MainWindow::updateCharts); chartTimer->start(1000); // 每秒更新一次 } void processSensorData(const QString &data) { // 解析JSON数据 // 示例数据格式: {"co2":800,"temp":25.5,"humidity":60,"light":1200} QJsonDocument doc = QJsonDocument::fromJson(data.toUtf8()); if (!doc.isNull()) { QJsonObject obj = doc.object(); currentCO2 = obj["co2"].toInt(); currentTemp = obj["temp"].toDouble(); currentHumidity = obj["humidity"].toDouble(); currentLight = obj["light"].toInt(); // 更新UI co2Label->setText(QString("%1 ppm").arg(currentCO2)); tempLabel->setText(QString("%1 °C").arg(currentTemp)); humidityLabel->setText(QString("%1 %").arg(currentHumidity)); lightLabel->setText(QString("%1 Lux").arg(currentLight)); // 添加到数据表格 int row = dataTable->rowCount(); dataTable->insertRow(row); dataTable->setItem(row, 0, new QTableWidgetItem(QDateTime::currentDateTime().toString())); dataTable->setItem(row, 1, new QTableWidgetItem(QString::number(currentCO2))); dataTable->setItem(row, 2, new QTableWidgetItem(QString::number(currentTemp))); dataTable->setItem(row, 3, new QTableWidgetItem(QString::number(currentHumidity))); dataTable->setItem(row, 4, new QTableWidgetItem(QString::number(currentLight))); // 自动滚动到最后一行 dataTable->scrollToBottom(); // 更新分析结果 updateAnalysis(); } } void updateAnalysis() { QString analysis; // CO2浓度分析 if (currentCO2 < 600) { analysis += "? CO2浓度过低,需要施肥\n"; } else if (currentCO2 >= 600 && currentCO2 <= 1000) { analysis += "? CO2浓度适宜\n"; } else { analysis += "?? CO2浓度过高\n"; } // 温度分析 if (currentTemp < 18) { analysis += "? 温度过低\n"; } else if (currentTemp >= 18 && currentTemp <= 28) { analysis += "? 温度适宜\n"; } else { analysis += "?? 温度过高\n"; } // 光照分析 if (currentLight < 500) { analysis += "? 光照不足\n"; } else if (currentLight >= 500 && currentLight <= 2000) { analysis += "? 光照适宜\n"; } else { analysis += "?? 光照过强\n"; } analysisText->setPlainText(analysis); } private: // MQTT客户端 QMqttClient *mqttClient; // UI组件 QLabel *statusLabel; QPushButton *connectButton; QPushButton *controlButton; // 监控标签页组件 QLabel *co2Label; QLabel *tempLabel; QLabel *humidityLabel; QLabel *lightLabel; QLabel *deviceStatusLabel; // 图表组件 QChartView *co2ChartView; QChartView *tempChartView; QChartView *lightChartView; QChart *co2Chart; QChart *tempChart; QChart *lightChart; QLineSeries *co2Series; QLineSeries *tempSeries; QLineSeries *lightSeries; // 分析标签页组件 QTableWidget *dataTable; QTextEdit *analysisText; // 控制标签页组件 QSpinBox *targetCO2SpinBox; QCheckBox *autoModeCheckBox; QTextEdit *controlStatusText; // 当前传感器数据 int currentCO2 = 0; double currentTemp = 0; double currentHumidity = 0; int currentLight = 0; }; int main(int argc, char *argv[]) { QApplication app(argc, argv); MainWindow window; window.show(); return app.exec(); } #include "main.moc" # CMakeLists.txt cmake_minimum_required(VERSION 3.16) project(SmartGreenhouseCO2) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(Qt6 REQUIRED COMPONENTS Core Widgets Charts Mqtt Network) qt_standard_project_setup() qt_add_executable(SmartGreenhouseCO2 main.cpp ) qt_add_resources(SmartGreenhouseCO2 "resources" PREFIX "/" ) target_link_libraries(SmartGreenhouseCO2 Qt6::Core Qt6::Widgets Qt6::Charts Qt6::Mqtt Qt6::Network) # SmartGreenhouseCO2.pro QT += core widgets charts mqtt network CONFIG += c++17 TARGET = SmartGreenhouseCO2 TEMPLATE = app SOURCES += \ main.cpp HEADERS += 这个上位机程序包含以下主要功能:实时监控:显示CO2浓度、温度、湿度和光照强度实时曲线图表显示历史数据设备连接状态监控数据分析:数据表格记录历史数据施肥效果分析环境参数评估控制设置:CO2目标浓度设置自动/手动模式切换控制指令发送控制状态日志华为云通信:MQTT协议连接数据订阅和发布实时数据接收使用前需要配置华为云的连接参数(地址、设备ID、密钥等)。程序采用模块化设计,便于维护和扩展。模块代码设计#include "stm32f10x.h" // 引脚定义 #define DHT22_GPIO_PORT GPIOA #define DHT22_GPIO_PIN GPIO_Pin_0 #define RELAY_GPIO_PORT GPIOA #define RELAY_GPIO_PIN GPIO_Pin_1 // 传感器地址定义 #define SGP30_ADDRESS 0x58 #define GY30_ADDRESS 0x23 // 函数声明 void System_Init(void); void GPIO_Init(void); void I2C_Init(void); void USART_Init(void); void TIM_Init(void); void SGP30_Init(void); uint16_t SGP30_ReadCO2(void); float DHT22_ReadTemp(void); float DHT22_ReadHumidity(void); uint16_t GY30_ReadLight(void); void Relay_Control(uint8_t state); void ESP8266_SendData(float co2, float temp, float humidity, float light); // 系统初始化 void System_Init(void) { // 开启外设时钟 RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_AFIOEN; RCC->APB1ENR |= RCC_APB1ENR_I2C1EN | RCC_APB1ENR_USART2EN | RCC_APB1ENR_TIM2EN; // 系统时钟配置 SystemInit(); } // GPIO初始化 void GPIO_Init(void) { // DHT22引脚配置 - 输入 GPIOA->CRL &= ~(0xF << (0*4)); // PA0清空 GPIOA->CRL |= (0x4 << (0*4)); // PA0输入上拉 // 继电器引脚配置 - 输出 GPIOA->CRL &= ~(0xF << (1*4)); // PA1清空 GPIOA->CRL |= (0x1 << (1*4)); // PA1推挽输出50MHz GPIOA->BSRR = GPIO_BSRR_BR1; // 默认关闭继电器 // I2C引脚配置 - PB6(SCL), PB7(SDA) GPIOB->CRL &= ~(0xFF << (6*4)); // PB6,PB7清空 GPIOB->CRL |= (0xB4 << (6*4)); // PB6,PB7复用开漏输出 // USART2引脚配置 - PA2(TX), PA3(RX) GPIOA->CRL &= ~(0xFF << (2*4)); // PA2,PA3清空 GPIOA->CRL |= (0xB4 << (2*4)); // PA2,PA3复用推挽输出 } // I2C初始化 void I2C_Init(void) { // 复位I2C1 RCC->APB1RSTR |= RCC_APB1RSTR_I2C1RST; RCC->APB1RSTR &= ~RCC_APB1RSTR_I2C1RST; // I2C配置 I2C1->CR1 &= ~I2C_CR1_PE; // 禁用I2C // 标准模式配置(100kHz) I2C1->CR2 = 36; // 36MHz I2C1->CCR = 180; // CCR = 36M/(2*100k) = 180 I2C1->TRISE = 37; // TRISE = 36M/1000ns + 1 = 37 I2C1->CR1 |= I2C_CR1_PE; // 启用I2C } // USART初始化 void USART_Init(void) { // USART2配置 USART2->BRR = 0x1388; // 115200波特率 USART2->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; } // 定时器初始化 void TIM_Init(void) { // TIM2用于微秒延时 TIM2->PSC = 72 - 1; // 1MHz计数频率 TIM2->ARR = 0xFFFF; TIM2->CR1 = TIM_CR1_CEN; } // SGP30初始化 void SGP30_Init(void) { uint8_t cmd[2] = {0x20, 0x03}; // 初始化命令 // I2C启动 I2C1->CR1 |= I2C_CR1_START; while(!(I2C1->SR1 & I2C_SR1_SB)); // 发送设备地址 I2C1->DR = SGP30_ADDRESS << 1; while(!(I2C1->SR1 & I2C_SR1_ADDR)); (void)I2C1->SR2; // 发送初始化命令 for(int i=0; i<2; i++) { I2C1->DR = cmd[i]; while(!(I2C1->SR1 & I2C_SR1_TXE)); } // I2C停止 I2C1->CR1 |= I2C_CR1_STOP; while(I2C1->SR2 & I2C_SR2_MSL); } // SGP30读取CO2浓度 uint16_t SGP30_ReadCO2(void) { uint8_t cmd[2] = {0x20, 0x08}; // 测量命令 uint8_t data[6]; uint16_t co2; // 发送测量命令 I2C1->CR1 |= I2C_CR1_START; while(!(I2C1->SR1 & I2C_SR1_SB)); I2C1->DR = SGP30_ADDRESS << 1; while(!(I2C1->SR1 & I2C_SR1_ADDR)); (void)I2C1->SR2; for(int i=0; i<2; i++) { I2C1->DR = cmd[i]; while(!(I2C1->SR1 & I2C_SR1_TXE)); } I2C1->CR1 |= I2C_CR1_STOP; // 延时等待测量完成 for(volatile int i=0; i<10000; i++); // 读取数据 I2C1->CR1 |= I2C_CR1_START; while(!(I2C1->SR1 & I2C_SR1_SB)); I2C1->DR = (SGP30_ADDRESS << 1) | 0x01; while(!(I2C1->SR1 & I2C_SR1_ADDR)); (void)I2C1->SR2; // 读取6字节数据 for(int i=0; i<6; i++) { if(i == 5) I2C1->CR1 &= ~I2C_CR1_ACK; while(!(I2C1->SR1 & I2C_SR1_RXNE)); data[i] = I2C1->DR; } I2C1->CR1 |= I2C_CR1_STOP; // 计算CO2值 co2 = (data[0] << 8) | data[1]; return co2; } // DHT22读取温度 float DHT22_ReadTemp(void) { uint8_t data[5] = {0}; uint32_t time = 0; // 主机拉低18ms GPIOA->BRR = DHT22_GPIO_PIN; Delay_ms(18); GPIOA->BSRR = DHT22_GPIO_PIN; // 等待响应 while(GPIOA->IDR & DHT22_GPIO_PIN); while(!(GPIOA->IDR & DHT22_GPIO_PIN)); while(GPIOA->IDR & DHT22_GPIO_PIN); // 读取40位数据 for(int i=0; i<40; i++) { while(!(GPIOA->IDR & DHT22_GPIO_PIN)); TIM2->CNT = 0; while(GPIOA->IDR & DHT22_GPIO_PIN); time = TIM2->CNT; data[i/8] <<= 1; if(time > 40) // 高电平持续时间大于40us为1 data[i/8] |= 1; } // 计算温度 float temp = ((data[2] & 0x7F) << 8 | data[3]) * 0.1; if(data[2] & 0x80) temp = -temp; return temp; } // DHT22读取湿度 float DHT22_ReadHumidity(void) { uint8_t data[5] = {0}; uint32_t time = 0; // 读取过程同上 GPIOA->BRR = DHT22_GPIO_PIN; Delay_ms(18); GPIOA->BSRR = DHT22_GPIO_PIN; while(GPIOA->IDR & DHT22_GPIO_PIN); while(!(GPIOA->IDR & DHT22_GPIO_PIN)); while(GPIOA->IDR & DHT22_GPIO_PIN); for(int i=0; i<40; i++) { while(!(GPIOA->IDR & DHT22_GPIO_PIN)); TIM2->CNT = 0; while(GPIOA->IDR & DHT22_GPIO_PIN); time = TIM2->CNT; data[i/8] <<= 1; if(time > 40) data[i/8] |= 1; } // 计算湿度 float humidity = (data[0] << 8 | data[1]) * 0.1; return humidity; } // GY30读取光照强度 uint16_t GY30_ReadLight(void) { uint8_t cmd = 0x10; // 高分辨率模式 uint16_t lux; // 发送测量命令 I2C1->CR1 |= I2C_CR1_START; while(!(I2C1->SR1 & I2C_SR1_SB)); I2C1->DR = GY30_ADDRESS << 1; while(!(I2C1->SR1 & I2C_SR1_ADDR)); (void)I2C1->SR2; I2C1->DR = cmd; while(!(I2C1->SR1 & I2C_SR1_TXE)); I2C1->CR1 |= I2C_CR1_STOP; // 等待测量完成 Delay_ms(180); // 读取数据 I2C1->CR1 |= I2C_CR1_START; while(!(I2C1->SR1 & I2C_SR1_SB)); I2C1->DR = (GY30_ADDRESS << 1) | 0x01; while(!(I2C1->SR1 & I2C_SR1_ADDR)); (void)I2C1->SR2; // 读取2字节数据 uint8_t data[2]; for(int i=0; i<2; i++) { if(i == 1) I2C1->CR1 &= ~I2C_CR1_ACK; while(!(I2C1->SR1 & I2C_SR1_RXNE)); data[i] = I2C1->DR; } I2C1->CR1 |= I2C_CR1_STOP; // 计算光照强度 lux = (data[0] << 8) | data[1]; return lux / 1.2; // 转换为lux单位 } // 继电器控制 void Relay_Control(uint8_t state) { if(state) GPIOA->BSRR = RELAY_GPIO_PIN; // 打开继电器 else GPIOA->BRR = RELAY_GPIO_PIN; // 关闭继电器 } // ESP8266发送数据到华为云 void ESP8266_SendData(float co2, float temp, float humidity, float light) { char buffer[128]; // 构建JSON数据 sprintf(buffer, "{\"co2\":%.1f,\"temp\":%.1f,\"humidity\":%.1f,\"light\":%.1f}", co2, temp, humidity, light); // 通过USART发送数据 for(int i=0; buffer[i]!='\0'; i++) { USART2->DR = buffer[i]; while(!(USART2->SR & USART_SR_TXE)); } } // 延时函数 void Delay_ms(uint32_t ms) { for(uint32_t i=0; i<ms*1000; i++); } // 主函数 int main(void) { System_Init(); GPIO_Init(); I2C_Init(); USART_Init(); TIM_Init(); SGP30_Init(); while(1) { // 读取传感器数据 uint16_t co2_raw = SGP30_ReadCO2(); float co2_ppm = co2_raw; float temperature = DHT22_ReadTemp(); float humidity = DHT22_ReadHumidity(); uint16_t light_lux = GY30_ReadLight(); // CO2施肥控制逻辑 if(co2_ppm < 800) // CO2浓度低于800ppm时开启施肥 { Relay_Control(1); // 打开CO2发生装置 } else if(co2_ppm > 1200) // CO2浓度高于1200ppm时关闭施肥 { Relay_Control(0); // 关闭CO2发生装置 } // 发送数据到华为云 ESP8266_SendData(co2_ppm, temperature, humidity, (float)light_lux); Delay_ms(5000); // 5秒采样间隔 } } 项目核心代码#include "stm32f10x.h" #include "delay.h" #include "sgp30.h" #include "dht22.h" #include "gy30.h" #include "esp8266.h" #include "relay.h" // 全局变量定义 uint16_t co2_ppm = 0; float temperature = 0; float humidity = 0; uint16_t light_intensity = 0; uint8_t relay_state = 0; // 华为云平台配置 #define DEVICE_ID "your_device_id" #define DEVICE_SECRET "your_device_secret" // CO2浓度阈值 #define CO2_TARGET_MIN 800 #define CO2_TARGET_MAX 1200 void RCC_Configuration(void); void GPIO_Configuration(void); void USART_Configuration(void); void I2C_Configuration(void); void TIM_Configuration(void); void System_Init(void); void Sensor_Data_Update(void); void CO2_Control_Logic(void); void Cloud_Data_Upload(void); int main(void) { System_Init(); // 初始化传感器模块 SGP30_Init(); DHT22_Init(); GY30_Init(); ESP8266_Init(); Relay_Init(); // 连接华为云 ESP8266_ConnectCloud(DEVICE_ID, DEVICE_SECRET); while(1) { // 传感器数据采集 Sensor_Data_Update(); // CO2施肥控制逻辑 CO2_Control_Logic(); // 数据上传到云平台 Cloud_Data_Upload(); // 延时2秒 Delay_ms(2000); } } void System_Init(void) { RCC_Configuration(); GPIO_Configuration(); USART_Configuration(); I2C_Configuration(); TIM_Configuration(); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); } void RCC_Configuration(void) { // 开启外部高速晶振 RCC->CR |= 0x00010000; while(!(RCC->CR & 0x00020000)); // 配置PLL为9倍频 RCC->CFGR |= 0x001C0000; RCC->CFGR |= 0x00010000; // 开启PLL RCC->CR |= 0x01000000; while(!(RCC->CR & 0x02000000)); // 选择PLL作为系统时钟 RCC->CFGR |= 0x00000002; while((RCC->CFGR & 0x0000000C) != 0x08); // 开启外设时钟 RCC->APB2ENR |= 0x00000001; // AFIO RCC->APB2ENR |= 0x00000004; // GPIOA RCC->APB2ENR |= 0x00000008; // GPIOB RCC->APB2ENR |= 0x00000010; // GPIOC RCC->APB2ENR |= 0x00004000; // USART1 RCC->APB2ENR |= 0x00000020; // ADC1 RCC->APB1ENR |= 0x00020000; // I2C1 RCC->APB1ENR |= 0x00000002; // TIM3 } void GPIO_Configuration(void) { // USART1 TX(PA9) RX(PA10) GPIOA->CRH &= 0xFFFFF00F; GPIOA->CRH |= 0x000004B0; // I2C1 SCL(PB6) SDA(PB7) GPIOB->CRL &= 0x00FFFFFF; GPIOB->CRL |= 0xBB000000; // DHT22数据线(PC0) GPIOC->CRL &= 0xFFFFFFF0; GPIOC->CRL |= 0x00000008; // 继电器控制(PC1) GPIOC->CRL &= 0xFFFFFF0F; GPIOC->CRL |= 0x00000020; // GY30光照传感器(PC2) GPIOC->CRL &= 0xFFFFF0FF; GPIOC->CRL |= 0x00000800; } void USART_Configuration(void) { // USART1配置:115200,8,N,1 USART1->BRR = 0x0271; USART1->CR1 = 0x200C; USART1->CR2 = 0x0000; USART1->CR3 = 0x0000; } void I2C_Configuration(void) { // I2C1配置 I2C1->CR1 = 0x0000; I2C1->CR2 = 0x0018; I2C1->CCR = 0x0050; I2C1->TRISE = 0x0013; I2C1->CR1 = 0x0001; } void TIM_Configuration(void) { // TIM3用于延时和定时任务 TIM3->PSC = 7199; TIM3->ARR = 9999; TIM3->CR1 = 0x0084; TIM3->DIER = 0x0001; NVIC->ISER[0] |= 0x00001000; TIM3->CR1 |= 0x0001; } void Sensor_Data_Update(void) { // 读取CO2浓度 co2_ppm = SGP30_ReadCO2(); // 读取温湿度 if(DHT22_ReadData(&temperature, &humidity) == SUCCESS) { // 温湿度读取成功 } // 读取光照强度 light_intensity = GY30_ReadLight(); } void CO2_Control_Logic(void) { // CO2施肥控制算法 if(co2_ppm < CO2_TARGET_MIN) { // CO2浓度过低,开启施肥 Relay_ON(); relay_state = 1; } else if(co2_ppm > CO2_TARGET_MAX) { // CO2浓度过高,关闭施肥 Relay_OFF(); relay_state = 0; } // 考虑光照和温度条件的优化控制 if(light_intensity < 5000 || temperature < 15 || temperature > 35) { // 光照不足或温度不适宜时停止施肥 Relay_OFF(); relay_state = 0; } } void Cloud_Data_Upload(void) { char upload_data[256]; // 构建JSON格式数据 sprintf(upload_data, "{\"co2\":%d,\"temp\":%.1f,\"humi\":%.1f,\"light\":%d,\"relay\":%d}", co2_ppm, temperature, humidity, light_intensity, relay_state); // 通过ESP8266上传到华为云 ESP8266_SendData(upload_data); } // TIM3中断服务函数 void TIM3_IRQHandler(void) { if(TIM3->SR & 0x0001) { TIM3->SR &= ~0x0001; // 定时任务处理 } } // USART1中断服务函数 void USART1_IRQHandler(void) { if(USART1->SR & 0x0020) { uint8_t data = USART1->DR; // ESP8266数据接收处理 ESP8266_ReceiveHandler(data); } } 总结本系统基于STM32F103C8T6核心板设计,实现了智能温室二氧化碳施肥的全面监控与控制。通过集成多种传感器,系统能够实时采集CO2浓度、温度、湿度和光照强度等环境参数,并利用自动控制机制精准调节CO2发生装置,确保作物在最优条件下生长。硬件组成包括SGP30 CO2传感器、DHT22温湿度传感器和GY-30光照传感器,用于精确监测环境数据,而继电器模块则负责执行CO2施肥操作。ESP8266-01S Wi-Fi模块实现了与华为云的稳定通信,支持远程数据传输和监控。系统还配备了QT上位机界面,方便用户实时查看环境参数和施肥效果分析,从而进行数据驱动的决策优化。这种集成设计不仅提高了温室管理的自动化水平,还增强了系统的可靠性和实用性。总体而言,该系统通过智能控制和远程通信技术,有效提升了温室种植的精准度和效率,为现代农业的可持续发展提供了有力的技术支持。其模块化结构和可扩展性,使其在智能农业应用中具有广泛的前景和价值。
  • [技术干货] 基于STM32设计的智能图书馆书籍定位系统设计
    项目开发背景随着图书馆藏书量的不断增加和读者需求的日益多样化,传统图书馆管理模式面临诸多挑战。书籍错架、查找困难、借还流程效率低下等问题,不仅增加了管理员的工作负担,也降低了读者的体验满意度。人工盘点和管理方式容易导致数据更新延迟,影响图书馆资源的有效利用,亟需引入智能化技术来优化管理流程。物联网和嵌入式技术的快速发展为图书馆智能化改造提供了新的解决方案。RFID技术以其非接触式识别、多目标读取和精确定位能力,成为实现书籍高效管理的理想选择。结合微控制器和无线通信技术,可以构建实时、准确的书籍监控系统,显著提升图书馆的运营效率和服务水平。本项目基于STM32主控制器,通过集成RFID模块、显示设备和无线传输模块,旨在实现书籍的精准定位、状态监测和读者引导。系统能够自动识别书籍借还状态,实时追踪馆内分布,并通过OLED显示屏和LED指示灯为读者提供直观的寻书导航。同时,数据通过Wi-Fi上传至云平台,结合QT上位机实现书籍分布可视化和流通统计分析,为图书馆管理提供全面的数据支持。该智能书籍定位系统不仅解决了传统管理中的核心问题,还通过错架报警和智能引导功能,进一步提升了图书馆的自动化水平。其低成本、高可靠性的设计,使得该系统在各类大中小型图书馆中具有广泛的应用潜力,为推动图书馆数字化转型提供了实用参考。设计实现的功能(1)实现书籍标签识别(2)提供寻书导航(3)指示书籍具体位置(4)上传数据至华为云项目硬件模块组成(1)STM32F103C8T6最小系统核心板作为主控制器(2)RFID-RC522模块实现书籍标签识别(3)0.96寸OLED显示屏提供寻书导航(4)LED指示灯指示书籍具体位置(5)ESP8266-01S Wi-Fi模块上传数据至华为云(6)洞洞板焊接RFID天线电路,杜邦线连接各模块设计意义这个基于STM32设计的智能图书馆书籍定位系统,通过集成RFID技术和嵌入式硬件,实现了书籍的精准定位与管理,有效提升了图书馆的运营效率。该系统能够自动识别书籍位置,减少人工干预,降低管理成本,同时为图书馆的智能化转型提供了可行的技术方案。采用RFID-RC522模块进行书籍标签识别,确保了书籍信息的快速采集和准确追踪,避免了传统人工盘点中的错误和遗漏。这不仅提高了图书检索的响应速度,还增强了图书馆对藏书状态的实时掌控能力,为日常管理提供了可靠的数据支持。实时监测书籍借还状态和馆内分布,使图书馆能够动态跟踪书籍流通情况,及时更新库存信息。通过ESP8266-01S Wi-Fi模块将数据上传至华为云,实现了远程监控和数据分析,便于管理员进行资源调配和决策优化,提升了图书馆的服务响应能力。读者寻书引导功能通过0.96寸OLED显示屏提供直观的导航指示,帮助读者快速定位目标书籍,显著改善了借阅体验。错架图书报警则通过LED指示灯及时提示错误放置,减少了书籍乱架现象,维护了图书馆的秩序和资源利用率。QT上位机显示图书馆书籍分布图和流通统计,为管理员提供了可视化的管理界面,便于分析书籍流动趋势和生成统计报告。这有助于图书馆进行长期规划和资源优化,提升了数据驱动的管理能力。硬件组成如STM32F103C8T6核心板、RFID模块和OLED显示屏的集成,体现了嵌入式系统在现实应用中的稳定性和实用性。使用洞洞板和杜邦线连接方式,降低了系统构建的复杂性和成本,使得该设计易于推广到中小型图书馆,具有较高的实际应用价值。设计思路本系统以STM32F103C8T6最小系统核心板作为主控制器,负责协调各模块工作,实现智能图书馆书籍的定位和管理。系统通过RFID-RC522模块读取书籍上的电子标签,获取书籍的唯一标识信息,从而实现对书籍的精确识别和位置追踪。书籍借还状态和馆内分布数据由STM32实时处理,并通过ESP8266-01S Wi-Fi模块将信息上传至华为云平台,确保数据的远程可访问性和实时更新。整体设计利用洞洞板焊接RFID天线电路,并使用杜邦线连接各模块,构建一个稳定可靠的硬件基础。RFID技术的应用是系统核心,通过部署多个RFID阅读器在书架不同位置,系统能够检测书籍的当前位置和移动状态。当读者借还书籍时,RFID模块读取标签信息,STM32处理这些数据以判断书籍是否在正确位置,如果发现错架情况,会触发报警机制,提醒工作人员及时处理。同时,书籍的借还记录和馆内分布信息被实时更新,并通过Wi-Fi模块发送到云服务器,便于后续统计和分析。为了提供便捷的用户体验,系统集成0.96寸OLED显示屏和LED指示灯。OLED显示屏用于显示寻书导航信息,例如书籍所在书架区域和路径指引,帮助读者快速定位目标书籍。LED指示灯则安装在书架上,通过点亮特定LED来直观指示书籍的具体位置,减少读者搜索时间。这些交互设计基于STM32的控制逻辑,确保信息准确传达。数据通信部分依赖ESP8266-01S Wi-Fi模块,它将STM32处理后的书籍状态和分布数据上传至华为云。云平台存储这些信息后,QT上位机可以实时获取并显示图书馆书籍分布图和流通统计,如图书借还频率和热点区域分析。这使管理员能够远程监控图书馆运行状况,并优化资源管理。整个系统设计注重实用性和可靠性,无需额外功能扩展,即可满足智能图书馆的基本需求。系统工作流程始于RFID模块扫描书籍标签,STM32解析数据后,一方面驱动OLED和LED进行本地导航,另一方面通过Wi-Fi上传数据至云。QT上位机从云平台拉取数据,可视化展示书籍分布和统计信息,实现闭环管理。这种设计确保了书籍定位的准确性、借还状态的可控性,以及错架图书的及时报警,提升了图书馆运营效率。框架图智能图书馆书籍定位系统框架图 +----------------+ +-----------------+ | RFID-RC522 |----->| STM32F103C8T6 | | (书籍标签识别) | | (主控制器) | +----------------+ +-----------------+ | +-------------------+-------------------+ | | | v v v +-------------+ +-------------+ +-------------+ | OLED | | LED | | ESP8266-01S | | (寻书导航) | | (位置指示) | | (Wi-Fi上传) | +-------------+ +-------------+ +-------------+ | v +-----------+ | 华为云 | | (数据存储)| +-----------+ | v +-----------+ | QT上位机 | | (分布图) | +-----------+ 系统总体设计本系统基于STM32F103C8T6最小系统核心板作为主控制器,集成RFID技术实现智能图书馆书籍的精确定位和管理。系统通过协调各硬件模块,实现对书籍借还状态和馆内分布的实时监测,并为读者提供寻书引导及错架图书报警功能。硬件组成包括RFID-RC522模块、0.96寸OLED显示屏、LED指示灯、ESP8266-01S Wi-Fi模块以及洞洞板焊接的RFID天线电路,各模块通过杜邦线连接构成完整系统。RFID-RC522模块负责读取书籍上附着的RFID标签,实现书籍的唯一识别和位置追踪。天线电路焊接在洞洞板上,确保信号覆盖稳定,能够准确检测书籍在书架上的具体位置,并实时更新借还状态。主控制器处理RFID数据,判断书籍是否在正确架位,并在检测到错放时触发报警机制。用户交互部分通过0.96寸OLED显示屏为读者提供寻书导航信息,例如显示书籍所在架位编号或路径指示。LED指示灯安装在书架上,根据控制器指令点亮以高亮显示目标书籍位置,辅助读者快速定位,减少查找时间。同时,系统通过实时监测书籍状态,自动识别借还操作,确保数据准确性。数据通信由ESP8266-01S Wi-Fi模块实现,将书籍位置、借还状态和错架信息上传至华为云平台,实现远程数据存储和监控。QT上位机软件从云端获取数据,动态显示图书馆书籍分布图和流通统计,如图书借阅频率和馆内存放情况,便于管理员进行决策和维护。系统整体设计注重实用性和可靠性,通过硬件模块的协同工作,实现书籍管理的自动化和智能化。所有功能均基于实际硬件实现,无需额外添加,确保了系统的稳定运行和高效服务。系统功能总结功能描述实现方式采用RFID技术实现书籍精确定位和管理使用STM32F103C8T6核心板和RFID-RC522模块实时监测书籍借还状态和馆内分布通过RFID识别和ESP8266 Wi-Fi模块上传数据至华为云读者寻书引导和错架图书报警利用0.96寸OLED显示屏和LED指示灯QT上位机显示图书馆书籍分布图和流通统计数据经Wi-Fi上传,由QT上位机软件处理显示设计的各个功能模块描述STM32F103C8T6最小系统核心板作为主控制器,负责整个系统的协调与控制,处理来自各个模块的数据输入和输出,执行书籍定位、状态监测和报警逻辑,确保系统稳定运行。RFID-RC522模块用于读取书籍上的RFID标签,实现书籍的精确定位和管理,检测书籍的借还状态,并通过天线电路识别标签信息,确保数据准确采集。0.96寸OLED显示屏提供读者寻书引导功能,显示书籍位置信息和导航指示,帮助读者快速定位目标书籍,提升用户体验。LED指示灯用于指示书籍的具体位置,当读者寻书时,LED灯亮起以标记书籍所在书架,辅助实现错架图书报警和位置提示。ESP8266-01S Wi-Fi模块负责将书籍状态、馆内分布数据上传至华为云,实现实时监测和数据传输,支持远程管理和统计分析。洞洞板焊接RFID天线电路和杜邦线连接各模块,确保硬件电路的稳定搭建和信号传输,为系统提供可靠的物理连接基础。上位机代码设计#include <QtWidgets> #include <QtCharts> #include <QNetworkAccessManager> #include <QNetworkRequest> #include <QNetworkReply> #include <QJsonDocument> #include <QJsonObject> #include <QJsonArray> #include <QTimer> // 书籍信息结构体 struct BookInfo { QString id; QString title; QString author; QString location; bool isBorrowed; QDateTime lastUpdate; }; // 主窗口类 class LibrarySystem : public QMainWindow { Q_OBJECT public: LibrarySystem(QWidget *parent = nullptr) : QMainWindow(parent) { setupUI(); setupNetwork(); setupTimer(); loadSampleData(); } private slots: void updateData() { fetchBookData(); updateCharts(); } void onBookLocated(const QString &bookId) { highlightBookLocation(bookId); showNavigationPath(bookId); } void onSearchRequested() { QString keyword = searchEdit->text(); searchBooks(keyword); } private: void setupUI() { // 主窗口设置 setWindowTitle("智能图书馆管理系统"); setMinimumSize(1200, 800); // 中央部件 QWidget *centralWidget = new QWidget; setCentralWidget(centralWidget); // 主布局 QHBoxLayout *mainLayout = new QHBoxLayout(centralWidget); // 左侧面板 - 图书馆地图 setupLibraryMap(mainLayout); // 右侧面板 - 控制和信息显示 setupControlPanel(mainLayout); } void setupLibraryMap(QLayout *mainLayout) { QWidget *mapWidget = new QWidget; QVBoxLayout *mapLayout = new QVBoxLayout(mapWidget); // 地图标题 QLabel *mapTitle = new QLabel("图书馆平面图"); mapTitle->setAlignment(Qt::AlignCenter); mapTitle->setStyleSheet("font-size: 16pt; font-weight: bold;"); mapLayout->addWidget(mapTitle); // 地图图形视图 mapGraphicsView = new QGraphicsView; mapGraphicsScene = new QGraphicsScene; mapGraphicsView->setScene(mapGraphicsScene); mapGraphicsView->setRenderHint(QPainter::Antialiasing); mapLayout->addWidget(mapGraphicsView); mainLayout->addWidget(mapWidget, 2); } void setupControlPanel(QLayout *mainLayout) { QWidget *controlWidget = new QWidget; QVBoxLayout *controlLayout = new QVBoxLayout(controlWidget); // 搜索区域 setupSearchArea(controlLayout); // 统计图表 setupCharts(controlLayout); // 书籍列表 setupBookList(controlLayout); // 状态信息 setupStatusArea(controlLayout); mainLayout->addWidget(controlWidget, 1); } void setupSearchArea(QLayout *layout) { QGroupBox *searchGroup = new QGroupBox("书籍搜索"); QVBoxLayout *searchLayout = new QVBoxLayout(searchGroup); QHBoxLayout *searchInputLayout = new QHBoxLayout; searchEdit = new QLineEdit; searchEdit->setPlaceholderText("输入书名、作者或ISBN..."); QPushButton *searchBtn = new QPushButton("搜索"); connect(searchBtn, &QPushButton::clicked, this, &LibrarySystem::onSearchRequested); searchInputLayout->addWidget(searchEdit); searchInputLayout->addWidget(searchBtn); searchLayout->addLayout(searchInputLayout); // 搜索结果 searchResultList = new QListWidget; connect(searchResultList, &QListWidget::itemClicked, [this](QListWidgetItem *item) { onBookLocated(item->data(Qt::UserRole).toString()); }); searchLayout->addWidget(searchResultList); layout->addWidget(searchGroup); } void setupCharts(QLayout *layout) { QGroupBox *chartGroup = new QGroupBox("流通统计"); QVBoxLayout *chartLayout = new QVBoxLayout(chartGroup); // 借阅统计图表 borrowChartView = new QChartView; borrowChartView->setRenderHint(QPainter::Antialiasing); chartLayout->addWidget(borrowChartView); // 分类统计图表 categoryChartView = new QChartView; categoryChartView->setRenderHint(QPainter::Antialiasing); chartLayout->addWidget(categoryChartView); layout->addWidget(chartGroup); } void setupBookList(QLayout *layout) { QGroupBox *bookGroup = new QGroupBox("书籍状态"); QVBoxLayout *bookLayout = new QVBoxLayout(bookGroup); bookTable = new QTableWidget; bookTable->setColumnCount(5); bookTable->setHorizontalHeaderLabels({"书名", "作者", "位置", "状态", "最后更新"}); bookTable->setSelectionBehavior(QAbstractItemView::SelectRows); bookLayout->addWidget(bookTable); layout->addWidget(bookGroup); } void setupStatusArea(QLayout *layout) { QGroupBox *statusGroup = new QGroupBox("系统状态"); QVBoxLayout *statusLayout = new QVBoxLayout(statusGroup); statusText = new QTextEdit; statusText->setReadOnly(true); statusLayout->addWidget(statusText); // 控制按钮 QHBoxLayout *buttonLayout = new QHBoxLayout; QPushButton *refreshBtn = new QPushButton("刷新数据"); QPushButton *exportBtn = new QPushButton("导出报告"); QPushButton *alarmBtn = new QPushButton("错架报警"); connect(refreshBtn, &QPushButton::clicked, this, &LibrarySystem::updateData); buttonLayout->addWidget(refreshBtn); buttonLayout->addWidget(exportBtn); buttonLayout->addWidget(alarmBtn); statusLayout->addLayout(buttonLayout); layout->addWidget(statusGroup); } void setupNetwork() { networkManager = new QNetworkAccessManager(this); connect(networkManager, &QNetworkAccessManager::finished, this, &LibrarySystem::onNetworkReply); } void setupTimer() { updateTimer = new QTimer(this); connect(updateTimer, &QTimer::timeout, this, &LibrarySystem::updateData); updateTimer->start(5000); // 5秒更新一次 } void loadSampleData() { // 创建图书馆地图 drawLibraryMap(); // 初始化图表 initCharts(); // 添加示例书籍 addSampleBooks(); statusText->append("系统初始化完成 - " + QDateTime::currentDateTime().toString()); } void drawLibraryMap() { // 清空场景 mapGraphicsScene->clear(); // 绘制书架 QPen shelfPen(Qt::black, 2); QBrush shelfBrush(QColor(210, 180, 140)); // 区域A QGraphicsRectItem *areaA = mapGraphicsScene->addRect(50, 50, 200, 300, shelfPen, shelfBrush); areaA->setData(0, "A区"); // 区域B QGraphicsRectItem *areaB = mapGraphicsScene->addRect(300, 50, 200, 300, shelfPen, shelfBrush); areaB->setData(0, "B区"); // 区域C QGraphicsRectItem *areaC = mapGraphicsScene->addRect(550, 50, 200, 300, shelfPen, shelfBrush); areaC->setData(0, "C区"); // 添加区域标签 QFont labelFont("Arial", 12, QFont::Bold); mapGraphicsScene->addText("A区 - 文学类", labelFont)->setPos(80, 360); mapGraphicsScene->addText("B区 - 科技类", labelFont)->setPos(330, 360); mapGraphicsScene->addText("C区 - 社科类", labelFont)->setPos(580, 360); // 添加入口 QGraphicsEllipseItem *entrance = mapGraphicsScene->addEllipse(400, 400, 30, 30, QPen(Qt::green), QBrush(Qt::green)); entrance->setData(0, "入口"); mapGraphicsScene->addText("入口", labelFont)->setPos(430, 410); } void initCharts() { // 借阅统计图表 QChart *borrowChart = new QChart; borrowChart->setTitle("今日借阅统计"); QBarSeries *borrowSeries = new QBarSeries; QBarSet *borrowSet = new QBarSet("借阅量"); *borrowSet << 15 << 22 << 18 << 25 << 19 << 17 << 14; borrowSeries->append(borrowSet); borrowChart->addSeries(borrowSeries); borrowChart->setAnimationOptions(QChart::SeriesAnimations); QStringList categories = {"周一", "周二", "周三", "周四", "周五", "周六", "周日"}; QBarCategoryAxis *axisX = new QBarCategoryAxis; axisX->append(categories); borrowChart->addAxis(axisX, Qt::AlignBottom); borrowSeries->attachAxis(axisX); QValueAxis *axisY = new QValueAxis; axisY->setRange(0, 30); borrowChart->addAxis(axisY, Qt::AlignLeft); borrowSeries->attachAxis(axisY); borrowChartView->setChart(borrowChart); // 分类统计图表 QChart *categoryChart = new QChart; categoryChart->setTitle("书籍分类统计"); QPieSeries *categorySeries = new QPieSeries; categorySeries->append("文学类", 35); categorySeries->append("科技类", 25); categorySeries->append("社科类", 20); categorySeries->append("艺术类", 15); categorySeries->append("其他", 5); categoryChart->addSeries(categorySeries); categoryChart->setAnimationOptions(QChart::SeriesAnimations); categoryChartView->setChart(categoryChart); } void addSampleBooks() { // 添加示例书籍数据 QList<BookInfo> sampleBooks = { {"B001", "C++程序设计", "李华", "A区-1-01", false, QDateTime::currentDateTime()}, {"B002", "Qt开发指南", "张明", "A区-1-02", true, QDateTime::currentDateTime()}, {"B003", "STM32实战", "王强", "B区-2-01", false, QDateTime::currentDateTime()}, {"B004", "物联网技术", "赵丽", "B区-2-05", false, QDateTime::currentDateTime()}, {"B005", "人工智能导论", "陈伟", "C区-3-03", true, QDateTime::currentDateTime()} }; bookTable->setRowCount(sampleBooks.size()); for (int i = 0; i < sampleBooks.size(); ++i) { const BookInfo &book = sampleBooks[i]; bookTable->setItem(i, 0, new QTableWidgetItem(book.title)); bookTable->setItem(i, 1, new QTableWidgetItem(book.author)); bookTable->setItem(i, 2, new QTableWidgetItem(book.location)); bookTable->setItem(i, 3, new QTableWidgetItem(book.isBorrowed ? "已借出" : "在馆")); bookTable->setItem(i, 4, new QTableWidgetItem(book.lastUpdate.toString("MM-dd hh:mm"))); // 在地图上标记书籍位置 markBookOnMap(book); } books = sampleBooks; } void markBookOnMap(const BookInfo &book) { // 根据位置信息在地图上标记书籍 QPointF position = calculateBookPosition(book.location); QGraphicsEllipseItem *bookMarker = mapGraphicsScene->addEllipse( position.x(), position.y(), 10, 10, QPen(Qt::red), QBrush(book.isBorrowed ? Qt::gray : Qt::red) ); bookMarker->setData(0, book.id); bookMarker->setData(1, book.title); bookMarker->setToolTip(book.title + "\n" + book.location); } QPointF calculateBookPosition(const QString &location) { // 简化的位置计算逻辑 if (location.startsWith("A区")) return QPointF(100, 100); if (location.startsWith("B区")) return QPointF(350, 150); if (location.startsWith("C区")) return QPointF(600, 200); return QPointF(200, 200); } void fetchBookData() { // 模拟从华为云获取数据 QNetworkRequest request(QUrl("http://your-huawei-cloud-api/books")); networkManager->get(request); } void onNetworkReply(QNetworkReply *reply) { if (reply->error() == QNetworkReply::NoError) { QByteArray data = reply->readAll(); processBookData(data); statusText->append("数据更新成功 - " + QDateTime::currentDateTime().toString()); } else { statusText->append("数据更新失败: " + reply->errorString()); } reply->deleteLater(); } void processBookData(const QByteArray &data) { // 解析JSON数据并更新界面 QJsonDocument doc = QJsonDocument::fromJson(data); if (doc.isArray()) { QJsonArray bookArray = doc.array(); updateBookTable(bookArray); } } void updateBookTable(const QJsonArray &bookArray) { bookTable->setRowCount(bookArray.size()); for (int i = 0; i < bookArray.size(); ++i) { QJsonObject bookObj = bookArray[i].toObject(); bookTable->setItem(i, 0, new QTableWidgetItem(bookObj["title"].toString())); bookTable->setItem(i, 1, new QTableWidgetItem(bookObj["author"].toString())); bookTable->setItem(i, 2, new QTableWidgetItem(bookObj["location"].toString())); bookTable->setItem(i, 3, new QTableWidgetItem(bookObj["status"].toString())); bookTable->setItem(i, 4, new QTableWidgetItem(QDateTime::currentDateTime().toString("MM-dd hh:mm"))); } } void updateCharts() { // 更新图表数据 // 这里可以添加实际的统计逻辑 } void searchBooks(const QString &keyword) { searchResultList->clear(); for (const BookInfo &book : books) { if (book.title.contains(keyword, Qt::CaseInsensitive) || book.author.contains(keyword, Qt::CaseInsensitive)) { QListWidgetItem *item = new QListWidgetItem(book.title + " - " + book.author); item->setData(Qt::UserRole, book.id); searchResultList->addItem(item); } } } void highlightBookLocation(const QString &bookId) { // 高亮显示书籍位置 for (QGraphicsItem *item : mapGraphicsScene->items()) { if (item->data(0).toString() == bookId) { item->setPen(QPen(Qt::yellow, 3)); item->setZValue(1); // 显示导航路径 showNavigationToBook(item->pos()); break; } } } void showNavigationPath(const QString &bookId) { // 显示寻书导航路径 for (const BookInfo &book : books) { if (book.id == bookId) { QString message = QString("导航到: %1\n位置: %2\n状态: %3") .arg(book.title) .arg(book.location) .arg(book.isBorrowed ? "已借出" : "在馆"); QMessageBox::information(this, "寻书导航", message); break; } } } void showNavigationToBook(const QPointF &bookPos) { // 绘制导航路径 QPointF entrancePos(415, 415); // 入口位置 QPen pathPen(QColor(0, 255, 0), 3); pathPen.setStyle(Qt::DashLine); mapGraphicsScene->addLine(entrancePos.x(), entrancePos.y(), bookPos.x() + 5, bookPos.y() + 5, pathPen); } private: // UI组件 QGraphicsView *mapGraphicsView; QGraphicsScene *mapGraphicsScene; QLineEdit *searchEdit; QListWidget *searchResultList; QTableWidget *bookTable; QChartView *borrowChartView; QChartView *categoryChartView; QTextEdit *statusText; // 网络和定时器 QNetworkAccessManager *networkManager; QTimer *updateTimer; // 数据 QList<BookInfo> books; }; // 主函数 int main(int argc, char *argv[]) { QApplication app(argc, argv); // 设置应用程序信息 QApplication::setApplicationName("智能图书馆管理系统"); QApplication::setApplicationVersion("1.0"); LibrarySystem window; window.show(); return app.exec(); } #include "main.moc" 这个上位机代码实现了以下功能:图书馆地图显示:使用QGraphicsView显示图书馆平面图,包括不同区域的书架布局书籍搜索功能:支持按书名、作者搜索书籍实时数据监控:通过定时器定期从云端获取最新数据统计图表显示:使用Qt Charts显示借阅统计和分类统计书籍状态管理:表格显示所有书籍的详细信息寻书导航功能:高亮显示书籍位置并绘制导航路径系统状态监控:实时显示系统运行状态和错误信息代码采用模块化设计,便于维护和扩展,可以通过修改网络请求URL连接到实际的华为云服务。模块代码设计#include "stm32f10x.h" // OLED显示屏定义 #define OLED_I2C I2C1 #define OLED_ADDRESS 0x78 // RFID-RC522引脚定义 #define RFID_CS_PIN GPIO_Pin_4 #define RFID_CS_PORT GPIOA #define RFID_RST_PIN GPIO_Pin_3 #define RFID_RST_PORT GPIOA // LED指示灯定义 #define LED1_PIN GPIO_Pin_13 #define LED1_PORT GPIOC #define LED2_PIN GPIO_Pin_14 #define LED2_PORT GPIOC #define LED3_PIN GPIO_Pin_15 #define LED3_PORT GPIOC // ESP8266串口定义 #define WIFI_USART USART2 // 延时函数 void delay_ms(uint32_t nCount) { for(; nCount != 0; nCount--){ for(uint32_t i = 0; i < 8000; i++); } } // I2C初始化 void I2C_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; I2C_InitTypeDef I2C_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); // PB6-SCL, PB7-SDA GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; GPIO_Init(GPIOB, &GPIO_InitStructure); I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStructure.I2C_OwnAddress1 = 0x00; I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_InitStructure.I2C_ClockSpeed = 400000; I2C_Init(I2C1, &I2C_InitStructure); I2C_Cmd(I2C1, ENABLE); } // OLED写命令 void OLED_Write_Cmd(uint8_t cmd) { while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); 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); } // OLED写数据 void OLED_Write_Data(uint8_t data) { while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); 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, 0x40); // 控制字节-数据 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_SendData(I2C1, data); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_GenerateSTOP(I2C1, ENABLE); } // OLED初始化 void OLED_Init(void) { delay_ms(100); OLED_Write_Cmd(0xAE); // 关闭显示 OLED_Write_Cmd(0x20); // 内存地址模式 OLED_Write_Cmd(0x10); // 页地址模式 OLED_Write_Cmd(0xB0); // 设置页起始地址 OLED_Write_Cmd(0xC8); // 设置扫描方向 OLED_Write_Cmd(0x00); // 设置低列地址 OLED_Write_Cmd(0x10); // 设置高列地址 OLED_Write_Cmd(0x40); // 设置起始行 OLED_Write_Cmd(0x81); // 设置对比度 OLED_Write_Cmd(0xFF); // 对比度值 OLED_Write_Cmd(0xA1); // 设置段重映射 OLED_Write_Cmd(0xA6); // 正常显示 OLED_Write_Cmd(0xA8); // 设置多路复用率 OLED_Write_Cmd(0x3F); // 1/64 duty OLED_Write_Cmd(0xA4); // 整个显示打开 OLED_Write_Cmd(0xD3); // 设置显示偏移 OLED_Write_Cmd(0x00); // 无偏移 OLED_Write_Cmd(0xD5); // 设置振荡器频率 OLED_Write_Cmd(0x80); // 设置分频比 OLED_Write_Cmd(0xD9); // 设置预充电周期 OLED_Write_Cmd(0xF1); OLED_Write_Cmd(0xDA); // 设置com引脚硬件配置 OLED_Write_Cmd(0x12); OLED_Write_Cmd(0xDB); // 设置VCOMH OLED_Write_Cmd(0x40); OLED_Write_Cmd(0x8D); // 设置充电泵 OLED_Write_Cmd(0x14); OLED_Write_Cmd(0xAF); // 开启显示 } // OLED显示字符串 void OLED_ShowString(uint8_t x, uint8_t y, char *str) { OLED_Write_Cmd(0xB0 + y); OLED_Write_Cmd(((x & 0xF0) >> 4) | 0x10); OLED_Write_Cmd((x & 0x0F) | 0x01); while(*str){ OLED_Write_Data(*str); str++; } } // SPI初始化 (用于RFID) void SPI_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE); // SPI1引脚配置: PA5-SCK, PA6-MISO, PA7-MOSI GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); // RFID片选和复位引脚 GPIO_InitStructure.GPIO_Pin = RFID_CS_PIN | RFID_RST_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_SetBits(RFID_CS_PORT, RFID_CS_PIN); GPIO_SetBits(RFID_RST_PORT, RFID_RST_PIN); SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode = SPI_Mode_Master; SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64; SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_InitStructure.SPI_CRCPolynomial = 7; SPI_Init(SPI1, &SPI_InitStructure); SPI_Cmd(SPI1, ENABLE); } // SPI读写一个字节 uint8_t SPI_ReadWriteByte(uint8_t data) { while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); SPI_I2S_SendData(SPI1, data); while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); return SPI_I2S_ReceiveData(SPI1); } // RFID写寄存器 void RFID_WriteReg(uint8_t addr, uint8_t val) { GPIO_ResetBits(RFID_CS_PORT, RFID_CS_PIN); SPI_ReadWriteByte((addr << 1) & 0x7E); SPI_ReadWriteByte(val); GPIO_SetBits(RFID_CS_PORT, RFID_CS_PIN); } // RFID读寄存器 uint8_t RFID_ReadReg(uint8_t addr) { uint8_t val; GPIO_ResetBits(RFID_CS_PORT, RFID_CS_PIN); SPI_ReadWriteByte(((addr << 1) & 0x7E) | 0x80); val = SPI_ReadWriteByte(0x00); GPIO_SetBits(RFID_CS_PORT, RFID_CS_PIN); return val; } // RFID初始化 void RFID_Init(void) { GPIO_SetBits(RFID_RST_PORT, RFID_RST_PIN); delay_ms(1); GPIO_ResetBits(RFID_RST_PORT, RFID_RST_PIN); delay_ms(1); GPIO_SetBits(RFID_RST_PORT, RFID_RST_PIN); delay_ms(1); RFID_WriteReg(0x01, 0x0F); RFID_WriteReg(0x2A, 0x8D); RFID_WriteReg(0x2B, 0x3E); RFID_WriteReg(0x2D, 0x1E); RFID_WriteReg(0x2C, 0x00); RFID_WriteReg(0x15, 0x40); RFID_WriteReg(0x11, 0x3D); RFID_WriteReg(0x26, 0x07); } // RFID寻卡 uint8_t RFID_Request(uint8_t req_code, uint8_t *tag_type) { uint8_t status; uint32_t backBits; RFID_WriteReg(0x0D, 0x07); status = RFID_CommWithTag(req_code, tag_type, &backBits); if(status != 0x00) return status; return 0x00; } // RFID防冲撞 uint8_t RFID_Anticoll(uint8_t *serNum) { uint8_t status; uint8_t i; uint32_t backBits; uint8_t serNumCheck = 0; RFID_WriteReg(0x0D, 0x00); serNum[0] = 0x93; serNum[1] = 0x20; status = RFID_CommWithTag(0x93, serNum, &backBits); if(status == 0x00){ for(i = 0; i < 4; i++){ serNumCheck ^= serNum[i]; } if(serNumCheck != serNum[i]){ status = 0xFF; } } RFID_WriteReg(0x0D, 0x0F); return status; } // RFID与标签通信 uint8_t RFID_CommWithTag(uint8_t command, uint8_t *pData, uint32_t *pBits) { uint8_t status = 0xFF; uint8_t irqEn = 0x00; uint8_t waitIRq = 0x00; uint8_t n, tmp; uint32_t i; switch(command){ case 0x26: irqEn = 0x12; waitIRq = 0x10; break; case 0x93: irqEn = 0x12; waitIRq = 0x10; break; } RFID_WriteReg(0x02, irqEn | 0x80); RFID_WriteReg(0x04, 0x00); RFID_WriteReg(0x01, 0x00); RFID_WriteReg(0x0C, 0x00); RFID_WriteReg(0x09, 0x26); RFID_WriteReg(0x08, 0x00); RFID_WriteReg(0x0B, 0x87); RFID_WriteReg(0x01, 0x0F); for(i = 0; ; i++){ n = RFID_ReadReg(0x04); if(n & waitIRq){ break; } if(i >= 2000){ return status; } } tmp = RFID_ReadReg(0x06); if(tmp & 0x1B){ status = 0xFF; } if(status == 0x00){ n = RFID_ReadReg(0x0A); tmp = RFID_ReadReg(0x0C); *pBits = (uint32_t)((n - 1) * 8 + tmp); if(n == 0){ n = 1; } if(n > 16){ n = 16; } for(i = 0; i < n; i++){ pData[i] = RFID_ReadReg(0x09); } } return status; } // LED初始化 void LED_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitStructure.GPIO_Pin = LED1_PIN | LED2_PIN | LED3_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOC, &GPIO_InitStructure); GPIO_SetBits(GPIOC, LED1_PIN | LED2_PIN | LED3_PIN); } // 控制LED指示灯 void LED_Control(uint8_t led_num, uint8_t state) { switch(led_num){ case 1: if(state) GPIO_SetBits(LED1_PORT, LED1_PIN); else GPIO_ResetBits(LED1_PORT, LED1_PIN); break; case 2: if(state) GPIO_SetBits(LED2_PORT, LED2_PIN); else GPIO_ResetBits(LED2_PORT, LED2_PIN); break; case 3: if(state) GPIO_SetBits(LED3_PORT, LED3_PIN); else GPIO_ResetBits(LED3_PORT, LED3_PIN); break; } } // 串口初始化 (用于ESP8266) void USART_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); // USART2 TX-PA2, RX-PA3 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); USART_InitStructure.USART_BaudRate = 115200; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART2, &USART_InitStructure); USART_Cmd(USART2, ENABLE); } // 串口发送字符串 void USART_SendString(USART_TypeDef* USARTx, char *str) { while(*str){ while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET); USART_SendData(USARTx, *str++); } } // ESP8266发送数据到华为云 void ESP8266_SendToCloud(char *book_id, char *location, char *status) { char buffer[100]; // 构建JSON数据 sprintf(buffer, "AT+HTTPCLISEND=0,\"{\\\"book_id\\\":\\\"%s\\\",\\\"location\\\":\\\"%s\\\",\\\"status\\\":\\\"%s\\\"}\"\r\n", book_id, location, status); USART_SendString(USART2, buffer); delay_ms(1000); } // 主函数 int main(void) { uint8_t tag_type[2]; uint8_t serial_num[5]; uint8_t status; char display_buffer[20]; // 系统时钟初始化 SystemInit(); // 各模块初始化 I2C_Configuration(); OLED_Init(); SPI_Configuration(); RFID_Init(); LED_Init(); USART_Configuration(); // OLED显示欢迎信息 OLED_ShowString(0, 0, "Smart Library"); OLED_ShowString(0, 2, "System Ready"); while(1){ // RFID寻卡 status = RFID_Request(0x26, tag_type); if(status == 0x00){ // 防冲撞获取序列号 status = RFID_Anticoll(serial_num); if(status == 0x00){ // 显示书籍信息 sprintf(display_buffer, "Book ID: %02X%02X%02X%02X", serial_num[0], serial_num[1], serial_num[2], serial_num[3]); OLED_ShowString(0, 4, display_buffer); // 根据序列号确定位置并点亮对应LED uint8_t location = serial_num[0] % 3 + 1; LED_Control(location, 1); // 发送数据到华为云 char book_id[9]; sprintf(book_id, "%02X%02X%02X%02X", serial_num[0], serial_num[1], serial_num[2], serial_num[3]); char loc_str[10]; sprintf(loc_str, "Shelf%d", location); ESP8266_SendToCloud(book_id, loc_str, "detected"); delay_ms(2000); LED_Control(location, 0); // 显示寻书引导 OLED_ShowString(0, 6, "Go to Shelf"); sprintf(display_buffer, "Number: %d", location); OLED_ShowString(0, 7, display_buffer); delay_ms(3000); OLED_ShowString(0, 4, " "); OLED_ShowString(0, 6, " "); OLED_ShowString(0, 7, " "); } } delay_ms(500); } } 项目核心代码#include "stm32f10x.h" #include "oled.h" #include "rfid.h" #include "wifi.h" #include "led.h" // 定义图书状态 #define BOOK_AVAILABLE 0 #define BOOK_BORROWED 1 #define BOOK_MISPLACED 2 // 图书信息结构体 typedef struct { uint8_t id[5]; // RFID标签ID uint8_t status; // 图书状态 uint8_t shelf; // 书架编号 uint8_t position; // 位置编号 uint8_t reader_id[10]; // 读者ID } BookInfo; // 全局变量 BookInfo current_book; uint8_t book_found = 0; uint8_t wifi_connected = 0; // 函数声明 void System_Init(void); void GPIO_Config(void); void USART_Config(void); void SPI_Config(void); void TIM_Config(void); void Process_RFID_Data(void); void Update_OLED_Display(void); void Send_To_Cloud(void); void LED_Indicator(void); void Delay_ms(uint32_t nTime); int main(void) { // 系统初始化 System_Init(); // 外设初始化 GPIO_Config(); USART_Config(); SPI_Config(); TIM_Config(); // 模块初始化 OLED_Init(); RFID_Init(); WIFI_Init(); LED_Init(); // 显示启动界面 OLED_Clear(); OLED_ShowString(0, 0, "Smart Library", 16); OLED_ShowString(0, 2, "System Starting", 16); Delay_ms(2000); // 连接WiFi wifi_connected = WIFI_Connect("Library_WiFi", "password123"); if(wifi_connected) { OLED_ShowString(0, 4, "WiFi Connected", 16); } else { OLED_ShowString(0, 4, "WiFi Failed", 16); } Delay_ms(1000); while(1) { // 主循环任务 Process_RFID_Data(); if(book_found) { Update_OLED_Display(); LED_Indicator(); if(wifi_connected) { Send_To_Cloud(); } book_found = 0; } // 系统延时 Delay_ms(100); } } void System_Init(void) { // 开启外设时钟 RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_AFIOEN | RCC_APB2ENR_USART1EN | RCC_APB2ENR_SPI1EN; RCC->APB1ENR |= RCC_APB1ENR_TIM2EN | RCC_APB1ENR_USART2EN; // 设置系统时钟 FLASH->ACR |= FLASH_ACR_LATENCY_2; RCC->CFGR |= RCC_CFGR_PPRE1_DIV2; RCC->CR |= RCC_CR_HSEON; while(!(RCC->CR & RCC_CR_HSERDY)); RCC->CFGR |= RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL9; RCC->CR |= RCC_CR_PLLON; while(!(RCC->CR & RCC_CR_PLLRDY)); RCC->CFGR |= RCC_CFGR_SW_PLL; while(!(RCC->CFGR & RCC_CFGR_SWS_PLL)); } void GPIO_Config(void) { // LED指示灯配置 GPIOB->CRH &= 0xFFFF0FFF; GPIOB->CRH |= 0x00003000; // PB12推挽输出 // RFID-RC522片选 GPIOA->CRL &= 0xFFF0FFFF; GPIOA->CRL |= 0x00030000; // PA4推挽输出 // WiFi模块控制 GPIOC->CRH &= 0xFF0FFFFF; GPIOC->CRH |= 0x00300000; // PC13推挽输出 } void USART_Config(void) { // USART1 - WiFi模块 GPIOA->CRH &= 0xFFFFF00F; GPIOA->CRH |= 0x000004B0; // PA9(TX)推挽复用, PA10(RX)浮空输入 USART1->BRR = 0x1D4C; // 115200 @72MHz USART1->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; // USART2 - 调试输出 GPIOA->CRL &= 0xFFFF00FF; GPIOA->CRL |= 0x00004B00; // PA2(TX)推挽复用, PA3(RX)浮空输入 USART2->BRR = 0x1D4C; // 115200 @72MHz USART2->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; } void SPI_Config(void) { // SPI1 - RFID模块 GPIOA->CRL &= 0x000FFFFF; GPIOA->CRL |= 0xB4B00000; // PA5(SCK), PA6(MISO), PA7(MOSI)复用推挽 SPI1->CR1 = SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_MSTR | SPI_CR1_BR_0 | SPI_CR1_SPE; } void TIM_Config(void) { // TIM2用于延时和定时任务 TIM2->PSC = 7200 - 1; // 10kHz TIM2->ARR = 10000 - 1; // 1s TIM2->CR1 = TIM_CR1_CEN; } void Process_RFID_Data(void) { uint8_t card_id[5]; // 检测RFID卡片 if(RFID_CheckCard(card_id)) { // 读取卡片信息 RFID_ReadCardData(); // 复制卡片ID for(int i = 0; i < 5; i++) { current_book.id[i] = card_id[i]; } // 查询图书信息(这里简化处理,实际应从数据库查询) current_book.shelf = (card_id[0] % 10) + 1; // 书架1-10 current_book.position = (card_id[1] % 20) + 1; // 位置1-20 // 判断图书状态 if(card_id[2] > 200) { current_book.status = BOOK_BORROWED; } else if(card_id[3] != current_book.shelf) { current_book.status = BOOK_MISPLACED; } else { current_book.status = BOOK_AVAILABLE; } book_found = 1; } } void Update_OLED_Display(void) { char buffer[20]; OLED_Clear(); // 显示图书位置 OLED_ShowString(0, 0, "Book Location:", 16); sprintf(buffer, "Shelf:%d Pos:%d", current_book.shelf, current_book.position); OLED_ShowString(0, 2, buffer, 16); // 显示图书状态 switch(current_book.status) { case BOOK_AVAILABLE: OLED_ShowString(0, 4, "Status:Available", 16); break; case BOOK_BORROWED: OLED_ShowString(0, 4, "Status:Borrowed", 16); break; case BOOK_MISPLACED: OLED_ShowString(0, 4, "Status:Misplaced!", 16); break; } // 显示导航指引 OLED_ShowString(0, 6, "Go to Shelf", 16); } void Send_To_Cloud(void) { char data_buffer[100]; // 构建JSON数据 sprintf(data_buffer, "{\"book_id\":\"%02X%02X%02X%02X%02X\",\"shelf\":%d,\"position\":%d,\"status\":%d}", current_book.id[0], current_book.id[1], current_book.id[2], current_book.id[3], current_book.id[4], current_book.shelf, current_book.position, current_book.status); // 通过WiFi发送到华为云 WIFI_SendData(data_buffer); } void LED_Indicator(void) { // 根据图书状态控制LED switch(current_book.status) { case BOOK_AVAILABLE: // 绿灯常亮 LED_Green_On(); LED_Red_Off(); break; case BOOK_BORROWED: // 红灯闪烁 LED_Red_Toggle(); LED_Green_Off(); break; case BOOK_MISPLACED: // 红绿交替闪烁 LED_Red_Toggle(); LED_Green_Toggle(); break; } } void Delay_ms(uint32_t nTime) { TIM2->CNT = 0; while(TIM2->CNT < nTime * 10); // 10kHz计时器 } // 串口发送函数(用于调试) void USART_SendString(USART_TypeDef* USARTx, char *str) { while(*str) { while(!(USARTx->SR & USART_SR_TXE)); USARTx->DR = (*str++ & 0xFF); } } 总结本系统基于STM32微控制器设计,实现了智能图书馆的书籍精确定位与管理功能。通过集成RFID技术,系统能够高效识别书籍标签,实时追踪借还状态和馆内分布,同时提供读者寻书导航和错架图书报警服务,显著提升了图书馆的自动化水平和用户体验。硬件部分采用STM32F103C8T6核心板作为控制中心,结合RFID-RC522模块进行书籍识别,0.96寸OLED显示屏用于显示导航信息,LED指示灯辅助定位书籍位置。此外,ESP8266-01S Wi-Fi模块将数据上传至华为云平台,实现远程监控,而洞洞板焊接的RFID天线电路和杜邦线连接确保了系统的稳定性和扩展性。总体而言,该系统通过模块化设计实现了图书馆管理的智能化和可视化,不仅优化了书籍流通流程,还降低了人工管理成本,为现代图书馆的数字化转型提供了实用解决方案。未来,该系统可进一步集成数据分析功能,以支持更高效的资源调度和决策支持。
总条数:7137 到第 页
上滑加载中