基于STM32设计的智能桌面暖风机(华为云IOT)

一、前言

1.1 项目开发背景

随着智能家居技术的迅猛发展,传统家用电器正逐步向智能化方向转型。暖风机作为冬季广泛使用的取暖设备,其智能化升级不仅能够提高用户的使用体验,还能通过物联网技术实现远程控制和数据监控,赋予其更高的使用价值和市场竞争力。

在日常生活中,用户对暖风机的使用需求多样化,既需要实时了解室内环境温湿度数据,也希望能够通过简单的方式对暖风机的状态进行控制,如调整档位、开关设备等。同时,考虑到节能和高效的取暖效果,采用PTC陶瓷材料加热器作为核心发热元件,以其高效、稳定、安全的特点满足现代家庭对取暖设备的性能需求。

语音识别技术的成熟为智能家居设备的交互方式带来了新的可能性。用户无需繁琐的按钮操作,仅通过语音指令即可实现对设备的控制,大幅提升了使用的便捷性和智能化水平。此外,物联网技术的发展使得设备可以通过网络与云平台实现数据交互,为设备提供远程监控、控制和数据分析的能力,这不仅能够提升用户体验,还为设备的运维与优化提供了可能。

为了满足用户对智能暖风机的需求,本项目基于STM32F103C8T6主控芯片设计一款智能暖风机设备,集成环境温湿度实时检测、本地LCD显示、语音控制以及物联网功能。通过ESP8266模块连接华为云IoT物联网平台,实现设备状态的云端展示与远程控制。同时,设计Android手机APP和Windows上位机软件,提供多平台的交互支持,用户可以随时随地掌握设备运行状态并进行控制。这一设计不仅结合了物联网和智能控制技术,还符合现代用户对舒适、便捷、智能生活的追求,具有广阔的市场前景和应用价值。

APP的控制界面:

image-20241122112820532

这是设计的硬件模型:

image-20241122111059643

image-20241122111733026

image-20241122111500109

image-20241122111533163

image-20241122111855769

1.2 设计实现的功能

本项目使用的相关开发工具,资料都已经上传到网盘,需要的可自行下载。

https://ccnr8sukk85n.feishu.cn/wiki/QjY8weDYHibqRYkFP2qcA9aGnvb?from=from_copylink

本项目设计的智能暖风机支持实时检测并显示环境温度和湿度,通过集成传感器获取室内环境数据,并在本地LCD显示屏上实时展示。这些数据不仅帮助用户了解室内环境状况,还为设备的智能控制提供支持。

暖风机具备多档位调节功能,用户可以根据需求选择不同的取暖模式。设备状态,包括开关状态、取暖档位以及当前时间等信息,均清晰显示在LCD屏上,方便用户随时掌握设备的运行情况。基于PTC陶瓷加热技术,暖风机能够提供稳定且高效的热量输出,同时通过内置风扇将热空气均匀分布,提升取暖效果。

语音识别控制功能是本项目的一大亮点。用户无需传统的物理按键操作,只需通过语音指令即可完成设备的开关和档位调节。这不仅增强了设备的智能化和交互体验,还为老人和儿童等特殊群体提供了更加友好的使用方式。

设备支持物联网功能,通过ESP8266-WIFI模块和MQTT协议连接至华为云IoT平台。实时上传环境数据和设备状态至云端,用户可通过Android手机APP或Windows上位机远程查看和控制设备。无论是家中还是外出,用户都能轻松掌握设备的运行状态并进行远程操作,从而实现智能化、便捷化的暖风机控制。

智能暖风机的Android APP和Windows上位机软件基于Qt (C++)设计,具备友好的用户界面和稳定的交互功能。通过这些平台,用户可以实时监控设备数据,包括环境温湿度、设备档位和开关状态,并对设备进行远程开关操作。这种跨平台的支持确保了用户能够在多种设备上享受统一的控制体验。

实现的功能详解如下:

(1) 环境数据检测
支持实时监测环境温度和湿度,采用DHT11传感器(后续计划升级为SHT30)采集数据,为设备运行提供环境依据,并在LCD屏上直观显示。

(2) 本地状态显示
通过LCD显示屏,实时展示设备的温湿度信息、开关状态、取暖档位以及当前时间,帮助用户随时掌握设备运行状态。

(3) 语音识别控制
集成HLK-V20语音识别模块,支持用户通过语音指令对设备进行开关控制及档位调节,提供高效便捷的语音交互功能。

(4) 红外遥控功能
支持通过遥控器使用NEC红外线协议对设备进行无线控制,包括开关操作和档位调整,为用户提供另一种便捷的控制方式。

(5) 联网功能
通过ESP8266-01S模块和MQTT协议接入华为云IoT平台,设备可实时上传环境数据和运行状态,并接收来自云端的控制指令。

(6) 远程控制功能
设计基于Qt (C++)开发的Android手机APP和Windows电脑上位机,用户可通过这些平台远程查看暖风机的状态(如开关状态、档位、温湿度)并实现控制操作,无论身在何处都能轻松掌控设备。

(7) 多档位取暖调节
设备支持多档位调节功能,用户可根据室内温度或取暖需求选择不同档位,实现舒适化和节能化的取暖效果。

(8) 高效取暖与安全保护
采用PTC陶瓷加热器提供稳定、高效的加热效果,结合内置风扇实现均匀热风输出。同时,通过PTC特性及过热保护设计,确保设备在使用中的安全性和可靠性。

(9) 数据存储与云同步
设备将环境数据和运行状态上传至华为云IoT平台,便于用户通过云端查看历史记录,并为数据分析和优化提供支持。

(10) 多方式控制
支持语音、遥控器、本地按键及远程APP多种控制方式,满足用户在不同场景下的操作需求,增强设备的适用性和用户体验。

1.3 项目硬件模块组成

(1) 主控模块
采用STM32F103C8T6作为主控芯片,负责协调各功能模块,包括环境数据处理、设备状态管理、语音控制、远程通信及显示等功能。

(2) WIFI通信模块
使用ESP8266-01S模块,基于MQTT协议实现与华为云IoT平台的无线连接,支持数据上传和远程控制。

(3) 语音识别模块
选用海凌科HLK-V20语音识别模块,用于识别语音指令,实现暖风机的开关控制和档位调整,提供智能语音交互功能。

(4) 红外遥控模块
集成NEC红外线协议的接收模块,用于接收遥控器指令,实现暖风机的无线遥控操作,包括开关和档位调节。

(5) 环境传感器模块
采用DHT11传感器(后续升级为SHT30)实时检测环境温湿度数据,为设备运行提供环境参数参考,并在LCD屏上显示。

(6) 显示模块
使用LCD显示屏,实时显示设备状态,包括环境温湿度、暖风机开关状态、档位及当前时间,帮助用户直观了解设备运行情况。

(7) 加热模块
采用PTC陶瓷加热器作为核心发热部件,利用其正温度系数特性提供安全、高效的加热效果,并配备内置风扇将热风均匀分布。

(8) 按键控制模块
设备配备物理按键,用于手动操作暖风机开关和档位调节,作为语音和遥控控制的备选方案。

