• 方案介绍
  • 相关推荐
申请入驻 产业图谱

基于STM32设计的智能冷链物流监控系统

10/14 10:49
1369
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

项目开发背景

随着全球贸易和电子商务的快速发展,冷链物流在食品、药品等易腐物品的运输中扮演着关键角色。这些物品对温度、湿度等环境条件极为敏感,任何偏差都可能导致质量下降、腐败甚至安全风险,因此确保运输过程中的环境稳定性至关重要。传统冷链监控往往依赖人工记录或简单设备,无法实现实时、全面的数据采集和远程管理,这增加了运营成本并降低了可靠性。

在实际运输中,温度波动、湿度变化以及意外震动等异常情况频繁发生,但现有系统多数缺乏高效的实时监测和自动响应机制。这导致问题发现延迟、追溯困难,以及潜在的货物损失。此外,GPS定位和轨迹记录的缺失使得物流公司难以优化路线、提高效率,并应对突发事件。

为了应对这些挑战,本项目旨在开发一个基于STM32的智能冷链物流监控系统,通过集成多传感器和无线通信技术,实现全天候的环境数据采集、精确定位和云端数据传输。该系统不仅能够实时监测温度、湿度和震动,还能在异常时自动报警并记录日志,从而提升冷链运输的透明度和可控性。

通过结合STM32微控制器的低成本、高性能特点,以及华为云平台的数据处理能力,本项目为冷链物流行业提供了一个高效、可靠的解决方案,有助于降低损耗、提高客户满意度,并推动物流智能化的发展。

设计实现的功能

