STM32F103C8T6的智能实验室危化品管理系统设计与华为云实现

发布于:2025-09-02 ⋅ 阅读:(17) ⋅ 点赞:(0)

项目开发背景

随着实验室安全管理要求的不断提高,危化品的规范管理已成为科研机构和企业面临的重要挑战。传统人工记录方式存在效率低、易出错、无法实时监控等问题,尤其在温湿度敏感、易燃易爆或有毒危化品的存储和使用过程中,潜在安全隐患尤为突出。

危化品的泄漏、超量使用或未经授权取用可能引发严重安全事故,而现有管理系统往往缺乏多参数实时监测与智能预警能力。此外,危化品使用记录追溯困难、权限管理不严格等问题,也增加了实验室运营风险。

物联网技术的快速发展为危化品管理提供了新的解决方案。通过集成传感器技术、无线通信和云平台,可实现危化品存储环境的动态监控、使用过程的精准记录以及异常状态的即时告警。基于STM32微控制器与华为云的方案,能够构建低成本、高可靠性且易于扩展的智能管理系统,满足实验室对危化品全生命周期管理的迫切需求。

设计实现的功能

(1) 实时监测气体泄漏(使用MQ-2气体传感器)
(2) 称重传感器监测危化品使用量并记录使用日志(使用HX711模块)
(3) RFID身份认证与权限管理(使用RFID-RC522模块)
(4) 数据上传至华为云(使用ESP8266模块),以支持QT上位机实现危化品库存管理、使用追溯和预警处理

项目硬件模块组成

(1) STM32F103C8T6最小系统核心板作为主控制器
(2) HX711称重传感器模块监测药品重量
(3) MQ-2气体传感器检测泄漏情况
(4) RFID-RC522模块实现人员身份认证
(5) ESP8266-01S Wi-Fi模块上传数据至华为云
(6) 洞洞板焊接传感器接口,杜邦线连接功能模块

设计意义

该系统的设计意义在于提升实验室危化品管理的安全性和效率。通过实时监测温度、湿度和气体泄漏,系统能够及时检测环境异常,防止因温度过高、湿度过大或气体泄漏引发的安全事故,保障实验室人员和设备的安全,减少潜在风险。

采用称重传感器监测危化品使用量并记录日志,结合RFID身份认证与权限管理,实现了对危化品使用的精确追踪和权限控制,避免了未经授权的访问和误操作,提高了管理精度和 accountability,同时自动化记录减少了人工干预的错误和遗漏。

QT上位机实现库存管理、使用追溯和预警处理,使得管理人员能够直观查看数据、生成报告并及时响应预警,增强了危化品管理的可视化和响应速度,便于审计和合规性检查,提升了整体运营效率。

硬件上使用STM32F103C8T6核心板作为主控制器,搭配常见传感器模块如HX711、MQ-2和RFID-RC522,通过洞洞板焊接和杜邦线连接,确保了系统的灵活性和低成本实现,适用于多种实验室环境,同时便于维护和扩展。

通过ESP8266-01S Wi-Fi模块将数据上传至华为云,实现了远程数据存储和访问,支持多终端监控和数据分析,增强了系统的可靠性和可扩展性,为实验室管理提供了云端支持,符合现代物联网趋势。

设计思路

系统以STM32F103C8T6最小系统核心板作为主控制器,负责协调各个功能模块的工作。通过连接温湿度传感器(如DHT11)、MQ-2气体传感器、HX711称重传感器模块、RFID-RC522模块和ESP8266-01S Wi-Fi模块,实现危化品柜的全面监控和数据上传。

实时监测功能通过温湿度传感器监测柜内温度和湿度,以及MQ-2气体传感器检测气体泄漏情况。STM32定期读取这些传感器数据,并进行初步处理和异常判断,确保环境参数在安全范围内。

称重监测使用HX711模块,实时监测危化品的重量变化,STM32记录每次使用量并生成使用日志,包括时间、重量变化和操作状态,便于后续追溯和管理。

RFID身份认证通过RFID-RC522模块实现,工作人员刷卡时,STM32读取RFID标签信息,验证权限等级,决定是否允许操作危化品柜,从而实现严格的权限管理。

数据上传通过ESP8266-01S Wi-Fi模块实现,将传感器数据、重量日志和认证记录通过MQTT或HTTP协议上传至华为云IoT平台,实现远程数据存储和实时监控。

