基于华为云的STM32智能仓储防火防盗监控系统

举报
DS小龙哥 发表于 2025/08/25 11:14:34 2025/08/25
【摘要】 本项目基于STM32微控制器和多种传感器,旨在开发一个集防火防盗功能于一体的智能监控系统。

 

项目开发背景

随着现代物流行业的快速发展,仓储管理面临着日益严峻的安全挑战,尤其是防火和防盗问题。仓库内通常存放大量贵重物品,一旦发生火灾或非法入侵,不仅会造成巨大的经济损失,还可能危及人员安全。因此,实现高效、实时的监控系统至关重要,以预防和及时响应潜在风险。

传统的仓储监控系统往往依赖人工巡检或简单的传感器报警,存在响应延迟、集成度低和覆盖范围有限等问题。这些系统通常无法实现多参数综合监测,如同时处理烟雾、温度和人体信号,且缺乏云端数据备份和远程访问功能,导致管理效率低下和应急处理能力不足。

近年来,物联网技术和云平台的兴起为智能监控提供了新的解决方案。通过将嵌入式系统与云服务结合,可以实现数据的实时采集、传输和分析,提升监控的自动化和智能化水平。华为云作为可靠的云服务提供商,能够支持大规模数据存储和处理,为仓储监控系统提供稳定、高效的后端支持。

本项目基于STM32微控制器和多种传感器,旨在开发一个集防火防盗功能于一体的智能监控系统。通过集成烟雾、温度和人体红外传感器,系统能够实时监测仓库环境,并在检测到异常时触发本地报警和图像抓拍。同时,利用ESP8266 Wi-Fi模块将报警信息和图像数据上传至华为云平台,实现远程监控和数据管理。配合QT开发的上位机软件,用户可以直观查看多仓库的实时画面、历史记录和报警数据,从而提高整体安全性和管理效率。

这一系统的开发不仅满足了现代仓储对高效安全监控的需求,还体现了物联网技术在传统行业中的应用潜力,为未来智能仓储的推广和实施提供了可行参考。

设计实现的功能

(1)实时监测仓库内烟雾浓度、温度及人体红外信号。
(2)发现异常时触发本地声光报警并抓拍图像。
(3)报警信息及图像数据上传至华为云平台。
(4)QT上位机显示多仓库监控画面、报警记录及实时数据。

项目硬件模块组成

(1)STM32F103C8T6最小系统核心板(主控)。
(2)MQ-2烟雾传感器。
(3)DHT11温度传感器。
(4)HC-SR501人体红外传感器。
(5)OV2640摄像头模块(图像抓拍)。
(6)ESP8266-01S Wi-Fi模块(华为云通信)。
(7)洞洞板焊接传感器接口电路,杜邦线连接模块。

设计意义

该系统设计旨在实现仓库环境的智能监控,通过集成多种传感器和云平台技术,提升仓储管理的安全性和效率。系统能够实时监测烟雾浓度、温度和人体红外信号,及时发现火灾隐患和非法入侵行为,从而有效保护仓库内货物安全,防止因意外事件导致的财产损失。

当检测到异常情况时,系统立即触发本地声光报警并抓拍图像,这种快速响应机制有助于在紧急情况下迅速采取行动,减少潜在风险,并为事后分析提供视觉证据。报警信息和图像数据通过Wi-Fi模块上传至华为云平台,实现了远程监控和数据存储,使得管理人员能够随时随地通过网络访问系统状态,及时处理报警事件,提高了管理的灵活性和响应速度。

QT上位机软件提供多仓库监控画面、报警记录及实时数据显示功能,便于用户集中管理多个仓库点,历史数据回溯和分析有助于识别 patterns 或优化安防策略,从而提升整体仓储运营的智能化水平。该系统采用成本效益高的硬件组件,如STM32主控和常见传感器,易于部署和维护,适用于中小型仓库的 practical 应用,体现了物联网技术在传统行业中的创新集成。

设计思路

该系统以STM32F103C8T6最小系统核心板作为主控制器,负责协调整个监控系统的运行。通过洞洞板焊接传感器接口电路,使用杜邦线连接各模块,确保硬件连接稳定可靠。主控板实时采集来自MQ-2烟雾传感器、DHT11温度传感器和HC-SR501人体红外传感器的数据,实现仓库内环境参数的持续监控。

传感器数据采集通过STM32的ADC和GPIO接口完成,烟雾浓度和温度值由模拟或数字信号读取,人体红外信号则通过中断方式检测。主控程序循环扫描这些数据,并与预设阈值进行比较,以判断是否出现异常情况,如烟雾浓度超标、温度过高或检测到非法入侵。

当发现异常时,系统立即触发本地声光报警装置,例如通过蜂鸣器和LED灯进行警示,同时启动OV2640摄像头模块抓拍现场图像。图像数据被暂存到缓冲区,主控板协调处理报警信息和图像,确保数据完整性。

报警信息和抓拍的图像通过ESP8266-01S Wi-Fi模块上传至华为云平台。STM32通过串口与Wi-Fi模块通信,使用AT指令配置网络连接,并按照华为云API协议封装数据包,实现可靠的上传功能,确保云端及时接收和处理报警事件。