(1)实时监测环境温度数据(通过DS18B20传感器)
(2)实时监测湿度数据(通过DHT22传感器)
(3)实时监测震动数据(通过SW-420传感器)
(4)精确定位运输位置(通过ATGM336H GPS模块
(5)记录运输轨迹(STM32处理并存储GPS数据)
(6)检测异常情况(如温度、湿度、震动超出阈值)
(7)自动报警异常情况(通过STM32触发报警机制)
(8)记录事件日志(STM32存储异常事件信息)
(9)上传实时数据至华为云(通过ESP8266 Wi-Fi模块
(10)支持QT上位机显示温湿度曲线和轨迹地图(基于上传的云数据)

项目硬件模块组成

(1)STM32F103C8T6最小系统核心板作为主控制器
(2)DS18B20防水温度传感器监测环境温度
(3)DHT22温湿度传感器监测湿度数据
(4)SW-420震动传感器检测运输震动
(5)ATGM336H GPS模块实现精确定位
(6)ESP8266-01S Wi-Fi模块上传数据至华为云

设计意义

基于STM32设计的智能冷链物流监控系统通过集成温度、湿度和震动传感器,结合GPS定位和无线通信技术,实现了对冷链运输环境的全方位实时监控,有效提升了物流过程的透明度和可控性,这对于保障生鲜食品、药品等易腐货物的质量安全具有重要意义。

该系统能够实时采集并传输温度、湿度和震动数据,确保运输环境始终处于预设的安全范围内,一旦检测到异常如温度超标或剧烈震动,便会自动触发报警机制并记录事件日志,从而帮助运营人员及时干预,避免货物变质或损坏,减少经济损失。

GPS模块提供精确定位和轨迹记录功能,配合QT上位机软件可视化显示温湿度曲线和地图轨迹,使得用户能够直观跟踪运输全程,优化物流路线和管理决策,增强了对运输过程的洞察力和响应能力。

通过Wi-Fi模块将数据上传至华为云平台,系统支持数据的远程存储和访问,便于进行历史数据分析和趋势预测,这不仅提高了监控的效率和可靠性,还为冷链物流的智能化升级提供了坚实的技术基础,符合现代物联网应用的发展方向。

设计思路

设计思路基于STM32F103C8T6最小系统核心板作为主控制器,负责协调整个系统的运行。系统通过集成多种传感器实时采集冷链运输环境中的数据,包括使用DS18B20防水温度传感器监测环境温度,DHT22温湿度传感器监测湿度数据,以及SW-420震动传感器检测运输过程中的震动情况。同时,ATGM336H GPS模块用于精确定位运输位置并记录轨迹数据,确保全程监控。

数据采集后,STM32控制器进行实时处理和分析,检查温度、湿度和震动数据是否超出预设阈值。如果检测到异常情况,如温度过高或震动过大,系统会自动触发报警机制,并通过内部日志记录事件详情,包括时间、位置和异常类型,以便后续查询和分析。

为了远程监控数据存储,系统使用ESP8266-01S Wi-Fi模块将采集到的传感器数据、GPS定位信息以及事件日志上传至华为云平台。数据传输采用定期上传和事件触发上传相结合的方式,确保数据的实时性和可靠性,同时减少功耗。

在上位机端,基于QT开发的上位机软件接收华为云的数据,并实时显示运输全程的温湿度曲线和轨迹地图。用户可以通过界面直观查看历史数据和当前状态,支持数据导出和报警通知功能,从而实现对冷链物流过程的全面可视化监控。

框架图

智能冷链物流监控系统框架图:

数据流:传感器数据(温度、湿度、震动、GPS)采集到STM32,经处理通过ESP8266上传至华为云,QT上位机从云获取数据并显示曲线和地图。异常报警由STM32触发并通过云记录。

系统总体设计

系统总体设计基于STM32F103C8T6最小系统核心板作为主控制器,负责协调整个系统的运行。该系统旨在实时监测冷链运输过程中的环境参数,包括温度、湿度和震动数据,并通过集成多种传感器实现数据采集。DS18B20防水温度传感器用于精确测量环境温度,DHT22温湿度传感器同时监测湿度和温度数据,SW-420震动传感器检测运输过程中的震动情况,确保货物在运输中的稳定性。

GPS定位部分采用ATGM336H模块,实现精确定位并记录运输轨迹,数据通过串口通信传输至STM32主控制器进行处理和存储。异常情况如温度超标、湿度异常或震动过大时,系统会自动触发报警机制,并通过事件日志记录相关数据,便于后续分析和追溯。

数据传输通过ESP8266-01S Wi-Fi模块实现,将采集到的传感器数据、GPS位置信息以及事件日志上传至华为云平台,实现远程监控和数据备份。云平台负责数据存储和管理,为上位机提供数据源。

上位机部分采用QT开发,显示运输全程的温湿度曲线和轨迹地图,用户可以通过图形界面实时查看历史数据和当前状态,辅助决策和监控。整个系统设计注重实际应用,确保数据准确性和可靠性,满足冷链物流的监控需求。

系统功能总结

功能 描述 实现硬件/方式
温度监测 实时监测环境温度 DS18B20防水温度传感器
湿度监测 实时监测环境湿度 DHT22温湿度传感器
震动监测 检测运输过程中的震动 SW-420震动传感器
定位与轨迹记录 GPS精确定位运输位置并记录轨迹 ATGM336H GPS模块
数据上传 将传感器数据上传至云平台 ESP8266-01S Wi-Fi模块上传至华为云
异常报警与日志记录 当温度、湿度或震动异常时自动报警,并记录事件日志 STM32主控制器处理数据,判断异常,并通过Wi-Fi发送报警或本地记录
上位机显示 QT软件显示运输全程的温湿度曲线和轨迹地图 数据通过Wi-Fi上传至云,QT软件从云获取数据并可视化

设计的各个功能模块描述

STM32F103C8T6最小系统核心板作为主控制器,负责协调整个系统的运行,初始化各个传感器和模块,实时采集和处理温度、湿度、震动以及GPS数据,并根据预设阈值判断异常情况,触发报警机制,同时控制数据上传和日志记录。

DS18B20防水温度传感器用于监测环境温度,通过单总线协议与STM32通信,提供高精度的温度读数,其防水设计使其适合冷链运输的潮湿环境,确保温度数据的可靠性和实时性。

DHT22温湿度传感器用于监测湿度数据,同时提供辅助温度信息,通过数字信号输出,STM32定期读取其数据以补充湿度监测,并与DS18B20数据交叉验证,增强系统可靠性。

SW-420震动传感器检测运输过程中的震动事件,当震动强度超过设定阈值时,会向STM32发送信号,触发异常报警,STM32记录震动事件并纳入事件日志中。

ATGM336H GPS模块实现精确定位功能,STM32通过串口接收GPS数据,解析出经纬度信息,实时记录运输位置并生成轨迹数据,用于上传和后续地图显示。

ESP8266-01S Wi-Fi模块负责无线通信,将STM32处理后的传感器数据、GPS轨迹和事件日志通过MQTT或HTTP协议上传至华为云平台,实现数据的远程存储和实时监控。

QT上位机软件运行于PC端,从华为云平台获取数据,图形化显示运输全程的温湿度变化曲线和轨迹地图,用户可通过界面查看历史数据和报警事件,完成监控需求。

上位机代码设计

以下是基于QT C++开发的智能冷链物流监控系统上位机代码设计。代码包括主窗口类、MQTT客户端处理、图表显示和地图集成。假设数据通过MQTT从华为云接收,数据格式为JSON,包含温度、湿度、震动、经纬度等信息。

项目文件结构:

  • main.cpp应用程序入口。
  • mainwindow.h:主窗口头文件。
  • mainwindow.cpp:主窗口实现文件。
  • map.html:用于显示地图的HTML文件(作为资源嵌入)。
  • ColdChainMonitor.pro:QT项目文件。

代码实现:

1. ColdChainMonitor.pro(QT项目文件):
QT       += core gui mqtt charts webenginewidgets

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = ColdChainMonitor
TEMPLATE = app

SOURCES += main.cpp 
           mainwindow.cpp

HEADERS += mainwindow.h

RESOURCES += resources.qrc
2. resources.qrc(资源文件,包含map.html):
<RCC>
    <qresource prefix="/">
        <file>map.html</file>
    </qresource>
</RCC>
3. main.cpp
#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}
4. mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QtMqtt/QtMqtt>
#include <QChartView>
#include <QLineSeries>
#include <QWebEngineView>
#include <QTextEdit>

QT_CHARTS_USE_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private slots:
    void onConnected();
    void onMessageReceived(const QByteArray &message, const QMqttTopicName &topic);
    void updateChart(double temperature, double humidity);
    void updateMap(double lat, double lon);
    void addLog(const QString &log);

private:
    QMqttClient *m_client;
    QChart *m_chart;
    QLineSeries *m_tempSeries;
    QLineSeries *m_humSeries;
    QChartView *m_chartView;
    QWebEngineView *m_mapView;
    QTextEdit *m_logEdit;
    void setupUI();
    void setupMQTT();
};

#endif // MAINWINDOW_H
5. mainwindow.cpp
#include "mainwindow.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QWebChannel>
#include <QMessageBox>
#include <QDateTime>
#include <QJsonDocument>
#include <QJsonObject>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    setupUI();
    setupMQTT();
}