(9) 电源模块
为整个系统提供稳定可靠的电源供应,包括主控芯片、WIFI模块、传感器、语音模块、显示屏及加热模块的电源需求,确保设备稳定运行。

(10) 风道与散热模块
设计合理的风道结构,通过内置风扇加速热风扩散,优化散热效率,避免设备长时间运行时因过热而影响安全性和使用寿命。

(11) 外壳及防护模块
采用耐高温材料制作外壳,结合散热设计和绝缘处理,确保设备运行安全,同时增强结构强度和美观性。

1.4 设计思路

硬件设计以STM32F103C8T6为核心处理单元,结合ESP8266-WIFI模块、语音识别模块(HLK-V20)和PTC陶瓷加热器等关键组件,实现设备的核心功能。STM32F103C8T6凭借其高性能和丰富的外设接口,能够满足传感器数据采集、LCD显示、语音指令处理及网络通信的多任务需求。传感器部分选用高精度温湿度传感器用于环境参数监测,将数据实时传输给MCU进行处理并显示在LCD屏上。

语音控制模块通过HLK-V20芯片实现,对用户语音指令进行识别和处理,结合MCU实现暖风机的开关控制和档位调整。这种设计避免了传统按钮操作的繁琐,提升了设备的人机交互水平。PTC陶瓷加热器作为暖风机的核心部件,结合内置风扇,提供稳定高效的取暖效果,并充分利用其正温度系数特性,确保设备运行的安全性和节能性。

其网络功能通过ESP8266模块和MQTT协议实现。设备与华为云IoT平台的连接使其具备数据上传和远程控制的能力。环境温湿度数据和设备运行状态会周期性上传至云平台,用户可以通过手机APP或Windows上位机软件随时查看和控制设备。采用MQTT协议能够保证数据传输的高效性和实时性,适应智能家居设备对低延时、高可靠性的要求。

软件设计方面,本地端采用Qt (C++)开发用户控制应用,覆盖Android和Windows两个平台,提供统一的操作界面。用户通过APP或上位机软件,可以直观地查看设备运行状态,包括环境数据、档位信息和开关状态,并可进行远程开关和档位调节操作。APP和上位机的软件架构与华为云IoT平台深度集成,确保数据同步的实时性和准确性。

在系统功能的实现中,采用模块化设计思路,各子模块(如传感器数据采集、语音控制、网络通信、APP控制等)独立开发并通过主控MCU进行协同管理。这种设计不仅提高了系统开发的效率和稳定性,还便于后续功能扩展和维护。

整体设计注重硬件与软件的协同工作,将传感技术、语音交互和物联网技术融合,为用户提供便捷、安全、高效的智能取暖解决方案,同时展现了现代智能家居产品的功能集成和技术创新。

1.5 系统功能总结

功能类别具体功能描述
环境数据监测温湿度检测实时监测环境温度和湿度,提供环境数据参考,并在LCD上显示。
显示功能本地LCD显示实时显示环境温湿度、设备开关状态、取暖档位及当前时间,为用户提供清晰的运行状态信息。
控制功能语音控制用户通过语音指令控制设备开关和档位调节,提升智能交互体验。
红外遥控支持遥控器使用NEC红外线协议进行无线控制,实现开关和档位调整功能。
按键控制设备支持物理按键手动操作,作为其他控制方式的补充方案。
远程控制用户通过Android APP或Windows上位机进行远程操作,查看设备状态并控制开关及档位调整。
联网功能云平台接入通过ESP8266模块和MQTT协议接入华为云IoT平台,实现数据上传和云端控制功能。
数据同步与存储实时上传环境数据和设备状态至云端,支持历史数据存储及查看。
取暖功能多档位调节提供多档取暖模式,用户可根据环境和需求选择适合的取暖档位。
高效加热使用PTC陶瓷加热器提供稳定高效的加热效果,内置风扇实现热量均匀分布。
安全功能过热保护通过过热保护和PTC正温度系数特性确保设备运行安全性,防止温度过高导致的风险。
散热优化优化风道和外壳散热设计,延长设备使用寿命并保障安全。
时间显示功能当前时间显示LCD屏支持显示当前时间,提升设备的功能性和实用性。
多控制方式支持综合控制方式集成语音、遥控器、按键及远程APP多种控制方式,满足用户不同场景下的操作需求。
远程管理功能跨平台支持Android手机APP和Windows上位机均可对设备进行状态查看和操作,提供统一且便捷的远程管理体验。

1.6 开发工具的选择

【1】设备端开发

STM32的编程语言选择C语言,C语言执行效率高,大学里主学的C语言,C语言编译出来的可执行文件最接近于机器码,汇编语言执行效率最高,但是汇编的移植性比较差,目前在一些操作系统内核里还有一些低配的单片机使用的较多,平常的单片机编程还是以C语言为主。C语言的执行效率仅次于汇编,语法理解简单、代码通用性强,也支持跨平台,在嵌入式底层、单片机编程里用的非常多,当前的设计就是采用C语言开发。

开发工具选择Keil,keil是一家世界领先的嵌入式微控制器软件开发商,在2015年,keil被ARM公司收购。因为当前芯片选择的是STM32F103系列,STMF103是属于ARM公司的芯片构架、Cortex-M3内核系列的芯片,所以使用Kile来开发STM32是有先天优势的,而keil在各大高校使用的也非常多,很多教科书里都是以keil来教学,开发51单片机、STM32单片机等等。目前作为MCU芯片开发的软件也不只是keil一家独大,IAR在MCU微处理器开发领域里也使用的非常多,IAR扩展性更强,也支持STM32开发,也支持其他芯片,比如:CC2530,51单片机的开发。从软件的使用上来讲,IAR比keil更加简洁,功能相对少一些。如果之前使用过keil,而且使用频率较多,已经习惯再使用IAR是有点不适应界面的。

image-20221210225339928

【2】上位机开发

上位机的开发选择Qt框架,编程语言采用C++;Qt是一个1991年由Qt Company开发的跨平台C++图形用户界面应用程序开发框架。它既可以开发GUI程序,也可用于开发非GUI程序,比如控制台工具和服务器。Qt是面向对象的框架,使用特殊的代码生成扩展(称为元对象编译器(Meta Object Compiler, moc))以及一些宏,Qt很容易扩展,并且允许真正地组件编程。Qt能轻松创建具有原生C++性能的连接设备、用户界面(UI)和应用程序。它功能强大且结构紧凑,拥有直观的工具和库。

image-20230218001243591

image-20230218001219105

1.7 模块的技术详情介绍

【1】ESP8266-WIFI模块

ESP8266是由乐鑫科技(Espressif Systems)开发的一款低成本、低功耗的Wi-Fi无线模块。它是一种集成度很高的系统级芯片(SoC),内置了处理器、Wi-Fi功能、内存和其他基本外设,能够使嵌入式设备具备无线通信能力。ESP8266模块以其出色的性能和极高的性价比,广泛应用于物联网(IoT)设备、智能家居、远程控制等领域。