QT上位机软件负责显示多仓库的监控画面、报警记录及实时数据。上位机通过网络接口从华为云平台获取数据,解析并展示传感器读数、报警历史和实时图像,提供用户友好的界面用于远程监控和管理,增强系统的可视化和响应能力。

框架图

+-------------------------+
|      传感器模块          |
| - MQ-2烟雾传感器         |
| - DHT11温度传感器       |
| - HC-SR501人体红外传感器|
| - OV2640摄像头模块       |
+-------------------------+
         |
         v
+-------------------------+
|      STM32F103C8T6      |
|      主控核心板          |
| - 数据处理              |
| - 控制逻辑              |
+-------------------------+
         |\
         | \-> [声光报警模块] (本地)
         |
         v
+-------------------------+
|     ESP8266-01S         |
|     Wi-Fi模块           |
+-------------------------+
         |
         v
+-------------------------+
|     华为云平台           |
| - 数据存储              |
| - 消息推送              |
+-------------------------+
         |
         v
+-------------------------+
|     QT上位机软件         |
| - 多仓库监控画面         |
| - 报警记录              |
| - 实时数据              |
+-------------------------+

系统总体设计

系统以STM32F103C8T6最小系统核心板作为主控制器,负责协调整个监控系统的运行。该系统通过集成多种传感器实时采集仓库环境数据,包括MQ-2烟雾传感器检测烟雾浓度、DHT11温度传感器测量环境温度、以及HC-SR501人体红外传感器感知人体活动信号。这些传感器数据由STM32主控进行周期性读取和处理,确保实时监测仓库状态。

当系统检测到异常情况,如烟雾浓度超标、温度过高或人体红外信号触发时,STM32主控会立即启动本地声光报警装置,并通过OV2640摄像头模块抓拍现场图像。报警信息和图像数据被暂存于主控内存中,为后续上传做准备。

系统利用ESP8266-01S Wi-Fi模块建立与华为云平台的连接,通过MQTT或HTTP协议将报警信息及抓拍的图像数据上传至云端。这一过程确保了数据的远程存储和可访问性,同时STM32主控会处理网络通信的稳定性,包括重连机制和数据校验。

QT上位机软件作为监控界面,从华为云平台获取多仓库的实时数据、报警记录和图像信息,并以图形化方式显示监控画面、历史报警和传感器读数。上位机还支持用户交互,如查看详细报警日志和实时数据趋势,从而实现对仓库环境的远程监控和管理。

整个系统的硬件连接通过洞洞板焊接传感器接口电路,并使用杜邦线连接各模块,确保了结构的灵活性和可维护性。STM32主控程序采用嵌入式C语言开发,实现数据采集、处理、报警触发和通信功能,而华为云平台和QT上位机则共同完成数据的云端存储和可视化展示。

系统功能总结

功能描述 实现方式
实时监测烟雾浓度 MQ-2烟雾传感器
实时监测温度 DHT11温度传感器
实时监测人体红外信号 HC-SR501人体红外传感器
异常时触发本地声光报警 STM32主控控制声光报警器件
异常时抓拍图像 OV2640摄像头模块
上传报警信息及图像数据至华为云 ESP8266-01S Wi-Fi模块
QT上位机显示多仓库监控画面、报警记录及实时数据 通过Wi-Fi传输数据,QT软件处理

设计的各个功能模块描述

STM32F103C8T6最小系统核心板作为主控模块,负责协调整个系统的运行,通过ADC和GPIO接口读取传感器数据,处理逻辑判断,并在检测到异常时控制声光报警和图像抓拍,同时通过串口与Wi-Fi模块通信实现数据上传。

MQ-2烟雾传感器模块用于实时监测仓库内的烟雾浓度,其模拟输出连接到STM32的ADC引脚,STM32定期采样并转换为烟雾浓度值,当浓度超过预设阈值时触发报警。

DHT11温度传感器模块监测仓库环境温度,通过单总线协议与STM32通信,提供数字温度数据,系统实时显示并判断是否异常超温。

HC-SR501人体红外传感器模块检测人体红外信号,用于防盗监控,其数字输出连接到STM32的GPIO引脚,当检测到移动人体时输出高电平,STM32据此判断入侵事件。

OV2640摄像头模块用于图像抓拍,在系统检测到异常(如烟雾、温度或人体信号)时,STM32通过I2C接口控制摄像头启动,捕获JPEG图像并暂存,准备上传。

ESP8266-01S Wi-Fi模块作为云通信模块,通过AT指令与STM32串口交互,将报警信息(如传感器数据)和图像数据通过HTTP或MQTT协议上传至华为云平台,实现远程监控。

洞洞板焊接的传感器接口电路提供稳定的电源和信号连接,使用杜邦线灵活连接各模块到STM32,确保传感器和模块的可靠电气接口,减少噪声干扰。

本地声光报警模块由LED和蜂鸣器组成,直接由STM32的GPIO引脚控制,当系统检测到异常时,STM输出驱动信号,触发声光报警以提醒现场人员。

QT上位机软件模块运行于PC端,通过网络连接华为云平台,实时显示多仓库的监控画面、报警记录和传感器数据,提供图形化界面用于远程监控和管理。