MainWindow::~MainWindow()
{
    if (m_client)
        m_client->disconnect();
}

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

    QVBoxLayout *leftLayout = new QVBoxLayout();
    m_chart = new QChart();
    m_tempSeries = new QLineSeries();
    m_tempSeries->setName("Temperature (°C)");
    m_humSeries = new QLineSeries();
    m_humSeries->setName("Humidity (%)");
    m_chart->addSeries(m_tempSeries);
    m_chart->addSeries(m_humSeries);
    m_chart->createDefaultAxes();
    m_chart->setTitle("Temperature and Humidity Monitoring");
    m_chartView = new QChartView(m_chart);
    leftLayout->addWidget(m_chartView);

    m_logEdit = new QTextEdit();
    m_logEdit->setReadOnly(true);
    leftLayout->addWidget(m_logEdit);

    m_mapView = new QWebEngineView();
    m_mapView->load(QUrl("qrc:/map.html"));

    mainLayout->addLayout(leftLayout, 2);
    mainLayout->addWidget(m_mapView, 3);

    setCentralWidget(centralWidget);
    setWindowTitle("Smart Cold Chain Monitoring System");
    resize(1200, 600);
}

void MainWindow::setupMQTT()
{
    m_client = new QMqttClient(this);
    m_client->setHostname("your_mqtt_broker_address"); // Replace with actual Huawei Cloud MQTT broker
    m_client->setPort(1883); // Default MQTT port
    // If authentication is required, set username and password:
    // m_client->setUsername("username");
    // m_client->setPassword("password");

    connect(m_client, &QMqttClient::connected, this, &MainWindow::onConnected);
    connect(m_client, &QMqttClient::messageReceived, this, [this](const QByteArray &message, const QMqttTopicName &topic) {
        onMessageReceived(message, topic);
    });

    m_client->connectToHost();
}

void MainWindow::onConnected()
{
    addLog("Connected to MQTT broker");
    m_client->subscribe("coldchain/data"); // Subscribe to the topic where data is published
}

void MainWindow::onMessageReceived(const QByteArray &message, const QMqttTopicName &topic)
{
    QString msg = QString::fromUtf8(message);
    addLog("Received: " + msg);

    QJsonDocument doc = QJsonDocument::fromJson(message);
    if (doc.isNull()) {
        addLog("Error: Invalid JSON data");
        return;
    }

    QJsonObject obj = doc.object();
    double temperature = obj["temperature"].toDouble();
    double humidity = obj["humidity"].toDouble();
    double vibration = obj["vibration"].toDouble();
    double lat = obj["latitude"].toDouble();
    double lon = obj["longitude"].toDouble();

    updateChart(temperature, humidity);
    updateMap(lat, lon);

    if (vibration > 100) { // Example vibration threshold
        addLog("Alert: High vibration detected - " + QString::number(vibration));
        QMessageBox::warning(this, "Vibration Alert", "High vibration detected during transport!");
    }
    if (temperature < 2 || temperature > 8) { // Temperature range for cold chain
        addLog("Alert: Temperature out of range - " + QString::number(temperature));
        QMessageBox::warning(this, "Temperature Alert", "Temperature is outside safe range (2-8°C)!");
    }
}

void MainWindow::updateChart(double temperature, double humidity)
{
    static int xValue = 0;
    m_tempSeries->append(xValue, temperature);
    m_humSeries->append(xValue, humidity);
    xValue++;
    m_chart->axes(Qt::Horizontal).first()->setRange(0, xValue);
    m_chart->axes(Qt::Vertical).first()->setRange(0, 100); // Adjust based on expected range
}

void MainWindow::updateMap(double lat, double lon)
{
    m_mapView->page()->runJavaScript(QString("updateMarker(%1, %2);").arg(lat).arg(lon));
}