ESP8266的核心是一个低功耗的32位微处理器,通常运行频率为80MHz或者160MHz,具有一定的计算能力,能够执行一些简单的控制任务。模块内置的Wi-Fi功能支持802.11 b/g/n协议,具备无线网络连接、TCP/IP协议栈和安全加密等功能,因此可以通过Wi-Fi网络与其他设备或服务器进行通信。ESP8266不仅支持AP(Access Point)模式,还支持STA(Station)模式,这意味着它既可以作为Wi-Fi接入点,也可以作为Wi-Fi客户端连接到现有的无线网络。

ESP8266模块通过UART(串口)接口与外部设备进行通信,通常使用AT指令集进行控制和配置。在这种工作模式下,用户可以通过串口发送AT指令来控制模块的网络连接、数据传输、配置设置等操作。除了基本的Wi-Fi功能,ESP8266还支持TCP、UDP、HTTP、MQTT等网络协议,可以方便地进行数据交互和远程控制。

ESP8266模块的另一个重要特点是其低功耗特性。在休眠模式下,ESP8266的功耗极低,非常适合于电池供电的物联网设备。在正常工作时,它的功耗也比较低,适合长时间在线运行。对于需要较长工作时间的无线传感器、智能家居设备等应用,ESP8266提供了一个非常高效的解决方案。

在开发环境方面,ESP8266非常灵活。它可以通过Arduino IDE进行编程,开发者可以利用现有的Arduino开发经验和大量的开源库进行项目开发。此外,ESP8266还支持NodeMCU开发板,这是一款基于ESP8266的开源开发板,集成了Lua脚本和简单的编程接口,使得编程更为简便。通过这些开发环境,开发者可以快速上手并进行原型设计和调试。

ESP8266模块的应用场景非常广泛,尤其是在物联网(IoT)领域。例如,用户可以使用ESP8266开发智能家居系统,利用Wi-Fi连接控制灯光、温控器、安全设备等。它也广泛用于智能插座、智能传感器、远程监控、无线数据传输等设备中。其低成本和高效能使其成为许多家庭自动化和小型物联网应用的首选平台。

ESP8266是一款功能强大、性价比高的Wi-Fi无线模块。其集成度高、易于编程、低功耗的特点,使其成为物联网应用中的理想选择。随着IoT技术的不断发展,ESP8266模块仍将在各类智能设备中发挥重要作用。

【2】MQTT协议

MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议)是一种轻量级、发布/订阅模式的消息传输协议,专为低带宽、不可靠网络环境设计。它最早由IBM提出,现已成为物联网(IoT)通信的重要协议之一。由于其高效、低功耗和实时性等特点,MQTT在智能家居、工业自动化、远程监控和车联网等领域得到了广泛应用。

MQTT的工作原理基于发布/订阅模型。这种模型有别于传统的客户端-服务器模型,通信方不需要直接建立连接。MQTT由三个核心组件构成:客户端、代理(Broker)和主题(Topic)。客户端可以作为消息的发布者或订阅者,消息通过代理进行路由。代理是一个中间服务端,用于接收和分发来自不同客户端的消息。发布者发送消息到一个特定的主题上,代理负责将这些消息分发给所有订阅了该主题的客户端。通过这种解耦的架构设计,客户端之间可以实现松耦合的通信,降低了复杂性和依赖性。

在MQTT协议中,消息被分为不同的主题(Topic),例如“home/sensor/temperature”可以用来代表温度传感器数据。客户端可以订阅这个主题,当发布者发送新的数据到该主题时,所有订阅该主题的客户端都会收到更新信息。这种灵活的主题结构和层次化的命名规则,使得MQTT在复杂场景下也能快速组织和管理消息流。

MQTT协议支持三种服务质量(QoS)等级,分别为“至多一次”(QoS 0)、“至少一次”(QoS 1)和“仅一次”(QoS 2)。QoS 0表示消息传输尽力而为,可能会丢失或重复;QoS 1确保消息至少送达一次,但可能会有重复;QoS 2则确保消息恰好传输一次,保证消息的严格可靠性。这种设计使MQTT能够适应不同的应用场景,用户可以根据应用需求选择合适的QoS级别。

为了保证通信的安全性,MQTT支持用户名和密码验证,代理可以对连接进行身份认证。此外,许多实现中还支持TLS/SSL加密通信,确保数据在传输过程中不会被窃取或篡改。用户也可以使用不同的认证方式来增强系统的安全性,适应物联网应用中对安全性的高需求。

MQTT非常注重轻量化和低功耗。它的报文头非常小,通信开销很低,这使其特别适合在资源受限的设备或不稳定的网络环境中使用。MQTT支持“保持连接”和“遗嘱消息”功能,客户端可以在连接断开时自动向代理发送遗嘱消息,通知其他客户端连接状态的变化。这种特性有助于提高网络的健壮性和系统的可用性。

MQTT的典型使用场景包括物联网设备数据采集、实时监控、消息推送和控制命令的发布。比如在智能家居中,传感器可以发布环境数据,如温湿度、烟雾浓度等,控制设备根据收到的消息作出响应,实现自动化操作。在工业场景中,MQTT可以帮助收集和管理大规模设备的运行状态,实现集中化和高效的设备监控。

总的来说,MQTT协议凭借其低功耗、高效能、实时性强等优势,已成为物联网通信的主要协议之一。它的发布/订阅模式简化了设备之间的通信,使其特别适合多对多、低延迟、高可靠性的数据传输场景。MQTT易于使用、拓展性强,为开发者提供了灵活的解决方案来构建各种物联网应用。

【3】SHT30模块

SHT30模块是一款高精度、数字化的温湿度传感器模块,由Sensirion公司生产。该模块内置SHT30传感器,采用CMOSens®技术,能够精准测量环境的温度和相对湿度,并提供数字输出,广泛应用于温湿度监测、智能家居、环境监测和物联网设备中。SHT30具有高度集成的特点,包括温湿度传感元件和信号处理电路,因此能够在小体积内提供精确而稳定的测量结果。

SHT30模块的温度测量范围为-40°C至+125°C,精度可达±0.3°C,湿度测量范围为0%至100% RH,相对湿度精度在±2% RH左右,满足绝大多数环境监测需求。SHT30传感器通过I²C接口与外部设备通信,使数据读取变得简单而高效。I²C总线采用标准的7位地址,模块可以与多种MCU或单片机系统无缝对接,适合各种嵌入式应用。除了I²C接口,SHT30还支持单次测量模式和周期测量模式,使用户可以根据功耗和实时性需求灵活配置。

SHT30模块的内部结构经过优化,使其具有快速响应时间和出色的抗干扰性能。模块外壳设计紧凑,能够有效防尘防潮,适合应用于恶劣环境中。为了保证测量稳定性,模块具备内置加热功能,可以有效减少冷凝现象对测量的影响,确保在高湿度环境下仍然能够获得准确数据。此外,SHT30模块经过了严格的出厂校准和温湿度补偿,具有良好的长期稳定性,漂移极小,因此在实际应用中不需要频繁重新校准。