上位机代码设计

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 <QNetworkAccessManager>
#include <QNetworkReply>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QLabel>
#include <QTableWidget>
#include <QListWidget>
#include <QTimer>
#include <QPushButton>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QGroupBox>

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private slots:
    void fetchRealTimeData();
    void fetchAlarmRecords();
    void fetchImages();
    void onRealTimeDataReceived(QNetworkReply *reply);
    void onAlarmRecordsReceived(QNetworkReply *reply);
    void onImagesReceived(QNetworkReply *reply);
    void updateDisplay();

private:
    QNetworkAccessManager *networkManager;
    QTimer *dataTimer;
    QLabel *temperatureLabel;
    QLabel *smokeLabel;
    QLabel *pirLabel;
    QTableWidget *alarmTable;
    QListWidget *imageList;
    QList<QLabel*> warehouseImageLabels;
    QList<QString> warehouseIds; // Assume multiple warehouse IDs

    void setupUI();
    void parseRealTimeData(const QJsonObject &json);
    void parseAlarmRecords(const QJsonArray &jsonArray);
    void parseImages(const QJsonArray &jsonArray);
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include <QHeaderView>
#include <QImage>
#include <QPixmap>
#include <QBuffer>
#include <QMessageBox>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), networkManager(new QNetworkAccessManager(this))
{
    setupUI();
    dataTimer = new QTimer(this);
    connect(dataTimer, &QTimer::timeout, this, &MainWindow::fetchRealTimeData);
    dataTimer->start(5000); // Update every 5 seconds

    // Fetch initial data
    fetchRealTimeData();
    fetchAlarmRecords();
    fetchImages();

    // Connect network signals
    connect(networkManager, &QNetworkAccessManager::finished, this, [this](QNetworkReply *reply) {
        if (reply->error() == QNetworkReply::NoError) {
            QString url = reply->url().toString();
            if (url.contains("realtime-data")) {
                onRealTimeDataReceived(reply);
            } else if (url.contains("alarms")) {
                onAlarmRecordsReceived(reply);
            } else if (url.contains("images")) {
                onImagesReceived(reply);
            }
        } else {
            QMessageBox::warning(this"Network Error", reply->errorString());
        }
        reply->deleteLater();
    });

    // Initialize warehouse IDs (example IDs, replace with actual)
    warehouseIds << "warehouse1" << "warehouse2" << "warehouse3";
}

MainWindow::~MainWindow()
{
}

void MainWindow::setupUI()
{
    QWidget *centralWidget = new QWidget(this);
    QHBoxLayout *mainLayout = new QHBoxLayout(centralWidget);

    // Left side: real-time data and alarm records
    QVBoxLayout *leftLayout = new QVBoxLayout();

    // Real-time data group
    QGroupBox *realTimeGroup = new QGroupBox("Real-time Data");
    QVBoxLayout *realTimeLayout = new QVBoxLayout();
    temperatureLabel = new QLabel("Temperature: -- °C");
    smokeLabel = new QLabel("Smoke Concentration: -- ppm");
    pirLabel = new QLabel("PIR Signal: --");
    realTimeLayout->addWidget(temperatureLabel);
    realTimeLayout->addWidget(smokeLabel);
    realTimeLayout->addWidget(pirLabel);
    realTimeGroup->setLayout(realTimeLayout);
    leftLayout->addWidget(realTimeGroup);

    // Alarm records group
    QGroupBox *alarmGroup = new QGroupBox("Alarm Records");
    QVBoxLayout *alarmLayout = new QVBoxLayout();
    alarmTable = new QTableWidget(04);
    alarmTable->setHorizontalHeaderLabels({"Time""Warehouse""Type""Value"});
    alarmTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
    alarmLayout->addWidget(alarmTable);
    alarmGroup->setLayout(alarmLayout);
    leftLayout->addWidget(alarmGroup);

    mainLayout->addLayout(leftLayout, 1);

    // Right side: warehouse images
    QGroupBox *imageGroup = new QGroupBox("Warehouse Monitoring");
    QVBoxLayout *imageLayout = new QVBoxLayout();
    // Create image labels for each warehouse
    for (int i = 0; i < warehouseIds.size(); ++i) {
        QLabel *imageLabel = new QLabel();
        imageLabel->setFixedSize(320240);
        imageLabel->setStyleSheet("border: 1px solid black;");
        imageLabel->setAlignment(Qt::AlignCenter);
        imageLabel->setText("No image");
        warehouseImageLabels.append(imageLabel);
        imageLayout->addWidget(new QLabel("Warehouse " + QString::number(i+1)));
        imageLayout->addWidget(imageLabel);
    }
    imageGroup->setLayout(imageLayout);
    mainLayout->addWidget(imageGroup, 2);

    centralWidget->setLayout(mainLayout);
    setCentralWidget(centralWidget);
    setWindowTitle("Smart Warehouse Monitoring System");
    resize(1200800);
}

void MainWindow::fetchRealTimeData()
{
    // Example API endpoint: replace with actual Huawei Cloud URL
    QUrl url("https://your-huawei-cloud-api.com/api/realtime-data");
    networkManager->get(QNetworkRequest(url));
}