void MainWindow::addLog(const QString &log)
{
    m_logEdit->append(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss") + " - " + log);
}
6. map.html(保存为资源文件):
<!DOCTYPE html>
<html>
<head>
    <title>Transport Map</title>
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
    <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
    <style>
        #map { height: 100%; width: 100%; }
    </style>
</head>
<body>
    <div id="map"></div>
    <script>
        var map = L.map('map').setView([39.9042, 116.4074], 10); // Initial view
        L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
            attribution: '? OpenStreetMap contributors'
        }).addTo(map);
        var marker = L.marker([39.9042, 116.4074]).addTo(map);

        function updateMarker(lat, lon) {
            map.setView([lat, lon], 13);
            marker.setLatLng([lat, lon]);
        }
    </script>
</body>
</html>

使用说明:

  1. 将上述代码保存到相应文件中。
  2. 在QT Creator中创建新项目,并替换默认文件内容。
  3. 添加resources.qrc文件,包含map.html
  4. 更新MQTT broker信息(主机名、端口、认证等)以匹配华为云配置。
  5. 编译并运行项目。

此代码提供了一个基本的上位机界面,实时显示温湿度曲线、GPS轨迹地图和事件日志。异常情况会弹出警报并记录日志。确保华为云MQTT服务已正确设置,设备数据发布到"coldchain/data"主题。

模块代码设计

#include "stm32f10x.h"

// 寄存器地址定义
#define RCC_BASE 0x40021000
#define GPIOA_BASE 0x40010800
#define GPIOB_BASE 0x40010C00
#define UART1_BASE 0x40013800
#define UART2_BASE 0x40004400
#define UART3_BASE 0x40004800

#define RCC_CR (*(volatile unsigned int *)(RCC_BASE))
#define RCC_CFGR (*(volatile unsigned int *)(RCC_BASE + 0x04))
#define RCC_APB2ENR (*(volatile unsigned int *)(RCC_BASE + 0x18))
#define RCC_APB1ENR (*(volatile unsigned int *)(RCC_BASE + 0x1C))

#define GPIOA_CRL (*(volatile unsigned int *)(GPIOA_BASE + 0x00))
#define GPIOA_CRH (*(volatile unsigned int *)(GPIOA_BASE + 0x04))
#define GPIOA_IDR (*(volatile unsigned int *)(GPIOA_BASE + 0x08))
#define GPIOA_ODR (*(volatile unsigned int *)(GPIOA_BASE + 0x0C))

#define GPIOB_CRL (*(volatile unsigned int *)(GPIOB_BASE + 0x00))
#define GPIOB_CRH (*(volatile unsigned int *)(GPIOB_BASE + 0x04))
#define GPIOB_IDR (*(volatile unsigned int *)(GPIOB_BASE + 0x08))
#define GPIOB_ODR (*(volatile unsigned int *)(GPIOB_BASE + 0x0C))

#define UART1_SR (*(volatile unsigned int *)(UART1_BASE))
#define UART1_DR (*(volatile unsigned int *)(UART1_BASE + 0x04))
#define UART1_BRR (*(volatile unsigned int *)(UART1_BASE + 0x08))
#define UART1_CR1 (*(volatile unsigned int *)(UART1_BASE + 0x0C))

#define UART2_SR (*(volatile unsigned int *)(UART2_BASE))
#define UART2_DR (*(volatile unsigned int *)(UART2_BASE + 0x04))
#define UART2_BRR (*(volatile unsigned int *)(UART2_BASE + 0x08))
#define UART2_CR1 (*(volatile unsigned int *)(UART2_BASE + 0x0C))

#define UART3_SR (*(volatile unsigned int *)(UART3_BASE))
#define UART3_DR (*(volatile unsigned int *)(UART3_BASE + 0x04))
#define UART3_BRR (*(volatile unsigned int *)(UART3_BASE + 0x08))
#define UART3_CR1 (*(volatile unsigned int *)(UART3_BASE + 0x0C))

// 引脚定义
#define DS18B20_PIN 0  // PA0
#define DHT22_PIN 1    // PA1
#define SW420_PIN 2    // PA2

// 函数声明
void SystemClock_Config(void);
void GPIO_Init(void);
void UART1_Init(void);
void UART2_Init(void);
void UART3_Init(void);
void DS18B20_Init(void);
float DS18B20_ReadTemp(void);
void DHT22_Init(void);
void DHT22_Read(float *temp, float *hum);
uint8_t SW420_Read(void);
void GPS_Init(void);
void GPS_Read(void);
void WiFi_Init(void);
void WiFi_SendData(float temp, float hum, uint8_t shock, const char *gps_data);
void Delay_us(uint32_t us);
void Delay_ms(uint32_t ms);

// 全局变量
char gps_buffer[100];
volatile uint8_t uart2_rx_index = 0;
volatile uint8_t uart2_rx_buffer[100];

int main(void) {
    SystemClock_Config();
    GPIO_Init();
    UART1_Init(); // For debug
    UART2_Init(); // For GPS
    UART3_Init(); // For WiFi
    DS18B20_Init();
    DHT22_Init();
    // SW420 is input, already initialized in GPIO_Init

    while (1) {
        float temperature = DS18B20_ReadTemp();
        float humidity, temp_dht;
        DHT22_Read(&temp_dht, &humidity); // DHT22 also provides temperature, but we use DS18B20 for temp
        uint8_t shock = SW420_Read();
        GPS_Read(); // Read GPS data into gps_buffer

        // Check for anomalies
        if (temperature > 8.0 || temperature < 2.0 || humidity > 80.0 || shock == 1) {
            // Trigger alarm, e.g., set a GPIO or send via WiFi
            // Here, we'll send via WiFi
        }

        WiFi_SendData(temperature, humidity, shock, gps_buffer);

        Delay_ms(5000); // Send data every 5 seconds
    }
}