QT上位机应用程序从华为云获取数据,实现危化品库存管理、使用记录追溯和异常情况预警处理。QT界面提供图形化显示,支持数据查询、报表生成和警报通知,增强用户体验和管理效率。

硬件连接使用洞洞板焊接传感器接口电路,通过杜邦线连接STM32与各模块,确保电气连接的可靠性和稳定性。软件层面,STM32编程使用C语言 with HAL库或标准外设库,实现数据采集、处理和通信;华为云平台配置设备接入和数据路由;QT使用C++开发,实现数据解析和界面逻辑。

框架图

+-------------------------+
|       Sensors:          |
|  - DHT11 (Temp/Hum)     |
|  - MQ-2 (Gas Leak)      |
|  - HX711 (Weight)       |
+-------------------------+
            |
            | (Analog/Digital Pins)
            v
+-------------------------+
|   STM32F103C8T6         |
|   Main Controller       |
|   (Min System Board)    |
+-------------------------+
            |
            | (SPI/I2C/UART)
            v
+-------------------------+
|   RFID-RC522 Module     |
|   (Authentication)      |
+-------------------------+
            |
            | (UART)
            v
+-------------------------+
|   ESP8266-01S Wi-Fi     |
|   Module                |
+-------------------------+
            |
            | (Internet via Wi-Fi)
            v
+-------------------------+
|       华为云            |
|   (Huawei Cloud IoT)    |
+-------------------------+
            |
            | (API/MQTT)
            v
+-------------------------+
|       QT上位机           |
|   (Inventory Management,|
|    Logging, Alerts)     |
+-------------------------+

系统总体设计

该智能实验室危化品管理系统以STM32F103C8T6最小系统核心板作为主控制器,负责协调和处理所有传感器数据及系统逻辑。系统通过集成多种传感器模块,实现对危化品柜的全面监控和管理,确保实验室安全。

系统使用HX711称重传感器模块监测危化品的重量变化,记录使用量并生成使用日志,便于追踪和审计。MQ-2气体传感器检测柜内气体泄漏情况,及时发出警报。RFID-RC522模块用于人员身份认证与权限管理,只有授权人员才能进行相关操作,增强安全性。

数据通信部分依赖ESP8266-01S Wi-Fi模块,将实时传感器数据上传至华为云平台,实现远程数据存储和监控。硬件连接采用洞洞板焊接传感器接口,并通过杜邦线将各功能模块与STM32核心板连接,确保稳定性和可维护性。

QT上位机应用程序负责危化品库存管理、使用追溯和预警处理,与云平台交互,提供用户友好的界面进行数据查看和系统控制。整个设计注重实际应用,基于给定硬件实现功能需求。

系统功能总结

功能 实现方式
气体泄漏检测 MQ-2气体传感器
重量监测 HX711称重传感器模块
RFID身份认证 RFID-RC522模块
数据上传至华为云 ESP8266-01S Wi-Fi模块
系统控制与处理 STM32F103C8T6最小系统核心板
硬件接口与连接 洞洞板焊接传感器接口,杜邦线连接
上位机管理 QT上位机软件

设计的各个功能模块描述

STM32F103C8T6最小系统核心板作为主控制器,负责协调整个系统的运行,初始化各种传感器和模块,采集并处理数据,包括温度、湿度监测(通过未指定的传感器实现),气体泄漏检测、重量测量以及RFID认证逻辑,同时通过Wi-Fi模块上传数据至云端,确保系统的实时性和稳定性。

HX711称重传感器模块用于监测危化品的重量变化,通过测量药品的减少量来记录使用日志,数据由STM32读取并处理,实现使用量的精确追踪和记录,为库存管理提供基础数据。

MQ-2气体传感器检测危化品柜内的气体泄漏情况,当检测到可燃或有毒气体浓度超标时,STM32会及时处理并可能触发预警机制,确保安全监控的有效性。

RFID-RC522模块实现人员身份认证与权限管理,用户通过刷卡进行身份验证,STM32核对权限信息后控制访问权限,确保只有授权人员才能操作危化品柜,增强系统的安全性。

ESP8266-01S Wi-Fi模块负责将STM32收集的传感器数据、使用日志和预警信息上传至华为云平台,实现数据的远程存储和监控,为上位机软件提供数据支持。