SHT30模块的低功耗设计使其适用于电池供电的物联网设备。传感器在单次测量模式下仅在测量期间消耗少量功率,待机时功耗极低,可延长电池寿命。同时,模块还支持内置CRC校验功能,确保数据通信的可靠性,避免因干扰或传输错误导致的测量不准确。模块的应用广泛而灵活,适合集成在温湿度监测站、工业控制、自动化设备、智能家居系统以及农业和医药环境监测中,能够为各种应用提供高精度的环境数据支持。

二、 ESP8266-WIFI模块调试过程

如果大家第一次使用ESP8266,写代码之前,正常都需要进行调试,测试模块。然后再开始去写对应的代码。

如果是转接板,只需要接 TXD RXD VCC GND 即可。

视频:https://www.bilibili.com/video/BV1ku4y1E74M

2.1 接电脑USB口调试

image-20231029164547420

2.2 ESP8266的STA+TCP客户端配置

按下面的命令顺序配置,测试WIFI的链接。

//STA+TCP客户端模式
char *ESP8266_STA_Client[]=
{
    "AT\r\n",
    "ATE0\r\n",
    "AT+CWMODE=1\r\n",
    "AT+RST\r\n",
    "ATE0\r\n",
    "AT+CWLAP\r\n", //查询可以连接WIFI
    "AT+CWJAP=\"ChinaNet-wbyw\",\"12345678\"\r\n",
    "AT+CIPMUX=0\r\n",
    "AT+CIPMODE=1\r\n",
    "AT+CIPSTART=\"TCP\",\"192.168.101.6\",8088\r\n",
    "AT+CIPSEND\r\n",  
};

以下是每个指令的解释:

(1)“AT\r\n”:这是用于测试ESP8266模块是否正常工作的基本指令。发送此指令后,模块应该回复 “OK\r\n”。

(2)“ATE0\r\n”:这个指令用于关闭回显功能。当回显功能关闭时,模块不会将接收到的指令发送回串口。回复 “OK\r\n” 表示指令执行成功。

(3)“AT+CWMODE=1\r\n”:这个指令设置ESP8266模块为STA(站点)模式,即将其配置为连接到现有无线网络的客户端。回复 “OK\r\n” 表示指令执行成功。

(4)“AT+RST\r\n”:这个指令用于重启ESP8266模块。模块将重新启动并返回 “ready\r\n” 表示准备就绪。

(5)“ATE0\r\n”:同第二条指令一样,这个指令再次关闭回显功能。

(6)“AT+CWLAP\r\n”:这个指令用于查询附近可连接的无线网络。模块将返回一个列表,包含可用网络的信息。

(7)“AT+CWJAP=“ChinaNet-wbyw”,“12345678”\r\n”:这个指令用于连接到指定的无线网络。在此示例中,通过SSID为 “ChinaNet-wbyw” 的无线网络,并使用密码 “12345678” 进行连接。回复 “OK\r\n” 表示指令执行成功。

(8)“AT+CIPMUX=0\r\n”:这个指令用于禁用多连接模式。回复 “OK\r\n” 表示指令执行成功。

(9)“AT+CIPMODE=1\r\n”:这个指令用于设置传输模式为透明传输模式。在透明传输模式下,ESP8266模块将直接将串口数据发送到网络连接中。回复 “OK\r\n” 表示指令执行成功。

(10)“AT+CIPSTART=“TCP”,“192.168.101.6”,8088\r\n”:这个指令用于建立与指定TCP服务器的连接。在此示例中,将与IP地址为 “192.168.101.6”、端口号为8088的服务器建立连接。回复 “OK\r\n” 表示指令执行成功。

(11)“AT+CIPSEND\r\n”:这个指令用于发送数据到已建立的TCP连接。发送此指令后,模块将进入发送模式,并等待用户输入要发送的数据。可以通过向模块发送数据来进行通信。

三、部署华为云物联网平台

华为云官网: https://www.huaweicloud.com/

打开官网,搜索物联网,就能快速找到 设备接入IoTDA

image-20221204193824815

3.1 物联网平台介绍

华为云物联网平台(IoT 设备接入云服务)提供海量设备的接入和管理能力,将物理设备联接到云,支撑设备数据采集上云和云端下发命令给设备进行远程控制,配合华为云其他产品,帮助我们快速构筑物联网解决方案。

使用物联网平台构建一个完整的物联网解决方案主要包括3部分:物联网平台、业务应用和设备。

物联网平台作为连接业务应用和设备的中间层,屏蔽了各种复杂的设备接口,实现设备的快速接入;同时提供强大的开放能力,支撑行业用户构建各种物联网解决方案。

设备可以通过固网、2G/3G/4G/5G、NB-IoT、Wifi等多种网络接入物联网平台,并使用LWM2M/CoAP、MQTT、HTTPS协议将业务数据上报到平台,平台也可以将控制命令下发给设备。

业务应用通过调用物联网平台提供的API,实现设备数据采集、命令下发、设备管理等业务场景。

img

3.2 开通物联网服务

地址: https://www.huaweicloud.com/product/iothub.html

image-20241028135834377

开通免费单元。

image-20241028135935457

点击立即创建

image-20240117134653452

正在创建标准版实例,需要等待片刻。

image-20241028140048811

创建完成之后,点击详情。 可以看到标准版实例的设备接入端口和地址。

image-20241028140129102

下面框起来的就是端口号域名

image-20241028140229696

点击实例名称,可以查看当前免费单元的配置情况。

image-20241028140331523

image-20241028140428663

开通之后,点击接入信息,也能查看接入信息。 我们当前设备准备采用MQTT协议接入华为云平台,这里可以看到MQTT协议的地址和端口号等信息。

image-20241028140511105

总结:

端口号:   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:\Users\Lenovo>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:\Users\Lenovo>

MQTT协议接入端口号有两个,1883是非加密端口,8883是证书加密端口,单片机无法加载证书,所以使用1883端口合适

3.3 创建产品

链接:https://console.huaweicloud.com/iotdm/?region=cn-north-4#/dm-dev/all-product?instanceId=03c5c68c-e588-458c-90c3-9e4c640be7af

(1)创建产品

image-20241028141601305

(2)填写产品信息

根据自己产品名字填写,下面的设备类型选择自定义类型。

image-20240612094809689

(3)产品创建成功

image-20240612095148945

创建完成之后点击查看详情。

image-20240612095134263

(4)添加自定义模型

产品创建完成之后,点击进入产品详情页面,翻到最下面可以看到模型定义。

模型简单来说: 就是存放设备上传到云平台的数据。

你可以根据自己的产品进行创建。

比如:

烟雾可以叫  MQ2
温度可以叫  Temperature
湿度可以叫  humidity
火焰可以叫  flame
其他的传感器自己用单词简写命名即可。 这就是你的单片机设备端上传到服务器的数据名字。

先点击自定义模型。

image-20240612095517900

再创建一个服务ID。

image-20240612095542749

接着点击新增属性。

image-20240612095648815

image-20240612095711898

3.4 添加设备

产品是属于上层的抽象模型,接下来在产品模型下添加实际的设备。添加的设备最终需要与真实的设备关联在一起,完成数据交互。

(1)注册设备

image-20240425181935561

(2)根据自己的设备填写

image-20240612100115167

(3)保存设备信息