void SystemClock_Config(void) {
    // Enable HSE
    RCC_CR |= (1 << 16); // HSEON
    while (!(RCC_CR & (1 << 17))); // Wait for HSERDY

    // Configure PLL: HSE as source, multiply by 9 -> 72MHz
    RCC_CFGR |= (1 << 16); // PLLSRC = HSE
    RCC_CFGR |= (7 << 18); // PLLMUL = 9 (0111)
    RCC_CR |= (1 << 24); // PLLON
    while (!(RCC_CR & (1 << 25))); // Wait for PLLRDY

    // Configure FLASH latency
    FLASH_ACR = 0x12; // Two wait states for 48 < HCLK <= 72MHz

    // Switch to PLL
    RCC_CFGR |= (2 << 0); // SW = PLL
    while ((RCC_CFGR & (3 << 2)) != (2 << 2)); // Wait for SWS to be PLL
}

void GPIO_Init(void) {
    // Enable GPIOA and GPIOB clocks
    RCC_APB2ENR |= (1 << 2) | (1 << 3); // GPIOA and GPIOB enable

    // DS18B20 on PA0: output open-drain
    GPIOA_CRL &= ~(0xF << (0 * 4));
    GPIOA_CRL |= (0x4 << (0 * 4)); // Output open-drain, 50MHz

    // DHT22 on PA1: output open-drain
    GPIOA_CRL &= ~(0xF << (1 * 4));
    GPIOA_CRL |= (0x4 << (1 * 4)); // Output open-drain, 50MHz

    // SW420 on PA2: input pull-up
    GPIOA_CRL &= ~(0xF << (2 * 4));
    GPIOA_CRL |= (0x8 << (2 * 4)); // Input with pull-up/pull-down
    GPIOA_ODR |= (1 << 2); // Set pull-up

    // UART2 on PA2 (TX) and PA3 (RX)
    GPIOA_CRL &= ~(0xFF << (2 * 4)); // Clear bits for PA2 and PA3
    GPIOA_CRL |= (0x4 << (2 * 4)); // PA2: Alternate function push-pull, 50MHz
    GPIOA_CRL |= (0x4 << (3 * 4)); // PA3: Input floating (RX is input)

    // UART3 on PB10 (TX) and PB11 (RX)
    GPIOB_CRH &= ~(0xFF << (2 * 4)); // Clear bits for PB10 and PB11 (bits 8-15 for CRH)
    GPIOB_CRH |= (0x4 << (10 - 8) * 4); // PB10: Alternate function push-pull, 50MHz
    GPIOB_CRH |= (0x4 << (11 - 8) * 4); // PB11: Input floating
}

void UART1_Init(void) {
    // Enable UART1 clock
    RCC_APB2ENR |= (1 << 14); // USART1 enable

    // Configure UART1: 9600 baud, 8 data bits, no parity, 1 stop bit
    UART1_BRR = 0x1D4C; // 72MHz / 9600 = 7500 -> 0x1D4C
    UART1_CR1 |= (1 << 13) | (1 << 3) | (1 << 2); // UE, TE, RE
}

void UART2_Init(void) {
    // Enable UART2 clock
    RCC_APB1ENR |= (1 << 17); // USART2 enable

    // Configure UART2: 9600 baud for GPS
    UART2_BRR = 0x1D4C; // 72MHz / 9600 = 7500 -> 0x1D4C
    UART2_CR1 |= (1 << 13) | (1 << 3) | (1 << 2); // UE, TE, RE

    // Enable RX interrupt for GPS
    UART2_CR1 |= (1 << 5); // RXNEIE
    NVIC_EnableIRQ(USART2_IRQn);
}

void UART3_Init(void) {
    // Enable UART3 clock
    RCC_APB1ENR |= (1 << 18); // USART3 enable

    // Configure UART3: 115200 baud for WiFi
    UART3_BRR = 0x0271; // 72MHz / 115200 ≈ 625 -> 0x0271
    UART3_CR1 |= (1 << 13) | (1 << 3) | (1 << 2); // UE, TE, RE
}

void DS18B20_Init(void) {
    // Initialization is done in read function
}