硬件连接方面,使用洞洞板焊接传感器接口,并通过杜邦线将各个功能模块连接到STM32核心板,确保电气连接的可靠性和灵活性,便于系统调试和维护。

QT上位机软件实现危化品库存管理、使用记录追溯和预警处理功能,与华为云数据交互,提供图形化界面用于监控库存状态、查询使用历史和处理警报信息,提升管理效率。

上位机代码设计

SmartLabSystem.pro

QT       += core gui network

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

SOURCES += \
    main.cpp \
    mainwindow.cpp

HEADERS += \
    mainwindow.h

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

main.cpp

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QLabel>
#include <QTableWidget>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QTimer>

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void onNetworkReply(QNetworkReply *reply);
    void updateData();

private:
    QLabel *labelTemperature;
    QLabel *labelHumidity;
    QLabel *labelGas;
    QLabel *labelWeight;
    QLabel *labelRFID;
    QLabel *labelTime;
    QTableWidget *logTable;
    QNetworkAccessManager *networkManager;
    QTimer *timer;
    void parseData(const QByteArray &data);
    void checkAlarms(float temperature, float humidity, bool gasLeak, float weight);
    void updateLogTable(const QJsonArray &logs);
};
#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include <QNetworkRequest>
#include <QUrl>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QMessageBox>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QWidget>
#include <QHeaderView>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , labelTemperature(new QLabel("Temperature: -- °C", this))
    , labelHumidity(new QLabel("Humidity: -- %", this))
    , labelGas(new QLabel("Gas Leak: No", this))
    , labelWeight(new QLabel("Weight: -- g", this))
    , labelRFID(new QLabel("RFID: --", this))
    , labelTime(new QLabel("Last Update: --", this))
    , logTable(new QTableWidget(this))
    , networkManager(new QNetworkAccessManager(this))
    , timer(new QTimer(this))
{
    QWidget *centralWidget = new QWidget(this);
    QVBoxLayout *mainLayout = new QVBoxLayout(centralWidget);

    // Sensor data labels
    QWidget *sensorWidget = new QWidget;
    QVBoxLayout *sensorLayout = new QVBoxLayout(sensorWidget);
    sensorLayout->addWidget(labelTemperature);
    sensorLayout->addWidget(labelHumidity);
    sensorLayout->addWidget(labelGas);
    sensorLayout->addWidget(labelWeight);
    sensorLayout->addWidget(labelRFID);
    sensorLayout->addWidget(labelTime);
    sensorWidget->setLayout(sensorLayout);

    // Log table
    logTable->setColumnCount(4);
    logTable->setHorizontalHeaderLabels({"Timestamp", "RFID", "Action", "Weight Change"});
    logTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);

    mainLayout->addWidget(sensorWidget);
    mainLayout->addWidget(logTable);
    centralWidget->setLayout(mainLayout);
    setCentralWidget(centralWidget);

    connect(networkManager, &QNetworkAccessManager::finished, this, &MainWindow::onNetworkReply);
    connect(timer, &QTimer::timeout, this, &MainWindow::updateData);

    timer->start(5000); // Update every 5 seconds
    updateData(); // Initial update
}

MainWindow::~MainWindow()
{
    // Child widgets are automatically deleted
}

void MainWindow::updateData()
{
    QUrl url("https://your-huawei-cloud-api.com/sensordata"); // Replace with actual Huawei Cloud API endpoint
    QNetworkRequest request(url);
    networkManager->get(request);
}

void MainWindow::onNetworkReply(QNetworkReply *reply)
{
    if (reply->error() == QNetworkError::NoError) {
        QByteArray data = reply->readAll();
        parseData(data);
    } else {
        QMessageBox::warning(this, "Error", "Failed to fetch data from cloud.");
    }
    reply->deleteLater();
}

void MainWindow::parseData(const QByteArray &data)
{
    QJsonDocument doc = QJsonDocument::fromJson(data);
    if (doc.isNull() || !doc.isObject()) {
        return;
    }

    QJsonObject obj = doc.object();
    float temperature = obj["temperature"].toDouble();
    float humidity = obj["humidity"].toDouble();
    bool gasLeak = obj["gas_leak"].toBool();
    float weight = obj["weight"].toDouble();
    QString rfidId = obj["rfid_id"].toString();
    QString timestamp = obj["timestamp"].toString();
    QJsonArray logs = obj["logs"].toArray(); // Assume API includes logs array

    labelTemperature->setText(QString("Temperature: %1 °C").arg(temperature));
    labelHumidity->setText(QString("Humidity: %1 %").arg(humidity));
    labelGas->setText(QString("Gas Leak: %1").arg(gasLeak ? "Yes" : "No"));
    labelWeight->setText(QString("Weight: %1 g").arg(weight));
    labelRFID->setText(QString("RFID: %1").arg(rfidId));
    labelTime->setText(QString("Last Update: %1").arg(timestamp));

    updateLogTable(logs);
    checkAlarms(temperature, humidity, gasLeak, weight);
}