创建完毕之后,点击保存并关闭,得到创建的设备密匙信息。该信息在后续生成MQTT三元组的时候需要使用。

image-20240612100128061

(4)设备创建完成

image-20240612100147232

(5)设备详情

image-20240612100202960

image-20240612100217236

3.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

img

业务流程:

img

(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

image-20221207153310037

对于设备而言,一般会订阅平台下发消息给设备 这个主题。

设备想接收平台下发的消息,就需要订阅平台下发消息给设备 的主题,订阅后,平台下发消息给设备,设备就会收到消息。

如果设备想要知道平台下发的消息,需要订阅上面图片里标注的主题。

以当前设备为例,最终订阅主题的格式如下:
$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

image-20221207153637391

根据帮助文档的介绍, 当前设备发布主题,上报属性的格式总结如下:

发布的主题格式:
$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}}]}

3.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

image-20240509193207359

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

image-20240425182610048

(2)生成MQTT三元组

华为云提供了一个在线工具,用来生成MQTT鉴权三元组: https://iot-tool.obs-website.cn-north-4.myhuaweicloud.com/

打开这个工具,填入设备的信息(也就是刚才创建完设备之后保存的信息),点击生成,就可以得到MQTT的登录信息了。

下面是打开的页面:

image-20240425183025893

填入设备的信息: (上面两行就是设备创建完成之后保存得到的)

直接得到三元组信息。

image-20240509193310020

得到三元组之后,设备端通过MQTT协议登录鉴权的时候,填入参数即可。

ClientId  663cb18871d845632a0912e7_dev1_0_0_2024050911
Username  663cb18871d845632a0912e7_dev1
Password  71b82deae83e80f04c4269b5bbce3b2fc7c13f610948fe210ce18650909ac237

3.7 模拟设备登录测试

经过上面的步骤介绍,已经创建了产品,设备,数据模型,得到MQTT登录信息。 接下来就用MQTT客户端软件模拟真实的设备来登录平台。测试与服务器通信是否正常。

(1)填入登录信息

打开MQTT客户端软件,对号填入相关信息(就是上面的文本介绍)。然后,点击登录,订阅主题,发布主题。

image-20240509193457358

(2)打开网页查看

完成上面的操作之后,打开华为云网页后台,可以看到设备已经在线了。

image-20240612100508790

点击详情页面,可以看到上传的数据:

image-20240612100529581

到此,云平台的部署已经完成,设备已经可以正常上传数据了。

(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}}]}

3.8 创建IAM账户

创建一个IAM账户,因为接下来开发上位机,需要使用云平台的API接口,这些接口都需要token进行鉴权。简单来说,就是身份的认证。 调用接口获取Token时,就需要填写IAM账号信息。所以,接下来演示一下过程。

地址: https://console.huaweicloud.com/iam/?region=cn-north-4#/iam/users

**【1】获取项目凭证 ** 点击左上角用户名,选择下拉菜单里的我的凭证

image-20240509193646253

image-20240509193701262

项目凭证:

28add376c01e4a61ac8b621c714bf459

【2】创建IAM用户

鼠标放在左上角头像上,在下拉菜单里选择统一身份认证

image-20240509193729078

点击左上角创建用户

image-20240509193744287

image-20240314153208692

image-20240314153228359

image-20240314153258229

创建成功:

image-20240314153315444

【3】创建完成

image-20240509193828289

用户信息如下:

主用户名  l19504562721
IAM用户  ds_abc
密码     DS12345678

3.9 获取影子数据

帮助文档:https://support.huaweicloud.com/api-iothub/iot_06_v5_0079.html

设备影子介绍:

设备影子是一个用于存储和检索设备当前状态信息的JSON文档。
每个设备有且只有一个设备影子,由设备ID唯一标识
设备影子仅保存最近一次设备的上报数据和预期数据
无论该设备是否在线,都可以通过该影子获取和设置设备的属性

简单来说:设备影子就是保存,设备最新上传的一次数据。

我们设计的软件里,如果想要获取设备的最新状态信息,就采用设备影子接口。

如果对接口不熟悉,可以先进行在线调试:https://apiexplorer.developer.huaweicloud.com/apiexplorer/doc?product=IoTDA&api=ShowDeviceShadow

在线调试接口,可以请求影子接口,了解请求,与返回的数据格式。

调试完成看右下角的响应体,就是返回的影子数据。

image-20240509194152229

设备影子接口返回的数据如下:

{
 "device_id": "663cb18871d845632a0912e7_dev1",
 "shadow": [
  {
   "service_id": "stm32",
   "desired": {
    "properties": null,
    "event_time": null
   },
   "reported": {
    "properties": {
     "DHT11_T": 18,
     "DHT11_H": 90,
     "BH1750": 38,
     "MQ135": 70
    },
    "event_time": "20240509T113448Z"
   },
   "version": 3
  }
 ]
}

调试成功之后,可以得到访问影子数据的真实链接,接下来的代码开发中,就采用Qt写代码访问此链接,获取影子数据,完成上位机开发。

image-20240509194214716

链接如下:

https://ad635970a1.st1.iotda-app.cn-north-4.myhuaweicloud.com:443/v5/iot/28add376c01e4a61ac8b621c714bf459/devices/663cb18871d845632a0912e7_dev1/shadow

3.10 访问接口的代码实现

(1)配置 Qt 项目

在 Qt 项目的 .pro 文件中,加入对 libcurl 的支持:

QT += core
CONFIG += console
CONFIG -= app_bundle

INCLUDEPATH += /usr/include/curl  # 根据你的系统设置 libcurl 的路径
LIBS += -lcurl  # 链接 libcurl 库

SOURCES += main.cpp

(2)代码实现

main.cpp 文件中实现代码如下:

#include <QCoreApplication>
#include <curl/curl.h>
#include <QDebug>
#include <QString>
#include <QByteArray>

// 回调函数,处理libcurl下载数据
size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) {
    size_t totalSize = size * nmemb;
    QByteArray *response = static_cast<QByteArray *>(userp);
    response->append(static_cast<char *>(contents), totalSize);
    return totalSize;
}

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    // 初始化libcurl
    CURL *curl;
    CURLcode res;
    QByteArray responseData;  // 用于存储响应数据

    curl_global_init(CURL_GLOBAL_DEFAULT);
    curl = curl_easy_init();
    if (curl) {
        // 设置访问URL
        const QString url = "https://ad635970a1.st1.iotda-app.cn-north-4.myhuaweicloud.com:443/v5/iot/28add376c01e4a61ac8b621c714bf459/devices/663cb18871d845632a0912e7_dev1/shadow";

        // 设置HTTP请求头
        struct curl_slist *headers = NULL;
        headers = curl_slist_append(headers, "Authorization: Bearer <Your_Access_Token>"); // 这里需要替换为你的实际 token

        curl_easy_setopt(curl, CURLOPT_URL, url.toStdString().c_str());
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &responseData);

        // 发起GET请求
        res = curl_easy_perform(curl);

        if (res != CURLE_OK) {
            qDebug() << "Curl request failed:" << curl_easy_strerror(res);
        } else {
            qDebug() << "Response data:" << responseData;
        }

        // 清理
        curl_easy_cleanup(curl);
        curl_slist_free_all(headers);
    }

    curl_global_cleanup();

    return a.exec();
}