float DS18B20_ReadTemp(void) {
    uint8_t temp_l, temp_h;
    int16_t temp;

    // Reset pulse
    GPIOA_ODR &= ~(1 << DS18B20_PIN); // Pull low
    Delay_us(480);
    GPIOA_ODR |= (1 << DS18B20_PIN); // Release
    Delay_us(60);
    if (!(GPIOA_IDR & (1 << DS18B20_PIN))) {
        // Presence pulse detected
        Delay_us(240);
    }

    // Skip ROM command
    DS18B20_WriteByte(0xCC);
    // Convert T command
    DS18B20_WriteByte(0x44);
    Delay_ms(750); // Wait for conversion

    // Reset again
    GPIOA_ODR &= ~(1 << DS18B20_PIN);
    Delay_us(480);
    GPIOA_ODR |= (1 << DS18B20_PIN);
    Delay_us(60);
    if (!(GPIOA_IDR & (1 << DS18B20_PIN))) {
        Delay_us(240);
    }

    // Skip ROM
    DS18B20_WriteByte(0xCC);
    // Read scratchpad
    DS18B20_WriteByte(0xBE);

    temp_l = DS18B20_ReadByte();
    temp_h = DS18B20_ReadByte();

    temp = (temp_h << 8) | temp_l;
    return temp / 16.0;
}

void DS18B20_WriteByte(uint8_t data) {
    for (int i = 0; i < 8; i++) {
        GPIOA_ODR &= ~(1 << DS18B20_PIN); // Pull low
        if (data & 0x01) {
            Delay_us(5);
            GPIOA_ODR |= (1 << DS18B20_PIN); // Release for logic 1
        } else {
            Delay_us(60);
            GPIOA_ODR |= (1 << DS18B20_PIN);
        }
        data >>= 1;
        Delay_us(60);
    }
}

uint8_t DS18B20_ReadByte(void) {
    uint8 data = 0;
    for (int i = 0; i < 8; i++) {
        GPIOA_ODR &= ~(1 << DS18B20_PIN);
        Delay_us(1);
        GPIOA_ODR |= (1 << DS18B20_PIN);
        Delay_us(14);
        if (GPIOA_IDR & (1 << DS18B20_PIN)) {
            data |= (1 << i);
        }
        Delay_us(45);
    }
    return data;
}

void DHT22_Init(void) {
    // Initialization is done in read function
}

void DHT22_Read(float *temp, float *hum) {
    uint8_t data[5] = {0};
    uint8_t checksum;

    // Send start signal
    GPIOA_ODR &= ~(1 << DHT22_PIN);
    Delay_ms(1);
    GPIOA_ODR |= (1 << DHT22_PIN);
    Delay_us(30);

    // Wait for response
    while (GPIOA_IDR & (1 << DHT22_PIN));
    while (!(GPIOA_IDR & (1 << DHT22_PIN)));
    while (GPIOA_IDR & (1 << DHT22_PIN));

    // Read data
    for (int i = 0; i < 40; i++) {
        while (!(GPIOA_IDR & (1 << DHT22_PIN))); // Wait for low to go high
        Delay_us(40);
        if (GPIOA_IDR & (1 << DHT22_PIN)) {
            data[i/8] |= (1 << (7 - (i % 8)));
            while (GPIOA_IDR & (1 << DHT22_PIN));
        }
    }

    checksum = data[0] + data[1] + data[2] + data[3];
    if (checksum == data[4]) {
        *hum = ((data[0] << 8) | data[1]) / 10.0;
        *temp = (((data[2] & 0x7F) << 8) | data[3]) / 10.0;
        if (data[2] & 0x80) *temp = -(*temp);
    } else {
        *hum = 0.0;
        *temp = 0.0;
    }
}

uint8_t SW420_Read(void) {
    return (GPIOA_IDR & (1 << SW420_PIN)) ? 0 : 1; // Active low? Check sensor datasheet. Assuming shock when low.
}

void GPS_Init(void) {
    // UART2 already initialized
}

void GPS_Read(void) {
    // Data is received via interrupt, so we handle it in ISR
    // For simplicity, we assume gps_buffer is filled in ISR
}

void USART2_IRQHandler(void) {
    if (UART2_SR & (1 << 5)) { // RXNE
        uint8_t data = UART2_DR;
        if (data == 'n') {
            gps_buffer[uart2_rx_index] = '?';
            uart2_rx_index = 0;
        } else {
            gps_buffer[uart2_rx_index++] = data;
            if (uart2_rx_index >= sizeof(gps_buffer) - 1) uart2_rx_index = 0;
        }
    }
}

void WiFi_Init(void) {
    // Send AT commands to initialize ESP8266
    // This is done once at startup
    const char *init_cmds[] = {
        "ATrn",
        "AT+CWMODE=1rn",
        "AT+CWJAP="SSID","PASSWORD"rn", // Replace with your WiFi credentials
        "AT+CIPSTART="TCP","华为云地址",端口号rn", // Replace with Huawei cloud details
        NULL
    };
    for (int i = 0; init_cmds[i] != NULL; i++) {
        WiFi_SendCommand(init_cmds[i]);
        Delay_ms(1000);
    }
}

void WiFi_SendCommand(const char *cmd) {
    while (*cmd) {
        UART3_DR = *cmd++;
        while (!(UART3_SR & (1 << 7))); // Wait for TXE
    }
}