void MainWindow::updateLogTable(const QJsonArray &logs)
{
    logTable->setRowCount(0);
    for (int i = 0; i < logs.size(); ++i) {
        QJsonObject log = logs[i].toObject();
        QString timestamp = log["timestamp"].toString();
        QString rfid = log["rfid"].toString();
        QString action = log["action"].toString();
        float weightChange = log["weight_change"].toDouble();

        int row = logTable->rowCount();
        logTable->insertRow(row);
        logTable->setItem(row, 0, new QTableWidgetItem(timestamp));
        logTable->setItem(row, 1, new QTableWidgetItem(rfid));
        logTable->setItem(row, 2, new QTableWidgetItem(action));
        logTable->setItem(row, 3, new QTableWidgetItem(QString::number(weightChange)));
    }
}

void MainWindow::checkAlarms(float temperature, float humidity, bool gasLeak, float weight)
{
    if (temperature > 30.0) {
        QMessageBox::warning(this, "High Temperature", "Temperature is too high!");
    }
    if (humidity > 80.0) {
        QMessageBox::warning(this, "High Humidity", "Humidity is too high!");
    }
    if (gasLeak) {
        QMessageBox::critical(this, "Gas Leak", "Gas leak detected!");
    }
    if (weight < 50.0) {
        QMessageBox::warning(this, "Low Weight", "Weight is low, may need restock.");
    }
}

模块代码设计

#include "stm32f10x.h"

// Pin definitions
#define HX711_DATA_PIN GPIO_Pin_0
#define HX711_DATA_PORT GPIOA
#define HX711_SCK_PIN GPIO_Pin_1
#define HX711_SCK_PORT GPIOA

#define MQ2_ADC_CHANNEL ADC_Channel_2
#define ADC1_DR_ADDRESS ((uint32_t)0x4001244C)

#define RFID_NSS_PIN GPIO_Pin_4
#define RFID_NSS_PORT GPIOA
#define RFID_SCK_PIN GPIO_Pin_5
#define RFID_SCK_PORT GPIOA
#define RFID_MISO_PIN GPIO_Pin_6
#define RFID_MISO_PORT GPIOA
#define RFID_MOSI_PIN GPIO_Pin_7
#define RFID_MOSI_PORT GPIOA
#define RFID_RST_PIN GPIO_Pin_3
#define RFID_RST_PORT GPIOA

#define DHT11_PIN GPIO_Pin_8
#define DHT11_PORT GPIOA

// Function prototypes
void SystemClock_Config(void);
void GPIO_Init(void);
void ADC1_Init(void);
void USART1_Init(void);
void SPI1_Init(void);
void HX711_Init(void);
int32_t HX711_Read(void);
void DHT11_Init(void);
int8_t DHT11_Read(uint8_t *temperature, uint8_t *humidity);
uint16_t ADC1_Read(uint8_t channel);
void MQ2_Read(float *gas_level);
void RFID_Init(void);
uint8_t RFID_ReadCard(uint8_t *uid);
void ESP8266_Init(void);
void ESP8266_SendData(float temp, float hum, float gas, float weight);
void USART1_SendChar(char ch);
void USART1_SendString(char *str);

int main(void) {
    SystemClock_Config();
    GPIO_Init();
    ADC1_Init();
    USART1_Init();
    SPI1_Init();
    HX711_Init();
    DHT11_Init();
    RFID_Init();
    ESP8266_Init();

    while(1) {
        uint8_t temp, hum;
        float gas, weight;
        uint8_t uid[10];
        uint8_t rfid_status;

        if(DHT11_Read(&temp, &hum) == 0) {
            // Temperature and humidity read successfully
        }
        MQ2_Read(&gas);
        weight = (float)HX711_Read() / 100.0; // Assume calibration factor
        rfid_status = RFID_ReadCard(uid);

        ESP8266_SendData((float)temp, (float)hum, gas, weight);

        for(int i=0; i<1000000; i++); // Simple delay
    }
}