3.11 数据解析代码

在 Qt 中使用 CJSON (一个用于解析 JSON 数据的轻量级 C 库) 来解析返回的 JSON 数据。

(1)配置 Qt 项目

在 Qt 项目的 .pro 文件中,确保包括了 CJSON 的头文件,并链接 CJSON 的源代码。

QT += core
CONFIG += console
CONFIG -= app_bundle

SOURCES += main.cpp \
           cJSON.c  # 将 cJSON.c 文件添加到你的项目中

INCLUDEPATH += path/to/cjson/  # 添加 CJSON 头文件的路径

LIBS += -lcurl  # 链接 libcurl 库

(2)解析 JSON 数据的完整代码

main.cpp 中,以下代码展示了如何解析你提供的 JSON 数据。

#include <QCoreApplication>
#include <curl/curl.h>
#include <QDebug>
#include <QString>
#include <QByteArray>
#include "cJSON.h"

// 回调函数,处理libcurl下载数据
size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) {
    size_t totalSize = size * nmemb;
    QByteArray *response = static_cast<QByteArray *>(userp);
    response->append(static_cast<char *>(contents), totalSize);
    return totalSize;
}

// 解析 JSON 数据
void parseJson(const QByteArray &data) {
    // 将 QByteArray 转换为 char*
    const char* jsonData = data.constData();

    // 解析 JSON
    cJSON *root = cJSON_Parse(jsonData);
    if (root == NULL) {
        qDebug() << "Error parsing JSON.";
        return;
    }

    // 解析 "device_id"
    cJSON *deviceId = cJSON_GetObjectItemCaseSensitive(root, "device_id");
    if (cJSON_IsString(deviceId) && (deviceId->valuestring != NULL)) {
        qDebug() << "Device ID:" << deviceId->valuestring;
    }

    // 解析 "shadow" 数组
    cJSON *shadow = cJSON_GetObjectItemCaseSensitive(root, "shadow");
    if (cJSON_IsArray(shadow)) {
        cJSON *shadowItem = NULL;
        cJSON_ArrayForEach(shadowItem, shadow) {
            // 解析每个 shadow 项目
            cJSON *serviceId = cJSON_GetObjectItemCaseSensitive(shadowItem, "service_id");
            if (cJSON_IsString(serviceId) && (serviceId->valuestring != NULL)) {
                qDebug() << "Service ID:" << serviceId->valuestring;
            }

            // 解析 "reported" 对象
            cJSON *reported = cJSON_GetObjectItemCaseSensitive(shadowItem, "reported");
            if (cJSON_IsObject(reported)) {
                // 解析 "properties" 对象
                cJSON *properties = cJSON_GetObjectItemCaseSensitive(reported, "properties");
                if (cJSON_IsObject(properties)) {
                    cJSON *data1 = cJSON_GetObjectItemCaseSensitive(properties, "data1");
                    if (cJSON_IsNumber(data1)) {
                        qDebug() << "data1:" << data1->valueint;
                    }
                    cJSON *data2 = cJSON_GetObjectItemCaseSensitive(properties, "data2");
                    if (cJSON_IsNumber(data2)) {
                        qDebug() << "data2:" << data2->valueint;
                    }
                    cJSON *data3 = cJSON_GetObjectItemCaseSensitive(properties, "data3");
                    if (cJSON_IsNumber(data3)) {
                        qDebug() << "data3:" << data3->valueint;
                    }
                    cJSON *data4 = cJSON_GetObjectItemCaseSensitive(properties, "data4");
                    if (cJSON_IsNumber(data4)) {
                        qDebug() << "data4:" << data4->valueint;
                    }
                }

                // 解析 "event_time"
                cJSON *eventTime = cJSON_GetObjectItemCaseSensitive(reported, "event_time");
                if (cJSON_IsString(eventTime) && (eventTime->valuestring != NULL)) {
                    qDebug() << "Event Time:" << eventTime->valuestring;
                }
            }

            // 解析 version
            cJSON *version = cJSON_GetObjectItemCaseSensitive(shadowItem, "version");
            if (cJSON_IsNumber(version)) {
                qDebug() << "Version:" << version->valueint;
            }
        }
    }

    // 释放 JSON 对象
    cJSON_Delete(root);
}

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    // 模拟获取到的 JSON 数据
    QByteArray jsonData = R"(
    {
        "device_id": "663cb18871d845632a0912e7_dev1",
        "shadow": [
            {
                "service_id": "stm32",
                "desired": {
                    "properties": null,
                    "event_time": null
                },
                "reported": {
                    "properties": {
                        "data1": 18,
                        "data2": 90,
                        "data3": 38,
                        "data4": 70
                    },
                    "event_time": "20240509T113448Z"
                },
                "version": 3
            }
        ]
    })";

    // 调用解析函数
    parseJson(jsonData);

    return a.exec();
}

四、STM32设备端代码设计

项目里使用的模块代码可以在网盘里的传感器目录找到代码工程。

https://ccnr8sukk85n.feishu.cn/wiki/QjY8weDYHibqRYkFP2qcA9aGnvb?from=from_copylink

下面提供完整的 main.c 代码,展示了如何集成各个模块,初始化硬件,处理设备状态,以及与云平台的通信。

#include "stm32f1xx_hal.h"
#include "esp8266.h"        // 假设已封装的ESP8266通信模块驱动
#include "lcd_display.h"    // 假设已封装的LCD显示模块驱动
#include "dht11.h"          // 假设已封装的DHT11温湿度传感器模块
#include "voice_control.h"  // 假设已封装的语音识别模块
#include "infrared_remote.h"// 假设已封装的红外遥控模块
#include "mqtt.h"           // 假设已封装的MQTT通信模块
#include "heating_control.h"// 假设已封装的取暖控制模块

// 定义全局变量
uint8_t heating_level = 0;    // 暖风机档位,0表示关闭
uint8_t device_status = 0;     // 设备状态,0表示关闭,1表示开启
float temperature = 0.0f;     // 环境温度
float humidity = 0.0f;        // 环境湿度

// 定义定时器句柄
TIM_HandleTypeDef htim2;

// 定时器中断服务函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM2) {
        // 每秒读取一次温湿度数据
        if (DHT11_Read(&temperature, &humidity) == HAL_OK) {
            LCD_DisplayTemperatureHumidity(temperature, humidity);  // 显示温湿度
        }
        // 发送环境数据到云平台
        MQTT_PublishData("home/temperature", temperature);
        MQTT_PublishData("home/humidity", humidity);
    }
}