void WiFi_SendData(float temp, float hum, uint8_t shock, const char *gps_data) {
    char buffer[100];
    sprintf(buffer, "AT+CIPSEND=%drn", strlen(buffer)); // First send length
    WiFi_SendCommand(buffer);
    Delay_ms(100);
    sprintf(buffer, "temp=%.2f,hum=%.2f,shock=%d,gps=%srn", temp, hum, shock, gps_data);
    WiFi_SendCommand(buffer);
}

void Delay_us(uint32_t us) {
    us *= 72; // For 72MHz, approximate
    while (us--) {
        __NOP();
    }
}

void Delay_ms(uint32_t ms) {
    while (ms--) {
        Delay_us(1000);
    }
}

项目核心代码

#include "stm32f10x.h"

// 假设其他模块的头文件已存在
#include "ds18b20.h"
#include "dht22.h"
#include "gps.h"
#include "wifi.h"

// 定义传感器引脚
#define DS18B20_PIN    GPIO_Pin_0
#define DS18B20_PORT   GPIOA
#define DHT22_PIN      GPIO_Pin_1
#define DHT22_PORT     GPIOA
#define SW420_PIN      GPIO_Pin_2
#define SW420_PORT     GPIOA

// 异常阈值
#define TEMP_LOW_LIMIT    -20.0
#define TEMP_HIGH_LIMIT   10.0
#define HUMIDITY_LIMIT    80.0
#define SHAKE_THRESHOLD   1  // 假设震动次数阈值

// 全局变量
float temperature = 0.0;
float humidity = 0.0;
uint8_t shake_count = 0;
char gps_data[100] = {0};
uint8_t alert_flag = 0;

// 函数声明
void System_Init(void);
void GPIO_Init(void);
void USART_Init(void);
void Timer_Init(void);
void Read_Sensors(void);
void Process_GPS(void);
void Check_Alerts(void);
void Upload_Data(void);
void Delay_ms(uint32_t ms);

int main(void) {
    System_Init();
    GPIO_Init();
    USART_Init();
    Timer_Init();
    
    // 初始化模块
    DS18B20_Init();
    DHT22_Init();
    GPS_Init();
    WIFI_Init();
    
    while (1) {
        Read_Sensors();
        Process_GPS();
        Check_Alerts();
        Upload_Data();
        Delay_ms(5000); // 每5秒执行一次
    }
}

void System_Init(void) {
    // 设置系统时钟为72MHz
    RCC->CFGR |= RCC_CFGR_PLLMULL9; // PLL multiplier 9
    RCC->CFGR |= RCC_CFGR_PLLSRC;   // PLL source HSE
    RCC->CR |= RCC_CR_HSEON;        // Enable HSE
    while (!(RCC->CR & RCC_CR_HSERDY)); // Wait for HSE ready
    RCC->CR |= RCC_CR_PLLON;        // Enable PLL
    while (!(RCC->CR & RCC_CR_PLLRDY)); // Wait for PLL ready
    RCC->CFGR |= RCC_CFGR_SW_PLL;   // Switch to PLL
    while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); // Wait for switch
}

void GPIO_Init(void) {
    // 启用GPIOA时钟
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
    
    // 配置DS18B20引脚为推挽输出(初始)
    DS18B20_PORT->CRL &= ~(0x0F << (0 * 4)); // PA0
    DS18B20_PORT->CRL |= (0x03 << (0 * 4));  // Output, 50MHz
    
    // 配置DHT22引脚为推挽输出(初始)
    DHT22_PORT->CRL &= ~(0x0F << (1 * 4)); // PA1
    DHT22_PORT->CRL |= (0x03 << (1 * 4));  // Output, 50MHz
    
    // 配置SW-420引脚为输入上拉
    SW420_PORT->CRL &= ~(0x0F << (2 * 4)); // PA2
    SW420_PORT->CRL |= (0x08 << (2 * 4));  // Input, pull-up
    SW420_PORT->ODR |= SW420_PIN;          // Set pull-up
}

void USART_Init(void) {
    // 启用USART1时钟(用于Wi-Fi)
    RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
    // 启用USART2时钟(用于GPS)
    RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
    
    // 配置USART1 (PA9 TX, PA10 RX)
    GPIOA->CRH &= ~(0xFF << 4); // Clear PA9 and PA10
    GPIOA->CRH |= (0x0B << 4);  // PA9: Output, 50MHz, alt func
    GPIOA->CRH |= (0x04 << 8);  // PA10: Input, pull-up
    USART1->BRR = 72000000 / 115200; // Baud rate 115200
    USART1->CR1 |= USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; // Enable USART, TX, RX
    
    // 配置USART2 (PA2 TX, PA3 RX) for GPS
    GPIOA->CRL &= ~(0xFF << 8); // Clear PA2 and PA3
    GPIOA->CRL |= (0x0B << 8);  // PA2: Output, 50MHz, alt func
    GPIOA->CRL |= (0x04 << 12); // PA3: Input, pull-up
    USART2->BRR = 72000000 / 9600; // Baud rate 9600 for GPS
    USART2->CR1 |= USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; // Enable USART, TX, RX
}