void MainWindow::fetchAlarmRecords()
{
    QUrl url("https://your-huawei-cloud-api.com/api/alarms");
    networkManager->get(QNetworkRequest(url));
}

void MainWindow::fetchImages()
{
    for (const QString &warehouseId : warehouseIds) {
        QUrl url("https://your-huawei-cloud-api.com/api/images/" + warehouseId);
        networkManager->get(QNetworkRequest(url));
    }
}

void MainWindow::onRealTimeDataReceived(QNetworkReply *reply)
{
    QByteArray data = reply->readAll();
    QJsonDocument jsonDoc = QJsonDocument::fromJson(data);
    if (jsonDoc.isObject()) {
        parseRealTimeData(jsonDoc.object());
    }
}

void MainWindow::onAlarmRecordsReceived(QNetworkReply *reply)
{
    QByteArray data = reply->readAll();
    QJsonDocument jsonDoc = QJsonDocument::fromJson(data);
    if (jsonDoc.isArray()) {
        parseAlarmRecords(jsonDoc.array());
    }
}

void MainWindow::onImagesReceived(QNetworkReply *reply)
{
    QByteArray data = reply->readAll();
    // Assume the reply contains image data or JSON with image URL
    // For simplicity, assume direct image data in response
    QImage image;
    if (image.loadFromData(data)) {
        // Extract warehouse ID from URL to determine which label to update
        QString urlStr = reply->url().toString();
        for (int i = 0; i < warehouseIds.size(); ++i) {
            if (urlStr.contains(warehouseIds[i])) {
                QPixmap pixmap = QPixmap::fromImage(image);
                warehouseImageLabels[i]->setPixmap(pixmap.scaled(320240, Qt::KeepAspectRatio));
                break;
            }
        }
    } else {
        // Try parsing as JSON if image data is not direct
        QJsonDocument jsonDoc = QJsonDocument::fromJson(data);
        if (jsonDoc.isObject()) {
            QJsonObject obj = jsonDoc.object();
            if (obj.contains("imageUrl")) {
                QString imageUrl = obj["imageUrl"].toString();
                // Download image from imageUrl - for simplicity, we skip here
            }
        }
    }
}

void MainWindow::parseRealTimeData(const QJsonObject &json)
{
    if (json.contains("temperature")) {
        temperatureLabel->setText("Temperature: " + QString::number(json["temperature"].toDouble()) + " °C");
    }
    if (json.contains("smoke")) {
        smokeLabel->setText("Smoke Concentration: " + QString::number(json["smoke"].toDouble()) + " ppm");
    }
    if (json.contains("pir")) {
        pirLabel->setText("PIR Signal: " + QString::number(json["pir"].toInt()));
    }
}

void MainWindow::parseAlarmRecords(const QJsonArray &jsonArray)
{
    alarmTable->setRowCount(0);
    for (const QJsonValue &value : jsonArray) {
        QJsonObject obj = value.toObject();
        int row = alarmTable->rowCount();
        alarmTable->insertRow(row);
        alarmTable->setItem(row, 0new QTableWidgetItem(obj["time"].toString()));
        alarmTable->setItem(row, 1new QTableWidgetItem(obj["warehouse"].toString()));
        alarmTable->setItem(row, 2new QTableWidgetItem(obj["type"].toString()));
        alarmTable->setItem(row, 3new QTableWidgetItem(QString::number(obj["value"].toDouble())));
    }
}

void MainWindow::parseImages(const QJsonArray &jsonArray)
{
    // If images are returned as JSON array, but we handle in onImagesReceived
}

项目文件说明

  •  main.cpp: 应用程序入口点,创建主窗口并启动事件循环。
  •  mainwindow.h: 主窗口类的声明,包括槽函数和私有成员。
  •  mainwindow.cpp: 主窗口类的实现,包括UI设置、网络请求和数据解析。

使用说明

  1. 1. 确保已安装QT和C++编译器。
  2. 2. 创建一个新的QT Widgets应用程序项目。
  3. 3. 将上述代码复制到相应的文件中。
  4. 4. 替换API端点URL为实际的华为云API地址。
  5. 5. 根据需要调整仓库ID和UI布局。
  6. 6. 编译并运行项目。

此代码提供了一个基本框架,用于从华为云获取数据并显示。实际部署时,需完善错误处理、认证(如API密钥)和图像处理逻辑。

模块代码设计

#include "stm32f10x.h"  // 假设有基本类型定义,但寄存器方式下,我们直接定义寄存器

// 寄存器地址定义
#define RCC_BASE        0x40021000
#define GPIOA_BASE      0x40010800
#define GPIOB_BASE      0x40010C00
#define GPIOC_BASE      0x40011000
#define ADC1_BASE       0x40012400
#define USART1_BASE     0x40013800

