项目开发背景
随着现代医疗技术的快速发展,药品的安全存储与管理成为医疗质量控制中的重要环节。许多药品对存储环境的温湿度具有严格的要求,一旦超出允许范围,药品的理化性质可能发生改变,甚至失效,直接影响患者的用药安全与治疗效果。然而,传统的药品存储柜大多依赖人工巡检和记录,存在效率低、误差大、实时性差等问题,难以满足日益严格的药品管理规范。
在此背景下,物联网技术的兴起为药品存储管理提供了新的解决方案。通过传感器实时采集环境数据,结合无线通信技术将信息上传至云平台,能够实现对药品存储状态的远程监控与智能预警。这不仅大大提高了药品管理的自动化水平和数据准确性,也为医疗机构降低了人为管理成本与风险。
本项目基于STM32F103C8T6主控制器,融合温湿度传感、门状态检测、时钟记录与Wi-Fi通信等技术,构建了一套智能药品存储柜系统,并通过华为云实现数据的集中管理与分析。该系统具备环境监测、自动调控、数据追溯和远程管理等功能,旨在为医疗机构提供一种可靠、高效的药品存储管理手段,提升药品管理的智能化水平和医疗安全质量。
设计实现的功能
(1)实时监测药品柜内温湿度及门开关状态。
(2)温湿度超标时自动启动温控设备并报警。
(3)药品存取记录及环境数据上传至华为云平台。
(4)QT上位机实现药品管理、环境数据查询及报警信息处理。
项目开发背景
随着医疗行业的快速发展,药品的安全存储成为保障患者用药安全和治疗效果的关键环节。许多药品对存储环境有严格的温湿度要求,例如疫苗、胰岛素等生物制剂需要在特定温度范围内保存,否则容易失效甚至产生有害物质。然而,传统的药品存储柜往往依赖人工定期检查,这种方式效率低下且容易因疏忽导致环境参数超标,无法实现实时监控和及时干预,从而增加了药品变质和医疗风险的可能性。
此外,医疗机构的药品管理还面临着记录不完整、追溯困难等问题。手动记录药品存取信息不仅耗时耗力,还容易出现错误或遗漏,这在紧急情况下可能影响药品的快速调配和使用。随着物联网和云计算技术的兴起,智能化的药品存储解决方案逐渐成为趋势,它能够通过自动化监测、数据远程传输和智能报警,显著提升药品管理的效率和可靠性。
本项目旨在设计并实现一个基于STM32F103C8T6微控制器的智能医疗药品存储柜系统,结合华为云平台,实现对柜内温湿度、门开关状态的实时监测,并在环境参数超标时自动启动温控设备并发出报警。通过集成Wi-Fi模块,系统将药品存取记录和环境数据上传至云平台,便于远程监控和数据分析;同时,QT上位机软件提供友好的用户界面,支持药品管理、历史查询和报警处理,从而为医疗机构提供一套高效、安全且可扩展的智能存储解决方案。
项目硬件模块组成
(1)STM32F103C8T6最小系统核心板作为主控制器。
(2)DHT22温湿度传感器监测柜内环境。
(3)干簧管门磁传感器检测柜门开关状态。
(4)DS1302时钟模块记录药品存取时间。
(5)ESP8266-01S Wi-Fi模块实现云平台通信。
(6)洞洞板焊接信号处理电路,杜邦线连接各传感器。
设计意义
该智能医疗药品存储柜系统设计基于STM32F103C8T6主控制器,实现了对药品存储环境的精准监控与管理,具有重要的实际应用价值。系统通过集成温湿度传感器和门状态检测,确保了药品在适宜的环境中存储,防止因温湿度波动导致的药品变质或失效,从而保障医疗用药的安全性和有效性。
系统具备自动温控和报警功能,在环境参数超标时及时响应,减少了人为干预的需求,提高了运维效率。这不仅降低了药品损失的风险,还增强了医疗机构的应急处理能力,为日常药品管理提供了可靠的技术支持。
通过华为云平台实现数据上传和QT上位机进行远程管理,系统实现了药品存取记录的数字化和环境的实时可追溯。这便于医护人员查询历史数据、处理报警信息,并支持大数据分析,为优化药品存储策略和提升医疗服务质量奠定了基础。整体设计提升了医疗资源管理的智能化水平,符合现代医疗信息化的发展趋势。
设计实现的功能
(1)实时监测药品柜内温湿度及门开关状态。
(2)温湿度超标时自动启动温控设备并报警。
(3)药品存取记录及环境数据上传至华为云平台。
(4)QT上位机实现药品管理、环境数据查询及报警信息处理。
项目硬件模块组成
(1)STM32F103C8T6最小系统核心板作为主控制器。
(2)DHT22温湿度传感器监测柜内环境。
(3)干簧管门磁传感器检测柜门开关状态。
(4)DS1302时钟模块记录药品存取时间。
(5)ESP8266-01S Wi-Fi模块实现云平台通信。
(6)洞洞板焊接信号处理电路,杜邦线连接各传感器。
设计思路
系统设计以STM32F103C8T6最小系统核心板作为主控制器,负责协调整个系统的运行。该控制器通过GPIO接口连接各传感器模块,初始化外设并设置中断处理,确保实时性。系统上电后,STM32进入主循环,持续采集传感器数据并执行控制逻辑,同时通过串口与Wi-Fi模块通信,实现数据上传和命令接收。
温湿度监测由DHT22传感器完成,STM32通过单总线协议读取传感器数据,定期采样柜内环境参数。门开关状态检测使用干簧管门磁传感器,连接至STM32的GPIO引脚并配置为输入模式,利用中断或轮询方式实时监测门状态变化,确保任何开门或关门事件都能及时捕获。
数据处理部分包括温湿度阈值判断,当检测到温度或湿度超出预设范围时,STM32自动启动温控设备如风扇或加热器,并通过蜂鸣器或LED进行报警提示。控制逻辑基于简单比较算法,确保响应快速且可靠,避免误动作。
时间记录依靠DS1302时钟模块,STM32通过SPI或类似接口读取当前时间,为药品存取事件添加时间戳。这些时间数据与传感器数据一起存储到本地缓冲区,并在需要时上传,保证记录的准确性和完整性。
云平台通信通过ESP8266-01S Wi-Fi模块实现,STM32通过串口AT指令与模块交互,连接至华为云平台。系统定期将温湿度数据、门状态事件以及药品存取记录打包为JSON格式,通过MQTT或HTTP协议上传,同时支持从云平台接收配置更新或查询指令。
QT上位机作为用户界面,运行于PC端,通过TCP/IP协议与云平台或直接与STM32通信(需网络配置)。它提供药品管理功能如录入和查询存取记录,实时显示环境数据曲线,并处理报警信息,允许用户确认和日志导出,增强系统的可管理性和可视化。
设计意义
智能医疗药品存储柜系统基于STM32F103C8T6主控制器设计,旨在提升药品存储的安全性和管理效率。该系统通过实时监测柜内温湿度及门开关状态,确保药品处于适宜环境中,防止因温湿度波动导致的药品变质或失效,从而保障医疗用药的有效性和患者安全。
温湿度超标时自动启动温控设备并报警功能,能够及时响应环境异常,减少人工干预的需求,避免药品损坏风险。这种自动化控制不仅提高了系统的可靠性,还降低了医疗机构的运营成本,通过即时报警机制确保问题得到快速处理。
药品存取记录及环境数据上传至华为云平台,实现了数据的远程存储和可追溯性。这使得医护人员能够通过云平台随时访问历史数据,进行分析和审计,支持合规性管理和决策制定,同时增强了药品管理的透明度和 accountability。
QT上位机软件提供了友好的用户界面,简化了药品管理、环境数据查询和报警信息处理流程。它使操作人员能够直观地监控系统状态,快速响应报警,并高效管理药品库存,提升了整体工作效率和用户体验。
硬件组成如DHT22传感器、干簧管门磁和DS1302时钟模块,确保了数据采集的准确性和时序记录的真实性。ESP8266-01S Wi-Fi模块实现了稳定的云平台通信,而洞洞板焊接和杜邦线连接则体现了系统的灵活性和成本效益,适用于各种医疗环境部署。
框架图
设计思路
设计思路基于STM32F103C8T6最小系统核心板作为主控制器,负责协调整个系统的运行。该系统通过集成多种传感器和执行器,实现智能医疗药品存储柜的自动化管理。核心板处理来自传感器的数据,执行控制逻辑,并通过Wi-Fi模块与云平台通信,同时上位机软件提供用户界面进行监控和管理。
系统首先通过DHT22温湿度传感器实时监测柜内环境参数,传感器数据通过GPIO引脚读取,STM32进行ADC转换或数字信号处理,确保数据的准确性。干簧管门磁传感器用于检测柜门开关状态,通过中断或轮询方式读取状态变化,从而记录门的开闭事件。DS1302时钟模块提供实时时间戳,用于标记药品存取和环境数据的时间,确保记录的可追溯性。
当温湿度数据超出预设阈值时,STM32触发控制逻辑,自动启动温控设备如风扇或加热器,以调节环境条件,同时通过声光报警装置(如蜂鸣器和LED)发出警报,提醒用户及时处理。这一过程基于软件中的比较算法,实时监控数据并做出响应,确保药品存储环境的安全。
ESP8266-01S Wi-Fi模块负责与华为云平台通信,STM32通过UART接口与ESP8266交互,使用AT指令或自定义协议建立Wi-Fi连接。环境数据、门状态事件和药品存取记录被封装成JSON格式,通过MQTT或HTTP协议上传到云平台,实现数据的远程存储和监控。云平台配置为接收和处理这些数据,支持后续的数据分析和告警推送。
QT上位机应用程序运行在PC端,通过串口或网络与STM32系统通信,实现药品管理功能,如添加、删除和查询药品信息,同时可以实时查看环境数据历史记录和报警信息。上位机提供图形化界面,方便用户处理报警事件和生成报告,增强了系统的可用性和管理效率。整个系统的硬件连接采用洞洞板焊接信号处理电路,杜邦线连接各传感器和模块,确保电路的稳定性和可维护性。
系统总体设计
该系统基于STM32F103C8T6最小系统核心板作为主控制器,实现智能医疗药品存储柜的监控与管理。系统通过DHT22温湿度传感器实时采集柜内环境数据,并结合干簧管门磁传感器检测柜门的开关状态,确保环境参数和门状态得到持续监测。
当温湿度数据超出预设阈值时,系统自动启动温控设备(如风扇或加热器)进行调节,并触发报警机制,例如通过蜂鸣器或LED指示,以提醒用户及时处理异常情况,保障药品存储安全。
药品存取记录和环境数据通过DS1302时钟模块标记时间戳,并通过ESP8266-01S Wi-Fi模块将数据上传至华为云平台,实现远程数据存储和访问。硬件连接采用洞洞板焊接信号处理电路,并使用杜邦线灵活连接各传感器和模块,确保系统稳定性和可维护性。
此外,QT上位机软件提供药品管理、环境数据查询和报警信息处理功能,用户可以通过图形界面直观地查看历史记录、处理报警事件,并管理药品库存,提升系统的实用性和用户体验。整个设计注重实际应用,确保功能可靠且易于扩展。
框架图
系统功能总结
功能描述 | 实现方式 |
---|---|
实时监测柜内温湿度 | DHT22温湿度传感器 |
实时监测柜门开关状态 | 干簧管门磁传感器 |
温湿度超标自动控制与报警 | 通过STM32控制温控设备(如风扇/加热器)并触发报警装置 |
记录药品存取时间 | DS1302时钟模块 |
上传药品存取记录及环境数据至华为云平台 | ESP8266-01S Wi-Fi模块 |
药品管理、环境数据查询及报警信息处理 | QT上位机软件 |
系统主控制与数据处理 | STM32F103C8T6最小系统核心板 |
系统总体设计
系统总体设计基于STM32F103C8T6最小系统核心板作为主控制器,负责协调整个智能医疗药品存储柜的运行。该系统通过集成多种传感器和执行器,实现药品存储环境的实时监控和数据管理。硬件组成包括DHT22温湿度传感器用于采集柜内环境数据,干簧管门磁传感器检测门开关状态,DS1302时钟模块提供准确的时间记录,ESP8266-01S Wi-Fi模块处理与华为云平台的通信,所有电路通过洞洞板焊接和杜邦线连接确保稳定性和灵活性。
传感器数据采集由STM32主控制器定期轮询完成。DHT22传感器实时测量温湿度数值,干簧管门磁传感器输出门状态信号,这些数据通过ADC和GPIO接口读入STM32进行处理。DS1302时钟模块为每次事件提供时间戳,确保药品存取记录的准确性。主控制器对采集到的数据进行初步滤波和校验,以消除噪声并提高可靠性。
当温湿度数据超出预设阈值时,系统自动触发控制逻辑。STM32通过GPIO输出信号启动温控设备(如风扇或加热器),以调节柜内环境,同时激活报警装置(如蜂鸣器或LED指示灯)进行本地警示。这一过程确保药品存储条件始终符合要求,防止环境异常导致的药品变质。
所有环境数据和事件记录(包括温湿度、门状态和时间戳)通过ESP8266-01S Wi-Fi模块上传至华为云平台。STM32通过串口与Wi-Fi模块通信,使用MQTT或HTTP协议将数据打包发送,实现远程监控和存储。云平台负责数据持久化和分析,为上位机提供查询基础。
QT上位机软件作为用户界面,实现药品管理、环境数据查询和报警信息处理。它通过云平台API获取数据,显示实时温湿度曲线、门状态历史记录和报警事件,并允许用户配置阈值和管理药品信息。上位机与STM32系统间接交互,通过云平台同步数据,确保系统的远程可管理性和用户体验。
设计的各个功能模块描述
STM32F103C8T6最小系统核心板作为主控制器,负责协调整个系统的运行,包括采集传感器数据、处理逻辑判断、控制外部设备以及管理通信模块。它通过GPIO接口连接各传感器和执行器,实现实时数据采集和控制输出。
DHT22温湿度传感器用于实时监测药品柜内的温度和湿度环境,其数字输出信号直接连接到STM32的GPIO引脚,STM32定期读取传感器数据以进行环境监测和超标判断。
干簧管门磁传感器检测柜门的开关状态,当门打开或关闭时,传感器状态变化通过GPIO输入到STM32,系统据此记录门状态事件并可能触发相关操作如记录存取时间。
DS1302时钟模块提供实时时间信息,用于精确记录药品存取事件的时间戳,STM32通过串行通信接口读取时钟数据,确保记录准确性并支持时间相关功能。
ESP8266-01S Wi-Fi模块实现与华为云平台的通信,STM32通过串口将温湿度数据、门状态事件和存取记录发送给ESP8266,由后者通过Wi-Fi网络上传数据到云平台,同时接收可能的云指令。
洞洞板焊接的信号处理电路用于接口和信号调理,例如可能包括电平转换或滤波电路,以确保传感器信号稳定可靠地传输到STM32,杜邦线用于灵活连接各组件。
温控设备在温湿度超标时由STM32控制启动,例如通过继电器驱动风扇或加热器,以调节柜内环境,同时系统会触发报警机制如声音或光指示,确保及时处理异常情况。
系统功能总结
功能 | 实现方式 |
---|---|
实时监测药品柜内温湿度 | 使用DHT22温湿度传感器 |
实时监测门开关状态 | 使用干簧管门磁传感器 |
温湿度超标时自动启动温控设备并报警 | STM32F103C8T6控制温控设备,触发报警机制 |
记录药品存取时间 | 使用DS1302时钟模块 |
药品存取记录及环境数据上传至华为云平台 | 通过ESP8266-01S Wi-Fi模块实现通信 |
QT上位机实现药品管理、环境数据查询及报警信息处理 | 基于QT开发的上位机软件 |
设计的各个功能模块描述
STM32F103C8T6最小系统核心板作为主控制器,负责协调整个系统的运行。它通过读取传感器数据、处理逻辑控制指令以及管理外设模块来实现功能需求。主控制器实时采集温湿度传感器和门状态传感器的信号,根据预设阈值判断是否启动温控设备或触发报警,同时记录时间信息并通过Wi-Fi模块上传数据到云平台。
DHT22温湿度传感器用于监测药品柜内的环境参数,实时检测温度和湿度值。传感器将采集到的数据以数字信号形式传输给主控制器,主控制器据此进行监控和决策,确保柜内环境符合药品存储要求,并在超标时采取相应措施。
干簧管门磁传感器检测柜门的开关状态,当门打开或关闭时,传感器会产生信号变化并通知主控制器。这一功能用于记录药品存取事件,并结合时钟模块提供时间戳,确保门状态变化的准确记录和报警触发。
DS1302时钟模块提供实时时钟功能,用于记录药品存取的具体时间。模块与主控制器连接,确保时间数据的准确性和一致性,为上传到云平台的数据添加时间标签,便于后续查询和分析。
ESP8266-01S Wi-Fi模块实现与华为云平台的通信功能,负责将采集到的温湿度数据、门状态记录以及报警信息上传到云。模块通过串口与主控制器交互,配置网络参数并处理数据传输,确保数据的可靠性和实时性。
洞洞板焊接的信号处理电路用于稳定和调理传感器信号,确保数据采集的准确性。电路可能包括滤波、电平转换或保护元件,以适应不同传感器的输出特性,并通过杜邦线连接到主控制器和其他模块。
杜邦线用于灵活连接各传感器和模块到主控制器,便于系统的组装、调试和维护。这种连接方式提供了良好的可扩展性和可靠性,确保信号传输的稳定性。
上位机代码设计
以下是基于Qt C++开发的智能医疗药品存储柜系统上位机代码。代码包括主窗口类,实现药品管理、环境数据查询和报警信息处理功能。使用QNetworkAccessManager与华为云平台通信,假设API端点为硬编码值(实际应用中应配置化)。
文件结构:
main.cpp
:应用程序入口。MainWindow.h
:主窗口头文件。MainWindow.cpp
:主窗口实现文件。Medicine.h
:药品数据模型头文件(可选,简化处理)。Medicine.cpp
:药品数据模型实现文件(可选,简化处理)。
由于篇幅限制,这里提供核心代码。药品数据模型简化处理,直接使用QList存储药品名称。
main.cpp
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
cpp
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTabWidget>
#include <QListWidget>
#include <QPushButton>
#include <QLineEdit>
#include <QLabel>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QDateTime>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void onAddMedicine();
void onDeleteMedicine();
void onRefreshEnvironment();
void onRefreshAlarms();
void onEnvironmentReplyFinished(QNetworkReply *reply);
void onAlarmsReplyFinished(QNetworkReply *reply);
private:
void setupUI();
void fetchEnvironmentData();
void fetchAlarmsData();
QTabWidget *tabWidget;
QListWidget *medicineList;
QLineEdit *medicineInput;
QPushButton *addButton;
QPushButton *deleteButton;
QListWidget *environmentList;
QPushButton *refreshEnvButton;
QListWidget *alarmsList;
QPushButton *refreshAlarmsButton;
QNetworkAccessManager *networkManager;
QString baseUrl = "http://example.com/api/"; // 假设的API基URL,实际应配置
};
#endif // MAINWINDOW_H
MainWindow.cpp
cpp
#include "MainWindow.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QMessageBox>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), networkManager(new QNetworkAccessManager(this))
{
setupUI();
connect(networkManager, &QNetworkAccessManager::finished, this, &MainWindow::onEnvironmentReplyFinished);
connect(networkManager, &QNetworkAccessManager::finished, this, &MainWindow::onAlarmsReplyFinished);
fetchEnvironmentData();
fetchAlarmsData();
}
MainWindow::~MainWindow()
{
}
void MainWindow::setupUI()
{
setWindowTitle("智能医疗药品存储柜系统");
setGeometry(100, 100, 800, 600);
tabWidget = new QTabWidget(this);
setCentralWidget(tabWidget);
// 药品管理标签
QWidget *medicineTab = new QWidget;
QVBoxLayout *medicineLayout = new QVBoxLayout;
medicineList = new QListWidget;
medicineInput = new QLineEdit;
medicineInput->setPlaceholderText("输入药品名称");
QHBoxLayout *inputLayout = new QHBoxLayout;
addButton = new QPushButton("添加");
deleteButton = new QPushButton("删除");
inputLayout->addWidget(medicineInput);
inputLayout->addWidget(addButton);
inputLayout->addWidget(deleteButton);
medicineLayout->addWidget(medicineList);
medicineLayout->addLayout(inputLayout);
medicineTab->setLayout(medicineLayout);
tabWidget->addTab(medicineTab, "药品管理");
// 环境数据标签
QWidget *envTab = new QWidget;
QVBoxLayout *envLayout = new QVBoxLayout;
environmentList = new QListWidget;
refreshEnvButton = new QPushButton("刷新");
envLayout->addWidget(environmentList);
envLayout->addWidget(refreshEnvButton);
envTab->setLayout(envLayout);
tabWidget->addTab(envTab, "环境数据");
// 报警信息标签
QWidget *alarmsTab = new QWidget;
QVBoxLayout *alarmsLayout = new QVBoxLayout;
alarmsList = new QListWidget;
refreshAlarmsButton = new QPushButton("刷新");
alarmsLayout->addWidget(alarmsList);
alarmsLayout->addWidget(refreshAlarmsButton);
alarmsTab->setLayout(alarmsLayout);
tabWidget->addTab(alarmsTab, "报警信息");
// 连接信号和槽
connect(addButton, &QPushButton::clicked, this, &MainWindow::onAddMedicine);
connect(deleteButton, &QPushButton::clicked, this, &MainWindow::onDeleteMedicine);
connect(refreshEnvButton, &QPushButton::clicked, this, &MainWindow::onRefreshEnvironment);
connect(refreshAlarmsButton, &QPushButton::clicked, this, &MainWindow::onRefreshAlarms);
}
void MainWindow::onAddMedicine()
{
QString medicine = medicineInput->text().trimmed();
if (!medicine.isEmpty()) {
medicineList->addItem(medicine);
medicineInput->clear();
// 这里应添加代码将药品上传到云平台,假设通过API POST
// 简化处理,仅本地添加
}
}
void MainWindow::onDeleteMedicine()
{
QList<QListWidgetItem*> selected = medicineList->selectedItems();
for (QListWidgetItem *item : selected) {
delete item;
// 这里应添加代码从云平台删除药品,假设通过API DELETE
}
}
void MainWindow::onRefreshEnvironment()
{
fetchEnvironmentData();
}
void MainWindow::onRefreshAlarms()
{
fetchAlarmsData();
}
void MainWindow::fetchEnvironmentData()
{
QUrl url(baseUrl + "environment");
QNetworkRequest request(url);
networkManager->get(request);
}
void MainWindow::fetchAlarmsData()
{
QUrl url(baseUrl + "alarms");
QNetworkRequest request(url);
networkManager->get(request);
}
void MainWindow::onEnvironmentReplyFinished(QNetworkReply *reply)
{
if (reply->error() == QNetworkReply::NoError) {
QByteArray response = reply->readAll();
QJsonDocument jsonDoc = QJsonDocument::fromJson(response);
QJsonArray jsonArray = jsonDoc.array();
environmentList->clear();
for (const QJsonValue &value : jsonArray) {
QJsonObject obj = value.toObject();
QString temp = obj["temperature"].toString();
QString humidity = obj["humidity"].toString();
QString time = obj["timestamp"].toString();
environmentList->addItem(QString("温度: %1°C, 湿度: %2%%, 时间: %3").arg(temp).arg(humidity).arg(time));
}
} else {
QMessageBox::warning(this, "错误", "获取环境数据失败: " + reply->errorString());
}
reply->deleteLater();
}
void MainWindow::onAlarmsReplyFinished(QNetworkReply *reply)
{
if (reply->error() == QNetworkReply::NoError) {
QByteArray response = reply->readAll();
QJsonDocument jsonDoc = QJsonDocument::fromJson(response);
QJsonArray jsonArray = jsonDoc.array();
alarmsList->clear();
for (const QJsonValue &value : jsonArray) {
QJsonObject obj = value.toObject();
QString message = obj["message"].toString();
QString time = obj["timestamp"].toString();
alarmsList->addItem(QString("报警: %1, 时间: %2").arg(message).arg(time));
}
} else {
QMessageBox::warning(this, "错误", "获取报警数据失败: " + reply->errorString());
}
reply->deleteLater();
}
说明:
- 此代码是一个基本框架,实际应用中需要根据华为云API的具体细节调整URL和JSON解析逻辑。
- 药品管理功能目前仅本地操作,应添加云同步代码(例如,在onAddMedicine和onDeleteMedicine中实现网络请求)。
- 环境数据和报警数据通过HTTP GET请求获取,假设API返回JSON数组。
- 使用Qt的网络和JSON模块,确保在.pro文件中添加
QT += network
。
编译和运行此代码需要Qt开发环境。根据实际API调整基URL和JSON处理。
上位机代码设计
cpp
#include <QApplication>
#include <QMainWindow>
#include <QTabWidget>
#include <QTableWidget>
#include <QPushButton>
#include <QLineEdit>
#include <QLabel>
#include <QTextEdit>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QChartView>
#include <QLineSeries>
#include <QValueAxis>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QMessageBox>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QDialog>
#include <QFormLayout>
#include <QDialogButtonBox>
QT_CHARTS_USE_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void onAddMedicine();
void onDeleteMedicine();
void onRefreshData();
void onNetworkReply(QNetworkReply *reply);
private:
void setupUI();
void fetchDataFromCloud();
QTabWidget *tabWidget;
QTableWidget *medicineTable;
QTableWidget *envDataTable;
QTextEdit *alarmTextEdit;
QNetworkAccessManager *networkManager;
};
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
setupUI();
networkManager = new QNetworkAccessManager(this);
connect(networkManager, &QNetworkAccessManager::finished, this, &MainWindow::onNetworkReply);
fetchDataFromCloud();
}
MainWindow::~MainWindow()
{
}
void MainWindow::setupUI()
{
setWindowTitle("智能医疗药品存储柜管理系统");
setGeometry(100, 100, 800, 600);
tabWidget = new QTabWidget(this);
// 药品管理标签
QWidget *medicineTab = new QWidget;
QVBoxLayout *medicineLayout = new QVBoxLayout;
QHBoxLayout *buttonLayout = new QHBoxLayout;
QPushButton *addButton = new QPushButton("添加药品");
QPushButton *deleteButton = new QPushButton("删除药品");
QPushButton *refreshButton = new QPushButton("刷新数据");
connect(addButton, &QPushButton::clicked, this, &MainWindow::onAddMedicine);
connect(deleteButton, &QPushButton::clicked, this, &MainWindow::onDeleteMedicine);
connect(refreshButton, &QPushButton::clicked, this, &MainWindow::onRefreshData);
buttonLayout->addWidget(addButton);
buttonLayout->addWidget(deleteButton);
buttonLayout->addWidget(refreshButton);
medicineTable = new QTableWidget;
medicineTable->setColumnCount(4);
medicineTable->setHorizontalHeaderLabels(QStringList() << "药品ID" << "药品名称" << "数量" << "生产日期");
medicineLayout->addLayout(buttonLayout);
medicineLayout->addWidget(medicineTable);
medicineTab->setLayout(medicineLayout);
tabWidget->addTab(medicineTab, "药品管理");
// 环境数据标签
QWidget *envDataTab = new QWidget;
QVBoxLayout *envLayout = new QVBoxLayout;
envDataTable = new QTableWidget;
envDataTable->setColumnCount(3);
envDataTable->setHorizontalHeaderLabels(QStringList() << "时间" << "温度" << "湿度");
envLayout->addWidget(envDataTable);
QChart *chart = new QChart;
QLineSeries *tempSeries = new QLineSeries;
QLineSeries *humiditySeries = new QLineSeries;
chart->addSeries(tempSeries);
chart->addSeries(humiditySeries);
chart->setTitle("温湿度历史数据");
chart->createDefaultAxes();
QChartView *chartView = new QChartView(chart);
chartView->setRenderHint(QPainter::Antialiasing);
envLayout->addWidget(chartView);
envDataTab->setLayout(envLayout);
tabWidget->addTab(envDataTab, "环境数据");
// 报警信息标签
QWidget *alarmTab = new QWidget;
QVBoxLayout *alarmLayout = new QVBoxLayout;
alarmTextEdit = new QTextEdit;
alarmTextEdit->setReadOnly(true);
alarmLayout->addWidget(alarmTextEdit);
alarmTab->setLayout(alarmLayout);
tabWidget->addTab(alarmTab, "报警信息");
setCentralWidget(tabWidget);
}
void MainWindow::onAddMedicine()
{
QDialog dialog(this);
dialog.setWindowTitle("添加药品");
QFormLayout form(&dialog);
QLineEdit *nameEdit = new QLineEdit;
QLineEdit *quantityEdit = new QLineEdit;
QLineEdit *dateEdit = new QLineEdit;
form.addRow("药品名称:", nameEdit);
form.addRow("数量:", quantityEdit);
form.addRow("生产日期:", dateEdit);
QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dialog);
form.addRow(&buttonBox);
connect(&buttonBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
connect(&buttonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
if (dialog.exec() == QDialog::Accepted) {
int row = medicineTable->rowCount();
medicineTable->insertRow(row);
medicineTable->setItem(row, 0, new QTableWidgetItem(QString::number(row + 1)));
medicineTable->setItem(row, 1, new QTableWidgetItem(nameEdit->text()));
medicineTable->setItem(row, 2, new QTableWidgetItem(quantityEdit->text()));
medicineTable->setItem(row, 3, new QTableWidgetItem(dateEdit->text()));
}
}
void MainWindow::onDeleteMedicine()
{
int currentRow = medicineTable->currentRow();
if (currentRow >= 0) {
medicineTable->removeRow(currentRow);
} else {
QMessageBox::warning(this, "警告", "请选择要删除的药品行");
}
}
void MainWindow::onRefreshData()
{
fetchDataFromCloud();
}
void MainWindow::fetchDataFromCloud()
{
QUrl url("https://your-huawei-cloud-api.com/data");
QNetworkRequest request(url);
networkManager->get(request);
}
void MainWindow::onNetworkReply(QNetworkReply *reply)
{
if (reply->error() == QNetworkReply::NoError) {
QByteArray data = reply->readAll();
QJsonDocument doc = QJsonDocument::fromJson(data);
if (doc.isArray()) {
QJsonArray array = doc.array();
envDataTable->setRowCount(0);
for (int i = 0; i < array.size(); ++i) {
QJsonObject obj = array[i].toObject();
QString time = obj["time"].toString();
double temperature = obj["temperature"].toDouble();
double humidity = obj["humidity"].toDouble();
int row = envDataTable->rowCount();
envDataTable->insertRow(row);
envDataTable->setItem(row, 0, new QTableWidgetItem(time));
envDataTable->setItem(row, 1, new QTableWidgetItem(QString::number(temperature)));
envDataTable->setItem(row, 2, new QTableWidgetItem(QString::number(humidity)));
}
}
// 假设报警数据也在回复中
if (doc.isObject()) {
QJsonObject obj = doc.object();
if (obj.contains("alarms")) {
QJsonArray alarms = obj["alarms"].toArray();
alarmTextEdit->clear();
for (const QJsonValue &value : alarms) {
QJsonObject alarm = value.toObject();
QString alarmMsg = alarm["message"].toString();
alarmTextEdit->append(alarmMsg);
}
}
}
} else {
QMessageBox::critical(this, "错误", "获取数据失败: " + reply->errorString());
}
reply->deleteLater();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
#include "main.moc"
模块代码设计
c
#include "stm32f10x.h"
// 引脚定义
#define DHT22_PIN GPIO_Pin_0
#define DHT22_PORT GPIOA
#define DOOR_SENSOR_PIN GPIO_Pin_1
#define DOOR_SENSOR_PORT GPIOA
#define DS1302_CE_PIN GPIO_Pin_2
#define DS1302_CE_PORT GPIOA
#define DS1302_IO_PIN GPIO_Pin_3
#define DS1302_IO_PORT GPIOA
#define DS1302_SCLK_PIN GPIO_Pin_4
#define DS1302_SCLK_PORT GPIOA
#define TEMP_CONTROL_PIN GPIO_Pin_5
#define TEMP_CONTROL_PORT GPIOA
#define ALARM_PIN GPIO_Pin_6
#define ALARM_PORT GPIOA
// UART1 for ESP8266
#define ESP8266_UART USART1
// 温湿度阈值
#define TEMP_HIGH_THRESHOLD 30.0
#define TEMP_LOW_THRESHOLD 2.0
#define HUMIDITY_HIGH_THRESHOLD 80.0
// 函数声明
void SystemInit(void);
void GPIO_Init(void);
void UART1_Init(void);
void DHT22_Init(void);
float DHT22_ReadTemperature(void);
float DHT22_ReadHumidity(void);
uint8_t DHT22_ReadByte(void);
void DS1302_Init(void);
void DS1302_WriteByte(uint8_t data);
uint8_t DS1302_ReadByte(void);
void DS1302_GetTime(uint8_t *time);
void DoorSensor_Init(void);
uint8_t DoorSensor_Read(void);
void ESP8266_Init(void);
void ESP8266_SendCmd(char *cmd);
void ESP8266_SendData(char *data);
void Delay_ms(uint32_t nTime);
void Delay_us(uint32_t nTime);
int main(void) {
SystemInit();
GPIO_Init();
UART1_Init();
DHT22_Init();
DS1302_Init();
DoorSensor_Init();
ESP8266_Init();
while (1) {
// 读取温湿度
float temp = DHT22_ReadTemperature();
float humidity = DHT22_ReadHumidity();
// 读取门状态
uint8_t door_state = DoorSensor_Read();
// 读取时间
uint8_t time[7];
DS1302_GetTime(time);
// 检查温湿度阈值
if (temp > TEMP_HIGH_THRESHOLD || temp < TEMP_LOW_THRESHOLD || humidity > HUMIDITY_HIGH_THRESHOLD) {
GPIO_SetBits(TEMP_CONTROL_PORT, TEMP_CONTROL_PIN); // 启动温控设备
GPIO_SetBits(ALARM_PORT, ALARM_PIN); // 报警
} else {
GPIO_ResetBits(TEMP_CONTROL_PORT, TEMP_CONTROL_PIN);
GPIO_ResetBits(ALARM_PORT, ALARM_PIN);
}
// 准备数据上传到华为云
char data_str[100];
sprintf(data_str, "temp=%.2f&humidity=%.2f&door=%d&time=%02d:%02d:%02d", temp, humidity, door_state, time[2], time[1], time[0]);
ESP8266_SendData(data_str);
Delay_ms(5000); // 每5秒上传一次
}
}
void SystemInit(void) {
// 设置系统时钟为72MHz
RCC->CFGR |= RCC_CFGR_PLLMULL9;
RCC->CFGR |= RCC_CFGR_PLLSRC;
RCC->CR |= RCC_CR_PLLON;
while (!(RCC->CR & RCC_CR_PLLRDY));
RCC->CFGR |= RCC_CFGR_SW_PLL;
while (!(RCC->CFGR & RCC_CFGR_SWS_PLL));
}
void GPIO_Init(void) {
// 启用GPIOA和UART1时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_USART1EN;
// 配置DHT22引脚为输入
GPIOA->CRL &= ~(0xF << (0 * 4)); // PA0: input
GPIOA->CRL |= (0x4 << (0 * 4)); // Floating input
// 配置门传感器引脚为输入带上拉
GPIOA->CRL &= ~(0xF << (1 * 4)); // PA1: input
GPIOA->CRL |= (0x8 << (1 * 4)); // Pull-up input
GPIOA->ODR |= DOOR_SENSOR_PIN; // Enable pull-up
// 配置DS1302引脚:CE、IO、SCLK为输出
GPIOA->CRL &= ~(0xFF << (2 * 4)); // Clear PA2, PA3, PA4
GPIOA->CRL |= (0x3 << (2 * 4)) | (0x3 << (3 * 4)) | (0x3 << (4 * 4)); // PA2, PA3, PA4: output, 50MHz
GPIOA->ODR &= ~(DS1302_CE_PIN | DS1302_IO_PIN | DS1302_SCLK_PIN); // Set low initially
// 配置温控和报警引脚为输出
GPIOA->CRL &= ~(0xF << (5 * 4)); // PA5: output
GPIOA->CRL |= (0x3 << (5 * 4)); // Output, 50MHz
GPIOA->CRL &= ~(0xF << (6 * 4)); // PA6: output
GPIOA->CRL |= (0x3 << (6 * 4)); // Output, 50MHz
// 配置UART1引脚: PA9 as TX, PA10 as RX
GPIOA->CRH &= ~(0xFF << 4); // Clear PA9 and PA10
GPIOA->CRH |= (0xB << 4) | (0x4 << 8); // PA9: AF output, 50MHz; PA10: input floating
}
void UART1_Init(void) {
// 配置UART1: 9600 baud, 8 data bits, no parity, 1 stop bit
USART1->BRR = 72000000 / 9600; // Set baud rate
USART1->CR1 |= USART_CR1_TE | USART_CR1_RE; // Enable TX and RX
USART1->CR1 |= USART_CR1_UE; // Enable UART
}
void DHT22_Init(void) {
// 初始化代码,设置引脚
GPIO_ResetBits(DHT22_PORT, DHT22_PIN); // Set low initially
Delay_ms(1000);
}
float DHT22_ReadTemperature(void) {
uint8_t data[5];
// 启动信号
GPIO_SetBits(DHT22_PORT, DHT22_PIN);
Delay_us(30);
GPIO_ResetBits(DHT22_PORT, DHT22_PIN);
Delay_ms(1);
GPIO_SetBits(DHT22_PORT, DHT22_PIN);
Delay_us(40);
// 等待响应
while (GPIO_ReadInputDataBit(DHT22_PORT, DHT22_PIN));
while (!GPIO_ReadInputDataBit(DHT22_PORT, DHT22_PIN));
// 读取数据
for (int i = 0; i < 5; i++) {
data[i] = DHT22_ReadByte();
}
// 校验和检查
if (data[4] == (data[0] + data[1] + data[2] + data[3])) {
float temp = (data[2] & 0x7F) * 256 + data[3];
temp /= 10.0;
if (data[2] & 0x80) temp = -temp;
return temp;
}
return -1.0;
}
float DHT22_ReadHumidity(void) {
uint8_t data[5];
// 类似ReadTemperature,但返回湿度
// 启动信号
GPIO_SetBits(DHT22_PORT, DHT22_PIN);
Delay_us(30);
GPIO_ResetBits(DHT22_PORT, DHT22_PIN);
Delay_ms(1);
GPIO_SetBits(DHT22_PORT, DHT22_PIN);
Delay_us(40);
while (GPIO_ReadInputDataBit(DHT22_PORT, DHT22_PIN));
while (!GPIO_ReadInputDataBit(DHT22_PORT, DHT22_PIN));
for (int i = 0; i < 5; i++) {
data[i] = DHT22_ReadByte();
}
if (data[4] == (data[0] + data[1] + data[2] + data[3])) {
float humidity = data[0] * 256 + data[1];
humidity /= 10.0;
return humidity;
}
return -1.0;
}
uint8_t DHT22_ReadByte(void) {
uint8_t byte = 0;
for (int i = 0; i < 8; i++) {
while (!GPIO_ReadInputDataBit(DHT22_PORT, DHT22_PIN)); // Wait for high
Delay_us(30);
if (GPIO_ReadInputDataBit(DHT22_PORT, DHT22_PIN)) {
byte |= (1 << (7 - i));
}
while (GPIO_ReadInputDataBit(DHT22_PORT, DHT22_PIN)); // Wait for low
}
return byte;
}
void DS1302_Init(void) {
GPIO_ResetBits(DS1302_CE_PORT, DS1302_CE_PIN);
GPIO_ResetBits(DS1302_SCLK_PORT, DS1302_SCLK_PIN);
}
void DS1302_WriteByte(uint8_t data) {
for (int i = 0; i < 8; i++) {
GPIO_WriteBit(DS1302_IO_PORT, DS1302_IO_PIN, (data & (1 << i)) ? Bit_SET : Bit_RESET);
GPIO_SetBits(DS1302_SCLK_PORT, DS1302_SCLK_PIN);
Delay_us(1);
GPIO_ResetBits(DS1302_SCLK_PORT, DS1302_SCLK_PIN);
Delay_us(1);
}
}
uint8_t DS1302_ReadByte(void) {
uint8_t byte = 0;
for (int i = 0; i < 8; i++) {
if (GPIO_ReadInputDataBit(DS1302_IO_PORT, DS1302_IO_PIN)) {
byte |= (1 << i);
}
GPIO_SetBits(DS1302_SCLK_PORT, DS1302_SCLK_PIN);
Delay_us(1);
GPIO_ResetBits(DS1302_SCLK_PORT, DS1302_SCLK_PIN);
Delay_us(1);
}
return byte;
}
void DS1302_GetTime(uint8_t *time) {
GPIO_SetBits(DS1302_CE_PORT, DS1302_CE_PIN);
DS1302_WriteByte(0x81); // Read seconds
time[0] = DS1302_ReadByte();
DS1302_WriteByte(0x83); // Read minutes
time[1] = DS1302_ReadByte();
DS1302_WriteByte(0x85); // Read hours
time[2] = DS1302_ReadByte();
GPIO_ResetBits(DS1302_CE_PORT, DS1302_CE_PIN);
}
void DoorSensor_Init(void) {
// 已在GPIO_Init中初始化
}
uint8_t DoorSensor_Read(void) {
return GPIO_ReadInputDataBit(DOOR_SENSOR_PORT, DOOR_SENSOR_PIN);
}
void ESP8266_Init(void) {
ESP8266_SendCmd("AT+RST\r\n");
Delay_ms(1000);
ESP8266_SendCmd("AT+CWMODE=1\r\n");
Delay_ms(1000);
ESP8266_SendCmd("AT+CWJAP=\"YourSSID\",\"YourPassword\"\r\n");
Delay_ms(5000);
ESP8266_SendCmd("AT+CIPSTART=\"TCP\",\"华为云地址\",端口号\r\n");
Delay_ms(1000);
}
void ESP8266_SendCmd(char *cmd) {
while (*cmd) {
USART_SendData(ESP8266_UART, *cmd++);
while (USART_GetFlagStatus(ESP8266_UART, USART_FLAG_TC) == RESET);
}
}
void ESP8266_SendData(char *data) {
char cmd[50];
sprintf(cmd, "AT+CIPSEND=%d\r\n", strlen(data));
ESP8266_SendCmd(cmd);
Delay_ms(100);
ESP8266_SendCmd(data);
ESP8266_SendCmd("\r\n");
}
void Delay_ms(uint32_t nTime) {
for (uint32_t i = 0; i < nTime * 1000; i++) {
__NOP();
}
}
void Delay_us(uint32_t nTime) {
for (uint32_t i = 0; i < nTime; i++) {
__NOP();
}
}
模块代码设计
c
#include <stdint.h>
// Register definitions for STM32F103C8T6
#define GPIOA_BASE 0x40010800
#define GPIOA_CRL (*((volatile uint32_t *)(GPIOA_BASE + 0x00)))
#define GPIOA_CRH (*((volatile uint32_t *)(GPIOA_BASE + 0x04)))
#define GPIOA_IDR (*((volatile uint32_t *)(GPIOA_BASE + 0x08)))
#define GPIOA_ODR (*((volatile uint32_t *)(GPIOA_BASE + 0x0C)))
#define GPIOB_BASE 0x40010C00
#define GPIOB_CRL (*((volatile uint32_t *)(GPIOB_BASE + 0x00)))
#define GPIOB_CRH (*((volatile uint32_t *)(GPIOB_BASE + 0x04)))
#define GPIOB_IDR (*((volatile uint32_t *)(GPIOB_BASE + 0x08)))
#define GPIOB_ODR (*((volatile uint32_t *)(GPIOB_BASE + 0x0C)))
#define RCC_BASE 0x40021000
#define RCC_APB2ENR (*((volatile uint32_t *)(RCC_BASE + 0x18)))
#define USART1_BASE 0x40013800
#define USART1_SR (*((volatile uint32_t *)(USART1_BASE + 0x00)))
#define USART1_DR (*((volatile uint32_t *)(USART1_BASE + 0x04)))
#define USART1_BRR (*((volatile uint32_t *)(USART1_BASE + 0x08)))
#define USART1_CR1 (*((volatile uint32_t *)(USART1_BASE + 0x0C)))
#define SYSTICK_BASE 0xE000E010
#define SYST_CSR (*((volatile uint32_t *)(SYSTICK_BASE + 0x00)))
#define SYST_RVR (*((volatile uint32_t *)(SYSTICK_BASE + 0x04)))
#define SYST_CVR (*((volatile uint32_t *)(SYSTICK_BASE + 0x08)))
// Pin definitions
#define DHT22_PIN 0 // PA0
#define DOOR_SENSOR_PIN 1 // PA1
#define DS1302_CE_PIN 2 // PA2
#define DS1302_IO_PIN 3 // PA3
#define DS1302_SCLK_PIN 4 // PA4
#define TEMP_CTRL_PIN 5 // PA5 (e.g., relay for fan)
#define ALARM_PIN 6 // PA6 (e.g., LED)
#define ESP8266_TX_PIN 9 // PA9
#define ESP8266_RX_PIN 10 // PA10
// Constants
#define SYSTEM_CORE_CLOCK 8000000 // 8MHz
#define DHT22_TIMEOUT 10000
#define TEMP_THRESHOLD_HIGH 30.0 // Example temperature threshold
#define HUMIDITY_THRESHOLD_HIGH 80.0 // Example humidity threshold
// Global variables
volatile uint32_t msTicks = 0;
// SysTick interrupt handler
void SysTick_Handler(void) {
msTicks++;
}
// Delay in milliseconds
void Delay_ms(uint32_t ms) {
uint32_t startTicks = msTicks;
while ((msTicks - startTicks) < ms);
}
// Delay in microseconds (approximate for 8MHz)
void Delay_us(uint32_t us) {
us = us * 2; // Adjust for 8MHz, each loop ~0.5us
while (us--) {
__asm__("nop");
}
}
// GPIO initialization
void GPIO_Init(void) {
// Enable clock for GPIOA and GPIOB
RCC_APB2ENR |= (1 << 2) | (1 << 3); // GPIOA and GPIOB clock enable
// Configure PA0 (DHT22) as output open-drain initially
GPIOA_CRL &= ~(0xF << (DHT22_PIN * 4));
GPIOA_CRL |= (0x3 << (DHT22_PIN * 4)); // Output mode, max speed 50MHz
GPIOA_ODR |= (1 << DHT22_PIN); // Set high
// Configure PA1 (Door sensor) as input with pull-up
GPIOA_CRL &= ~(0xF << (DOOR_SENSOR_PIN * 4));
GPIOA_CRL |= (0x8 << (DOOR_SENSOR_PIN * 4)); // Input with pull-up/pull-down
GPIOA_ODR |= (1 << DOOR_SENSOR_PIN); // Pull-up
// Configure PA2, PA3, PA4 for DS1302: PA2(CE) and PA4(SCLK) as output, PA3(IO) as input/output
GPIOA_CRL &= ~(0xFFF << (DS1302_CE_PIN * 4)); // Clear bits for PA2, PA3, PA4
GPIOA_CRL |= (0x3 << (DS1302_CE_PIN * 4)) | (0x3 << (DS1302_SCLK_PIN * 4)); // Output for CE and SCLK
GPIOA_ODR &= ~(1 << DS1302_CE_PIN); // CE low
GPIOA_ODR &= ~(1 << DS1302_SCLK_PIN); // SCLK low
// For PA3(IO), set as input initially
GPIOA_CRL |= (0x8 << (DS1302_IO_PIN * 4)); // Input with pull-up/pull-down
GPIOA_ODR |= (1 << DS1302_IO_PIN); // Pull-up
// Configure PA5 (Temperature control) and PA6 (Alarm) as output
GPIOA_CRL &= ~(0xFF << (TEMP_CTRL_PIN * 4));
GPIOA_CRL |= (0x3 << (TEMP_CTRL_PIN * 4)) | (0x3 << (ALARM_PIN * 4)); // Output
GPIOA_ODR &= ~(1 << TEMP_CTRL_PIN); // Off initially
GPIOA_ODR &= ~(1 << ALARM_PIN); // Off initially
// Configure PA9 (USART1 TX) as alternate function output, PA10 (USART1 RX) as input
GPIOA_CRH &= ~(0xFF << ((ESP8266_TX_PIN - 8) * 4));
GPIOA_CRH |= (0xB << ((ESP8266_TX_PIN - 8) * 4)); // AF output for TX
GPIOA_CRH |= (0x4 << ((ESP8266_RX_PIN - 8) * 4)); // Input floating for RX
}
// USART1 initialization for ESP8266
void USART1_Init(void) {
// Enable clock for USART1
RCC_APB2ENR |= (1 << 14); // USART1 clock enable
// Configure USART1: 9600 baud, 8 data bits, no parity, 1 stop bit
USART1_BRR = 0x341; // 8MHz / 9600 = 833.33 -> 0x341 (mantissa 52, fraction 1)
USART1_CR1 |= (1 << 13) | (1 << 3) | (1 << 2); // UE, TE, RE
}
// USART1 send character
void USART1_SendChar(char c) {
while (!(USART1_SR & (1 << 7))); // Wait for TXE
USART1_DR = c;
}
// USART1 send string
void USART1_SendString(const char *str) {
while (*str) {
USART1_SendChar(*str++);
}
}
// DHT22 functions
void DHT22_Start(void) {
GPIOA_CRL &= ~(0xF << (DHT22_PIN * 4));
GPIOA_CRL |= (0x3 << (DHT22_PIN * 4)); // Output mode
GPIOA_ODR &= ~(1 << DHT22_PIN); // Pull low
Delay_ms(1); // Wait 1ms
GPIOA_ODR |= (1 << DHT22_PIN); // Pull high
Delay_us(30); // Wait 30us
GPIOA_CRL &= ~(0xF << (DHT22_PIN * 4));
GPIOA_CRL |= (0x8 << (DHT22_PIN * 4)); // Input mode
}
uint8_t DHT22_Check_Response(void) {
uint32_t timeout = 0;
while (GPIOA_IDR & (1 << DHT22_PIN)) { Wait for low
if (timeout++ > DHT22_TIMEOUT) return 0;
Delay_us(1);
}
timeout = 0;
while (!(GPIOA_IDR & (1 << DHT22_PIN))) { Wait for high
if (timeout++ > DHT22_TIMEOUT) return 0;
Delay_us(1);
}
timeout = 0;
while (GPIOA_IDR & (1 << DHT22_PIN)) { Wait for low again
if (timeout++ > DHT22_TIMEOUT) return 0;
Delay_us(1);
}
return 1;
}
uint8_t DHT22_Read_Bit(void) {
uint32_t timeout = 0;
while (!(GPIOA_IDR & (1 << DHT22_PIN))) { Wait for high
if (timeout++ > DHT22_TIMEOUT) return 0;
Delay_us(1);
}
Delay_us(40); // Wait 40us
if (GPIOA_IDR & (1 << DHT22_PIN)) return 1;
else return 0;
}
uint8_t DHT22_Read_Byte(void) {
uint8_t byte = 0;
for (int i = 0; i < 8; i++) {
byte <<= 1;
byte |= DHT22_Read_Bit();
}
return byte;
}
int DHT22_Read(float *temperature, float *humidity) {
uint8 data[5] = {0};
DHT22_Start();
if (!DHT22_Check_Response()) return 0;
for (int i = 0; i < 5; i++) {
data[i] = DHT22_Read_Byte();
}
// Checksum
if (data[4] != (data[0] + data[1] + data[2] + data[3])) return 0;
*humidity = (data[0] * 256 + data[1]) / 10.0;
*temperature = (data[2] * 256 + data[3]) / 10.0;
return 1;
}
// DS1302 functions
void DS1302_Write_Byte(uint8_t byte) {
GPIOA_CRL &= ~(0xF << (DS1302_IO_PIN * 4));
GPIOA_CRL |= (0x3 << (DS1302_IO_PIN * 4)); // Output mode
for (int i = 0; i < 8; i++) {
if (byte & 0x01) GPIOA_ODR |= (1 << DS1302_IO_PIN);
else GPIOA_ODR &= ~(1 << DS1302_IO_PIN);
GPIOA_ODR |= (1 << DS1302_SCLK_PIN); // SCLK high
Delay_us(1);
GPIOA_ODR &= ~(1 << DS1302_SCLK_PIN); // SCLK low
Delay_us(1);
byte >>= 1;
}
}
uint8_t DS1302_Read_Byte(void) {
uint8_t byte = 0;
GPIOA_CRL &= ~(0xF << (DS1302_IO_PIN * 4));
GPIOA_CRL |= (0x8 << (DS1302_IO_PIN * 4)); // Input mode
for (int i = 0; i < 8; i++) {
byte >>= 1;
if (GPIOA_IDR & (1 << DS1302_IO_PIN)) byte |= 0x80;
GPIOA_ODR |= (1 << DS1302_SCLK_PIN); // SCLK high
Delay_us(1);
GPIOA_ODR &= ~(1 << DS1302_SCLK_PIN); // SCLK low
Delay_us(1);
}
return byte;
}
void DS1302_Write_Register(uint8_t reg, uint8_t data) {
GPIOA_ODR |= (1 << DS1302_CE_PIN); // CE high
DS1302_Write_Byte(reg);
DS1302_Write_Byte(data);
GPIOA_ODR &= ~(1 << DS1302_CE_PIN); // CE low
}
uint8_t DS1302_Read_Register(uint8_t reg) {
GPIOA_ODR |= (1 << DS1302_CE_PIN); // CE high
DS1302_Write_Byte(reg | 0x01); // Read command
uint8_t data = DS1302_Read_Byte();
GPIOA_ODR &= ~(1 << DS1302_CE_PIN); // CE low
return data;
}
void DS1302_Init(void) {
// Disable write protection
DS1302_Write_Register(0x8E, 0x00);
// Enable clock
DS1302_Write_Register(0x80, 0x00); // Ensure clock is running
}
void DS1302_Get_Time(uint8_t *year, uint8_t *month, uint8_t *day, uint8_t *hour, uint8_t *minute, uint8_t *second) {
*second = DS1302_Read_Register(0x81);
*minute = DS1302_Read_Register(0x83);
*hour = DS1302_Read_Register(0x85);
*day = DS1302_Read_Register(0x87);
*month = DS1302_Read_Register(0x89);
*year = DS1302_Read_Register(0x8D);
}
// Door sensor read
uint8_t Door_Read(void) {
return (GPIOA_IDR & (1 << DOOR_SENSOR_PIN)) ? 1 : 0; // 1 means door closed? depends on wiring
}
// Control functions
void Temp_Ctrl_On(void) {
GPIOA_ODR |= (1 << TEMP_CTRL_PIN); // Turn on temp control device
}
void Temp_Ctrl_Off(void) {
GPIOA_ODR &= ~(1 << TEMP_CTRL_PIN); // Turn off
}
void Alarm_On(void) {
GPIOA_ODR |= (1 << ALARM_PIN); // Turn on alarm
}
void Alarm_Off(void) {
GPIOA_ODR &= ~(1 << ALARM_PIN); // Turn off
}
// ESP8266 functions for Huawei Cloud
void ESP8266_Init(void) {
USART1_SendString("AT+RST\r\n");
Delay_ms(1000);
USART1_SendString("AT+CWMODE=1\r\n");
Delay_ms(1000);
USART1_SendString("AT+CWJAP=\"SSID\",\"PASSWORD\"\r\n"); // Replace with your WiFi credentials
Delay_ms(5000);
USART1_SendString("AT+CIPSTART=\"TCP\",\"192.168.1.100\",8080\r\n"); // Replace with Huawei Cloud IP and port
Delay_ms(2000);
}
void ESP8266_Send_Data(float temp, float hum, uint8_t door状态, uint8_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) {
char buffer[100];
sprintf(buffer, "AT+CIPSEND=%d\r\n", strlen("Temp: XX.X, Hum: XX.X, Door: X, Time: YYYY-MM-DD HH:MM:SS"));
USART1_SendString(buffer);
Delay_ms(100);
sprintf(buffer, "Temp: %.1f, Hum: %.1f, Door: %d, Time: 20%02d-%02d-%02d %02d:%02d:%02d\r\n", temp, hum, door状态, year, month, day, hour, minute, second);
USART1_SendString(buffer);
Delay_ms(500);
}
// Main function
int main(void) {
// Initialize SysTick for 1ms interrupts
SYST_RVR = 8000 - 1; // Reload value for 1ms at 8MHz
SYST_CVR = 0;
SYST_CSR = (1 << 2) | (1 << 0); // Processor clock, enable
GPIO_Init();
USART1_Init();
DS1302_Init();
ESP8266_Init();
float temperature, humidity;
uint8_t door状态;
uint8_t year, month, day, hour, minute, second;
while (1) {
if (DHT22_Read(&temperature, &humidity)) {
door状态 = Door_Read();
DS1302_Get_Time(&year, &month, &day, &hour, &minute, &second);
// Check thresholds and control
if (temperature > TEMP_THRESHOLD_HIGH || humidity > HUMIDITY_THRESHOLD_HIGH) {
Temp_Ctrl_On();
Alarm_On();
} else {
Temp_Ctrl_Off();
Alarm_Off();
}
// Send data to cloud
ESP8266_Send_Data(temperature, humidity, door状态, year, month, day, hour, minute, second);
// Log access if door state changed (simple example)
static uint8_t last_door状态 = 0;
if (door状态 != last_door状态) {
// Record access event, e.g., send to cloud or store locally
last_door状态 = door状态;
}
}
Delay_ms(5000); // Read every 5 seconds
}
}
项目核心代码
c
#include <stdint.h>
// Register definitions for STM32F103
#define RCC_APB2ENR (*(volatile uint32_t*)0x40021018)
#define GPIOA_CRL (*(volatile uint32_t*)0x40010800)
#define GPIOA_CRH (*(volatile uint32_t*)0x40010804)
#define GPIOA_ODR (*(volatile uint32_t*)0x4001080C)
#define USART1_SR (*(volatile uint32_t*)0x40013800)
#define USART1_DR (*(volatile uint32_t*)0x40013804)
#define USART1_BRR (*(volatile uint32_t*)0x40013808)
#define USART1_CR1 (*(volatile uint32_t*)0x4001380C)
// External function declarations
extern void DHT22_Init(void);
extern void DHT22_Read(float *temp, float *humidity);
extern void DoorSensor_Init(void);
extern uint8_t DoorSensor_Read(void);
extern void DS1302_Init(void);
extern void DS1302_GetTime(char *timeBuffer);
extern void ESP8266_Init(void);
extern void ESP8266_SendData(const char *data);
// Pin definitions
#define FAN_PIN 2 // PA2
#define ALARM_PIN 3 // PA3
// GPIO initialization
void GPIO_Init(void) {
// Enable GPIOA clock
RCC_APB2ENR |= (1 << 2);
// Configure PA2 and PA3 as output push-pull, 50MHz
GPIOA_CRL &= ~(0xF << 8); // Clear bits for PA2
GPIOA_CRL |= (0x3 << 8); // Set PA2 to output push-pull
GPIOA_CRL &= ~(0xF << 12); // Clear bits for PA3
GPIOA_CRL |= (0x3 << 12); // Set PA3 to output push-pull
}
// USART1 initialization for ESP8266
void USART1_Init(void) {
// Enable USART1 clock
RCC_APB2ENR |= (1 << 14);
// Configure PA9 as alternative push-pull output (TX)
GPIOA_CRH &= ~(0xF << 4);
GPIOA_CRH |= (0xB << 4);
// Configure PA10 as input floating (RX)
GPIOA_CRH &= ~(0xF << 8);
GPIOA_CRH |= (0x4 << 8);
// Set baud rate to 9600 (72MHz clock)
USART1_BRR = 0x1D4C;
// Enable USART1, transmitter, and receiver
USART1_CR1 |= (1 << 13) | (1 << 3) | (1 << 2);
}
// Control functions
void Fan_On(void) {
GPIOA_ODR |= (1 << FAN_PIN);
}
void Fan_Off(void) {
GPIOA_ODR &= ~(1 << FAN_PIN);
}
void Alarm_On(void) {
GPIOA_ODR |= (1 << ALARM_PIN);
}
void Alarm_Off(void) {
GPIOA_ODR &= ~(1 << ALARM_PIN);
}
// Simple delay function
void Delay(void) {
for (volatile int i = 0; i < 500000; i++);
}
int main(void) {
// Initialize hardware
GPIO_Init();
USART1_Init();
DHT22_Init();
DoorSensor_Init();
DS1302_Init();
ESP8266_Init();
float temp, humidity;
uint8_t door_status;
char time_str[20];
while (1) {
// Read sensors
DHT22_Read(&temp, &humidity);
door_status = DoorSensor_Read();
DS1302_GetTime(time_str);
// Control logic
if (temp > 30.0 || humidity > 80.0) {
Fan_On();
Alarm_On();
} else {
Fan_Off();
Alarm_Off();
}
// Prepare data string
char data[100];
int len = 0;
len += sprintf(data + len, "Time: %s, Temp: %.2f C, Humidity: %.2f%%, Door: %s",
time_str, temp, humidity, door_status ? "Open" : "Closed");
// Send data to cloud
ESP8266_SendData(data);
// Delay
Delay();
}
}
总结
本系统基于STM32F103C8T6微控制器,成功设计并实现了一个智能医疗药品存储柜,核心功能包括实时监测柜内温湿度及门开关状态,确保药品存储环境符合标准,并在温湿度超标时自动启动温控设备和报警机制,从而保障药品的安全与有效性。
硬件组成上,系统采用了DHT22温湿度传感器进行环境数据采集,干簧管门磁传感器检测门状态,DS1302时钟模块记录精确时间信息,ESP8266-01S Wi-Fi模块实现与华为云平台的稳定通信,所有组件通过洞洞板焊接的信号处理电路和杜邦线连接,确保了系统的可靠性和易维护性。
软件方面,通过华为云平台集成,系统实现了环境数据和药品存取记录的上传与存储,QT上位机应用提供了友好的用户界面,支持药品管理、历史数据查询和报警信息处理,大大提升了系统的智能化水平和操作便利性。
总体而言,该系统将嵌入式硬件、云平台和上位机软件有机结合,为医疗药品存储提供了一套高效、可靠的解决方案,具有广泛的应用前景和推广价值。
项目核心代码
c
#include <stdint.h>
// 寄存器定义
#define RCC_BASE 0x40021000
#define GPIOA_BASE 0x40010800
#define USART1_BASE 0x40013800
#define RCC_CR (*((volatile uint32_t *)(RCC_BASE + 0x00)))
#define RCC_CFGR (*((volatile uint32_t *)(RCC_BASE + 0x04)))
#define RCC_APB2ENR (*((volatile uint32_t *)(RCC_BASE + 0x18)))
#define GPIOA_CRL (*((volatile uint32_t *)(GPIOA_BASE + 0x00)))
#define GPIOA_CRH (*((volatile uint32_t *)(GPIOA_BASE + 0x04)))
#define GPIOA_IDR (*((volatile uint32_t *)(GPIOA_BASE + 0x08)))
#define GPIOA_ODR (*((volatile uint32_t *)(GPIOA_BASE + 0x0C)))
#define USART1_SR (*((volatile uint32_t *)(USART1_BASE + 0x00)))
#define USART1_DR (*((volatile uint32_t *)(USART1_BASE + 0x04)))
#define USART1_BRR (*((volatile uint32_t *)(USART1_BASE + 0x08)))
#define USART1_CR1 (*((volatile uint32_t *)(USART1_BASE + 0x0C)))
// 假设其他模块函数原型
extern void DHT22_Init(void);
extern float DHT22_ReadTemperature(void);
extern float DHT22_ReadHumidity(void);
extern void DoorSensor_Init(void);
extern int DoorSensor_Read(void);
extern void DS1302_Init(void);
extern void DS1302_GetTime(char *timeStr);
extern void ESP8266_Init(void);
extern void ESP8266_SendData(const char *data);
// 引脚定义
#define HEATER_PIN 2 // PA2
#define ALARM_PIN 3 // PA3
void SystemInit(void) {
// 启用HSE并配置PLL为72MHz
RCC_CR |= (1 << 16); // 启用HSE
while (!(RCC_CR & (1 << 17))); // 等待HSE就绪
RCC_CFGR |= (1 << 16); // PLL源为HSE
RCC_CFGR |= (9 << 18); // PLL倍频9倍,HSE 8MHz * 9 = 72MHz
RCC_CR |= (1 << 24); // 启用PLL
while (!(RCC_CR & (1 << 25))); // 等待PLL就绪
RCC_CFGR |= (2 << 0); // 切换系统时钟到PLL
while ((RCC_CFGR & 0x0C) != 0x08); // 等待切换完成
}
void GPIO_Init(void) {
// 启用GPIOA时钟
RCC_APB2ENR |= (1 << 2); // IOPAEN
// 配置PA1为输入(门传感器),假设带上拉
GPIOA_CRL &= ~(0xF << 4); // 清除PA1配置
GPIOA_CRL |= (0x8 << 4); // 输入模式,带上拉/下拉
// 配置PA2和PA3为推挽输出(温控设备和报警)
GPIOA_CRL &= ~(0xF << 8); // 清除PA2配置
GPIOA_CRL |= (0x3 << 8); // 推挽输出,50MHz
GPIOA_CRL &= ~(0xF << 12); // 清除PA3配置
GPIOA_CRL |= (0x3 << 12); // 推挽输出,50MHz
// 配置PA9为USART1 TX(复用推挽输出),PA10为USART1 RX(输入浮空)
GPIOA_CRH &= ~(0xF << 4); // 清除PA9配置
GPIOA_CRH |= (0xB << 4); // 复用推挽输出,50MHz
GPIOA_CRH &= ~(0xF << 8); // 清除PA10配置
GPIOA_CRH |= (0x4 << 8); // 输入浮空
}
void USART1_Init(void) {
// 启用USART1时钟
RCC_APB2ENR |= (1 << 14); // USART1EN
// 配置USART1波特率为9600,72MHz时钟
USART1_BRR = 0x1D4C; // 72MHz / 9600 = 7500 -> 0x1D4C
USART1_CR1 |= (1 << 13); // 启用USART
USART1_CR1 |= (1 << 3) | (1 << 2); // 启用TX和RX
}
void Delay_ms(uint32_t ms) {
for (uint32_t i = 0; i < ms * 1000; i++) {
__asm__("nop"); // 无操作指令实现延迟
}
}
int main(void) {
SystemInit();
GPIO_Init();
USART1_Init();
DHT22_Init();
DoorSensor_Init();
DS1302_Init();
ESP8266_Init();
float temperature, humidity;
int doorStatus;
char timeStr[20];
while (1) {
temperature = DHT22_ReadTemperature();
humidity = DHT22_ReadHumidity();
doorStatus = DoorSensor_Read();
DS1302_GetTime(timeStr);
// 温湿度阈值检查(示例阈值:温度>25°C或湿度>60%)
if (temperature > 25.0 || humidity > 60.0) {
GPIOA_ODR |= (1 << HEATER_PIN); // 开启温控设备
GPIOA_ODR |= (1 << ALARM_PIN); // 开启报警
} else {
GPIOA_ODR &= ~(1 << HEATER_PIN); // 关闭温控设备
GPIOA_ODR &= ~(1 << ALARM_PIN); // 关闭报警
}
// 准备数据并上传到华为云
char data[100];
sprintf(data, "Time: %s, Temp: %.2f, Humidity: %.2f, Door: %d", timeStr, temperature, humidity, doorStatus);
ESP8266_SendData(data);
Delay_ms(5000); // 每5秒执行一次
}
}
总结
本系统基于STM32F103C8T6微控制器核心板,成功设计并实现了一个智能医疗药品存储柜系统,能够实时监测柜内温湿度及门开关状态,并在温湿度超标时自动启动温控设备并触发报警,确保了药品存储环境的安全与稳定。该系统通过集成多种传感器和执行器,实现了高效的本地控制与数据处理。
硬件组成包括DHT22温湿度传感器用于环境监测、干簧管门磁传感器检测门状态、DS1302时钟模块记录精确时间、以及ESP8266-01S Wi-Fi模块负责云平台通信。所有组件通过洞洞板焊接的信号处理电路和杜邦线连接,确保了系统的可靠性和扩展性,为后续功能升级提供了基础。
数据上传至华为云平台,实现了药品存取记录和环境数据的远程存储与访问,用户可以通过云服务实时监控柜内状态并接收报警信息,提升了医疗药品管理的智能化和远程化水平。此外,系统支持与QT上位机软件的交互,便于药品管理、历史数据查询和报警处理,增强了用户体验和操作便利性。
总体而言,该系统结合了嵌入式技术、物联网云平台和上位机软件,为医疗行业提供了一种高效、可靠的药品存储解决方案,具有较高的实用价值和推广前景。