// 主函数
int main(void)
{
    // 初始化硬件抽象层(HAL)
    HAL_Init();
  
    // 初始化各个硬件模块
    SystemClock_Config();
    LCD_Init();                     // 初始化LCD显示模块
    DHT11_Init();                   // 初始化DHT11传感器
    VoiceControl_Init();            // 初始化语音识别模块
    InfraredRemote_Init();          // 初始化红外遥控接收模块
    HeatingControl_Init();          // 初始化暖风机加热控制模块

    // 配置定时器中断,每秒读取温湿度并发送数据
    __HAL_RCC_TIM2_CLK_ENABLE();
    htim2.Instance = TIM2;
    htim2.Init.Prescaler = 7200 - 1;   // 72MHz / 7200 = 10kHz
    htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim2.Init.Period = 10000 - 1;     // 10kHz / 10000 = 1Hz
    htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    HAL_TIM_Base_Init(&htim2);
    HAL_TIM_Base_Start_IT(&htim2);     // 启动定时器中断

    // 初始化ESP8266 WIFI模块,连接到云平台
    ESP8266_Init();
    MQTT_Init();                      // 初始化MQTT通信
    MQTT_Connect("home_heating_system"); // 连接到MQTT服务器

    // 主循环
    while (1)
    {
        // 语音识别控制
        if (VoiceControl_GetCommand() == VOICE_CMD_ON) {
            device_status = 1;
            HeatingControl_SetLevel(heating_level);
        } else if (VoiceControl_GetCommand() == VOICE_CMD_OFF) {
            device_status = 0;
            HeatingControl_SetLevel(0);  // 关闭加热
        }

        // 红外遥控控制
        uint8_t remote_command = InfraredRemote_GetCommand();
        if (remote_command == REMOTE_CMD_ON) {
            device_status = 1;
            HeatingControl_SetLevel(heating_level);
        } else if (remote_command == REMOTE_CMD_OFF) {
            device_status = 0;
            HeatingControl_SetLevel(0);  // 关闭加热
        } else if (remote_command == REMOTE_CMD_UP) {
            if (heating_level < 3) {
                heating_level++;
                if (device_status == 1) {
                    HeatingControl_SetLevel(heating_level);
                }
            }
        } else if (remote_command == REMOTE_CMD_DOWN) {
            if (heating_level > 0) {
                heating_level--;
                if (device_status == 1) {
                    HeatingControl_SetLevel(heating_level);
                }
            }
        }

        // 控制LCD显示暖风机状态和档位
        LCD_DisplayDeviceStatus(device_status, heating_level);

    }
}


代码说明:

  1. 主控模块初始化:在 main() 中,初始化了系统时钟、LCD、DHT11传感器、语音控制、红外遥控、暖风机控制、MQTT通信等子模块。
  2. 定时器中断:通过定时器定期读取温湿度数据,并上传至MQTT服务器。HAL_TIM_PeriodElapsedCallback() 中每秒调用一次 DHT11_Read(),并通过 MQTT_PublishData() 将数据上传。
  3. 语音识别与遥控控制VoiceControl_GetCommand()InfraredRemote_GetCommand() 用于检查用户的语音指令或遥控器按键操作,从而控制暖风机的开关和档位。
  4. 设备状态显示:LCD显示模块通过 LCD_DisplayDeviceStatus()LCD_DisplayTemperatureHumidity() 函数来实时显示设备的状态信息。
  5. MQTT通信:初始化MQTT通信并连接到云平台,实时将设备的环境数据上传到云端。

五、上位机开发

为了方便查看设备上传的数据,接下来利用Qt开发一款Android手机APP 和 Windows上位机。

使用华为云平台提供的API接口获取设备上传的数据,进行可视化显示,以及远程控制设备。

5.1 Qt开发环境安装

Qt的中文官网: https://www.qt.io/zh-cn/

image-20221207160550486

image-20221207160606892

QT5.12.6的下载地址:https://download.qt.io/archive/qt/5.12/5.12.6

打开下载链接后选择下面的版本进行下载:

如果下载不了,可以在网盘里找到安装包下载:

https://ccnr8sukk85n.feishu.cn/wiki/QjY8weDYHibqRYkFP2qcA9aGnvb?from=from_copylink

软件安装时断网安装,否则会提示输入账户。

安装的时候,第一个复选框里的编译器可以全选,直接点击下一步继续安装。

image-20221203151742653

选择编译器: (一定要看清楚了)

image-20241028152725134

前面2讲解了需要用的API接口,接下来就使用Qt设计上位机,设计界面,完成整体上位机的逻辑设计。

【1】新建工程

image-20240117144052547

【2】设置项目的名称。

image-20241112142627805

【3】选择编译系统

image-20240117144239681

【4】选择默认继承的类

image-20240117144302275

【5】选择编译器

image-20241028153603487

【6】点击完成

image-20240117144354252

【7】工程创建完成

image-20241112142836874

5.3 切换编译器

在左下角是可以切换编译器的。 可以选择用什么样的编译器编译程序。

目前新建工程的时候选择了2种编译器。 一种是mingw32这个编译Windows下运行的程序。 一种是Android编译器,可以生成Android手机APP。

不过要注意:Android的编译器需要配置一些环境才可以正常使用,这个大家可以看下面的教程配置一下就行了。

Android环境搭建的博客链接: https://blog.csdn.net/xiaolong1126626497/article/details/117254453

windows的编译器就没有这么麻烦,安装好Qt就可以编译使用。

下面我这里就选择的 mingw32这个编译器,编译Windows下运行的程序。

image-20241112142912481

5.4 编译测试功能

创建完毕之后,编译测试一下功能是否OK。

点击左下角的绿色三角形按钮

正常运行就可以看到弹出一个白色的框框。这就表示工程环境没有问题了。 接下来就可以放心的设计界面了。

image-20241112142939735

5.5 设计UI界面与工程配置

【1】打开UI文件

image-20241112143019233

打开默认的界面如下:

image-20240425194845233

【2】开始设计界面

根据自己需求设计界面。

5.5 编译Windows上位机

点击软件左下角的绿色三角形按钮进行编译运行。

image-20241112153656462

5.6 配置Android环境

如果想编译Android手机APP,必须要先自己配置好自己的Android环境。(搭建环境的过程可以自行百度搜索学习)

然后才可以进行下面的步骤。

【1】选择Android编译器

选择编译器。

image-20240425232651515

切换编译器。

image-20241112153812833

【2】创建Android配置文件

image-20240117144604025

image-20240117144635052

image-20240117144652014

创建完成。

image-20241112153851571

【3】配置Android图标与名称

image-20241113114730689

【3】编译Android上位机

Qt本身是跨平台的,直接选择Android的编译器,就可以将程序编译到Android平台。

然后点击构建。

image-20241112154026342

成功之后,在目录下可以看到生成的apk文件,也就是Android手机的安装包,电脑端使用QQ发送给手机QQ,手机登录QQ接收,就能直接安装。

生成的apk的目录在哪里呢? 编译完成之后,在控制台会输出APK文件的路径。

知道目录在哪里之后,在Windows的文件资源管理器里,找到路径,具体看下图,找到生成的apk文件。

image-20241112154142209

  -- File: D:/QtProject/build-265_AgritechIoTManager-Android_for_arm64_v8a_Clang_Qt_5_12_6_for_Android_ARM64_v8a-Release/android-build//build/outputs/apk/debug/android-build-debug.apk

六、总结