#define RCC_APB2ENR     *(volatile unsigned long *)(RCC_BASE + 0x18)
#define GPIOA_CRL       *(volatile unsigned long *)(GPIOA_BASE + 0x00)
#define GPIOA_CRH       *(volatile unsigned long *)(GPIOA_BASE + 0x04)
#define GPIOA_IDR       *(volatile unsigned long *)(GPIOA_BASE + 0x08)
#define GPIOA_ODR       *(volatile unsigned long *)(GPIOA_BASE + 0x0C)
#define GPIOB_CRL       *(volatile unsigned long *)(GPIOB_BASE + 0x00)
#define GPIOB_CRH       *(volatile unsigned long *)(GPIOB_BASE + 0x04)
#define GPIOB_IDR       *(volatile unsigned long *)(GPIOB_BASE + 0x08)
#define GPIOB_ODR       *(volatile unsigned long *)(GPIOB_BASE + 0x0C)
#define GPIOC_CRH       *(volatile unsigned long *)(GPIOC_BASE + 0x04)
#define GPIOC_ODR       *(volatile unsigned long *)(GPIOC_BASE + 0x0C)
#define ADC1_SR         *(volatile unsigned long *)(ADC1_BASE + 0x00)
#define ADC1_CR2        *(volatile unsigned long *)(ADC1_BASE + 0x08)
#define ADC1_SQR3       *(volatile unsigned long *)(ADC1_BASE + 0x34)
#define ADC1_DR         *(volatile unsigned long *)(ADC1_BASE + 0x4C)
#define USART1_SR       *(volatile unsigned long *)(USART1_BASE + 0x00)
#define USART1_DR       *(volatile unsigned long *)(USART1_BASE + 0x04)
#define USART1_BRR      *(volatile unsigned long *)(USART1_BASE + 0x08)
#define USART1_CR1      *(volatile unsigned long *)(USART1_BASE + 0x0C)

// 引脚定义
#define MQ2_PIN         0  // PA0 for ADC
#define DHT11_PIN       1  // PA1 for DHT11 data
#define PIR_PIN         2  // PA2 for HC-SR501 output
#define BUZZER_PIN      3  // PA3 for buzzer
#define LED_PIN         13 // PC13 for LED
#define OV2640_SIOC_PIN 6  // PB6 for I2C SCL
#define OV2640_SIOD_PIN 7  // PB7 for I2C SDA
// OV2640 data pins assumed on PB8-PB15 for simplicity, but not fully implemented
#define WIFI_TX_PIN     9  // PA9 for USART1 TX
#define WIFI_RX_PIN     10 // PA10 for USART1 RX

// 阈值定义
#define SMOKE_THRESHOLD 500  // ADC value for smoke detection
#define TEMP_THRESHOLD  50   // Temperature in Celsius
#define PIR_DETECT      1    // High means detection

// 全局变量
volatile unsigned int smoke_value = 0;
volatile int temperature = 0;
volatile int humidity = 0;
volatile unsigned char pir_status = 0;
volatile unsigned char image_buffer[320*240]; //假设图像缓冲区,实际OV2640输出JPEG

// 函数声明
void SystemInit(void);
void GPIO_Init(void);
void ADC_Init(void);
void USART_Init(void);
void Delay_us(unsigned int us);
void Delay_ms(unsigned int ms);
unsigned int DHT11_Read(void);
unsigned int ADC_Read(unsigned char channel);
void Read_Sensors(void);
void Trigger_Alarm(void);
void OV2640_Init(void);
void OV2640_Capture(void);
void ESP8266_SendCommand(char *cmd);
void ESP8266_UploadData(void);
void USART_SendChar(char ch);
void USART_SendString(char *str);

// 系统初始化
void SystemInit(void) {
    // Enable clocks
    RCC_APB2ENR |= (1<<2) | (1<<3) | (1<<4) | (1<<14); // Enable GPIOA, GPIOB, GPIOC, ADC1 clocks
    RCC_APB2ENR |= (1<<0);  // Enable AFIO clock if needed, but not used here
    RCC_APB2ENR |= (1<<14); // Enable USART1 clock
}

// GPIO初始化
void GPIO_Init(void) {
    // PA0: analog input for MQ2
    GPIOA_CRL &= ~(0xF << (MQ2_PIN * 4));
    GPIOA_CRL |= (0x0 << (MQ2_PIN * 4)); // Analog mode

    // PA1: output for DHT11 (open drain)
    GPIOA_CRL &= ~(0xF << (DHT11_PIN * 4));
    GPIOA_CRL |= (0x4 << (DHT11_PIN * 4)); // Output open drain

    // PA2: input for PIR
    GPIOA_CRL &= ~(0xF << (PIR_PIN * 4));
    GPIOA_CRL |= (0x8 << (PIR_PIN * 4)); // Input with pull-up/pull-down? Assume floating

    // PA3: output for buzzer
    GPIOA_CRL &= ~(0xF << (BUZZER_PIN * 4));
    GPIOA_CRL |= (0x3 << (BUZZER_PIN * 4)); // Output push-pull

    // PC13: output for LED
    GPIOC_CRH &= ~(0xF << ((LED_PIN-8) * 4));
    GPIOC_CRH |= (0x3 << ((LED_PIN-8) * 4)); // Output push-pull

    // PB6 and PB7 for I2C (OV2640), but simplified here
    GPIOB_CRL &= ~(0xF << (OV2640_SIOC_PIN * 4));
    GPIOB_CRL |= (0x4 << (OV2640_SIOC_PIN * 4)); // Output open drain
    GPIOB_CRL &= ~(0xF << (OV2640_SIOD_PIN * 4));
    GPIOB_CRL |= (0x4 << (OV2640_SIOD_PIN * 4)); // Output open drain

    // PA9 and PA10 for USART1
    GPIOA_CRH &= ~(0xFF << ((WIFI_TX_PIN-8) * 4));
    GPIOA_CRH |= (0x0B << ((WIFI_TX_PIN-8) * 4)); // PA9: Alternate function push-pull
    GPIOA_CRH |= (0x04 << ((WIFI_RX_PIN-8) * 4)); // PA10: Input floating
}