void SystemClock_Config(void) {
    FLASH->ACR |= FLASH_ACR_LATENCY_2;
    RCC->CR |= RCC_CR_HSION;
    while(!(RCC->CR & RCC_CR_HSIRDY));
    RCC->CFGR |= RCC_CFGR_PLLSRC_HSI_Div2 | RCC_CFGR_PLLMULL18;
    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->CFGR |= RCC_CFGR_PPRE1_DIV2 | RCC_CFGR_PPRE2_DIV1;
}

void GPIO_Init(void) {
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_AFIOEN;
    GPIOA->CRL &= ~(0xF << 0*4); GPIOA->CRL |= (0x4 << 0*4); // PA0 input floating
    GPIOA->CRL &= ~(0xF << 1*4); GPIOA->CRL |= (0x3 << 1*4); // PA1 output push-pull 50MHz
    GPIOA->CRL &= ~(0xF << 2*4); GPIOA->CRL |= (0x0 << 2*4); // PA2 analog input
    GPIOA->CRL &= ~(0xF << 3*4); GPIOA->CRL |= (0x3 << 3*4); // PA3 output push-pull 50MHz
    GPIOA->CRL &= ~(0xF << 4*4); GPIOA->CRL |= (0x3 << 4*4); // PA4 output push-pull 50MHz
    GPIOA->CRL &= ~(0xF << 5*4); GPIOA->CRL |= (0xB << 5*4); // PA5 alternate function push-pull
    GPIOA->CRL &= ~(0xF << 6*4); GPIOA->CRL |= (0x4 << 6*4); // PA6 input floating
    GPIOA->CRL &= ~(0xF << 7*4); GPIOA->CRL |= (0xB << 7*4); // PA7 alternate function push-pull
    GPIOA->CRH &= ~(0xF << 0*4); GPIOA->CRH |= (0x3 << 0*4); // PA8 output push-pull 50MHz
    GPIOA->CRH &= ~(0xF << 4);   GPIOA->CRH |= (0xB << 4);   // PA9 alternate function push-pull
    GPIOA->CRH &= ~(0xF << 8);   GPIOA->CRH |= (0x4 << 8);   // PA10 input floating
}

void ADC1_Init(void) {
    RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
    ADC1->SQR1 = 0;
    ADC1->SQR2 = 0;
    ADC1->SQR3 = 2;
    ADC1->SMPR2 = 0;
    ADC1->CR2 |= ADC_CR2_ADON;
    ADC1->CR2 |= ADC_CR2_RSTCAL; while(ADC1->CR2 & ADC_CR2_RSTCAL);
    ADC1->CR2 |= ADC_CR2_CAL; while(ADC1->CR2 & ADC_CR2_CAL);
}

uint16_t ADC1_Read(uint8_t channel) {
    ADC1->SQR3 = channel & 0x1F;
    ADC1->CR2 |= ADC_CR2_ADON;
    while(!(ADC1->SR & ADC_SR_EOC));
    return ADC1->DR;
}

void MQ2_Read(float *gas_level) {
    uint16_t adc_value = ADC1_Read(2);
    *gas_level = (float)adc_value / 4095.0 * 3.3;
}

void USART1_Init(void) {
    RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
    USART1->BRR = 0x1D4C;
    USART1->CR1 |= USART_CR1_TE | USART_CR1_RE;
    USART1->CR1 |= USART_CR1_UE;
}

void USART1_SendChar(char ch) {
    while(!(USART1->SR & USART_SR_TXE));
    USART1->DR = ch;
}

void USART1_SendString(char *str) {
    while(*str) USART1_SendChar(*str++);
}

void ESP8266_Init(void) {
    USART1_SendString("AT+RST\r\n");
    for(int i=0; i<1000000; i++);
    USART1_SendString("AT+CWMODE=1\r\n");
    for(int i=0; i<1000000; i++);
    USART1_SendString("AT+CWJAP=\"SSID\",\"PASSWORD\"\r\n");
    for(int i=0; i<1000000; i++);
    USART1_SendString("AT+CIPSTART=\"TCP\",\"cloud.huawei.com\",80\r\n");
    for(int i=0; i<1000000; i++);
}

