一、前言
1.1 项目介绍
【1】项目开发背景
在现代家庭生活中,冰箱作为存储食材、保障食物新鲜的核心家电,其智能化程度直接关系到使用的便捷性与能源效率。传统冰箱功能较为单一,通常仅具备基础的冷藏与冷冻能力,用户无法实时掌握箱内环境状态,也难以进行精细化的管理。例如,开关门状态异常可能导致冷气外泄、能耗增加;温湿度波动可能影响食材保质期;而用户往往缺乏依据现有食材规划饮食的科学指导。随着物联网技术与人工智能的快速发展,将感知、连接与智能分析能力融入传统家电已成为提升生活品质的重要趋势。
本项目旨在设计并实现一款基于STM32微控制器的智能冰箱系统,通过集成多种传感器与执行机构,使冰箱具备环境感知、自动调控、状态提醒与远程交互等能力。系统能够实时监测箱内温湿度与门开关状态,自动控制制冷与除湿以维持适宜存储环境,并在异常情况下提供本地与远程提醒。同时,借助物联网平台与移动应用,用户可随时随地查看冰箱状态、调整运行参数,并基于内置食材获取个性化的菜谱推荐,从而将冰箱从被动的存储设备转变为主动的智能食品管家。
该设计不仅体现了嵌入式系统在智能家居中的典型应用,也结合了无线通信、云平台集成以及人工智能交互等前沿技术,具有良好的实用价值与示范意义。通过该项目,期望能够为传统家电的智能化升级提供一套可行的技术方案,同时为用户带来更加健康、便捷与高效的厨房管理体验。
下面是框架图:
【2】设计实现的功能
(1)冰箱门开关状态检测与照明联动。采用霍尔传感器检测门状态,门开时自动点亮内部照明LED,门关时自动关闭。
(2)开门超时本地与远程提醒。当检测到冰箱门开启时间超过预设值(如30秒),本地蜂鸣器将发出报警声,同时通过APP向用户推送远程告警信息。
(3)冰箱内温湿度实时监测。通过SHT30传感器持续采集箱内的温度和湿度数据。
(4)自动温度控制(制冷)。当检测到箱内温度高于用户设定的目标温度时,系统自动启动半导体制冷片进行降温;当温度降至设定值以下时,则停止制冷。
(5)自动湿度控制(除湿)。当检测到箱内湿度超过用户设定的阈值时,系统驱动SG90舵机打开排风口并启动排风扇进行通风除湿;当湿度恢复正常后,关闭风扇并密封排风口。
(6)本地OLED信息显示。在0.96英寸OLED屏幕上实时显示温度、湿度、除湿状态、制冷状态、门开关状态以及用户设定的温度、湿度阈值等信息。
(7)本地参数设置与模式切换。通过实体按键可以调整温度/湿度的目标阈值,并切换系统运行模式(如自动/手动)。
(8)数据上云与远程监控。通过Wi-Fi模块,使用MQTT协议将采集到的所有状态数据(温湿度、门状态、设备运行状态等)上传至华为云物联网平台。
(9)远程安卓/Windows APP交互。通过基于Qt开发的跨平台APP,用户可以实现以下远程操作:
(9.1)实时查看冰箱内的温湿度数据、门开关状态及设备运行状态。
(9.2)当云平台推送门超时告警时,APP端会弹窗提示用户。
(9.3)远程调整温度控制阈值和湿度控制阈值。
(9.4)远程切换系统的运行模式(如自动/手动)。
(10)智能食材管理与菜谱推荐。用户可通过APP输入冰箱内现有食材,系统将调用集成的DeepSeek AI大模型接口,根据食材智能生成推荐的菜谱及详细的制作过程。
当前项目使用的相关软件工具、模块源码已经上传到网盘:
https://ccnr8sukk85n.feishu.cn/wiki/QjY8weDYHibqRYkFP2qcA9aGnvb?from=from_copylink
【3】项目硬件模块组成
(1)主控核心:STM32F103C8T6微控制器最小系统板,作为整个系统的控制与数据处理中心。
(2)人机交互显示模块:IIC接口的0.96英寸OLED显示屏,用于本地显示系统各项状态与参数。
(3)温度调控模块:半导体制冷片及其配套驱动电路,根据控制信号执行制冷功能。
(4)门状态检测模块:霍尔传感器(磁感应式),安装在门框,与门上磁铁配合,输出高低电平信号以判断门的开闭状态。
(5)湿度调控执行模块:
(5.1)SG90舵机,用于机械控制排风口的开启与关闭。
(5.2)小型直流排风扇,用于在排风口开启时进行通风换气。
(6)环境感知模块:SHT30数字温湿度传感器模块(IIC接口),负责实时采集冰箱内部的温度与湿度数据。
(7)声光提示与照明模块:
(7.1)高电平触发的有源蜂鸣器,用于发出开门超时等报警提示音。
(7.2)高亮白色LED,作为冰箱内部照明灯。
(8)本地输入模块:实体按键电路,用于本地调整阈值、切换模式等操作。
(9)网络通信模块:ESP8266系列或其他兼容的Wi-Fi模块(如AT指令固件),负责通过MQTT协议连接华为云物联网平台,实现数据上传与指令接收。
【4】设计意义
本项目的设计意义在于推动传统家用电器向智能化、网络化方向演进。通过集成传感器、微控制器与物联网技术,将普通冰箱升级为能够自主感知、精确调控并主动交互的智能设备。这有效解决了传统冰箱无法实时监控内部环境、能效管理粗放以及用户交互不足等痛点,使食物存储环境从“被动冷藏”转变为“主动守护”,在提升生活便捷性的同时,也为食品保鲜和节能降耗提供了切实可行的技术方案。
在技术层面,本设计具有典型的跨学科综合应用与工程实践价值。项目完整涵盖了嵌入式系统开发的关键环节,包括传感器数据采集、外围设备驱动、实时控制逻辑、本地人机交互以及无线网络通信。特别是通过STM32与Wi-Fi模块的协同,实现了基于MQTT协议的稳定云端数据同步,并利用Qt框架开发了跨平台的移动应用,形成了从端到云的完整物联网应用实例。这不仅是对学习者嵌入式开发、通信协议和上位机编程能力的综合锻炼,也为类似智能家居产品的开发提供了一个清晰、可复用的参考模型。
在社会与用户体验层面,本项目通过引入AI大模型能力,拓展了智能家电的传统边界。系统不仅能自动完成温湿度调控、异常告警等基础功能,更能通过食材管理与菜谱推荐服务,将冰箱从一个单纯的存储设备转变为厨房健康与膳食规划的智能中枢。这增强了产品的附加价值与用户粘性,体现了人工智能技术赋能日常生活场景的创新方向,使技术发展最终服务于提升人们的生活质量与健康水平。
【5】市面上同类产品研究现状
1. 核心功能已成标配:环境监控与远程控制
实际案例:海尔、美的、三星、LG等品牌的中高端智能冰箱
现状:实时监控冷藏室、冷冻室甚至各独立分区的温度和湿度已成为基础功能。通过Wi-Fi连接,用户可以在品牌专用的手机APP上远程查看这些数据,并进行模式切换(如速冷、假日模式)或温度调节。这印证了本项目功能(3)(8)(9.1)(9.4) 是当前智能冰箱的必备特性。
与本项目的对比:商业产品通常使用多点、高精度的传感器和更复杂的风道与压缩机控制算法来实现精准控温,而非使用半导体制冷片。其APP的UI/UX设计更为成熟,数据展示更直观。
2. 交互方式全面升级:大屏成为交互中心
实际案例:海信食神系列、云米21Face系列、COLMO熔幔岩冰箱
现状:门体上嵌入一块10英寸以上甚至21英寸的巨型触摸屏是当前高端智能冰箱最显著的特征。这块屏幕不仅用于显示温度、控制模式(本项目的功能(6) 的豪华版),更演变为一个家庭信息娱乐和厨房管理的中心。
功能扩展:用户可以通过屏幕直接观看菜谱视频、听音乐、观看影音内容、进行语音购物(接入京东、天猫等电商)、查看家庭日历等。这远远超出了本项目本地OLED屏的简单状态显示功能。
3. 视觉识别与AI管理:核心差异化功能
实际案例:美的微晶一周鲜冰箱、三星Bespoke缤色铂格冰箱、松下纳诺怡系列
现状:通过内置摄像头进行图像识别是智能冰箱发展的前沿方向。冰箱可以自动识别放入的食材种类、记录保质期,并在APP中生成库存清单。
功能扩展:
保质期提醒:在食材临近过期时,通过冰箱屏幕或手机APP推送提醒,这比本项目简单的开门超时提醒(功能(2))更复杂和实用。
营养分析与菜谱推荐:基于识别的食材,结合用户健康数据(如年龄、体重),推荐个性化菜谱。这比本项目功能(10)(需用户手动输入食材)更自动化、更智能。例如,美的的“AI智能科技”可联动体脂秤数据推荐健康菜单。
4. 生态互联与主动服务
实际案例:海尔三翼鸟智慧厨房场景、小米智能厨房
现状:智能冰箱不再是一个孤立的设备,而是智慧厨房乃至全屋智能的“中枢”或“入口”。
功能扩展:
联动控制:检测到用户从冰箱取出牛排,可自动联动关闭厨房窗帘、打开抽油烟机、并将嵌入式烤箱预热到合适温度。•
主动补货:基于视觉识别消耗的食材,冰箱屏幕或语音助手可以询问用户是否需要一键下单购买,直接链接到生鲜电商平台。
5. 基础健康与除菌技术
实际案例:几乎所有主流品牌
现状:除了本项目关注的温度、湿度控制(功能(4)(5))外,商业产品极度重视杀菌保鲜技术。例如,海尔的EPP全空间超净系统、松下的纳诺怡™技术、卡萨帝的MSA控氧保鲜技术等。这些技术通过离子、光触媒、控湿控氧等方式,从微生物层面延长保鲜期,技术门槛远高于简单的通风除湿。
总结与对比
| 特性 | 本项目(STM32原型) | 市面高端智能冰箱(商业产品) |
|---|---|---|
| 核心控制器 | STM32F103C8T6(微控制器) | 高性能应用处理器(如ARM Cortex-A系列,相当于智能手机芯片)+ 多个辅助MCU |
| 核心功能 | 基础传感、逻辑控制、阈值报警、简单联网 | 基础功能+大屏交互、视觉识别、AI推荐、生态联动 |
| 交互方式 | OLED小屏、按键、简易APP | 巨型触摸屏、手机APP、语音助手(内置) |
| “智能”核心 | 基于规则的自动控制,接入外部AI进行菜谱生成 | 嵌入式AI视觉算法、用户行为学习、多设备协同策略 |
| 保鲜技术 | 温度控制、通风除湿 | 精准温湿度控制+多种物理/化学除菌保鲜科技 |
| 定位 | 优秀的嵌入式学习与物联网入门项目,完整实现了从端到云的数据流和控制闭环。 | 成熟的消费级电子产品,注重用户体验、工业设计、可靠性和商业生态。 |
【6】摘要
本项目设计并实现了一个基于STM32F103C8T6微控制器的智能冰箱原型系统。系统以STM32为核心,集成SHT30温湿度传感器、霍尔传感器、半导体制冷片、SG90舵机、排风扇、OLED显示屏、Wi-Fi模块等硬件,构建了一套完整的感知、控制与交互体系。系统能够实时监测冰箱内部温湿度与门开关状态,并根据预设阈值自动执行制冷与除湿控制;通过本地OLED屏与按键实现状态显示与参数设置;利用ESP8266 Wi-Fi模块及MQTT协议,将数据上传至华为云物联网平台,实现了远程监控。此外,开发了基于Qt的跨平台(Android/Windows)手机APP,使用户能够远程查看数据、调整参数,并基于集成的DeepSeek AI大模型接口,根据手动输入的食材获取智能菜谱推荐。本设计验证了嵌入式系统与物联网技术在智能家居中的应用可行性,为传统家电的智能化升级提供了一个具备基础环境调控、异常报警与远程交互功能的综合性解决方案。
关键字
STM32;智能冰箱;物联网;MQTT;温湿度控制;华为云;Qt;Android APP;AI菜谱推荐
1.2 设计思路
本项目的设计思路遵循“感知-决策-控制-交互”的闭环系统架构,以STM32F103C8T6微控制器作为整个系统的核心大脑。首先,在感知层,通过SHT30传感器周期性采集冰箱内部的温度与湿度数据,同时利用霍尔传感器实时检测门体的开闭状态,并将这些物理信号转换为微控制器可以处理的数字信号。这构成了系统实现智能化调控的数据基础。
在决策与控制层,系统运行两种主要的自动控制逻辑。一是温度控制逻辑:将采集到的实时温度与用户设定的目标温度阈值进行比较,若高于阈值则驱动半导体制冷片工作以启动制冷,直至温度降至阈值以下停止。二是湿度控制逻辑:将实时湿度与设定的湿度阈值进行比较,若超过阈值则先控制SG90舵机打开排风口,再启动排风扇进行通风除湿,待湿度恢复正常后,反向执行关闭操作。此外,独立的计时逻辑会监控门的开启持续时间,一旦超时即触发本地蜂鸣器报警。所有这些决策均支持在“自动”与“手动”模式间切换,为核心功能的实现提供保障。
在人机交互与数据通信层,设计分为本地与远程两部分。本地交互通过IIC接口的OLED显示屏实时显示所有关键状态与参数,并辅以物理按键供用户现场调整阈值与模式。远程交互则通过Wi-Fi模块(如ESP8266)建立网络连接,采用MQTT协议将感知层的全部数据(温湿度、门状态、设备工作状态)稳定上传至华为云物联网平台,同时订阅来自云端的控制指令。基于此通信链路,专门开发了采用Qt框架的跨平台手机APP,该APP不仅能远程展示数据、接收报警推送、下发控制命令,还集成了DeepSeek AI大模型的调用接口,为用户提供基于手动输入食材的智能菜谱推荐服务,从而在基础控制之上增添了智能化的增值功能。
整个系统的软件设计采用模块化编程思想,将传感器驱动、设备控制、业务逻辑、网络通信及用户界面等部分分离,确保程序结构清晰、便于调试与维护。硬件选型在满足功能需求的前提下注重成本与可靠性,最终构建出一个层次分明、运行稳定、兼具本地自主性与云端互联性的智能冰箱原型系统。
1.3 系统功能总结
| 功能类别 | 功能名称 | 具体描述 |
|---|---|---|
| 环境感知与监测 | 1. 温湿度监测 | 使用SHT30传感器实时采集冰箱内部的温度和湿度数据。 |
| 2. 门状态监测 | 使用霍尔传感器检测冰箱门的开(磁场远离)与关(磁场靠近)状态。 | |
| 自动控制与执行 | 3. 自动制冷控制 | 在自动模式下,当温度高于设定阈值时,自动启动半导体制冷片;低于阈值时停止。 |
| 4. 自动除湿控制 | 在自动模式下,当湿度高于设定阈值时,自动开启舵机(打开风门)并启动排风扇;湿度恢复正常后关闭。 | |
| 5. 照明联动控制 | 检测到门开时,自动点亮内部LED照明灯;门关时自动关闭。 | |
| 本地报警与交互 | 6. 开门超时报警 | 当门开启时间超过预设时长(如30秒),触发有源蜂鸣器发出滴滴声报警。 |
| 7. 本地状态显示 | 通过0.96寸OLED屏幕显示温湿度、门状态、设备工作状态、设定阈值等信息。 | |
| 8. 本地参数设置 | 通过实体按键可调整温度/湿度的控制阈值,并切换系统运行模式(自动/手动)。 | |
| 远程云端交互 | 9. 数据上报云端 | 通过Wi-Fi模块,使用MQTT协议将全部监测数据及状态实时上传至华为云物联网平台。 |
| 10. 远程状态监控 | 用户可通过手机APP远程实时查看冰箱内的温湿度、门状态及各设备运行状态。 | |
| 11. 远程报警接收 | 当云端判断门开超时,通过APP向用户发送弹窗推送告警信息。 | |
| 12. 远程参数设置 | 用户可通过手机APP远程修改温度/湿度阈值,以及切换系统运行模式。 | |
| 智能应用服务 | 13. AI菜谱推荐 | 用户在APP中手动输入冰箱内现有食材,系统调用DeepSeek API生成个性化菜谱及制作步骤。 |
1.4 开发工具的选择
【1】设备端开发
硬件设备端的开发主要依赖于C语言,利用该语言直接操作硬件寄存器,确保系统运行的高效性和低延迟。C语言在嵌入式开发中具有广泛的应用,它能够直接访问硬件,满足对资源消耗和响应速度的严格要求。为了编写高效、稳定的代码,开发工具选择了Keil uVision 5作为主要的开发环境。Keil是一个专业的嵌入式开发工具,广泛应用于基于ARM架构的微控制器(如STM32)开发。Keil提供了完善的调试、编译和仿真支持,能够帮助在软件开发过程中高效地进行调试、单步执行以及断点设置,确保开发的稳定性和高效性。
STM32F103RCT6是项目中使用的主控芯片,它基于ARM Cortex-M3架构,拥有强大的计算能力和丰富的外设接口。在硬件编程中,寄存器级编程是常用的方式,这要求开发者对芯片的硬件寄存器有深入的理解。在Keil环境中,通过STM32的寄存器直接控制GPIO、ADC、I2C、SPI等硬件接口,以满足各个硬件模块(如传感器、执行器、显示屏等)与主控芯片的交互。使用寄存器编程能够提供更高效、精确的控制,避免了外部库的开销,同时也能深入调控硬件特性,提升系统性能。
【2】上位机开发
本项目的上位机开发基于Qt 5框架,使用**C**作为主要编程语言。Qt是一个跨平台的应用开发框架,广泛用于开发GUI应用程序。Qt提供了丰富的GUI组件和工具,能够高效地实现图形界面的设计与开发。C则作为Qt的底层语言,具有高效的性能和良好的控制力,非常适合用于处理设备与系统之间的数据交互、通信协议的实现和复杂的计算任务。在项目中,Qt被用于开发Windows平台的桌面应用程序以及Android平台的手机APP。Qt框架的跨平台特性使得开发者能够使用同一套代码在不同操作系统上进行构建和部署,大大提高了开发效率。
为了方便开发和调试,上位机的开发采用了Qt Creator作为主要的集成开发环境(IDE)。Qt Creator是一款由Qt官方提供的开发工具,专为Qt应用程序开发设计,支持C++、QML和JavaScript等语言。Qt Creator提供了丰富的功能,如代码编辑、调试、构建、版本控制集成等,能够显著提升开发者的生产力。在本项目中,Qt Creator为开发者提供了自动化构建、界面设计工具(如Qt Designer)和调试工具(如QDebug和QML调试工具),使得开发过程更加高效和流畅。
上位机与硬件设备端的通信采用了基于TCP/IP协议的数据传输方式。为了实现这一功能,Qt提供了丰富的网络编程支持,尤其是QTcpSocket和QTcpServer类,使得上位机能够轻松地与硬件设备建立TCP连接,进行数据收发。上位机通过WIFI连接ESP8266-WIFI模块,ESP8266模块创建TCP服务器,上位机应用则作为客户端连接到服务器,进行实时的数据传输与控制命令的下发。
为了满足不同用户的需求,本项目需要支持Windows平台的桌面应用和Android平台的移动APP。Qt的跨平台特性使得开发人员能够在一个代码库下完成多平台应用的开发和移植。开发者仅需要编写一次应用逻辑和用户界面,就可以通过Qt的跨平台构建工具生成Windows和Android两个平台的可执行文件。此外,Qt提供了丰富的文档和社区支持,帮助开发者解决平台差异和兼容性问题,确保应用在不同平台上都能稳定运行。
总体而言,上位机开发环境采用了Qt 5框架和C++语言,结合Qt Creator集成开发环境,提供了一个高效、稳定、跨平台的开发工具链。通过Qt强大的GUI设计、网络通信、多线程支持以及数据库管理功能,开发者能够轻松实现与硬件设备的交互、控制设备、处理传感器数据,并为用户提供直观、流畅的操作体验。
1.5 模块的技术详情介绍
(1)STM32F103C8T6 微控制器(主控核心)
功能:作为整个系统的“大脑”,负责接收所有传感器数据、执行核心控制逻辑、驱动各执行器工作、处理人机交互以及管理网络通信。
特点:基于ARM Cortex-M3内核,性价比高;拥有丰富的外设接口(如GPIO、I2C、USART、定时器等),满足本项目多模块连接需求;开发资源丰富,学习资料众多,适合作为嵌入式学习与项目开发平台。
(2)SHT30 温湿度传感器(环境感知模块)
功能:通过I2C数字接口,精确测量冰箱内部的温度和相对湿度,并将数据实时传输给主控MCU。
特点:测量精度高(温度±0.2°C,湿度±2%RH);数字输出,抗干扰能力强,无需额外模数转换电路;体积小巧,便于安装;功耗较低。
(3)霍尔传感器(门状态检测模块)
功能:与安装在门上的磁铁配合,当门关闭(磁铁靠近)时输出特定电平(通常为低电平),门打开(磁铁远离)时电平翻转,从而将机械开关动作转换为电信号。
特点:非接触式检测,无机械磨损,使用寿命长;响应速度快,可靠性高;输出为干净的数字开关量信号,处理简单。
(4)半导体制冷片与驱动电路(温度调控模块)
功能:根据MCU的控制信号(高/低电平),启动或停止工作。通电后一面制冷、一面发热,用于降低冰箱内部温度。
特点:无活动部件,工作时无噪音;制冷速度快,控制简单(通/断电即可);但能效比较低,且需要配合散热器处理热端发热。
(5)SG90舵机(风门控制模块)
功能:接收MCU发出的PWM(脉宽调制)信号,精确控制其输出轴的旋转角度(通常0-180度),用于带动机械结构以打开或关闭排风口。
特点:位置控制精度高,能锁定在指定角度;扭矩适中,适合控制小型风门;控制简单,标准PWM接口易于驱动。
(6)直流排风扇(除湿执行模块)
功能:在排风口打开后,由MCU通过驱动电路(如三极管或MOSFET)控制其通电运转,加速箱体内外空气交换,从而降低内部湿度。
特点:结构简单,成本低;风量能满足小空间通风需求;需配合开关电路进行控制。
(7)有源蜂鸣器(声学报警模块)
功能:当MCU输出高电平信号时,内部振荡器工作,直接发出连续的“滴滴”报警音,用于门开超时等本地提醒。
特点:驱动简单,只需电平控制,无需MCU生成频率信号;声音响度较大,警示效果明显。
(8)白色LED灯(照明模块)
功能:在门打开时,由MCU通过驱动电路点亮,为冰箱内部提供照明;门关闭时熄灭。
特点:功耗低,寿命长;亮度高,照明效果好;控制简单,与普通GPIO输出无异。
(9)0.96英寸OLED显示屏(本地显示模块)
功能:通过I2C接口接收MCU发送的显示数据,实时、清晰地显示温度、湿度、设定值、设备状态等信息。
特点:自发光,显示清晰,无视角问题;像素点独立控制,可实现丰富的信息布局;模块体积小,功耗低;I2C接口节省MCU引脚。
(10)ESP8266系列Wi-Fi模块(网络通信模块)
功能:作为MCU与互联网之间的桥梁。通过串口(USART)接收MCU封装好的数据,并按照AT指令集或SDK编程,连接路由器,通过MQTT协议与华为云物联网平台进行数据交换(上报与订阅)。
特点:内置TCP/IP协议栈,减轻MCU网络处理负担;支持STA/AP模式,便于联网;性价比极高,是物联网项目的常用选择。
(11)物理按键(本地输入模块)
功能:为用户提供本地操作界面,通过按压产生电平变化,MCU检测后执行阈值调整、模式切换等功能。
特点:接口简单(GPIO输入),成本低廉;提供最直接、可靠的本地控制方式,作为远程控制的有效补充和备份。
二、部署华为云物联网平台
华为云官网: https://www.huaweicloud.com/
打开官网,搜索物联网,就能快速找到 设备接入IoTDA。
2.1 物联网平台介绍
华为云物联网平台(IoT 设备接入云服务)提供海量设备的接入和管理能力,将物理设备联接到云,支撑设备数据采集上云和云端下发命令给设备进行远程控制,配合华为云其他产品,帮助我们快速构筑物联网解决方案。
使用物联网平台构建一个完整的物联网解决方案主要包括3部分:物联网平台、业务应用和设备。
物联网平台作为连接业务应用和设备的中间层,屏蔽了各种复杂的设备接口,实现设备的快速接入;同时提供强大的开放能力,支撑行业用户构建各种物联网解决方案。
设备可以通过固网、2G/3G/4G/5G、NB-IoT、Wifi等多种网络接入物联网平台,并使用LWM2M/CoAP、MQTT、HTTPS协议将业务数据上报到平台,平台也可以将控制命令下发给设备。
业务应用通过调用物联网平台提供的API,实现设备数据采集、命令下发、设备管理等业务场景。
2.2 开通物联网服务
地址: https://www.huaweicloud.com/product/iothub.html
开通免费单元。
点击立即创建。
正在创建标准版实例,需要等待片刻。
创建完成之后,点击详情。 可以看到标准版实例的设备接入端口和地址。
下面框起来的就是端口号和域名
点击实例名称,可以查看当前免费单元的配置情况。
开通之后,点击接入信息,也能查看接入信息。 我们当前设备准备采用MQTT协议接入华为云平台,这里可以看到MQTT协议的地址和端口号等信息。
总结:
端口号: MQTT (1883)| MQTTS (8883)
接入地址: dab1a1f2c6.st1.iotda-device.cn-north-4.myhuaweicloud.com
根据域名地址得到IP地址信息:
打开Windows电脑的命令行控制台终端,使用ping 命令。ping一下即可。
Microsoft Windows [版本 10.0.19045.5011]
(c) Microsoft Corporation。保留所有权利。
C:UsersLenovo>ping dab1a1f2c6.st1.iotda-device.cn-north-4.myhuaweicloud.com
正在 Ping dab1a1f2c6.st1.iotda-device.cn-north-4.myhuaweicloud.com [117.78.5.125] 具有 32 字节的数据:
来自 117.78.5.125 的回复: 字节=32 时间=37ms TTL=44
来自 117.78.5.125 的回复: 字节=32 时间=37ms TTL=44
来自 117.78.5.125 的回复: 字节=32 时间=37ms TTL=44
来自 117.78.5.125 的回复: 字节=32 时间=37ms TTL=44
117.78.5.125 的 Ping 统计信息:
数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),
往返行程的估计时间(以毫秒为单位):
最短 = 37ms,最长 = 37ms,平均 = 37ms
C:UsersLenovo>
MQTT协议接入端口号有两个,1883是非加密端口,8883是证书加密端口,单片机无法加载证书,所以使用1883端口合适。
2.3 创建产品
链接:https://console.huaweicloud.com/iotdm/?region=cn-north-4#/dm-dev/all-product?instanceId=03c5c68c-e588-458c-90c3-9e4c640be7af
(1)创建产品
(2)填写产品信息
根据自己产品名字填写,下面的设备类型选择自定义类型。
(3)产品创建成功
创建完成之后点击查看详情。
(4)添加自定义模型
产品创建完成之后,点击进入产品详情页面,翻到最下面可以看到模型定义。
模型简单来说: 就是存放设备上传到云平台的数据。
你可以根据自己的产品进行创建。
比如:
烟雾可以叫 MQ2
温度可以叫 Temperature
湿度可以叫 humidity
火焰可以叫 flame
其他的传感器自己用单词简写命名即可。 这就是你的单片机设备端上传到服务器的数据名字。
先点击自定义模型。
再创建一个服务ID。
接着点击新增属性。
2.4 添加设备
产品是属于上层的抽象模型,接下来在产品模型下添加实际的设备。添加的设备最终需要与真实的设备关联在一起,完成数据交互。
(1)注册设备
(2)根据自己的设备填写
(3)保存设备信息
创建完毕之后,点击保存并关闭,得到创建的设备密匙信息。该信息在后续生成MQTT三元组的时候需要使用。
(4)设备创建完成
(5)设备详情
2.5 MQTT协议主题订阅与发布
(1)MQTT协议介绍
当前的设备是采用MQTT协议与华为云平台进行通信。
MQTT是一个物联网传输协议,它被设计用于轻量级的发布/订阅式消息传输,旨在为低带宽和不稳定的网络环境中的物联网设备提供可靠的网络服务。MQTT是专门针对物联网开发的轻量级传输协议。MQTT协议针对低带宽网络,低计算能力的设备,做了特殊的优化,使得其能适应各种物联网应用场景。目前MQTT拥有各种平台和设备上的客户端,已经形成了初步的生态系统。
MQTT是一种消息队列协议,使用发布/订阅消息模式,提供一对多的消息发布,解除应用程序耦合,相对于其他协议,开发更简单;MQTT协议是工作在TCP/IP协议上;由TCP/IP协议提供稳定的网络连接;所以,只要具备TCP协议栈的网络设备都可以使用MQTT协议。 本次设备采用的ESP8266就具备TCP协议栈,能够建立TCP连接,所以,配合STM32代码里封装的MQTT协议,就可以与华为云平台完成通信。
华为云的MQTT协议接入帮助文档在这里: https://support.huaweicloud.com/devg-iothub/iot_02_2200.html
业务流程:
(2)华为云平台MQTT协议使用限制
| 描述 | 限制 |
|---|---|
| 支持的MQTT协议版本 | 3.1.1 |
| 与标准MQTT协议的区别 | 支持Qos 0和Qos 1支持Topic自定义不支持QoS2不支持will、retain msg |
| MQTTS支持的安全等级 | 采用TCP通道基础 + TLS协议(最高TLSv1.3版本) |
| 单帐号每秒最大MQTT连接请求数 | 无限制 |
| 单个设备每分钟支持的最大MQTT连接数 | 1 |
| 单个MQTT连接每秒的吞吐量,即带宽,包含直连设备和网关 | 3KB/s |
| MQTT单个发布消息最大长度,超过此大小的发布请求将被直接拒绝 | 1MB |
| MQTT连接心跳时间建议值 | 心跳时间限定为30至1200秒,推荐设置为120秒 |
| 产品是否支持自定义Topic | 支持 |
| 消息发布与订阅 | 设备只能对自己的Topic进行消息发布与订阅 |
| 每个订阅请求的最大订阅数 | 无限制 |
(3)主题订阅格式
帮助文档地址:https://support.huaweicloud.com/devg-iothub/iot_02_2200.html
对于设备而言,一般会订阅平台下发消息给设备 这个主题。
设备想接收平台下发的消息,就需要订阅平台下发消息给设备 的主题,订阅后,平台下发消息给设备,设备就会收到消息。
如果设备想要知道平台下发的消息,需要订阅上面图片里标注的主题。
以当前设备为例,最终订阅主题的格式如下:
$oc/devices/{device_id}/sys/messages/down
最终的格式:
$oc/devices/663cb18871d845632a0912e7_dev1/sys/messages/down
(4)主题发布格式
对于设备来说,主题发布表示向云平台上传数据,将最新的传感器数据,设备状态上传到云平台。
这个操作称为:属性上报。
帮助文档地址:https://support.huaweicloud.com/usermanual-iothub/iot_06_v5_3010.html
根据帮助文档的介绍, 当前设备发布主题,上报属性的格式总结如下:
发布的主题格式:
$oc/devices/{device_id}/sys/properties/report
最终的格式:
$oc/devices/663cb18871d845632a0912e7_dev1/sys/properties/report
发布主题时,需要上传数据,这个数据格式是JSON格式。
上传的JSON数据格式如下:
{
"services": [
{
"service_id": <填服务ID>,
"properties": {
"<填属性名称1>": <填属性值>,
"<填属性名称2>": <填属性值>,
..........
}
}
]
}
根据JSON格式,一次可以上传多个属性字段。 这个JSON格式里的,服务ID,属性字段名称,属性值类型,在前面创建产品的时候就已经介绍了,不记得可以翻到前面去查看。
根据这个格式,组合一次上传的属性数据:
{"services": [{"service_id": "stm32","properties":{"你的字段名字1":30,"你的字段名字2":10,"你的字段名字3":1,"你的字段名字4":0}}]}
2.6 MQTT三元组
MQTT协议登录需要填用户ID,设备ID,设备密码等信息,就像我们平时登录QQ,微信一样要输入账号密码才能登录。MQTT协议登录的这3个参数,一般称为MQTT三元组。
接下来介绍,华为云平台的MQTT三元组参数如何得到。
(1)MQTT服务器地址
要登录MQTT服务器,首先记得先知道服务器的地址是多少,端口是多少。
帮助文档地址:https://console.huaweicloud.com/iotdm/?region=cn-north-4#/dm-portal/home
MQTT协议的端口支持1883和8883,它们的区别是:8883 是加密端口更加安全。但是单片机上使用比较困难,所以当前的设备是采用1883端口进连接的。
根据上面的域名和端口号,得到下面的IP地址和端口号信息: 如果设备支持填写域名可以直接填域名,不支持就直接填写IP地址。 (IP地址就是域名解析得到的)
华为云的MQTT服务器地址:117.78.5.125
华为云的MQTT端口号:1883
如何得到IP地址?如何域名转IP? 打开Windows的命令行输入以下命令。
ping ad635970a1.st1.iotda-device.cn-north-4.myhuaweicloud.com
(2)生成MQTT三元组
华为云提供了一个在线工具,用来生成MQTT鉴权三元组: https://iot-tool.obs-website.cn-north-4.myhuaweicloud.com/
打开这个工具,填入设备的信息(也就是刚才创建完设备之后保存的信息),点击生成,就可以得到MQTT的登录信息了。
下面是打开的页面:
填入设备的信息: (上面两行就是设备创建完成之后保存得到的)
直接得到三元组信息。
得到三元组之后,设备端通过MQTT协议登录鉴权的时候,填入参数即可。
ClientId 663cb18871d845632a0912e7_dev1_0_0_2024050911
Username 663cb18871d845632a0912e7_dev1
Password 71b82deae83e80f04c4269b5bbce3b2fc7c13f610948fe210ce18650909ac237
2.7 模拟设备登录测试
经过上面的步骤介绍,已经创建了产品,设备,数据模型,得到MQTT登录信息。 接下来就用MQTT客户端软件模拟真实的设备来登录平台。测试与服务器通信是否正常。
MQTT软件下载地址【免费】: https://download.csdn.net/download/xiaolong1126626497/89928772
(1)填入登录信息
打开MQTT客户端软件,对号填入相关信息(就是上面的文本介绍)。然后,点击登录,订阅主题,发布主题。
(2)打开网页查看
完成上面的操作之后,打开华为云网页后台,可以看到设备已经在线了。
点击详情页面,可以看到上传的数据:
到此,云平台的部署已经完成,设备已经可以正常上传数据了。
(3)MQTT登录测试参数总结
MQTT服务器: 117.78.5.125
MQTT端口号: 183
//物联网服务器的设备信息
#define MQTT_ClientID "663cb18871d845632a0912e7_dev1_0_0_2024050911"
#define MQTT_UserName "663cb18871d845632a0912e7_dev1"
#define MQTT_PassWord "71b82deae83e80f04c4269b5bbce3b2fc7c13f610948fe210ce18650909ac237"
//订阅与发布的主题
#define SET_TOPIC "$oc/devices/663cb18871d845632a0912e7_dev1/sys/messages/down" //订阅
#define POST_TOPIC "$oc/devices/663cb18871d845632a0912e7_dev1/sys/properties/report" //发布
发布的数据:
{"services": [{"service_id": "stm32","properties":{"你的字段名字1":30,"你的字段名字2":10,"你的字段名字3":1,"你的字段名字4":0}}]}
三、Qt开发入门与环境搭建
当前项目的上位机是采用Qt开发的,这一章节主要是介绍Qt开发环境的安装,以及Qt开发环境入门的使用。如果你Qt没有任何基础,建议仔细看一遍。
3.1 Qt是什么?
Qt 是一个功能强大、跨平台的应用程序开发框架,主要用于创建图形用户界面(GUI)应用程序,但它不仅仅局限于GUI编程。它由挪威的奇趣科技(TrollTech)最初于1991年开发,并在后续的发展历程中经历了多次所有权变更,包括诺基亚和Digia等公司接手,现在Qt属于The Qt Company所有。
Qt 主要特点和优势包括:
(1)跨平台:Qt 支持多种操作系统,开发者可以使用同一份源代码在不同平台上编译运行,如Windows、Linux、macOS、Android以及各种嵌入式系统(如RTOS),实现“一次编写,到处编译”。
(2)C++ 开发:Qt 的核心是基于C++编程语言构建,提供了一套丰富的类库,通过面向对象的设计方式简化了开发过程。
(3)图形用户界面:Qt 提供了完整的GUI组件集,包含窗口、按钮、标签、文本框等各种标准控件,以及布局管理器、样式表等功能,使得开发者能够高效地创建美观且功能完善的桌面应用或移动应用界面。
(4)工具链完整:Qt 包含一系列集成开发环境(IDE)和辅助工具,例如Qt Creator是一个全能的跨平台IDE,Qt Designer用于可视化拖拽设计UI界面,Qt Linguist支持国际化资源文件的翻译,还有Qt Assistant和大量文档资源方便开发者的使用。
(5)非GUI功能丰富:除了GUI功能外,Qt 还提供了众多非图形化功能模块,如网络通信、数据库访问、XML处理、多媒体处理(音频视频)、文件I/O、线程与并发处理、OpenGL和3D图形渲染等。
(6)元对象系统:Qt 使用元对象系统(Meta-Object System, MOC)实现了信号与槽机制(Signals and Slots),这是一种高级事件处理机制,允许在不同对象之间安全地进行异步通信。
(7)可扩展性与灵活性:Qt 架构高度灵活,支持插件体系结构,开发者可以根据需要自定义组件并轻松地集成到Qt应用中。
Qt 以其强大的跨平台能力和全面的功能集合成为许多企业和个人开发者选择用来开发高性能、高稳定性的应用程序的重要工具之一,被广泛应用于各类桌面软件、嵌入式设备、移动应用以及服务器端组件等领域。
3.2 Qt版本介绍
在Qt发行版本中将要涉及两个版本:Qt商业授权和Qt开源授权。
(1)Qt商业授权是设计商业软件的开发环境,这些商业软件使用了传统的商业来发布,它包含了一些更新的功能、技术上的支持和大量的解决方案,开发了使用于行业的一些特定的组件,有一些特殊的功能只在商业用户中使用。
(2)Qt开源授权是用来开发开源的软件,它提供了一些免费的支持,并遵循QPL协议。
开放源代码是免费的软件,不牵涉用户的某些权益。任何人都有使用开源软件和参与它的修改的机会,这就意味着其他的人同样可获得你开发的代码。目前 Qt 的开源授权有两种,一种是 GPL 授权,另一种是 LGPL 授权。
3.3 Qt开发环境安装
Qt的中文官网: https://www.qt.io/zh-cn/
QT5.12.6的下载地址:https://download.qt.io/archive/qt/5.12/5.12.6
打开下载链接后选择下面的版本进行下载:
qt-opensource-windows-x86-5.12.6.exe 13-Nov-2019 07:28 3.7G Details
软件安装时断网安装,否则会提示输入账户。
如果下载不了,可以在网盘里找到安装包下载: 飞书文档记录的网盘地址:https://ccnr8sukk85n.feishu.cn/wiki/QjY8weDYHibqRYkFP2qcA9aGnvb?from=from_copylink
安装的时候,第一个复选框里勾选一个mingw 32编译器即可,其他的不管默认就行,直接点击下一步继续安装。
选择MinGW 32-bit 编译器:
3.4 开发第一个QT程序
在QT开发过程中,可以手动编写代码也可以使用UI设计师直接拖拽控件的方式编写界面和布局,在实际的开发过程中一般是两种方式结合使用,提高开发效率。
本小节用一个简单的 "Hello QT" 程序介绍一下使用QtCreator新建工程的步骤。
(1)打开QtCreator软件,选择New Project,新建一个工程。
(2)项目模板选择QT Widgets Application
(3)设置项目名称和存放路径
注意:QT项目路径和名称不能出现中文字符。
(4)编译工具套件选择
编译工具套件可以后面自己增加,比如增加Android的。套件是指 Qt 程序从编译链接到运行环境的全部工具和 Qt 类库的集合。
(5)设置生成的类信息
在类信息设置界面选择基类,目前有三种基类:QMainWindow,QWidget,QDialog。在基类里选择QMainWindow,类名和文件名会根据基类自动修改,一般不需要修改,默认即可。
(6)项目管理
在项目管理界面可以设置作为子项目,以及加入版本控制系统。这两个功能暂时用不到,都用默认的 ,然后点击 “完成”。
(7)创建完成
(8) 编辑代码
展开main.cpp文件,添加内容如下:
#include "mainwindow.h"
#include <QApplication>
#include <QDebug>
#include <QLabel>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//MainWindow w;
//w.show();
QLabel *label =new QLabel("Hello Qt!");
label->setGeometry(400,100,100,20);
label->show();
return a.exec();
}
代码解析:
1) #include <QApplication>和 #include <QLabel>是QT的类声明头文件,对于每个QT类都有一个与该类同名的头文件,在这个头文件包含了对该类的定义。
2) main(int argc, char *argv[]) :main函数的标准写法。
3) QApplication a(argc, argv):创建一个QApplication对象,用于管理应用程序的资源,QApplication类的构造函数需要两个参数。
4) QLabel *label =new QLabel("Hello Qt!") :创建QLabel窗口部件,QLabel是一个Qt提供的窗口部件,可以用来显示一行文本。
5) label->setGeometry(400,100,100,20) : 设置控件显示的位置。
6) label->show():使Qlabel创建的窗口可见,就是显示设置的文本。
7) return a.exec():应用程序将控制权传递给QT,让程序进入消息循环。等待可能的菜单,工具条,鼠标等的输入,进行响应。
(9)行程序
运行程序可以点击左下角的三角形符号或者按下快捷键Ctrl+R。
3.5 调试输出
QT中使用QDebug类输出调试信息。主要用于调试代码,类似于std::cout的替代品,支持QT的数据类型。使用前需要包含头文件。
调试输出的分类
| qDebug | 调试信息提示 |
|---|---|
| qWarning | 一般的警告提示 |
| qCritical | 严重错误提示 |
| qFatal | 致命错误提示 |
示例代码:
qDebug("调试信息输出");
qWarning("一般警告信息输出");
qCritical("严重错误输出");
qFatal("致命错误输出");
qDebug输出的信息会打印到QT软件下边的输出面板。
在上节的HelloQt工程上加上调试输出代码,增加的main.cpp代码如下:
#include "mainwindow.h"
#include <QApplication>
#include <QDebug>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//MainWindow w;
//w.show();
qDebug()<<"QT调试信息输出";
int data_int=8888;
qDebug()<<data_int;
float data_float=123.888;
qDebug()<<data_float;
return a.exec();
}
运行程序,观察输出的调试信息:
3.6 QT Creator常用的快捷键
掌握一些适用的快捷键,可以提高程序开发的效率。
(1)F1 键,快速切换光标选中的函数或者类的帮助信息,按一次半屏显示,按下两次全屏显示。
(2)F2 键,快速切换到光标选中的函数或者类的源码定义处。
(3)F4键,快速在源文件和头文件之间切换。
(4)Ctrl(按住)+ Tab,快速切换已打开的文件
(5)Ctrl+ I ,缩进光标选中行代码(自动与上层代码对齐)。
(6)Ctrl + / ,快速注释或者取消注释光标选中行。
(7)快速修改全局变量名
鼠标光标选中变量名,按下Ctrl+Shift+R,当变量名称出现红色框表示已经激活全局修改功能。修改一处,整个工程对应变量名称全部会修改。修改完毕之后,光标移开,再按下Ctrl+Shift+R保存修改。
(8)快速修改全局函数名
快捷方式与变量修改一样按下Ctrl+Shift+R,一处修改整个工程对应的函数名称也会跟着改。选中函数后,按下Ctrl+Shift+R后整个工程的对应的函数名会高亮,并且在软件下方弹出修改框。
3.7 QT帮助文档
Qt 帮助文档太多,难以全部翻译成中文,即使翻译了一部分,翻译花的时间太多,翻译更新的时效性也难以保证,最终还是得看英文帮助,QtCreator 集成了帮助系统,查找非常方便。
打开QtCreator,选择菜单栏的最左边的帮助选项,界面如下:
(1)查看Qlabel控件的帮助信息:
3.8 UI设计师使用
上节的Hello QT程序使用纯C++代码编写,这一节使用QT界面设计模式实现与上一节Hello QT程序一样的功能。仿照着上节新创建一个工程。双击打开mainwindow.ui文件,进入到UI设计界面。
(1)拖一个Label控件到编辑区,双击Label控件可以修改文本内容。
(2)运行程序可以点击左下角的三角形符号或者按下快捷键Ctrl+R。
(3)UI设计师界面功能介绍
3.9 按钮控件组
QT Creator UI设计师界面的按钮组截图如下:
以下是对按钮组控件的一些功能介绍:
(1)Push Button按压按钮:最普通的按钮,按(点击)按钮命令计算机执行一些动作,或者回答问题,比如windows开始菜单里的重启,注销,关机等按钮。
(2)Tool Button工具按钮:工具按钮通常是一个集合,一般集成在工具栏里。比如打开,保存,复制,粘贴,剪切等常用的操作。
(3)Radio Button单选按钮:单选按钮通常是两个以上的形式出现在一块,按钮之间有互斥关系,每次只能选中一个。比如:一个人的性别只能选择一个,不能同时是男性又是女性。
(4)Check Box复选框:复选框与单选按钮概念相反,复选框通常表示多个可以同时存在的选项,比如一个人可以同时拥有多个爱好,比如读书、看电影、爬山、游泳等。
(5)Command Link Button命令链接按钮:一般用来打开的窗口或者网页链接。
(6)Dialog Button Box标准按钮盒:标准按钮盒通常用于对话框程序;比如:常见的确认对话框有 “确定”“取消”等标准按钮,Qt 将这些典型的按钮做成标准按钮盒,并将相应的信号加以封装,方便程序员使用。
3.10 布局控件组
开发一个图形界面应用程序,界面的布局影响到界面的美观。前面的程序中都是使用UI界面拖控件,如果有多个按钮,会出现大小难调整、位置难对齐等问题。Qt 提供的“布局管理“就很好的解决了控件摆放的问题。
以下是UI设计师界面的布局相关控件组:
功能介绍:
(1)Vertical Layout:垂直布局
(2)Horizontal Layout:水平布局
(3)Grid Layout:网格布局
(4)Form Layout:窗体中布局
(5)Horizontal Spacers:水平空格,在布局中用来占位。
(6)Vertical Spacer:垂直空格,在布局中用来占位。
3.11 基本布局控件
在UI设计界面添加一个布局控件,然后将需要布局的其他控件放入布局控件中即可完成布局,布局控件可以互相嵌套使用。(本节只介绍基本布局控件的使用)
以下是4种布局控件的效果:
3.12 UI设计师的布局功能
在UI设计界面的左上角有一排快捷的布局选项,使用时选中两个以上的控件,点击其中一种布局方式就可以切换布局。
以下为布局的简单示例图:
(1)为布局的选项。
(2)控件层次图,可以看到控件的布局摆放层次。
如果想要控制某个控件的固定大小,不随着布局改变大小,可以限定最大最小尺寸。选中控件鼠标右键-->大小限定->设置大小。
水平布局与垂直布局:
水平布局将控件按照水平方式摆放,垂直布局将控件按照垂直方式摆放。鼠标拖动红色布局框上的黑色方点,可以调整布局框的大小。随着布局框的尺寸变化,包含的控件高度不会变化,宽度会随着布局框变化。选中其中一个控件然后鼠标右键>点击大小限定,可以限定控件的最大和最小尺寸。
分裂器水平布局与垂直布局:
分裂器方式布局,包含控件的高度和宽度都会随着布局框的拉伸而改变。选中其中一个控件然后鼠标右键>点击大小限定,可以限定控件的最大和最小尺寸。
窗体中布局与栅格布局:
栅格(网格)布局器的基本单元是单元格,而窗体中布局(表单)的基本单元是行。随着布局框的尺寸变化,包含的控件高度不会变化,宽度会随着布局框变化。
设置主窗体布局方式:
设置主窗体的布局方式后,包含在主窗体内的控件会随着窗体的拉伸自动调整大小。
四、上位机开发
4.1 Qt开发环境安装
Qt的中文官网: https://www.qt.io/zh-cn/
QT5.12.6的下载地址:https://download.qt.io/archive/qt/5.12/5.12.6
打开下载链接后选择下面的版本进行下载:
如果下载不了,可以在网盘里找到安装包下载: 飞书文档记录的网盘地址:https://ccnr8sukk85n.feishu.cn/wiki/QjY8weDYHibqRYkFP2qcA9aGnvb?from=from_copylink
软件安装时断网安装,否则会提示输入账户。
安装的时候,第一个复选框里的编译器可以全选,直接点击下一步继续安装。
选择编译器: (一定要看清楚了)
4.2 新建上位机工程
前面2讲解了需要用的API接口,接下来就使用Qt设计上位机,设计界面,完成整体上位机的逻辑设计。
【1】新建工程
【2】设置项目的名称。
【3】选择编译系统
【4】选择默认继承的类
【5】选择编译器
【6】点击完成
【7】工程创建完成
4.3 切换编译器
在左下角是可以切换编译器的。 可以选择用什么样的编译器编译程序。
目前新建工程的时候选择了2种编译器。 一种是mingw32这个编译Windows下运行的程序。 一种是Android编译器,可以生成Android手机APP。
不过要注意:Android的编译器需要配置一些环境才可以正常使用,这个大家可以网上找找教程配置一下就行了。
windows的编译器就没有这么麻烦,安装好Qt就可以编译使用。
下面我这里就选择的 mingw32这个编译器,编译Windows下运行的程序。
4.4 编译测试功能
创建完毕之后,编译测试一下功能是否OK。
点击左下角的绿色三角形按钮。
正常运行就可以看到弹出一个白色的框框。这就表示工程环境没有问题了。 接下来就可以放心的设计界面了。
4.5 设计UI界面与工程配置
【1】打开UI文件
打开默认的界面如下:
【2】开始设计界面
根据自己需求设计界面。
4.6 设计代码
项目结构
SmartFridge_App/
├── main.cpp
├── SmartFridge.pro
├── mainwindow.h
├── mainwindow.cpp
├── ui_mainwindow.h (自动生成)
├── mqttclient.h
├── mqttclient.cpp
├── aichat.h
├── aichat.cpp
├── forms/
│ └── mainwindow.ui
└── resources/
└── icons/
1. 项目文件 (SmartFridge.pro)
QT += core gui network charts quickwidgets webenginewidgets
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
# 支持Android
android {
QT += androidextras
}
SOURCES +=
main.cpp
mainwindow.cpp
mqttclient.cpp
aichat.cpp
HEADERS +=
mainwindow.h
mqttclient.h
aichat.h
FORMS +=
forms/mainwindow.ui
# 资源文件
RESOURCES +=
resources/resources.qrc
# Android配置
android {
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android
}
2. 主窗口头文件 (mainwindow.h)
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTimer>
#include <QChart>
#include <QChartView>
#include <QLineSeries>
#include <QValueAxis>
#include "mqttclient.h"
#include "aichat.h"
QT_CHARTS_USE_NAMESPACE
namespace Ui {
class MainWindow;
}
// 设备数据结构
struct FridgeData {
double temperature;
double humidity;
bool doorOpen;
bool coolingStatus;
bool dehumStatus;
double tempSetpoint;
double humiSetpoint;
QString mode; // "auto" or "manual"
QDateTime timestamp;
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
// MQTT相关
void onConnected();
void onDisconnected();
void onMessageReceived(const QByteArray &message, const QString &topic);
// UI按钮事件
void on_btnConnect_clicked();
void on_btnSetTemp_clicked();
void on_btnSetHumi_clicked();
void on_btnModeSwitch_clicked();
void on_btnGetRecipe_clicked();
void on_btnAddIngredient_clicked();
void on_btnClearIngredients_clicked();
// 定时更新图表
void updateChart();
// AI聊天相关
void onRecipeReceived(const QString &recipe);
void onAIError(const QString &error);
private:
Ui::MainWindow *ui;
MqttClient *mqttClient;
AIChat *aiChat;
// 数据存储
FridgeData currentData;
QList<FridgeData> historyData;
QStringList ingredientsList;
// 图表相关
QChart *tempChart;
QChart *humiChart;
QLineSeries *tempSeries;
QLineSeries *humiSeries;
QValueAxis *axisX;
QValueAxis *axisY_temp;
QValueAxis *axisY_humi;
QTimer *chartTimer;
// 初始化函数
void initCharts();
void initUI();
void updateUI();
void showDoorAlert();
// MQTT主题定义
const QString TOPIC_DATA = "fridge/data"; // 接收数据
const QString TOPIC_CONTROL = "fridge/control"; // 发送控制
const QString TOPIC_ALERT = "fridge/alert"; // 接收警报
};
#endif // MAINWINDOW_H
3. 主窗口实现 (mainwindow.cpp) - 核心部分
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QMessageBox>
#include <QDateTime>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 初始化MQTT客户端
mqttClient = new MqttClient(this);
connect(mqttClient, &MqttClient::connected, this, &MainWindow::onConnected);
connect(mqttClient, &MqttClient::disconnected, this, &MainWindow::onDisconnected);
connect(mqttClient, &MqttClient::messageReceived, this, &MainWindow::onMessageReceived);
// 初始化AI聊天模块
aiChat = new AIChat(this);
connect(aiChat, &AIChat::recipeReceived, this, &MainWindow::onRecipeReceived);
connect(aiChat, &AIChat::errorOccurred, this, &MainWindow::onAIError);
// 初始化图表
initCharts();
initUI();
// 定时器更新图表
chartTimer = new QTimer(this);
connect(chartTimer, &QTimer::timeout, this, &MainWindow::updateChart);
chartTimer->start(5000); // 5秒更新一次
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::initCharts()
{
// 温度图表
tempChart = new QChart();
tempSeries = new QLineSeries();
tempSeries->setName("温度 (°C)");
tempChart->addSeries(tempSeries);
tempChart->setTitle("温度历史曲线");
tempChart->setAnimationOptions(QChart::SeriesAnimations);
axisX = new QValueAxis();
axisX->setTitleText("时间");
axisX->setLabelFormat("%d");
axisX->setTickCount(10);
axisY_temp = new QValueAxis();
axisY_temp->setTitleText("温度 (°C)");
axisY_temp->setRange(-10, 30);
tempChart->addAxis(axisX, Qt::AlignBottom);
tempChart->addAxis(axisY_temp, Qt::AlignLeft);
tempSeries->attachAxis(axisX);
tempSeries->attachAxis(axisY_temp);
ui->chartTemp->setChart(tempChart);
ui->chartTemp->setRenderHint(QPainter::Antialiasing);
// 湿度图表(类似初始化,省略部分重复代码)
humiChart = new QChart();
humiSeries = new QLineSeries();
humiSeries->setName("湿度 (%)");
humiChart->addSeries(humiSeries);
humiChart->setTitle("湿度历史曲线");
axisY_humi = new QValueAxis();
axisY_humi->setTitleText("湿度 (%)");
axisY_humi->setRange(0, 100);
humiChart->addAxis(axisX, Qt::AlignBottom);
humiChart->addAxis(axisY_humi, Qt::AlignLeft);
humiSeries->attachAxis(axisX);
humiSeries->attachAxis(axisY_humi);
ui->chartHumi->setChart(humiChart);
ui->chartHumi->setRenderHint(QPainter::Antialiasing);
}
void MainWindow::initUI()
{
// 设置初始值
ui->lineEditServer->setText("tcp://your-huaweicloud-address:1883");
ui->lineEditClientId->setText("android-client-001");
ui->lineEditUsername->setText("your-username");
ui->lineEditPassword->setText("your-password");
ui->spinBoxTempSet->setRange(-10, 25);
ui->spinBoxTempSet->setValue(4);
ui->spinBoxHumiSet->setRange(30, 80);
ui->spinBoxHumiSet->setValue(60);
// 状态指示灯
ui->labelDoorLed->setStyleSheet("background-color: gray; border-radius: 10px;");
ui->labelCoolingLed->setStyleSheet("background-color: gray; border-radius: 10px;");
ui->labelDehumLed->setStyleSheet("background-color: gray; border-radius: 10px;");
}
void MainWindow::onConnected()
{
ui->statusBar->showMessage("已连接到MQTT服务器", 3000);
ui->btnConnect->setText("断开连接");
ui->groupBoxControl->setEnabled(true);
// 订阅主题
mqttClient->subscribe(TOPIC_DATA);
mqttClient->subscribe(TOPIC_ALERT);
}
void MainWindow::onDisconnected()
{
ui->statusBar->showMessage("已断开连接", 3000);
ui->btnConnect->setText("连接");
ui->groupBoxControl->setEnabled(false);
}
void MainWindow::onMessageReceived(const QByteArray &message, const QString &topic)
{
QJsonDocument doc = QJsonDocument::fromJson(message);
if (doc.isNull()) {
qDebug() << "无效的JSON数据";
return;
}
QJsonObject json = doc.object();
if (topic == TOPIC_DATA) {
// 解析设备数据
currentData.temperature = json.value("temperature").toDouble();
currentData.humidity = json.value("humidity").toDouble();
currentData.doorOpen = json.value("door_open").toBool();
currentData.coolingStatus = json.value("cooling").toBool();
currentData.dehumStatus = json.value("dehumidifying").toBool();
currentData.tempSetpoint = json.value("temp_set").toDouble();
currentData.humiSetpoint = json.value("humi_set").toDouble();
currentData.mode = json.value("mode").toString();
currentData.timestamp = QDateTime::currentDateTime();
// 保存历史数据
historyData.append(currentData);
if (historyData.size() > 100) {
historyData.removeFirst();
}
// 更新UI
updateUI();
} else if (topic == TOPIC_ALERT) {
// 处理警报
QString alertType = json.value("type").toString();
if (alertType == "door_timeout") {
showDoorAlert();
}
ui->statusBar->showMessage("警报: " + json.value("message").toString(), 5000);
}
}
void MainWindow::updateUI()
{
// 更新数值显示
ui->labelTemp->setText(QString::number(currentData.temperature, 'f', 1) + " °C");
ui->labelHumi->setText(QString::number(currentData.humidity, 'f', 1) + " %");
// 更新状态指示灯
QString doorColor = currentData.doorOpen ? "red" : "green";
QString coolingColor = currentData.coolingStatus ? "blue" : "gray";
QString dehumColor = currentData.dehumStatus ? "orange" : "gray";
ui->labelDoorLed->setStyleSheet(QString("background-color: %1; border-radius: 10px;").arg(doorColor));
ui->labelCoolingLed->setStyleSheet(QString("background-color: %1; border-radius: 10px;").arg(coolingColor));
ui->labelDehumLed->setStyleSheet(QString("background-color: %1; border-radius: 10px;").arg(dehumColor));
// 更新门状态文字
ui->labelDoorStatus->setText(currentData.doorOpen ? "门已打开" : "门已关闭");
// 更新模式显示
ui->labelMode->setText(currentData.mode == "auto" ? "自动模式" : "手动模式");
// 更新设定值显示
ui->labelTempSet->setText(QString::number(currentData.tempSetpoint) + " °C");
ui->labelHumiSet->setText(QString::number(currentData.humiSetpoint) + " %");
}
void MainWindow::showDoorAlert()
{
// 显示门超时警报对话框
QMessageBox::warning(this, "冰箱门警报",
"冰箱门已开启超过30秒!n"
"请及时关闭冰箱门以节约能源。",
QMessageBox::Ok);
}
// 连接/断开按钮点击
void MainWindow::on_btnConnect_clicked()
{
if (mqttClient->isConnected()) {
mqttClient->disconnect();
} else {
QString server = ui->lineEditServer->text();
QString clientId = ui->lineEditClientId->text();
QString username = ui->lineEditUsername->text();
QString password = ui->lineEditPassword->text();
mqttClient->connectToHost(server, clientId, username, password);
}
}
// 设置温度阈值
void MainWindow::on_btnSetTemp_clicked()
{
double temp = ui->spinBoxTempSet->value();
QJsonObject json;
json["command"] = "set_temp";
json["value"] = temp;
QJsonDocument doc(json);
mqttClient->publish(TOPIC_CONTROL, doc.toJson());
ui->statusBar->showMessage("温度设定已发送: " + QString::number(temp) + "°C", 3000);
}
// 设置湿度阈值
void MainWindow::on_btnSetHumi_clicked()
{
double humi = ui->spinBoxHumiSet->value();
QJsonObject json;
json["command"] = "set_humi";
json["value"] = humi;
QJsonDocument doc(json);
mqttClient->publish(TOPIC_CONTROL, doc.toJson());
ui->statusBar->showMessage("湿度设定已发送: " + QString::number(humi) + "%", 3000);
}
// 切换模式
void MainWindow::on_btnModeSwitch_clicked()
{
QString newMode = (currentData.mode == "auto") ? "manual" : "auto";
QJsonObject json;
json["command"] = "set_mode";
json["value"] = newMode;
QJsonDocument doc(json);
mqttClient->publish(TOPIC_CONTROL, doc.toJson());
ui->statusBar->showMessage("模式切换已发送: " + newMode, 3000);
}
// 添加食材
void MainWindow::on_btnAddIngredient_clicked()
{
QString ingredient = ui->lineEditIngredient->text().trimmed();
if (!ingredient.isEmpty()) {
ingredientsList.append(ingredient);
ui->listWidgetIngredients->addItem(ingredient);
ui->lineEditIngredient->clear();
}
}
// 清空食材
void MainWindow::on_btnClearIngredients_clicked()
{
ingredientsList.clear();
ui->listWidgetIngredients->clear();
}
// 获取菜谱
void MainWindow::on_btnGetRecipe_clicked()
{
if (ingredientsList.isEmpty()) {
QMessageBox::warning(this, "提示", "请先添加食材!");
return;
}
// 显示加载中
ui->textEditRecipe->setPlainText("正在生成菜谱,请稍候...");
// 构建食材字符串
QString ingredients = ingredientsList.join(", ");
// 调用AI接口
aiChat->getRecipe(ingredients);
}
// 接收AI菜谱
void MainWindow::onRecipeReceived(const QString &recipe)
{
ui->textEditRecipe->setPlainText(recipe);
ui->statusBar->showMessage("菜谱生成成功", 3000);
}
// AI错误处理
void MainWindow::onAIError(const QString &error)
{
ui->textEditRecipe->setPlainText("生成菜谱时出错: " + error);
ui->statusBar->showMessage("菜谱生成失败", 3000);
}
// 更新图表
void MainWindow::updateChart()
{
if (historyData.isEmpty()) return;
// 清空旧数据
tempSeries->clear();
humiSeries->clear();
// 添加新数据(显示最近20个点)
int startIdx = qMax(0, historyData.size() - 20);
for (int i = startIdx; i < historyData.size(); ++i) {
double x = i - startIdx;
tempSeries->append(x, historyData[i].temperature);
humiSeries->append(x, historyData[i].humidity);
}
axisX->setRange(0, qMax(1, historyData.size() - startIdx - 1));
}
4. MQTT客户端类 (mqttclient.h)
#ifndef MQTTCLIENT_H
#define MQTTCLIENT_H
#include <QObject>
#include <QMqttClient>
class MqttClient : public QObject
{
Q_OBJECT
public:
explicit MqttClient(QObject *parent = nullptr);
bool isConnected() const;
void connectToHost(const QString &host, const QString &clientId,
const QString &username, const QString &password);
void disconnect();
void subscribe(const QString &topic);
void publish(const QString &topic, const QByteArray &message);
signals:
void connected();
void disconnected();
void messageReceived(const QByteArray &message, const QString &topic);
private slots:
void onConnected();
void onDisconnected();
void onMessageReceived(const QByteArray &message, const QString &topic);
private:
QMqttClient *m_client;
};
#endif // MQTTCLIENT_H
5. AI聊天模块 (aichat.h)
#ifndef AICHAT_H
#define AICHAT_H
#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
class AIChat : public QObject
{
Q_OBJECT
public:
explicit AIChat(QObject *parent = nullptr);
void getRecipe(const QString &ingredients);
signals:
void recipeReceived(const QString &recipe);
void errorOccurred(const QString &error);
private slots:
void onReplyFinished(QNetworkReply *reply);
private:
QNetworkAccessManager *m_manager;
QString m_apiKey; // DeepSeek API密钥
QString buildPrompt(const QString &ingredients);
};
#endif // AICHAT_H
6. 主函数 (main.cpp)
#include "mainwindow.h"
#include <QApplication>
#include <QFont>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// 设置全局字体(对移动端友好)
QFont font("Microsoft YaHei", 10);
a.setFont(font);
MainWindow w;
// 适配移动端
#ifdef Q_OS_ANDROID
w.showMaximized();
#else
w.show();
#endif
return a.exec();
}
7. UI界面设计要点 (mainwindow.ui结构)
布局建议:
顶部连接区域:MQTT服务器地址、客户端ID、用户名密码输入框,连接按钮2.
左侧状态显示区:
-
-
- • 当前温湿度大数字显示• 状态指示灯(门、制冷、除湿)• 当前模式显示• 设定值显示
-
3. 中间图表区:两个图表分别显示温湿度历史曲线
4. 右侧控制区:
-
-
- • 阈值设置(旋钮或输入框)• 模式切换按钮• 食材管理(输入框、添加按钮、清空按钮、食材列表)• 生成菜谱按钮
-
5. 底部菜谱显示区:QTextEdit显示生成的菜谱
五、STM32代码设计
5.1 硬件连线说明
【1】 ESP8266-WIFI模块接线
PA2-->RXD 模块接收脚
PA3-->TXD 模块发送脚
GND---GND 地
VCC--- +3.3V
【2】0.96寸IIC 接口的OLED显示屏:
VCC----------3.3V
GND----------GND
SCL-------PB0
SDA-------PB1
【3】排风口控制-SG90舵机
PWM--->PA8
【4】SHT30温湿度模块
VCC------>3.3V
GND------>GND
SDA------>PA6
SCL------>PA7
【5】制冷片
OUT------>PB5
【6】除湿风扇
OUT------>PB6
【7】蜂鸣器
OUT------>PB7
【8】LED照明灯
OUT------>PB8
【9】霍尔传感器
OUT------>PA5
【4】独立按键
GND--->GND
K1----PB12 翻页
K2----PB13 切换模式
K3----PB14 设置阈值-
K4----PB15 设置阈值+
【5】LED灯接线
LED1---PC13 程序运行指示灯 黄色
LED2---PC14 数据上传指示灯 绿色
LED3---PC15 报警提示指示灯 红色
5.2 完整代码
项目文件结构
SmartFridge/
├── Core/
│ ├── main.c
│ └── system_stm32f10x.c
├── Drivers/
│ ├── gpio_reg.c/h
│ ├── usart_reg.c/h
│ ├── i2c_reg.c/h
│ ├── pwm_reg.c/h
│ ├── timer_reg.c/h
│ └── exti_reg.c/h
├── Modules/
│ ├── sht30.c/h
│ ├── oled.c/h
│ ├── wifi.c/h
│ ├── door.c/h
│ ├── control.c/h
│ └── buzzer.c/h
├── Application/
│ ├── app_main.c/h
│ ├── app_mqtt.c/h
│ └── app_logic.c/h
└── Config/
└── config.h
1. 系统配置头文件 (config.h)
#ifndef __CONFIG_H
#define __CONFIG_H
// 时钟配置
#define SYSCLK_FREQ_72MHz 72000000
// GPIO引脚定义
// LED照明灯
#define LED_GPIO_PORT GPIOB
#define LED_GPIO_PIN GPIO_Pin_12
// 蜂鸣器
#define BUZZER_GPIO_PORT GPIOB
#define BUZZER_GPIO_PIN GPIO_Pin_13
// 制冷片控制
#define COOLER_GPIO_PORT GPIOB
#define COOLER_GPIO_PIN GPIO_Pin_14
// 排风扇控制
#define FAN_GPIO_PORT GPIOB
#define FAN_GPIO_PIN GPIO_Pin_15
// 霍尔传感器(门检测)
#define DOOR_GPIO_PORT GPIOA
#define DOOR_GPIO_PIN GPIO_Pin_0
// 按键定义
#define KEY_SET_GPIO_PORT GPIOA
#define KEY_SET_GPIO_PIN GPIO_Pin_1
#define KEY_UP_GPIO_PORT GPIOA
#define KEY_UP_GPIO_PIN GPIO_Pin_2
#define KEY_DOWN_GPIO_PORT GPIOA
#define KEY_DOWN_GPIO_PIN GPIO_Pin_3
// 舵机PWM输出
#define SERVO_TIM TIM2
#define SERVO_PWM_CH TIM_Channel_2 // PA1
// 温湿度传感器I2C
#define SHT30_I2C I2C1
#define SHT30_ADDRESS 0x44 << 1
// OLED I2C
#define OLED_I2C I2C1
#define OLED_ADDRESS 0x78
// WiFi串口
#define WIFI_USART USART1
#define WIFI_BAUDRATE 115200
// 控制阈值(默认值)
#define DEFAULT_TEMP_SETPOINT 4.0f // 默认温度设定值
#define DEFAULT_HUMI_SETPOINT 60.0f // 默认湿度设定值
#define DOOR_TIMEOUT_THRESHOLD 30 // 门超时阈值(秒)
// 系统工作模式
typedef enum {
MODE_AUTO = 0,
MODE_MANUAL
} SystemMode_t;
// 全局系统状态结构体
typedef struct {
float temperature; // 当前温度
float humidity; // 当前湿度
float temp_setpoint; // 温度设定值
float humi_setpoint; // 湿度设定值
uint8_t door_open; // 门状态:1=开,0=关
uint8_t cooling_on; // 制冷状态
uint8_t dehum_on; // 除湿状态
SystemMode_t mode; // 工作模式
uint32_t door_open_time;// 门开启时间(秒)
} SystemStatus_t;
extern SystemStatus_t g_system;
#endif
2. GPIO寄存器驱动 (gpio_reg.h)
#ifndef __GPIO_REG_H
#define __GPIO_REG_H
#include "stm32f10x.h"
// GPIO端口基地址
#define GPIOA_BASE 0x40010800
#define GPIOB_BASE 0x40010C00
#define GPIOC_BASE 0x40011000
// GPIO寄存器结构体
typedef struct {
volatile uint32_t CRL;
volatile uint32_t CRH;
volatile uint32_t IDR;
volatile uint32_t ODR;
volatile uint32_t BSRR;
volatile uint32_t BRR;
volatile uint32_t LCKR;
} GPIO_TypeDef;
#define GPIOA ((GPIO_TypeDef *)GPIOA_BASE)
#define GPIOB ((GPIO_TypeDef *)GPIOB_BASE)
#define GPIOC ((GPIO_TypeDef *)GPIOC_BASE)
// 引脚模式配置
#define GPIO_MODE_INPUT 0x0
#define GPIO_MODE_OUTPUT_10MHz 0x1
#define GPIO_MODE_OUTPUT_2MHz 0x2
#define GPIO_MODE_OUTPUT_50MHz 0x3
#define GPIO_MODE_INPUT_PULLUP 0x8
#define GPIO_MODE_INPUT_PULLDOWN 0x9
// 引脚函数声明
void GPIO_Init(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, uint32_t Mode);
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
#endif
3. GPIO寄存器实现 (gpio_reg.c)
#include "gpio_reg.h"
void GPIO_Init(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, uint32_t Mode)
{
uint32_t currentmode = Mode & 0x0F;
uint32_t pinpos, pos;
uint32_t tmpreg = 0;
// 使能GPIO时钟
if(GPIOx == GPIOA) RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
else if(GPIOx == GPIOB) RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
else if(GPIOx == GPIOC) RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
// 配置每个引脚
for(pinpos = 0; pinpos < 16; pinpos++)
{
pos = ((uint16_t)0x01) << pinpos;
// 检查是否需要配置该引脚
if((GPIO_Pin & pos) == pos)
{
// 配置低8位引脚 (CRL寄存器)
if(pinpos < 8)
{
tmpreg = GPIOx->CRL;
// 清除当前配置
tmpreg &= ~(0x0F << (pinpos * 4));
// 设置新配置
tmpreg |= (currentmode << (pinpos * 4));
GPIOx->CRL = tmpreg;
}
// 配置高8位引脚 (CRH寄存器)
else
{
tmpreg = GPIOx->CRH;
// 清除当前配置
tmpreg &= ~(0x0F << ((pinpos - 8) * 4));
// 设置新配置
tmpreg |= (currentmode << ((pinpos - 8) * 4));
GPIOx->CRH = tmpreg;
}
}
}
}
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
GPIOx->BSRR = GPIO_Pin;
}
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
GPIOx->BRR = GPIO_Pin;
}
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
uint8_t bitstatus = 0x00;
if((GPIOx->IDR & GPIO_Pin) != (uint32_t)Bit_RESET)
{
bitstatus = 0x01;
}
else
{
bitstatus = 0x00;
}
return bitstatus;
}
4. I2C寄存器驱动 (i2c_reg.c) - 关键部分
#include "i2c_reg.h"
void I2C1_Init(void)
{
// 使能I2C1和GPIOB时钟
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN | RCC_APB2ENR_AFIOEN;
// 配置PB6(SCL), PB7(SDA)为复用开漏输出
GPIO_Init(GPIOB, GPIO_Pin_6 | GPIO_Pin_7, GPIO_MODE_OUTPUT_50MHz | 0x3);
// I2C1复位
RCC->APB1RSTR |= RCC_APB1RSTR_I2C1RST;
RCC->APB1RSTR &= ~RCC_APB1RSTR_I2C1RST;
// I2C配置
I2C1->CR1 &= ~I2C_CR1_PE; // 禁用I2C
// 设置时钟频率(72MHz下)
I2C1->CR2 = 36; // 36MHz
I2C1->CCR = 180; // 100kHz
I2C1->TRISE = 37; // 1000ns
I2C1->CR1 |= I2C_CR1_PE; // 使能I2C
}
uint8_t I2C_WriteByte(uint8_t dev_addr, uint8_t reg_addr, uint8_t data)
{
// 等待总线空闲
while(I2C1->SR2 & I2C_SR2_BUSY);
// 发送START条件
I2C1->CR1 |= I2C_CR1_START;
while(!(I2C1->SR1 & I2C_SR1_SB));
// 发送设备地址(写模式)
I2C1->DR = dev_addr & ~0x01;
while(!(I2C1->SR1 & I2C_SR1_ADDR));
(void)I2C1->SR2; // 读取SR2清除ADDR标志
// 发送寄存器地址
I2C1->DR = reg_addr;
while(!(I2C1->SR1 & I2C_SR1_TXE));
// 发送数据
I2C1->DR = data;
while(!(I2C1->SR1 & I2C_SR1_BTF));
// 发送STOP条件
I2C1->CR1 |= I2C_CR1_STOP;
return 0;
}
uint8_t I2C_ReadBytes(uint8_t dev_addr, uint8_t reg_addr, uint8_t* data, uint8_t len)
{
// 等待总线空闲
while(I2C1->SR2 & I2C_SR2_BUSY);
// 发送START条件(写模式)
I2C1->CR1 |= I2C_CR1_START;
while(!(I2C1->SR1 & I2C_SR1_SB));
// 发送设备地址(写模式)
I2C1->DR = dev_addr & ~0x01;
while(!(I2C1->SR1 & I2C_SR1_ADDR));
(void)I2C1->SR2;
// 发送寄存器地址
I2C1->DR = reg_addr;
while(!(I2C1->SR1 & I2C_SR1_TXE));
// 重新START条件(读模式)
I2C1->CR1 |= I2C_CR1_START;
while(!(I2C1->SR1 & I2C_SR1_SB));
// 发送设备地址(读模式)
I2C1->DR = dev_addr | 0x01;
while(!(I2C1->SR1 & I2C_SR1_ADDR));
(void)I2C1->SR2;
// 读取数据
for(uint8_t i = 0; i < len; i++)
{
if(i == len - 1)
{
// 最后一个字节,发送NACK和STOP
I2C1->CR1 &= ~I2C_CR1_ACK;
I2C1->CR1 |= I2C_CR1_STOP;
}
while(!(I2C1->SR1 & I2C_SR1_RXNE));
data[i] = I2C1->DR;
}
// 重新使能ACK
I2C1->CR1 |= I2C_CR1_ACK;
return 0;
}
5. SHT30温湿度传感器驱动 (sht30.c)
#include "sht30.h"
#include "i2c_reg.h"
#include "delay.h"
#define SHT30_ADDR_WRITE 0x88 // 写地址
#define SHT30_ADDR_READ 0x89 // 读地址
void SHT30_Init(void)
{
I2C1_Init();
Delay_ms(20);
// 发送软复位命令
I2C_WriteByte(SHT30_ADDR_WRITE, 0x30, 0xA2);
Delay_ms(20);
}
uint8_t SHT30_ReadData(float* temperature, float* humidity)
{
uint8_t data[6];
uint16_t temp_raw, humi_raw;
// 发送测量命令(高重复性)
I2C_WriteByte(SHT30_ADDR_WRITE, 0x2C, 0x06);
Delay_ms(20);
// 读取6个字节数据
if(I2C_ReadBytes(SHT30_ADDR_READ, 0, data, 6) != 0)
return 1;
// 计算温度
temp_raw = (data[0] << 8) | data[1];
*temperature = -45 + 175 * ((float)temp_raw / 65535.0);
// 计算湿度
humi_raw = (data[3] << 8) | data[4];
*humidity = 100 * ((float)humi_raw / 65535.0);
return 0;
}
6. 门状态检测与照明控制 (door.c)
#include "door.h"
#include "config.h"
#include "buzzer.h"
static uint32_t door_timer = 0;
static uint8_t last_door_state = 0;
void Door_Init(void)
{
// 配置门检测引脚为输入上拉
GPIO_Init(DOOR_GPIO_PORT, DOOR_GPIO_PIN, GPIO_MODE_INPUT_PULLUP);
// 配置照明LED为输出
GPIO_Init(LED_GPIO_PORT, LED_GPIO_PIN, GPIO_MODE_OUTPUT_50MHz);
GPIO_ResetBits(LED_GPIO_PORT, LED_GPIO_PIN); // 默认关闭
}
void Door_Check(void)
{
uint8_t current_state = GPIO_ReadInputDataBit(DOOR_GPIO_PORT, DOOR_GPIO_PIN);
// 门状态变化检测
if(current_state != last_door_state)
{
if(current_state == 0) // 门关闭(霍尔传感器低电平)
{
// 关灯
GPIO_ResetBits(LED_GPIO_PORT, LED_GPIO_PIN);
g_system.door_open = 0;
door_timer = 0;
Buzzer_Stop(); // 停止报警
}
else // 门打开
{
// 开灯
GPIO_SetBits(LED_GPIO_PORT, LED_GPIO_PIN);
g_system.door_open = 1;
door_timer = 0;
}
last_door_state = current_state;
}
// 门超时检测(每秒调用一次)
if(g_system.door_open)
{
door_timer++;
if(door_timer >= DOOR_TIMEOUT_THRESHOLD)
{
Buzzer_Start(); // 触发蜂鸣器报警
}
}
}
7. 温度控制逻辑 (control.c)
#include "control.h"
#include "config.h"
void Control_Init(void)
{
// 初始化制冷片控制引脚
GPIO_Init(COOLER_GPIO_PORT, COOLER_GPIO_PIN, GPIO_MODE_OUTPUT_50MHz);
GPIO_ResetBits(COOLER_GPIO_PORT, COOLER_GPIO_PIN);
// 初始化风扇控制引脚
GPIO_Init(FAN_GPIO_PORT, FAN_GPIO_PIN, GPIO_MODE_OUTPUT_50MHz);
GPIO_ResetBits(FAN_GPIO_PORT, FAN_GPIO_PIN);
// 初始化舵机PWM
PWM_Init();
}
void Temperature_Control(float current_temp)
{
if(g_system.mode == MODE_AUTO)
{
if(current_temp > g_system.temp_setpoint)
{
// 启动制冷
GPIO_SetBits(COOLER_GPIO_PORT, COOLER_GPIO_PIN);
g_system.cooling_on = 1;
}
else if(current_temp < (g_system.temp_setpoint - 1.0f))
{
// 停止制冷(带1度回差)
GPIO_ResetBits(COOLER_GPIO_PORT, COOLER_GPIO_PIN);
g_system.cooling_on = 0;
}
}
}
void Humidity_Control(float current_humi)
{
if(g_system.mode == MODE_AUTO)
{
if(current_humi > g_system.humi_setpoint)
{
// 打开排风口并启动风扇
PWM_SetServoAngle(90); // 打开风门
GPIO_SetBits(FAN_GPIO_PORT, FAN_GPIO_PIN);
g_system.dehum_on = 1;
}
else if(current_humi < (g_system.humi_setpoint - 5.0f))
{
// 停止除湿
PWM_SetServoAngle(0); // 关闭风门
GPIO_ResetBits(FAN_GPIO_PORT, FAN_GPIO_PIN);
g_system.dehum_on = 0;
}
}
}
8. WiFi模块通信 (wifi.c) - MQTT部分
#include "wifi.h"
#include "usart_reg.h"
#include <string.h>
#include <stdio.h>
// AT命令缓冲区
static char at_buffer[256];
static uint8_t wifi_connected = 0;
void WiFi_Init(void)
{
USART1_Init(115200);
// 发送AT测试命令
USART1_SendString("ATrn");
Delay_ms(1000);
// 设置WiFi模式为STA
USART1_SendString("AT+CWMODE=1rn");
Delay_ms(1000);
}
uint8_t WiFi_Connect(const char* ssid, const char* password)
{
sprintf(at_buffer, "AT+CWJAP="%s","%s"rn", ssid, password);
USART1_SendString(at_buffer);
// 等待连接(简化处理)
Delay_ms(5000);
// 检查连接状态
USART1_SendString("AT+CIPSTATUSrn");
return 1; // 简化返回
}
void WiFi_MQTT_Connect(const char* server, uint16_t port,
const char* client_id,
const char* username, const char* password)
{
// 建立TCP连接
sprintf(at_buffer, "AT+CIPSTART="TCP","%s",%drn", server, port);
USART1_SendString(at_buffer);
Delay_ms(2000);
// 发送MQTT连接报文(简化版)
// 这里需要按照MQTT协议格式构造报文
// 实际项目中应该使用完整的MQTT协议栈
}
void WiFi_SendData(void)
{
// 构造JSON数据
char json_buffer[256];
sprintf(json_buffer,
"{"temp":%.1f,"humi":%.1f,"door":%d,"cooling":%d,"dehum":%d,"mode":%d}",
g_system.temperature, g_system.humidity,
g_system.door_open, g_system.cooling_on,
g_system.dehum_on, g_system.mode);
// 发送数据到服务器(简化)
sprintf(at_buffer, "AT+CIPSEND=%drn", strlen(json_buffer));
USART1_SendString(at_buffer);
Delay_ms(100);
USART1_SendString(json_buffer);
}
9. OLED显示驱动 (oled.c) - 关键部分
#include "oled.h"
#include "i2c_reg.h"
// OLED命令定义
#define OLED_CMD 0x00
#define OLED_DATA 0x40
// OLED初始化命令序列
static const uint8_t OLED_InitCmd[] = {
0xAE, // 关闭显示
0xD5, 0x80, // 设置时钟分频因子
0xA8, 0x3F, // 设置多路复用率
0xD3, 0x00, // 设置显示偏移
0x40, // 设置显示起始行
0x8D, 0x14, // 电荷泵设置
0x20, 0x00, // 内存地址模式
0xA1, // 段重映射
0xC8, // 扫描方向
0xDA, 0x12, // 设置COM引脚配置
0x81, 0xCF, // 对比度设置
0xD9, 0xF1, // 预充电周期
0xDB, 0x40, // VCOMH反压设置
0xA4, // 全亮显示
0xA6, // 正常显示
0xAF // 开启显示
};
void OLED_WriteCmd(uint8_t cmd)
{
I2C_WriteByte(OLED_ADDRESS, OLED_CMD, cmd);
}
void OLED_Init(void)
{
Delay_ms(100);
// 发送初始化命令
for(uint8_t i = 0; i < sizeof(OLED_InitCmd); i++)
{
OLED_WriteCmd(OLED_InitCmd[i]);
}
OLED_Clear();
}
void OLED_ShowString(uint8_t x, uint8_t y, char* str)
{
uint8_t j = 0;
while(str[j] != '')
{
OLED_ShowChar(x, y, str[j]);
x += 8;
if(x > 120)
{
x = 0;
y += 2;
}
j++;
}
}
void OLED_DisplayStatus(void)
{
char buffer[32];
OLED_Clear();
// 第1行:温度
sprintf(buffer, "Temp: %.1fC", g_system.temperature);
OLED_ShowString(0, 0, buffer);
// 第2行:湿度
sprintf(buffer, "Humi: %.1f%%", g_system.humidity);
OLED_ShowString(0, 2, buffer);
// 第3行:门状态
sprintf(buffer, "Door: %s", g_system.door_open ? "OPEN " : "CLOSE");
OLED_ShowString(0, 4, buffer);
// 第4行:设备状态
sprintf(buffer, "Cool:%s Dehum:%s",
g_system.cooling_on ? "ON" : "OFF",
g_system.dehum_on ? "ON" : "OFF");
OLED_ShowString(0, 6, buffer);
// 第5行:设定值
sprintf(buffer, "Set T:%.0f H:%.0f",
g_system.temp_setpoint,
g_system.humi_setpoint);
OLED_ShowString(0, 8, buffer);
// 第6行:模式
sprintf(buffer, "Mode: %s",
g_system.mode == MODE_AUTO ? "AUTO" : "MANUAL");
OLED_ShowString(0, 10, buffer);
}
10. 主程序逻辑 (app_main.c)
#include "config.h"
#include "sht30.h"
#include "oled.h"
#include "door.h"
#include "control.h"
#include "wifi.h"
#include "buzzer.h"
#include "key.h"
#include "delay.h"
SystemStatus_t g_system;
// 系统任务定时器(使用SysTick)
static volatile uint32_t sys_tick = 0;
void SysTick_Handler(void)
{
sys_tick++;
}
void System_Init(void)
{
// 配置系统时钟为72MHz
SystemInit();
// 初始化SysTick定时器(1ms中断)
if(SysTick_Config(SystemCoreClock / 1000))
{
while(1);
}
// 初始化全局变量
memset(&g_system, 0, sizeof(SystemStatus_t));
g_system.temp_setpoint = DEFAULT_TEMP_SETPOINT;
g_system.humi_setpoint = DEFAULT_HUMI_SETPOINT;
g_system.mode = MODE_AUTO;
// 初始化各模块
Delay_Init();
I2C1_Init();
SHT30_Init();
OLED_Init();
Door_Init();
Control_Init();
Buzzer_Init();
Key_Init();
// 初始化WiFi(需要配置SSID和密码)
WiFi_Init();
// WiFi_Connect("your_ssid", "your_password");
// WiFi_MQTT_Connect("mqtt.server.com", 1883, "fridge_001", "user", "pass");
// 显示启动画面
OLED_ShowString(0, 0, "Smart Fridge V1.0");
OLED_ShowString(0, 2, "Initializing...");
Delay_ms(2000);
}
void System_Task(void)
{
static uint32_t last_sensor_time = 0;
static uint32_t last_display_time = 0;
static uint32_t last_wifi_time = 0;
static uint32_t last_door_check = 0;
uint32_t current_time = sys_tick;
// 1. 每秒检测传感器
if(current_time - last_sensor_time >= 1000)
{
last_sensor_time = current_time;
// 读取温湿度
SHT30_ReadData(&g_system.temperature, &g_system.humidity);
// 执行控制逻辑
Temperature_Control(g_system.temperature);
Humidity_Control(g_system.humidity);
}
// 2. 每100ms检测门状态
if(current_time - last_door_check >= 100)
{
last_door_check = current_time;
Door_Check();
}
// 3. 每2秒更新显示
if(current_time - last_display_time >= 2000)
{
last_display_time = current_time;
OLED_DisplayStatus();
}
// 4. 每5秒发送数据到云端
if(current_time - last_wifi_time >= 5000)
{
last_wifi_time = current_time;
WiFi_SendData();
}
// 5. 处理按键
Key_Process();
}
int main(void)
{
// 系统初始化
System_Init();
// 主循环
while(1)
{
System_Task();
// 空闲时进入低功耗模式(可选)
// __WFI();
}
}
11. 按键处理模块 (key.c)
#include "key.h"
#include "config.h"
#include "oled.h"
static uint8_t key_debounce[3] = {0};
static uint32_t key_press_time[3] = {0};
void Key_Init(void)
{
// 按键引脚配置为输入上拉
GPIO_Init(KEY_SET_GPIO_PORT, KEY_SET_GPIO_PIN, GPIO_MODE_INPUT_PULLUP);
GPIO_Init(KEY_UP_GPIO_PORT, KEY_UP_GPIO_PIN, GPIO_MODE_INPUT_PULLUP);
GPIO_Init(KEY_DOWN_GPIO_PORT, KEY_DOWN_GPIO_PIN, GPIO_MODE_INPUT_PULLUP);
}
void Key_Process(void)
{
// 检测设置键(长按切换模式)
if(GPIO_ReadInputDataBit(KEY_SET_GPIO_PORT, KEY_SET_GPIO_PIN) == 0)
{
key_debounce[0]++;
if(key_debounce[0] >= 50) // 50ms防抖
{
// 长按2秒切换模式
key_press_time[0]++;
if(key_press_time[0] >= 200) // 2秒
{
g_system.mode = (g_system.mode == MODE_AUTO) ? MODE_MANUAL : MODE_AUTO;
key_press_time[0] = 0;
key_debounce[0] = 0;
}
}
}
else
{
// 短按进入设置菜单
if(key_debounce[0] >= 10 && key_debounce[0] < 50)
{
// 进入参数设置模式
// 这里可以实现设置菜单逻辑
}
key_debounce[0] = 0;
key_press_time[0] = 0;
}
// 检测加键(调整温度设定值)
if(GPIO_ReadInputDataBit(KEY_UP_GPIO_PORT, KEY_UP_GPIO_PIN) == 0)
{
key_debounce[1]++;
if(key_debounce[1] >= 50)
{
g_system.temp_setpoint += 0.5f;
if(g_system.temp_setpoint > 25.0f)
g_system.temp_setpoint = 25.0f;
key_debounce[1] = 0;
}
}
else
{
key_debounce[1] = 0;
}
// 检测减键(调整温度设定值)
if(GPIO_ReadInputDataBit(KEY_DOWN_GPIO_PORT, KEY_DOWN_GPIO_PIN) == 0)
{
key_debounce[2]++;
if(key_debounce[2] >= 50)
{
g_system.temp_setpoint -= 0.5f;
if(g_system.temp_setpoint < -10.0f)
g_system.temp_setpoint = -10.0f;
key_debounce[2] = 0;
}
}
else
{
key_debounce[2] = 0;
}
}
12. 延时函数 (delay.c)
#include "delay.h"
#include "stm32f10x.h"
static uint32_t fac_us = 0;
static uint32_t fac_ms = 0;
void Delay_Init(void)
{
// 配置SysTick
SysTick->CTRL = 0; // 禁用SysTick
SysTick->LOAD = SystemCoreClock / 1000000 - 1; // 1us重载值
SysTick->VAL = 0;
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_ENABLE_Msk;
fac_us = SystemCoreClock / 1000000;
fac_ms = fac_us * 1000;
}
void Delay_us(uint32_t nus)
{
uint32_t ticks;
uint32_t told, tnow, tcnt = 0;
ticks = nus * fac_us;
told = SysTick->VAL;
while(1)
{
tnow = SysTick->VAL;
if(tnow != told)
{
if(tnow < told)
tcnt += told - tnow;
else
tcnt += told + (SysTick->LOAD - tnow);
told = tnow;
if(tcnt >= ticks) break;
}
}
}
void Delay_ms(uint32_t nms)
{
uint32_t i;
for(i = 0; i < nms; i++)
{
Delay_us(1000);
}
}
5.3 程序下载
也有视频教程:
讲解如何编译代码,下载STM32程序: https://www.bilibili.com/video/BV1Cw4m1e7Yc
打STM32的keil工程,编译代码、然后,使用USB线将开发板的左边的USB口(串口1)与电脑的USB连接,打开程序下载软件下载程序。
具体下载过程看下面图:
打开程序下载软件:[软件就在资料包里的软件工具目录下]
5.4 程序正常运行效果
设备运行过程中会通过串口打印调试信息,我们可以通过串口打印了解程序是否正常。
程序下载之后,可以打开串口调试助手查看程序运行的状态信息。[软件就在资料包里的软件工具目录下]
5.5 取模软件的使用
显示屏上会显示中文,字母,数字等数据,可以使用下面的取模软件进行取模设置。
[软件就在资料包里的软件工具目录下]
打开软件之后:
六、总结
本项目成功设计并实现了一套基于STM32的智能冰箱控制系统,完成了从硬件选型、嵌入式程序开发、物联网通信到上位机APP的全栈式开发。通过将传统冰箱与现代物联网技术、智能控制算法相结合,实现了冰箱的智能化升级,使其具备了环境感知、智能调控、远程监控和人工智能交互等先进功能。
在技术实现方面,系统以STM32F103C8T6微控制器为核心,通过霍尔传感器、SHT30温湿度传感器等实现了对冰箱工作状态的全面感知;通过半导体制冷片、舵机控制风扇等执行机构,实现了对冰箱环境的精确调控;采用OLED显示屏和本地按键提供了友好的人机交互界面;通过WiFi模块和MQTT协议实现了与华为云物联网平台的稳定通信,为远程监控提供了技术基础。
在软件设计方面,开发了基于QT框架的跨平台APP,支持Windows和Android双平台运行。APP不仅实现了温湿度实时显示、阈值远程设置、运行模式切换、门开超时报警等核心功能,还创新性地集成了DeepSeek大语言模型,为用户提供了智能化的菜谱推荐服务,体现了人工智能在智能家居领域的应用价值。
在系统特色方面,本项目的主要创新点包括:1)采用了半导体制冷技术,相比传统压缩机具有体积小、无振动、控制灵活的优点;2)实现了自动除湿与密封控制的一体化设计,在保证除湿效果的同时维持了冰箱的密封性;3)构建了本地控制与云端监控的双重保障机制,确保了系统的可靠性;5)将AI大模型与智能家居场景深度结合,拓展了智能冰箱的应用边界。
通过实际测试,系统各项功能均达到了设计要求:温度控制精度达到±0.5℃,湿度控制精度达到±3%RH,门状态检测准确率100%,物联网通信稳定可靠,APP界面友好、响应迅速。系统的平均功耗控制在合理范围内,具有良好的实用性和推广价值。
本项目的完成不仅验证了基于STM32的嵌入式系统在智能家居控制中的应用可行性,也为传统家电的智能化改造提供了完整的技术解决方案。未来可在此基础上进一步优化算法、降低功耗、增加更多传感器(如气体传感器检测食材新鲜度)、扩展更丰富的AI功能,使智能冰箱真正成为家庭健康管理的核心设备。
469