// ADC初始化
void ADC_Init(void) {
    ADC1_CR2 = 0;
    ADC1_CR2 |= (1<<0);  // ADON: enable ADC
    Delay_ms(1);
    ADC1_CR2 |= (1<<3);  // RSTCAL: reset calibration
    while (ADC1_CR2 & (1<<3)); // Wait for reset
    ADC1_CR2 |= (1<<2);  // CAL: start calibration
    while (ADC1_CR2 & (1<<2)); // Wait for calibration
    ADC1_SQR3 = 0;       // Channel 0 as first conversion
}

// USART初始化
void USART_Init(void) {
    USART1_BRR = 0x1D4C// 9600 baud at 72MHz
    USART1_CR1 |= (1<<13) | (1<<3) | (1<<2); // UE, TE, RE
}

// 微秒延时
void Delay_us(unsigned int us) {
    us *= 72// Assuming 72MHz clock, adjust as needed
    while (us--) {
        __asm__("nop");
    }
}

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

// 读取DHT11
unsigned int DHT11_Read(void) {
    unsigned char data[5] = {0};
    unsigned int i, j;
    // Start signal
    GPIOA_ODR &= ~(1<<DHT11_PIN); // Set PA1 low
    Delay_ms(18);
    GPIOA_ODR |= (1<<DHT11_PIN); // Set PA1 high
    Delay_us(30);
    // Wait for response
    while (GPIOA_IDR & (1<<DHT11_PIN)); // Wait for low
    while (!(GPIOA_IDR & (1<<DHT11_PIN))); // Wait for high
    // Read data
    for (i=0; i<5; i++) {
        for (j=0; j<8; j++) {
            while (!(GPIOA_IDR & (1<<DHT11_PIN))); // Wait for high
            Delay_us(40);
            if (GPIOA_IDR & (1<<DHT11_PIN)) {
                data[i] |= (1<<(7-j));
                while (GPIOA_IDR & (1<<DHT11_PIN)); // Wait for low
            }
        }
    }
    if ((data[0] + data[1] + data[2] + data[3]) == data[4]) {
        humidity = data[0];
        temperature = data[2];
        return 1;
    }
    return 0;
}

// ADC读取
unsigned int ADC_Read(unsigned char channel) {
    ADC1_SQR3 = channel;
    ADC1_CR2 |= (1<<0); // ADON
    while (!(ADC1_SR & (1<<1))); // Wait for EOC
    return ADC1_DR;
}

// 读取所有传感器
void Read_Sensors(void) {
    smoke_value = ADC_Read(MQ2_PIN);
    DHT11_Read();
    pir_status = (GPIOA_IDR & (1<<PIR_PIN)) ? 1 : 0;
}

// 触发报警
void Trigger_Alarm(void) {
    GPIOA_ODR |= (1<<BUZZER_PIN); // Buzzer on
    GPIOC_ODR |= (1<<LED_PIN);    // LED on
    Delay_ms(500);
    GPIOA_ODR &= ~(1<<BUZZER_PIN); // Buzzer off
    GPIOC_ODR &= ~(1<<LED_PIN);    // LED off
}

// OV2640初始化(简化)
void OV2640_Init(void) {
    // 通过I2C配置OV2640,但这里简化
    // 实际需要发送多个寄存器配置
    Delay_ms(100);
}

// OV2640抓拍(简化)
void OV2640_Capture(void) {
    // 实际需要触发抓拍并读取图像数据到buffer
    // 这里模拟为填充缓冲区
    for (int i=0; i<320*240; i++) {
        image_buffer[i] = 0x55// 模拟数据
    }
}

// USART发送字符
void USART_SendChar(char ch) {
    while (!(USART1_SR & (1<<7))); // Wait for TXE
    USART1_DR = ch;
}

// USART发送字符串
void USART_SendString(char *str) {
    while (*str) {
        USART_SendChar(*str++);
    }
}

// ESP8266发送AT命令
void ESP8266_SendCommand(char *cmd) {
    USART_SendString(cmd);
    USART_SendChar('\r');
    USART_SendChar('\n');
    Delay_ms(1000);
}

// ESP8266上传数据到华为云
void ESP8266_UploadData(void) {
    // 假设华为云AT命令接口
    ESP8266_SendCommand("AT+CIPSTART=\"TCP\",\"cloud.huawei.com\",80");
    Delay_ms(2000);
    ESP8266_SendCommand("AT+CIPSEND=100");
    Delay_ms(1000);
    // 构建HTTP POST请求或MQTT消息,这里简化
    char buffer[100];
    sprintf(buffer, "Smoke:%d,Temp:%d,PIR:%d", smoke_value, temperature, pir_status);
    USART_SendString(buffer);
    USART_SendChar(0x1A); // Ctrl+Z to send
    Delay_ms(2000);
}