void ESP8266_SendData(float temp, float hum, float gas, float weight) {
    char buffer[100];
    sprintf(buffer, "AT+CIPSEND=%d\r\n", strlen("temp=%.2f,hum=%.2f,gas=%.2f,weight=%.2f"));
    USART1_SendString(buffer);
    for(int i=0; i<1000000; i++);
    sprintf(buffer, "temp=%.2f,hum=%.2f,gas=%.2f,weight=%.2f", temp, hum, gas, weight);
    USART1_SendString(buffer);
    USART1_SendString("\r\n");
}

void HX711_Init(void) {
    HX711_SCK_PORT->BSRR = HX711_SCK_PIN << 16;
}

int32_t HX711_Read(void) {
    int32_t value = 0;
    uint8_t i;
    while((HX711_DATA_PORT->IDR & HX711_DATA_PIN) != 0);
    for(i=0; i<24; i++) {
        HX711_SCK_PORT->BSRR = HX711_SCK_PIN;
        for(int j=0; j<10; j++);
        value <<= 1;
        if(HX711_DATA_PORT->IDR & HX711_DATA_PIN) value++;
        HX711_SCK_PORT->BSRR = HX711_SCK_PIN << 16;
        for(int j=0; j<10; j++);
    }
    for(i=0; i<1; i++) {
        HX711_SCK_PORT->BSRR = HX711_SCK_PIN;
        for(int j=0; j<10; j++);
        HX711_SCK_PORT->BSRR = HX711_SCK_PIN << 16;
        for(int j=0; j<10; j++);
    }
    return value;
}

void DHT11_Init(void) {
    DHT11_PORT->CRH &= ~(0xF << 0*4);
    DHT11_PORT->CRH |= (0x3 << 0*4);
}

int8_t DHT11_Read(uint8_t *temperature, uint8_t *humidity) {
    uint8_t data[5] = {0};
    uint8_t i, j;
    DHT11_PORT->BSRR = DHT11_PIN << 16;
    for(i=0; i<18000; i++);
    DHT11_PORT->BSRR = DHT11_PIN;
    for(i=0; i<30; i++);
    DHT11_PORT->CRH &= ~(0xF << 0*4);
    DHT11_PORT->CRH |= (0x4 << 0*4);
    while(DHT11_PORT->IDR & DHT11_PIN);
    while(!(DHT11_PORT->IDR & DHT11_PIN));
    while(DHT11_PORT->IDR & DHT11_PIN);
    for(i=0; i<5; i++) {
        for(j=0; j<8; j++) {
            while(!(DHT11_PORT->IDR & DHT11_PIN));
            for(int k=0; k<40; k++);
            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];
        *temperature = data[2];
        return 0;
    }
    return -1;
}

void SPI1_Init(void) {
    RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
    SPI1->CR1 = SPI_CR1_MSTR | SPI_CR1_BR_0 | SPI_CR1_BR_1 | SPI_CR1_SSM | SPI_CR1_SSI;
    SPI1->CR1 |= SPI_CR1_SPE;
}

void RFID_Init(void) {
    RFID_RST_PORT->BSRR = RFID_RST_PIN;
}

uint8_t RFID_ReadCard(uint8_t *uid) {
    // Placeholder for RFID card reading implementation
    return 0;
}

项目核心代码

#include "stm32f10x.h"
#include "hx711.h"
#include "mq2.h"
#include "rc522.h"
#include "esp8266.h"
#include "dht11.h"
#include <stdio.h>

// 定义系统时钟频率
#define SYSTEM_CLOCK 72000000  // 72MHz

// 调试USART2初始化
void USART2_Init(void) {
    // 使能GPIOA和USART2时钟
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
    RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
    
    // 配置PA2为USART2 TX(推挽输出,50MHz)
    GPIOA->CRL &= ~(GPIO_CRL_CNF2 | GPIO_CRL_MODE2);
    GPIOA->CRL |= GPIO_CRL_CNF2_1 | GPIO_CRL_MODE2;
    
    // 配置PA3为USART2 RX(浮空输入)
    GPIOA->CRL &= ~(GPIO_CRL_CNF3 | GPIO_CRL_MODE3);
    GPIOA->CRL |= GPIO_CRL_CNF3_0;
    
    // 设置波特率为9600(PCLK1 = 36MHz)
    USART2->BRR = (234 << 4) | 6;  // USARTDIV = 234.375
    
    // 使能USART2,TX和RX
    USART2->CR1 |= USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;
}