void Timer_Init(void) {
    // 启用TIM2时钟
    RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
    TIM2->PSC = 7200 - 1; // 预分频,10kHz
    TIM2->ARR = 10000 - 1; // 自动重载值,1秒
    TIM2->CR1 |= TIM_CR1_ARPE; // 自动重载预装载
    TIM2->DIER |= TIM_DIER_UIE; // 更新中断使能
    TIM2->CR1 |= TIM_CR1_CEN; // 启动定时器
    NVIC_EnableIRQ(TIM2_IRQn); // 使能TIM2中断
}

void TIM2_IRQHandler(void) {
    if (TIM2->SR & TIM_SR_UIF) {
        TIM2->SR &= ~TIM_SR_UIF; // 清除中断标志
        // 定时任务,例如检查震动
        if (SW420_PORT->IDR & SW420_PIN) {
            shake_count++; // 震动计数
        }
    }
}

void Read_Sensors(void) {
    temperature = DS18B20_ReadTemp(); // 假设函数返回float
    humidity = DHT22_ReadHumidity();  // 假设函数返回float
    // 震动数据通过中断计数,这里重置或处理
}

void Process_GPS(void) {
    // 假设GPS_ReadData函数从USART2读取并解析NMEA,存储到gps_data
    GPS_ReadData(gps_data);
}

void Check_Alerts(void) {
    alert_flag = 0;
    if (temperature < TEMP_LOW_LIMIT || temperature > TEMP_HIGH_LIMIT) {
        alert_flag |= 0x01; // 温度异常
    }
    if (humidity > HUMIDITY_LIMIT) {
        alert_flag |= 0x02; // 湿度异常
    }
    if (shake_count > SHAKE_THRESHOLD) {
        alert_flag |= 0x04; // 震动异常
        shake_count = 0; // 重置计数
    }
    if (alert_flag) {
        // 记录事件日志,假设有函数实现
        Log_Event(alert_flag, temperature, humidity, shake_count, gps_data);
    }
}

void Upload_Data(void) {
    char data_str[200];
    sprintf(data_str, "Temp:%.2f,Hum:%.2f,Shake:%d,GPS:%s,Alert:%d", 
            temperature, humidity, shake_count, gps_data, alert_flag);
    WIFI_SendData(data_str); // 假设函数通过USART1发送到ESP8266
}

void Delay_ms(uint32_t ms) {
    for (uint32_t i = 0; i < ms * 1000; i++) {
        __NOP();
    }
}

总结

本系统基于STM32F103C8T6微控制器设计,实现了智能冷链物流监控的核心功能,能够实时监测运输过程中的温度、湿度和震动数据,确保货物在运输途中处于适宜的环境条件。通过集成多种传感器和模块,系统有效提升了冷链物流的监控精度和响应速度。

硬件组成包括STM32F103C8T6最小系统核心板作为主控制器,DS18B20防水温度传感器用于环境温度监测,DHT22温湿度传感器提供湿度数据,SW-420震动传感器检测运输震动,ATGM336H GPS模块实现精确定位和轨迹记录,以及ESP8266-01S Wi-Fi模块将采集的数据上传至华为云平台,实现远程监控和数据存储。

此外,系统通过QT上位机显示运输全程的温湿度曲线和轨迹地图,支持异常情况自动报警并记录事件日志,增强了冷链物流的可靠性和管理效率。整体设计紧凑、成本效益高,适用于实际物流应用,为冷链运输提供了全面的智能化解决方案。

意法半导体

意法半导体

意法半导体(ST)集团于1987年6月成立,是由意大利的SGS微电子公司和法国Thomson半导体公司合并而成。1998年5月,SGS-THOMSON Microelectronics将公司名称改为意法半导体有限公司。意法半导体是世界最大的半导体公司之一,公司销售收入在半导体工业五大高速增长市场之间分布均衡(五大市场占2007年销售收入的百分比):通信(35%),消费(17%),计算机(16%),汽车(16%),工业(16%)。 据最新的工业统计数据,意法半导体是全球第五大半导体厂商,在很多市场居世界领先水平。例如,意法半导体是世界第一大专用模拟芯片和电源转换芯片制造商,世界第一大工业半导体和机顶盒芯片供应商,而且在分立器件、手机相机模块和车用集成电路领域居世界前列.

意法半导体(ST)集团于1987年6月成立,是由意大利的SGS微电子公司和法国Thomson半导体公司合并而成。1998年5月,SGS-THOMSON Microelectronics将公司名称改为意法半导体有限公司。意法半导体是世界最大的半导体公司之一,公司销售收入在半导体工业五大高速增长市场之间分布均衡(五大市场占2007年销售收入的百分比):通信(35%),消费(17%),计算机(16%),汽车(16%),工业(16%)。 据最新的工业统计数据,意法半导体是全球第五大半导体厂商,在很多市场居世界领先水平。例如,意法半导体是世界第一大专用模拟芯片和电源转换芯片制造商,世界第一大工业半导体和机顶盒芯片供应商,而且在分立器件、手机相机模块和车用集成电路领域居世界前列.收起

查看更多

相关推荐