// 主函数
int main(void) {
    SystemInit();
    GPIO_Init();
    ADC_Init();
    USART_Init();
    OV2640_Init();
    
    while (1) {
        Read_Sensors();
        if (smoke_value > SMOKE_THRESHOLD || temperature > TEMP_THRESHOLD || pir_status == PIR_DETECT) {
            Trigger_Alarm();
            OV2640_Capture();
            ESP8266_UploadData();
        }
        Delay_ms(1000); // 每1秒检查一次
    }
}

// 中断服务例程等省略,根据需要添加

项目核心代码

#include "stm32f10x.h"

// 引脚定义
#define MQ2_PIN         0  // PA0 for ADC
#define DHT11_PIN       1  // PA1
#define PIR_PIN         2  // PA2
#define LED_PIN         13 // PC13
#define BUZZER_PIN      14 // PC14

// 函数声明
void SystemInit_Config(void);
void GPIO_Init(void);
void ADC1_Init(void);
void USART1_Init(void);
void TIM2_Init(void)// 用于延时和DHT11时序
void Delay_ms(uint32_t ms);
void DHT11_Start(void);
uint8_t DHT11_Read_Byte(void);
uint8_t DHT11_Read_Data(float *temperature, float *humidity);
uint16_t ADC_Read(uint8_t channel);
void ESP8266_SendCmd(char *cmd);
void ESP8266_SendData(char *data);
void OV2640_Init(void);
void OV2640_Capture(void);
void Alarm_On(void);
void Alarm_Off(void);

// 全局变量
volatile uint32_t msTicks = 0;

// SysTick中断处理函数
void SysTick_Handler(void) {
    msTicks++;
}

// 毫秒延时函数
void Delay_ms(uint32_t ms) {
    uint32_t startTicks = msTicks;
    while ((msTicks - startTicks) < ms);
}

int main(void) {
    SystemInit_Config();
    GPIO_Init();
    ADC1_Init();
    USART1_Init();
    TIM2_Init();
    OV2640_Init();
    
    float temp, humidity;
    uint16_t smoke_value;
    uint8_t pir_status;
    uint8_t dht11_status;
    
    while (1) {
        // 读取烟雾传感器(MQ-2)
        smoke_value = ADC_Read(MQ2_PIN);
        
        // 读取温度湿度(DHT11)
        dht11_status = DHT11_Read_Data(&temp, &humidity);
        
        // 读取人体红外传感器(HC-SR501)
        pir_status = GPIOA->IDR & (1 << PIR_PIN); // 假设高电平有效
        
        // 检查异常:烟雾浓度高、温度高或检测到人
        if (smoke_value > 2000 || temp > 50.0 || pir_status) {
            Alarm_On(); // 触发声光报警
            OV2640_Capture(); // 抓拍图像
            
            // 上传报警信息和图像到华为云(通过ESP8266)
            char alertMsg[100];
            sprintf(alertMsg, "Alert: Smoke=%u, Temp=%.1f, PIR=%u", smoke_value, temp, pir_status);
            ESP8266_SendData(alertMsg);
            // 假设图像上传函数已处理
            
            Delay_ms(5000); // 报警持续5秒
            Alarm_Off();
        }
        
        Delay_ms(1000); // 每秒检测一次
    }
}

void SystemInit_Config(void) {
    // 设置系统时钟为72MHz(使用外部8MHz晶振)
    RCC->CR |= 0x00010000// HSEON
    while (!(RCC->CR & 0x00020000)); // 等待HSE就绪
    RCC->CFGR = 0x001D0400// PLL乘法9倍,APB1分频2,APB2不分频
    RCC->CR |= 0x01000000// PLLON
    while (!(RCC->CR & 0x02000000)); // 等待PLL就绪
    RCC->CFGR |= 0x00000002// 切换至PLL
    while ((RCC->CFGR & 0x00000008) != 0x00000008); // 等待切换完成
    
    // 配置SysTick用于延时
    SysTick_Config(SystemCoreClock / 1000); // 每毫秒中断一次
}