// 实现putchar用于printf
int __io_putchar(int ch) {
    while (!(USART2->SR & USART_SR_TXE));
    USART2->DR = (ch & 0xFF);
    return ch;
}

// Systick初始化用于延时
void SysTick_Init(void) {
    SysTick->LOAD = (SYSTEM_CLOCK / 1000) - 1;  // 1ms定时
    SysTick->VAL = 0;
    SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk;
}

// 毫秒延时函数
void delay_ms(uint32_t ms) {
    for (uint32_t i = 0; i < ms; i++) {
        while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk));
    }
}

// 系统时钟配置到72MHz(假设外部8MHz晶振)
void RCC_Configuration(void) {
    // 使能HSE并等待就绪
    RCC->CR |= RCC_CR_HSEON;
    while (!(RCC->CR & RCC_CR_HSERDY));
    
    // 配置PLL:HSE作为源,倍频到72MHz
    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);
    
    // 使能外设时钟:GPIOA, GPIOB, USART1, SPI1, ADC1, USART2
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_USART1EN | RCC_APB2ENR_SPI1EN | RCC_APB2ENR_ADC1EN;
    RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
}

int main(void) {
    // 初始化系统
    RCC_Configuration();
    SysTick_Init();
    USART2_Init();
    
    // 初始化模块(假设这些函数在其他文件中定义)
    HX711_Init();
    MQ2_Init();
    RC522_Init();
    ESP8266_Init();
    DHT11_Init();
    
    printf("System started\n");
    
    float temp = 0.0, hum = 0.0;
    uint16_t gas_value = 0;
    float weight = 0.0;
    uint8_t rfid_id[5] = {0};
    
    while (1) {
        // 读取温湿度
        if (DHT11_Read(&temp, &hum) == 0) {
            // 读取成功,可处理数据
        }
        
        // 读取气体值
        gas_value = MQ2_Read();
        
        // 读取重量
        weight = HX711_Read();
        
        // 检查RFID卡
        if (RC522_CheckCard(rfid_id)) {
            printf("RFID Card detected: %02X%02X%02X%02X%02X\n", 
                   rfid_id[0], rfid_id[1], rfid_id[2], rfid_id[3], rfid_id[4]);
            // 这里可添加权限检查和日志记录
        }
        
        // 准备数据字符串(JSON格式)
        char data_str[100];
        sprintf(data_str, "{\"temp\":%.1f,\"hum\":%.1f,\"gas\":%d,\"weight\":%.1f}", 
                temp, hum, gas_value, weight);
        
        // 通过ESP8266发送数据到华为云
        ESP8266_SendData(data_str);
        
        // 延时1秒
        delay_ms(1000);
    }
}

总结

本系统基于STM32F103C8T6核心板设计了一套智能实验室危化品管理系统,旨在通过集成多种传感器和模块,实现对危化品柜的全面监控与管理,提升实验室安全性和操作效率。系统通过实时采集温度、湿度、气体泄漏等环境数据,并结合称重传感器记录药品使用量,确保了危化品存储和使用的透明化和可追溯性。

硬件组成上,系统采用了HX711称重传感器模块监测药品重量,MQ-2气体传感器检测泄漏情况,RFID-RC522模块实现人员身份认证,以及ESP8266-01S Wi-Fi模块用于数据上传。这些模块通过洞洞板焊接和杜邦线连接,构建了一个稳定可靠的硬件平台,为主控制器提供了丰富的外设接口。

功能实现方面,系统通过STM32微控制器协调各传感器数据采集,并利用RFID技术进行权限管理,确保只有授权人员才能访问危化品。同时,QT上位机软件实现了库存管理、使用日志记录和预警处理,为用户提供了直观的操作界面和数据分析能力。

数据上传至华为云后,系统实现了远程监控和数据分析,使得管理人员可以随时随地查看危化品状态并及时响应异常情况。这种云平台集成不仅增强了系统的可扩展性,还为大数据分析和智能预警提供了基础。

总体而言,本系统成功地将硬件感知、本地控制和云端管理相结合,为实验室危化品管理提供了一套高效、安全的解决方案,有助于减少人为错误、预防事故的发生,并促进实验室管理的智能化升级。


网站公告

今日签到

点亮在社区的每一天
去签到