项目开发背景
随着实验室安全管理要求的不断提高,危化品的规范管理已成为科研机构和企业面临的重要挑战。传统人工记录方式存在效率低、易出错、无法实时监控等问题,尤其在温湿度敏感、易燃易爆或有毒危化品的存储和使用过程中,潜在安全隐患尤为突出。
危化品的泄漏、超量使用或未经授权取用可能引发严重安全事故,而现有管理系统往往缺乏多参数实时监测与智能预警能力。此外,危化品使用记录追溯困难、权限管理不严格等问题,也增加了实验室运营风险。
物联网技术的快速发展为危化品管理提供了新的解决方案。通过集成传感器技术、无线通信和云平台,可实现危化品存储环境的动态监控、使用过程的精准记录以及异常状态的即时告警。基于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上位机软件实现了库存管理、使用日志记录和预警处理,为用户提供了直观的操作界面和数据分析能力。
数据上传至华为云后,系统实现了远程监控和数据分析,使得管理人员可以随时随地查看危化品状态并及时响应异常情况。这种云平台集成不仅增强了系统的可扩展性,还为大数据分析和智能预警提供了基础。
总体而言,本系统成功地将硬件感知、本地控制和云端管理相结合,为实验室危化品管理提供了一套高效、安全的解决方案,有助于减少人为错误、预防事故的发生,并促进实验室管理的智能化升级。