本项目设计的智能暖风机集成了温湿度监测、语音交互、多档调节、物联网连接以及远程控制等多项智能化功能,为用户提供更加便捷、高效、安全的取暖体验。通过采用STM32F103C8T6作为主控芯片,结合PTC陶瓷加热技术和ESP8266物联网模块,系统在硬件性能与智能功能之间达到了良好的平衡。

智能暖风机通过语音识别技术简化了用户操作,使得设备交互更加直观和友好,同时保留了物理按键的手动控制功能,满足不同用户的需求。设备能够实时检测环境温湿度信息,并将数据上传至华为云IoT平台,用户通过手机APP或电脑上位机软件可以随时查看设备运行状态或远程控制设备。这种多平台、多功能的设计极大地提升了用户的操作便利性和使用体验。

此外,项目注重安全性和能效优化,通过PTC加热器的特性结合过热保护和散热优化设计,确保设备在长时间工作中的安全稳定运行。合理的模块化设计使系统功能易于扩展,为后续的功能优化和智能化升级提供了良好的基础。

智能暖风机项目不仅展示了智能家居产品的技术优势,也为用户提供了一个舒适、智能化的取暖解决方案。未来,可以在此基础上进一步开发更多创新功能,使其在智能家电领域具有更大的应用前景和市场竞争力。

<think>嗯,用户想用HLK-V20语音模块实现小车的语音控制,同时小车端用双机差速控制。之前已经有一些方案,现在需要整合成一个完整的方案。我需要先回顾一下双机差速控制的基本原理,然后再考虑如何将语音指令转化为控制信号。 首先,双机差速控制通常是左右两个电机分别控制,通过调整两边电机的转速差来实现转向。比如左转的话,右轮速度大于左轮,或者左轮减速甚至反转。前进后退则是两个电机同速同方向。停止就是两边都停止。 接下来,HLK-V20语音模块的使用。这个模块应该支持语音识别,用户说出指令,比如“前进”、“后退”等,模块识别后通过某种通信方式(比如串口)发送指令到主控板,比如Arduino或者STM32。主控板解析指令后,控制电机的转速和方向。 需要确定通信协议,语音模块和主控之间如何传输数据。可能用串口的AT指令,或者自定义的字符指令,比如发送'F'代表前进,'B'代表后退等。然后主控接收到字符后执行相应的动作。 然后要考虑电机驱动部分,主控需要输出PWM信号给电机驱动模块,比如L298N或TB6612,控制电机的转速和方向。双机差速需要独立控制两个电机,所以需要两个PWM通道和方向控制引脚。 另外,可能需要设计一个指令映射表,将语音指令对应的控制命令和电机动作对应起来。比如“左转”对应左轮减速,右轮加速,或者左轮停止,右轮前进,具体取决于差速的实现方式。 还需要考虑语音识别的准确性和响应时间,可能需要设置一些关键词,确保模块能正确识别用户的指令。同时,可能需要添加反馈机制,比如语音播报确认指令执行,或者LED指示灯提示。 然后,整个系统的流程应该是:用户说出指令→HLK-V20识别并发送指令→主控接收并解析→控制电机差速运动→小车执行动作。中间可能需要处理一些错误情况,比如无效指令或通信失败。 可能还需要电源管理,确保语音模块和主控、电机驱动有足够的电力供应,避免电压不稳导致的问题。 最后,测试和调试阶段,需要逐一验证每个语音指令是否准确触发对应的动作,调整电机的PWM占空比以达到理想的运动效果,比如转弯的半径和速度。 现在把这些思路整理成一个结构化的方案,分步骤说明硬件连接、软件配置、通信协议和代码示例,确保用户能按照步骤实现。</think>根据双机差速控制特性和HLK-v20模块特性,为您整合完整语音控制方案如下: --- ### 一、系统架构 ``` [语音输入] -> HLK-v20模块 -> 串口通信 -> 主控板(Arduino/STM32) -> 电机驱动模块 -> 双轮差速电机 ``` --- ### 二、硬件连接方案 1. **HLK-v20接线** - TX → 主控板RX (需电平匹配,HLK-v20为3.3V) - GND → 共地 - 独立供电(5V/2A) 2. **主控与电机驱动** - PWM1 → 左电机使能端 - PWM2 → 右电机使能端 - DIR1 → 左电机方向控制 - DIR2 → 右电机方向控制 --- ### 三、语音指令映射表 | 语音指令 | 串口输出 | 左轮动作 | 右轮动作 | |----------|----------|----------|----------| | 前进 | 'F' | 正转全速 | 正转全速 | | 后退 | 'B' | 反转全速 | 反转全速 | | 左转 | 'L' | 减速30% | 全速保持 | | 右转 | 'R' | 全速保持 | 减速30% | | 停止 | 'S' | 急停 | 急停 | --- ### 四、核心代码实现(Arduino示例) ```cpp // 电机控制引脚定义 #define L_PWM 5 #define R_PWM 6 #define L_DIR 7 #define R_DIR 8 void setup() { Serial.begin(9600); pinMode(L_PWM, OUTPUT); pinMode(R_PWM, OUTPUT); pinMode(L_DIR, OUTPUT); } void loop() { if(Serial.available()){ char cmd = Serial.read(); switch(cmd){ case 'F': // 前进 setMotor(255, 255); break; case 'B': // 后退 digitalWrite(L_DIR, LOW); digitalWrite(R_DIR, LOW); analogWrite(L_PWM, 255); analogWrite(R_PWM, 255); break; case 'L': // 左转 analogWrite(L_PWM, 178); // 70%速度 analogWrite(R_PWM, 255); break; case 'R': // 右转 analogWrite(L_PWM, 255); analogWrite(R_PWM, 178); break; case 'S': // 停止 analogWrite(L_PWM, 0); analogWrite(R_PWM, 0); break; } } } void setMotor(int lSpeed, int rSpeed) { digitalWrite(L_DIR, (lSpeed > 0) ? HIGH : LOW); digitalWrite(R_DIR, (rSpeed > 0) ? HIGH : LOW); analogWrite(L_PWM, abs(lSpeed)); analogWrite(R_PWM, abs(rSpeed)); } ``` --- ### 五、HLK-v20配置要点 1. **指令训练**(通过AT指令配置) ```at+word=0,前进,FF``` ```at+word=1,后退,BB``` ```...``` 设置触发词与ASCII码映射 2. **通信参数** ```at+uart=9600,8,n,1``` 需与主控板波特率一致 --- ### 六、差速优化建议 1. **加入PID控制** 通过编码器反馈实现精准转速控制: $$ \Delta V = K_p \cdot e(t) + K_i \cdot \int e(t)dt + K_d \cdot \frac{de(t)}{dt} $$ 2. **运动平滑处理** 添加加速度限制: $$ V_{target} = V_{current} + \text{sign}(ΔV) \cdot \min(|ΔV|, a_{max} \cdot Δt) $$ --- ### 七、系统调试流程 1. 单独测试语音模块串口输出 2. 验证主控板指令解析 3. 空载测试电机响应 4. 低速实地测试 5. 调整差速比例系数 建议在代码中添加串口调试输出,实时显示接收指令和电机PWM值,可通过OLED屏幕或蓝牙模块反馈状态信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DS小龙哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值