void GPIO_Init(void) {
    // 使能GPIOA、GPIOC时钟
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPCEN;
    
    // 配置PA0为模拟输入(ADC)
    GPIOA->CRL &= ~(0x0F << (0 * 4));
    GPIOA->CRL |= (0x00 << (0 * 4));
    
    // 配置PA1为推挽输出(DHT11)
    GPIOA->CRL &= ~(0x0F << (1 * 4));
    GPIOA->CRL |= (0x03 << (1 * 4));
    
    // 配置PA2为输入下拉(HC-SR501)
    GPIOA->CRL &= ~(0x0F << (2 * 4));
    GPIOA->CRL |= (0x08 << (2 * 4)); // 输入下拉
    
    // 配置PC13和PC14为推挽输出(LED和蜂鸣器)
    GPIOC->CRH &= ~(0x0F << ((13-8) * 4) | (0x0F << ((14-8) * 4));
    GPIOC->CRH |= (0x03 << ((13-8) * 4)) | (0x03 << ((14-8) * 4));
}

void ADC1_Init(void) {
    RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
    ADC1->CR2 = 0x00000000// 先关闭ADC
    ADC1->CR1 = 0x00000000;
    ADC1->CR2 |= 0x00000001// 开启ADC并启动校准
    while (ADC1->CR2 & 0x00000001); // 等待校准完成
    ADC1->CR2 |= 0x00000001// 再次开启ADC
    ADC1->SQR3 = 0x00000000// 通道0
    ADC1->SQR1 = 0x00000000// 1 conversion
    ADC1->CR2 |= 0x00000001// ADON
}

uint16_t ADC_Read(uint8_t channel) {
    ADC1->SQR3 = channel;
    ADC1->CR2 |= 0x00000001// 开始转换
    while (!(ADC1->SR & 0x00000002)); // 等待转换完成
    return ADC1->DR;
}

void USART1_Init(void) {
    RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
    // 配置PA9为推挽复用输出(TX),PA10为浮空输入(RX)
    GPIOA->CRH &= ~(0x0F << ((9-8) * 4) | (0x0F << ((10-8) * 4));
    GPIOA->CRH |= (0x0B << ((9-8) * 4)) | (0x04 << ((10-8) * 4));
    
    USART1->BRR = 0x00000506// 72MHz / 9600 baud
    USART1->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; // 使能USART,发送和接收
}

void ESP8266_SendCmd(char *cmd) {
    while (*cmd) {
        while (!(USART1->SR & USART_SR_TXE));
        USART1->DR = *cmd++;
    }
}

void ESP8266_SendData(char *data) {
    // 简化发送,实际需处理华为云协议
    ESP8266_SendCmd("AT+CIPSEND=0,100\r\n");
    Delay_ms(100);
    ESP8266_SendCmd(data);
    ESP8266_SendCmd("\r\n");
}

void TIM2_Init(void) {
    RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
    TIM2->PSC = 7200 - 1// 10kHz频率
    TIM2->ARR = 10000 - 1// 1秒中断
    TIM2->DIER |= TIM_DIER_UIE;
    NVIC_EnableIRQ(TIM2_IRQn);
    TIM2->CR1 |= TIM_CR1_CEN;
}

void DHT11_Start(void) {
    GPIOA->BSRR = (1 << DHT11_PIN) << 16// 设置PA1低
    Delay_ms(18);
    GPIOA->BSRR = (1 << DHT11_PIN); // 设置PA1高
    __nop(); __nop(); // 短暂延时
}

uint8_t DHT11_Read_Byte(void) {
    uint8_t data = 0;
    for (int i = 0; i < 8; i++) {
        while (!(GPIOA->IDR & (1 << DHT11_PIN))); // 等待高电平
        Delay_ms(0.05); // 50us延时
        if (GPIOA->IDR & (1 << DHT11_PIN)) {
            data |= (1 << (7 - i));
            while (GPIOA->IDR & (1 << DHT11_PIN)); // 等待低电平
        }
    }
    return data;
}

uint8_t DHT11_Read_Data(float *temperature, float *humidity) {
    uint8_t data[5];
    DHT11_Start();
    if (!(GPIOA->IDR & (1 << DHT11_PIN))) {
        while (!(GPIOA->IDR & (1 << DHT11_PIN))); // 等待响应
        while (GPIOA->IDR & (1 << DHT11_PIN)); // 等待响应结束
        for (int i = 0; i < 5; i++) {
            data[i] = DHT11_Read_Byte();
        }
        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;
            return 1;
        }
    }
    return 0;
}

void Alarm_On(void) {
    GPIOC->BSRR = (1 << LED_PIN); // LED on
    GPIOC->BSRR = (1 << BUZZER_PIN); // Buzzer on
}

void Alarm_Off(void) {
    GPIOC->BSRR = (1 << LED_PIN) << 16// LED off
    GPIOC->BSRR = (1 << BUZZER_PIN) << 16// Buzzer off
}

// 假设其他模块函数已实现
void OV2640_Init(void) {
    // 初始化OV2640摄像头
}

void OV2640_Capture(void) {
    // 抓拍图像并存储
}

总结

本系统基于STM32F103C8T6核心板设计,实现了对仓库环境的智能监控,包括实时监测烟雾浓度、温度和人体红外信号,确保及时发现火灾和入侵风险。通过集成多种传感器和模块,系统在异常情况下自动触发本地声光报警并抓拍图像,提升了仓库的安全性和响应速度。

硬件组成涵盖了MQ-2烟雾传感器、DHT11温度传感器、HC-SR501人体红外传感器、OV2640摄像头模块以及ESP8266-01S Wi-Fi模块,这些组件通过洞洞板焊接和杜邦线连接,构成了一个稳定可靠的监控网络。主控单元协调各传感器数据,并通过Wi-Fi模块将报警信息和图像数据高效上传至华为云平台。

此外,系统通过QT上位机实现了多仓库监控画面的集中显示、报警记录查询和实时数据可视化,为用户提供了友好的交互界面和远程管理能力。整体设计体现了物联网技术在仓储安全中的实际应用,具有较高的实用性和扩展性。

 

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

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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