• 方案介紹
    • 一、項目介紹
    • 二、設計思路總結
    • 三、部署華為云物聯(lián)網(wǎng)平臺
    • 四、上位機開發(fā)
    • 五、代碼實現(xiàn)
  • 附件下載
  • 相關推薦
申請入駐 產(chǎn)業(yè)圖譜

基于STM32+華為云IOT設計的智能衣柜

05/02 08:17
1087
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

更多詳細資料請聯(lián)系.docx

共1個文件

一、項目介紹

隨著智能家居的發(fā)展,人們對于家居設備的智能化遠程控制需求越來越高。智能衣柜作為智能家居的一部分,可以提供衣物存儲和保護的功能,并通過傳感器互聯(lián)網(wǎng)技術實現(xiàn)對衣柜內(nèi)部環(huán)境的監(jiān)測和控制,為用戶提供更好的使用體驗。

本項目基于STM32F103ZET6主控芯片設計了一個智能衣柜系統(tǒng),主要功能包括溫度和濕度的監(jiān)測以及烘干控制。為了實現(xiàn)溫濕度的監(jiān)測,采用了DHT11傳感器,可以準確地測量環(huán)境的溫度和濕度。通過將傳感器連接到STM32F103ZET6,可以實時獲取衣柜內(nèi)部的溫濕度數(shù)據(jù)。

為了實現(xiàn)烘干功能,系統(tǒng)使用加熱絲和小風扇來制造熱氣并循環(huán)衣柜內(nèi)部的空氣,以去除濕氣并防止衣物發(fā)霉。加熱絲的控制采用繼電器來控制加熱絲的通斷,從而控制烘干的開關。通過與STM32F103ZET6的連接,可以實現(xiàn)對加熱絲和小風扇的控制。

為了實現(xiàn)遠程監(jiān)控和控制,系統(tǒng)采用了ESP8266-WIFI模塊將采集到的溫濕度數(shù)據(jù)上傳到華為云物聯(lián)網(wǎng)平臺。用戶可以通過在Android手機上開發(fā)的Qt應用程序遠程查看衣柜的實時溫度和濕度,并設置濕度閥值。如果濕度超出閥值,系統(tǒng)會通過本地蜂鳴器報警和手機APP提示用戶,以防止衣物發(fā)霉。此外,用戶還可以通過手機APP遠程控制衣柜的烘干系統(tǒng),去除濕氣,防止衣物發(fā)霉或出現(xiàn)霉味。

整個系統(tǒng)通過將傳感器、主控芯片、繼電器、ESP8266-WIFI模塊和手機APP進行集成,實現(xiàn)了智能衣柜的溫濕度監(jiān)測和遠程控制功能,為用戶提供了便捷、智能的衣物存儲和保護解決方案。

image-20230728132232626

二、設計思路總結

2.1 硬件選型

在該項目中,以下是一些可能的硬件選型:

【1】主控芯片:STM32F103ZET6,它是一款性能強大的32位ARM Cortex-M3微控制器,具有豐富的外設和存儲器,適合用作智能衣柜系統(tǒng)的主控芯片。

【2】溫濕度傳感器:DHT11,能夠準確地測量環(huán)境的溫度和濕度。

【3】網(wǎng)絡通信模塊:ESP8266-WIFI模塊,可以實現(xiàn)與互聯(lián)網(wǎng)的連接,用于將采集到的溫濕度數(shù)據(jù)上傳到華為云物聯(lián)網(wǎng)平臺。

【4】繼電器:用于控制加熱絲的通斷,從而控制烘干的開關。選擇合適的繼電器型號和規(guī)格,以適應加熱絲的電流和電壓要求。

【5】蜂鳴器:用于本地報警,當濕度超出閥值時發(fā)出報警聲音。

【5】Android手機:作為用戶界面,通過Qt開發(fā)的Android手機APP實現(xiàn)遠程查看和控制衣柜的溫濕度以及烘干系統(tǒng)的開關控制。

2.2 硬件設計

  • 使用STM32F103ZET6作為主控芯片,連接溫濕度傳感器DHT11,繼電器,蜂鳴器和ESP8266-WIFI模塊。
  • 將DHT11傳感器連接到主控芯片的GPIO口,以實時獲取衣柜內(nèi)部的溫濕度數(shù)據(jù)。
  • 通過繼電器控制加熱絲的通斷,以控制烘干系統(tǒng)的開關。
  • 連接蜂鳴器到主控芯片的GPIO口,當濕度超出閥值時發(fā)出報警聲音。
  • 將ESP8266-WIFI模塊連接到主控芯片的串口,以實現(xiàn)與華為云物聯(lián)網(wǎng)平臺的通信。

2.3 軟件設計

  • 使用STM32的開發(fā)環(huán)境進行固件開發(fā),編寫相應的代碼來實現(xiàn)溫濕度傳感器的讀取、繼電器的控制和蜂鳴器的報警功能。
  • 編寫與ESP8266-WIFI模塊通信的代碼,實現(xiàn)將采集到的溫濕度數(shù)據(jù)上傳到華為云物聯(lián)網(wǎng)平臺。
  • 在華為云物聯(lián)網(wǎng)平臺上創(chuàng)建相應的設備和數(shù)據(jù)通道,以接收和存儲來自智能衣柜的溫濕度數(shù)據(jù)。
  • 開發(fā)基于Qt的Android手機APP,通過與華為云物聯(lián)網(wǎng)平臺的接口,實現(xiàn)遠程查看衣柜的實時溫濕度和控制烘干系統(tǒng)的開關。
  • 在手機APP上設置濕度閥值,當濕度超出閥值時,觸發(fā)本地蜂鳴器報警和手機APP的提示功能。

2.4 系統(tǒng)交互流程

  • STM32主控芯片讀取DHT11傳感器的溫濕度數(shù)據(jù)。
  • 根據(jù)采集到的數(shù)據(jù),控制繼電器開關加熱絲和小風扇,實現(xiàn)烘干功能。
  • 將溫濕度數(shù)據(jù)通過ESP8266-WIFI模塊上傳到華為云物聯(lián)網(wǎng)平臺。
  • 用戶通過Qt開發(fā)的Android手機APP遠程訪問華為云物聯(lián)網(wǎng)平臺,獲取衣柜的實時溫濕度數(shù)據(jù)。
  • 如果濕度超出設定的閥值,系統(tǒng)發(fā)出本地蜂鳴器報警和手機APP的提示,提醒用戶。
  • 用戶可以通過手機APP遠程控制烘干系統(tǒng)的開關,去除濕氣,防止衣物發(fā)霉或出現(xiàn)霉味。

通過上述系統(tǒng)設計思路,實現(xiàn)了智能衣柜的溫濕度監(jiān)測和遠程控制功能,提供了更智能、便捷的衣物存儲和保護解決方案。

三、部署華為云物聯(lián)網(wǎng)平臺

華為云官網(wǎng): https://www.huaweicloud.com/

打開官網(wǎng),搜索物聯(lián)網(wǎng),就能快速找到 設備接入IoTDA。

image-20221204193824815

3.1 物聯(lián)網(wǎng)平臺介紹

華為云物聯(lián)網(wǎng)平臺(IoT 設備接入云服務)提供海量設備的接入和管理能力,將物理設備聯(lián)接到云,支撐設備數(shù)據(jù)采集上云和云端下發(fā)命令給設備進行遠程控制,配合華為云其他產(chǎn)品,幫助我們快速構筑物聯(lián)網(wǎng)解決方案。

使用物聯(lián)網(wǎng)平臺構建一個完整的物聯(lián)網(wǎng)解決方案主要包括3部分:物聯(lián)網(wǎng)平臺、業(yè)務應用和設備。

物聯(lián)網(wǎng)平臺作為連接業(yè)務應用和設備的中間層,屏蔽了各種復雜的設備接口,實現(xiàn)設備的快速接入;同時提供強大的開放能力,支撐行業(yè)用戶構建各種物聯(lián)網(wǎng)解決方案。

設備可以通過固網(wǎng)、2G/3G/4G/5G、NB-IoT、Wifi等多種網(wǎng)絡接入物聯(lián)網(wǎng)平臺,并使用LWM2M/CoAP、MQTT、HTTPS協(xié)議將業(yè)務數(shù)據(jù)上報到平臺,平臺也可以將控制命令下發(fā)給設備。

業(yè)務應用通過調(diào)用物聯(lián)網(wǎng)平臺提供的API,實現(xiàn)設備數(shù)據(jù)采集、命令下發(fā)、設備管理等業(yè)務場景。

img

3.2 開通物聯(lián)網(wǎng)服務

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

image-20221204194233414

進來默認會提示開通標準版,在2023的1月1號年之后沒有基礎版了。

image-20230313171325183

開通之后,點擊總覽,查看接入信息。 我們當前設備準備采用MQTT協(xié)議接入華為云平臺,這里可以看到MQTT協(xié)議的地址和端口號等信息。

總結:

端口號:   MQTT (1883)| MQTTS (8883)   
接入地址: 7445c6bcd3.st1.iotda-app.cn-north-4.myhuaweicloud.com

根據(jù)域名地址得到IP地址信息:

Microsoft Windows [版本 10.0.19044.2728]
(c) Microsoft Corporation。保留所有權利。

C:Users11266>ping 7445c6bcd3.st1.iotda-device.cn-north-4.myhuaweicloud.com

正在 Ping 7445c6bcd3.st1.iotda-device.cn-north-4.myhuaweicloud.com [117.78.5.125] 具有 32 字節(jié)的數(shù)據(jù):
來自 117.78.5.125 的回復: 字節(jié)=32 時間=42ms TTL=30
來自 117.78.5.125 的回復: 字節(jié)=32 時間=35ms TTL=30
來自 117.78.5.125 的回復: 字節(jié)=32 時間=36ms TTL=30
來自 117.78.5.125 的回復: 字節(jié)=32 時間=36ms TTL=30

117.78.5.125 的 Ping 統(tǒng)計信息:
    數(shù)據(jù)包: 已發(fā)送 = 4,已接收 = 4,丟失 = 0 (0% 丟失),
往返行程的估計時間(以毫秒為單位):
    最短 = 35ms,最長 = 42ms,平均 = 37ms

C:Users11266>

image-20230321161044723

MQTT協(xié)議接入端口號有兩個,1883是非加密端口,8883是證書加密端口,單片機無法加載證書,所以使用1883端口比較合適。 接下來的ESP8266就采用1883端口連接華為云物聯(lián)網(wǎng)平臺。

3.3 創(chuàng)建產(chǎn)品

(1)創(chuàng)建產(chǎn)品

點擊產(chǎn)品頁,再點擊左上角創(chuàng)建產(chǎn)品。

image-20230313171503547

(2)填寫產(chǎn)品信息

根據(jù)自己產(chǎn)品名字填寫。

image-20230728130901496

(3)產(chǎn)品創(chuàng)建成功

image-20230728130915809

(4)添加自定義模型

產(chǎn)品創(chuàng)建完成之后,點擊進入產(chǎn)品詳情頁面,翻到最下面可以看到模型定義。

先點擊自定義模型。

image-20230313171815860

再創(chuàng)建一個服務ID。

接著點擊新增屬性。

3.4 添加設備

產(chǎn)品是屬于上層的抽象模型,接下來在產(chǎn)品模型下添加實際的設備。添加的設備最終需要與真實的設備關聯(lián)在一起,完成數(shù)據(jù)交互。

(1)注冊設備

image-20230313173158883

(2)根據(jù)自己的設備填寫

image-20230728131025569

(3)保存設備信息

創(chuàng)建完畢之后,點擊保存并關閉,得到創(chuàng)建的設備密匙信息。該信息在后續(xù)生成MQTT三元組的時候需要使用。

image-20230728131051244

(4) 設備創(chuàng)建完成

image-20230728131104843

3.5 MQTT協(xié)議主題訂閱與發(fā)布

(1)MQTT協(xié)議介紹

當前的設備是采用MQTT協(xié)議與華為云平臺進行通信。

MQTT是一個物聯(lián)網(wǎng)傳輸協(xié)議,它被設計用于輕量級的發(fā)布/訂閱式消息傳輸,旨在為低帶寬和不穩(wěn)定的網(wǎng)絡環(huán)境中的物聯(lián)網(wǎng)設備提供可靠的網(wǎng)絡服務。MQTT是專門針對物聯(lián)網(wǎng)開發(fā)的輕量級傳輸協(xié)議。MQTT協(xié)議針對低帶寬網(wǎng)絡,低計算能力的設備,做了特殊的優(yōu)化,使得其能適應各種物聯(lián)網(wǎng)應用場景。目前MQTT擁有各種平臺和設備上的客戶端,已經(jīng)形成了初步的生態(tài)系統(tǒng)。

MQTT是一種消息隊列協(xié)議,使用發(fā)布/訂閱消息模式,提供一對多的消息發(fā)布,解除應用程序耦合,相對于其他協(xié)議,開發(fā)更簡單;MQTT協(xié)議是工作在TCP/IP協(xié)議上;由TCP/IP協(xié)議提供穩(wěn)定的網(wǎng)絡連接;所以,只要具備TCP協(xié)議棧的網(wǎng)絡設備都可以使用MQTT協(xié)議。 本次設備采用的ESP8266就具備TCP協(xié)議棧,能夠建立TCP連接,所以,配合STM32代碼里封裝的MQTT協(xié)議,就可以與華為云平臺完成通信。

華為云的MQTT協(xié)議接入幫助文檔在這里: https://support.huaweicloud.com/devg-iothub/iot_02_2200.html

img

業(yè)務流程:

img

(2)華為云平臺MQTT協(xié)議使用限制

描述 限制
支持的MQTT協(xié)議版本 3.1.1
與標準MQTT協(xié)議的區(qū)別 支持Qos 0和Qos 1支持Topic自定義不支持QoS2不支持will、retain msg
MQTTS支持的安全等級 采用TCP通道基礎 + TLS協(xié)議(最高TLSv1.3版本)
單帳號每秒最大MQTT連接請求數(shù) 無限制
單個設備每分鐘支持的最大MQTT連接數(shù) 1
單個MQTT連接每秒的吞吐量,即帶寬,包含直連設備和網(wǎng)關 3KB/s
MQTT單個發(fā)布消息最大長度,超過此大小的發(fā)布請求將被直接拒絕 1MB
MQTT連接心跳時間建議值 心跳時間限定為30至1200秒,推薦設置為120秒
產(chǎn)品是否支持自定義Topic 支持
消息發(fā)布與訂閱 設備只能對自己的Topic進行消息發(fā)布與訂閱
每個訂閱請求的最大訂閱數(shù) 無限制

(3)主題訂閱格式

幫助文檔地址:https://support.huaweicloud.com/devg-iothub/iot_02_2200.html

image-20221207153310037

對于設備而言,一般會訂閱平臺下發(fā)消息給設備 這個主題。

設備想接收平臺下發(fā)的消息,就需要訂閱平臺下發(fā)消息給設備 的主題,訂閱后,平臺下發(fā)消息給設備,設備就會收到消息。

如果設備想要知道平臺下發(fā)的消息,需要訂閱上面圖片里標注的主題。

以當前設備為例,最終訂閱主題的格式如下:
$oc/devices/{device_id}/sys/messages/down

最終的格式:
$oc/devices/6419627e40773741f9fbdac7_dev1/sys/messages/down

?(4)主題發(fā)布格式

對于設備來說,主題發(fā)布表示向云平臺上傳數(shù)據(jù),將最新的傳感器數(shù)據(jù),設備狀態(tài)上傳到云平臺。

這個操作稱為:屬性上報。

幫助文檔地址:https://support.huaweicloud.com/usermanual-iothub/iot_06_v5_3010.html

image-20221207153637391

根據(jù)幫助文檔的介紹, 當前設備發(fā)布主題,上報屬性的格式總結如下:

發(fā)布的主題格式:
$oc/devices/{device_id}/sys/properties/report
 
最終的格式:
$oc/devices/6419627e40773741f9fbdac7_dev1/sys/properties/report
發(fā)布主題時,需要上傳數(shù)據(jù),這個數(shù)據(jù)格式是JSON格式。

上傳的JSON數(shù)據(jù)格式如下:

{
  "services": [
    {
      "service_id": <填服務ID>,
      "properties": {
        "<填屬性名稱1>": <填屬性值>,
        "<填屬性名稱2>": <填屬性值>,
        ..........
      }
    }
  ]
}
根據(jù)JSON格式,一次可以上傳多個屬性字段。 這個JSON格式里的,服務ID,屬性字段名稱,屬性值類型,在前面創(chuàng)建產(chǎn)品的時候就已經(jīng)介紹了,不記得可以翻到前面去查看。

根據(jù)這個格式,組合一次上傳的屬性數(shù)據(jù):
{"services": [{"service_id": "stm32","properties":{"DS18B20":18,"motor_water":1,"motor_oxygen":1,"temp_max":10,"water_hp":130,"motor_food":0,"time_food":0,"oxygen_food":3}}]}

3.6 MQTT三元組

MQTT協(xié)議登錄需要填用戶ID,設備ID,設備密碼等信息,就像我們平時登錄QQ,微信一樣要輸入賬號密碼才能登錄。MQTT協(xié)議登錄的這3個參數(shù),一般稱為MQTT三元組。

接下來介紹,華為云平臺的MQTT三元組參數(shù)如何得到。

(1)MQTT服務器地址

要登錄MQTT服務器,首先記得先知道服務器的地址是多少,端口是多少。

幫助文檔地址:https://console.huaweicloud.com/iotdm/?region=cn-north-4#/dm-portal/home

image-20230302135718882

MQTT協(xié)議的端口支持1883和8883,它們的區(qū)別是:8883 是加密端口更加安全。但是單片機上使用比較困難,所以當前的設備是采用1883端口進連接的。

根據(jù)上面的域名和端口號,得到下面的IP地址和端口號信息: 如果設備支持填寫域名可以直接填域名,不支持就直接填寫IP地址。 (IP地址就是域名解析得到的)

華為云的MQTT服務器地址:114.116.232.138
域名:7445c6bcd3.st1.iotda-device.cn-north-4.myhuaweicloud.com
華為云的MQTT端口號:1883

(2)生成MQTT三元組

華為云提供了一個在線工具,用來生成MQTT鑒權三元組: https://iot-tool.obs-website.cn-north-4.myhuaweicloud.com/

打開這個工具,填入設備的信息(也就是剛才創(chuàng)建完設備之后保存的信息),點擊生成,就可以得到MQTT的登錄信息了。

下面是打開的頁面:

image-20221207154917230

填入設備的信息: (上面兩行就是設備創(chuàng)建完成之后保存得到的)

直接得到三元組信息。

image-20230321160708924

image-20230321160718302

得到三元組之后,設備端通過MQTT協(xié)議登錄鑒權的時候,填入?yún)?shù)即可。

ClientId 6419627e40773741f9fbdac7_dev1_0_0_2023032108
Username 6419627e40773741f9fbdac7_dev1
Password 861ac9e6a579d36888b2aaf97714be7af6c77017b017162884592bd68b086a6e

3.7 模擬設備登錄測試

經(jīng)過上面的步驟介紹,已經(jīng)創(chuàng)建了產(chǎn)品,設備,數(shù)據(jù)模型,得到MQTT登錄信息。 接下來就用MQTT客戶端軟件模擬真實的設備來登錄平臺。測試與服務器通信是否正常。

(1)填入登錄信息

打開MQTT客戶端軟件,對號填入相關信息(就是上面的文本介紹)。然后,點擊登錄,訂閱主題,發(fā)布主題。

image-20230321161132971

(2)打開網(wǎng)頁查看

完成上面的操作之后,打開華為云網(wǎng)頁后臺,可以看到設備已經(jīng)在線了。

點擊詳情頁面,可以看到上傳的數(shù)據(jù)。

到此,云平臺的部署已經(jīng)完成,設備已經(jīng)可以正常上傳數(shù)據(jù)了。

四、上位機開發(fā)

為了方便查看設備上傳的數(shù)據(jù),對設備進行遠程控制,接下來利用Qt開發(fā)一款Android和windows系統(tǒng)的上位機。

使用華為云平臺提供的API接口獲取設備上傳的數(shù)據(jù),也可以給設備下發(fā)指令,控制設備。

為了方便查看設備上傳的數(shù)據(jù),對設備進行遠程控制,接下來利用Qt開發(fā)一款Android和windows系統(tǒng)的上位機。

使用華為云平臺提供的API接口獲取設備上傳的數(shù)據(jù),也可以給設備下發(fā)指令,控制設備。

4.1 Qt開發(fā)環(huán)境安裝

Qt的中文官網(wǎng): 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

軟件安裝時斷網(wǎng)安裝,否則會提示輸入賬戶。

安裝的時候,第一個復選框里勾選一個mingw 32編譯器即可,其他的不管默認就行,直接點擊下一步繼續(xù)安裝。

image-20221203151742653

說明: 我這里只是介紹PC端的環(huán)境搭建(這個比較簡單)。 Android的開發(fā)環(huán)境比較麻煩,可以去我的博客里看詳細文章。

選擇MinGW 32-bit 編譯器:

image-20221203151750344

4.2 創(chuàng)建IAM賬戶

創(chuàng)建一個IAM賬戶,因為接下來開發(fā)上位機,需要使用云平臺的API接口,這些接口都需要token進行鑒權。簡單來說,就是身份的認證。 調(diào)用接口獲取Token時,就需要填寫IAM賬號信息。所以,接下來演示一下過程。

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

獲取Token時,除了AIM賬號外,還需要項目憑證:

faa0973835ab409ab48182e2590f4ad3

image-20230321161348409

image-20230321161414487

鼠標點擊自己昵稱,點擊統(tǒng)一身份認證。

image-20230321161433209

點擊左上角創(chuàng)建用戶。

image-20230321161450557

image-20221207161209880

image-20221207161308917

創(chuàng)建成功:

image-20221212174359962

image-20221212174412097

image-20230321161557848

4.3 獲取影子數(shù)據(jù)

幫助文檔:https://support.huaweicloud.com/api-iothub/iot_06_v5_0079.html

設備影子介紹:

設備影子是一個用于存儲和檢索設備當前狀態(tài)信息的JSON文檔。
每個設備有且只有一個設備影子,由設備ID唯一標識
設備影子僅保存最近一次設備的上報數(shù)據(jù)和預期數(shù)據(jù)
無論該設備是否在線,都可以通過該影子獲取和設置設備的屬性

簡單來說:設備影子就是保存,設備最新上傳的一次數(shù)據(jù)。

我們設計的軟件里,如果想要獲取設備的最新狀態(tài)信息,就采用設備影子接口。

如果對接口不熟悉,可以先進行在線調(diào)試:https://apiexplorer.developer.huaweicloud.com/apiexplorer/doc?product=IoTDA&api=ShowDeviceShadow

在線調(diào)試接口,可以請求影子接口,了解請求,與返回的數(shù)據(jù)格式。

image-20230321161636567

image-20230321161701419

設備影子接口返回的數(shù)據(jù)如下:

{
 "device_id": "6419627e40773741f9fbdac7_dev1",
 "shadow": [
  {
   "service_id": "stm32",
   "desired": {
    "properties": null,
    "event_time": null
   },
   "reported": {
    "properties": {
     "DS18B20": 18,
     "motor_water": 1,
     "motor_oxygen": 1,
     "temp_max": 10,
     "water_hp": 130,
     "motor_food": 0,
     "time_food": 0,
     "oxygen_food": 3
    },
    "event_time": "20230321T081126Z"
   },
   "version": 0
  }
 ]
}

4.4 修改設備屬性

地址: https://support.huaweicloud.com/api-iothub/iot_06_v5_0034.html

接口說明

設備的產(chǎn)品模型中定義了物聯(lián)網(wǎng)平臺可向設備下發(fā)的屬性,應用服務器可調(diào)用此接口向指定設備下發(fā)屬性。平臺負責將屬性以同步方式發(fā)送給設備,并將設備執(zhí)行屬性結果同步返回。

修改設備屬性的接口,可以讓服務器給設備下發(fā)指令,如果需要控制設備。

在線調(diào)試地址:

https://apiexplorer.developer.huaweicloud.com/apiexplorer/doc?product=IoTDA&api=UpdateProperties

修改設備屬性是屬于同步命令,需要設備在線才可以進行調(diào)試,先使用MQTT客戶端登錄服務器,模擬設備上線。

然后進行調(diào)試,測試數(shù)據(jù)遠程下發(fā)給設備。

【1】利用MQTT客戶端先登錄設備 (這是同步命令,必須在線才能調(diào)試)

image-20230321161923007

【2】點擊調(diào)試

{"services":{"temp_max":100}}

【4】可以看到,MQTT客戶端軟件上已經(jīng)收到了服務器下發(fā)的消息

image-20230313175819901

由于是同步命令,服務器必須要收到設備的響應才能順利完成一個流程,設備響應了服務器才能確定數(shù)據(jù)下發(fā)成功。

MQTT設備端如何響應呢?

設備響應格式說明:https://support.huaweicloud.com/api-iothub/iot_06_v5_3008.html

下面進行實操:

當服務器通過在線調(diào)試,發(fā)送指令下來之后,客戶端將請求ID復制下來,添加到發(fā)布主題的格式里,再回復回去,服務器收到了響應,一次屬性修改就完美完成了。

image-20230321162053263

就是成功的狀態(tài):

image-20230321162026282

**下面是請求的總結: ** (響應服務器的修改設備屬性請求)

上報主題的格式:$oc/devices/{device_id}/sys/properties/set/response/request_id=

$oc/devices/6419627e40773741f9fbdac7_dev1/sys/properties/set/response/request_id=

響應的數(shù)據(jù):
{"result_code": 0,"result_desc": "success"}

4.5 設計上位機

前面2講解了需要用的API接口,接下來就使用Qt設計上位機,設計界面,完成整體上位機的邏輯設計。

【1】新建Qt工程

image-20230302144331541

選擇工程路徑,放在英文路徑下。

image-20230321162532573

 

image-20230313180451177

image-20230313180504518

image-20230321162620141

創(chuàng)建完畢。

新建Android的模板:

 

 

image-20230321162741791

image-20230321162812486

【2】界面設計

image-20230728132051169

【4】代碼設計:配置參數(shù)讀取與保存

/*
功能: 保存數(shù)據(jù)到文件
*/
void Widget::SaveDataToFile(QString text)
{
    /*保存數(shù)據(jù)到文件,方便下次加載*/
    QString file;
    file=QCoreApplication::applicationDirPath()+"/"+ConfigFile;
    QFile filesrc(file);
    filesrc.open(QIODevice::WriteOnly);
    QDataStream out(&filesrc);
    out << text;  //序列化寫字符串
    filesrc.flush();
    filesrc.close();
}


/*
功能: 從文件讀取數(shù)據(jù)
*/
QString Widget::ReadDataFile(void)
{
    //讀取配置文件
    QString text,data;
    text=QCoreApplication::applicationDirPath()+"/"+ConfigFile;

    //判斷文件是否存在
    if(QFile::exists(text))
    {
        QFile filenew(text);
        filenew.open(QIODevice::ReadOnly);
        QDataStream in(&filenew); // 從文件讀取序列化數(shù)據(jù)
        in >> data; //提取寫入的數(shù)據(jù)
        filenew.close();
    }
    return data; //返回值讀取的值
}

【3】代碼設計:云端數(shù)據(jù)解析

//解析反饋結果
void Widget::replyFinished(QNetworkReply *reply)
{
    QString displayInfo;

    int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();

    //讀取所有數(shù)據(jù)
    QByteArray replyData = reply->readAll();

    qDebug()<<"狀態(tài)碼:"<<statusCode;
    qDebug()<<"反饋的數(shù)據(jù):"<<QString(replyData);

    //更新token
    if(function_select==3)
    {
        displayInfo="token 更新失敗.";
        //讀取HTTP響應頭的數(shù)據(jù)
        QList<QNetworkReply::RawHeaderPair> RawHeader=reply->rawHeaderPairs();
        qDebug()<<"HTTP響應頭數(shù)量:"<<RawHeader.size();
        for(int i=0;i<RawHeader.size();i++)
        {
            QString first=RawHeader.at(i).first;
            QString second=RawHeader.at(i).second;
            if(first=="X-Subject-Token")
            {
                Token=second.toUtf8();
                displayInfo="token 更新成功.";

                //保存到文件
                SaveDataToFile(Token);
                break;
            }
        }
        QMessageBox::information(this,"提示",displayInfo,QMessageBox::Ok,QMessageBox::Ok);
        return;
    }

    //判斷狀態(tài)碼
    if(200 != statusCode)
    {
        //解析數(shù)據(jù)
        QJsonParseError json_error;
        QJsonDocument document = QJsonDocument::fromJson(replyData, &json_error);
        if(json_error.error == QJsonParseError::NoError)
        {
            //判斷是否是對象,然后開始解析數(shù)據(jù)
            if(document.isObject())
            {
                QString error_str="";
                QJsonObject obj = document.object();
                QString error_code;
                //解析錯誤代碼
                if(obj.contains("error_code"))
                {
                    error_code=obj.take("error_code").toString();
                    error_str+="錯誤代碼:";
                    error_str+=error_code;
                    error_str+="n";
                }
                if(obj.contains("error_msg"))
                {
                    error_str+="錯誤消息:";
                    error_str+=obj.take("error_msg").toString();
                    error_str+="n";
                }

                //顯示錯誤代碼
                QMessageBox::information(this,"提示",error_str,QMessageBox::Ok,QMessageBox::Ok);
            }
         }
        return;
    }

    //設置屬性
    if(function_select==12 || function_select==13)
    {
        //解析數(shù)據(jù)
        QJsonParseError json_error;
        QJsonDocument document = QJsonDocument::fromJson(replyData, &json_error);
        if(json_error.error == QJsonParseError::NoError)
        {
            //判斷是否是對象,然后開始解析數(shù)據(jù)
            if(document.isObject())
            {
                QJsonObject obj = document.object();
                if(obj.contains("response"))
                {
                    QJsonObject obj1=obj.take("response").toObject();
                    int val=0;
                    QString success;
                    if(obj1.contains("result_code"))
                    {
                         val=obj1.take("result_code").toInt();
                    }
                    if(obj1.contains("result_desc"))
                    {
                         success=obj1.take("result_desc").toString();
                    }

                    if(val==0 && success =="success")
                    {
                        //顯示狀態(tài)
                        QMessageBox::information(this,"提示","遠程命令操作完成.",QMessageBox::Ok,QMessageBox::Ok);
                        return;
                    }
                    else
                    {
                        //顯示狀態(tài)
                        QMessageBox::information(this,"提示","設備未正確回應.請檢查設備網(wǎng)絡.",QMessageBox::Ok,QMessageBox::Ok);
                        return;
                    }
                }
            }
         }
    }

    //查詢設備屬性
    if(function_select==0)
    {
        //解析數(shù)據(jù)
        QJsonParseError json_error;
        QJsonDocument document = QJsonDocument::fromJson(replyData, &json_error);
        if(json_error.error == QJsonParseError::NoError)
        {
            //判斷是否是對象,然后開始解析數(shù)據(jù)
            if(document.isObject())
            {
                QJsonObject obj = document.object();
                if(obj.contains("shadow"))
                {
                    QJsonArray array=obj.take("shadow").toArray();
                    for(int i=0;i<array.size();i++)
                    {
                        QJsonObject obj2=array.at(i).toObject();
                        if(obj2.contains("reported"))
                        {
                            QJsonObject obj3=obj2.take("reported").toObject();


                            if(obj3.contains("properties"))
                            {
                                QJsonObject properties=obj3.take("properties").toObject();

                                qDebug()<<"開始解析數(shù)據(jù)....";
                            }
                        }
                    }
                }
            }
         }
        return;
    }
}

五、代碼實現(xiàn)

5.1 ESP8266連接云平臺實現(xiàn)代碼

以下是使用STM32F103ZET6和ESP8266連接華為云物聯(lián)網(wǎng)平臺,通過MQTT協(xié)議實現(xiàn)設備登錄、主題訂閱和主題發(fā)布的實現(xiàn)代碼:

#include "stm32f10x.h"
#include "stdio.h"
#include "string.h"

// 定義ESP8266的串口
USART_TypeDef* ESP_USARTx = USART1;

// 定義MQTT服務器的地址和端口
const char* MQTT_SERVER = "mqtt.eclipse.org";
const int MQTT_PORT = 1883;

// 定義設備ID和設備密碼
const char* DEVICE_ID = "your_device_id";
const char* DEVICE_PASSWORD = "your_device_password";

// 定義訂閱的主題
const char* SUBSCRIBE_TOPIC = "your_subscribe_topic";

// 定義發(fā)布的主題
const char* PUBLISH_TOPIC = "your_publish_topic";

// 定義接收緩沖區(qū)和發(fā)送緩沖區(qū)的大小
#define RX_BUFFER_SIZE 1024
#define TX_BUFFER_SIZE 1024

// 定義接收緩沖區(qū)和發(fā)送緩沖區(qū)
char rxBuffer[RX_BUFFER_SIZE];
char txBuffer[TX_BUFFER_SIZE];

// 定義接收緩沖區(qū)的索引和標志位
volatile uint16_t rxIndex = 0;
volatile uint8_t rxComplete = 0;

// 發(fā)送數(shù)據(jù)到ESP8266
void ESP8266_SendData(const char* data) {
    sprintf(txBuffer, "%srn", data);
    USART_SendData(ESP_USARTx, (uint16_t)'r');
    USART_SendData(ESP_USARTx, (uint16_t)'n');
    USART_SendData(ESP_USARTx, (uint16_t)'r');
    USART_SendData(ESP_USARTx, (uint16_t)'n');
    USART_SendData(ESP_USARTx, (uint16_t)'r');
    USART_SendData(ESP_USARTx, (uint16_t)'n');
    USART_SendData(ESP_USARTx, (uint16_t)'r');
    USART_SendData(ESP_USARTx, (uint16_t)'n');
    USART_SendData(ESP_USARTx, (uint16_t)'r');
    USART_SendData(ESP_USARTx, (uint16_t)'n');
    USART_SendData(ESP_USARTx, (uint16_t)'r');
    USART_SendData(ESP_USARTx, (uint16_t)'n');
    USART_SendData(ESP_USARTx, (uint16_t)'r');
    USART_SendData(ESP_USARTx, (uint16_t)'n');
    USART_SendData(ESP_USARTx, (uint16_t)'r');
    USART_SendData(ESP_USARTx, (uint16_t)'n');
}

// 從ESP8266接收數(shù)據(jù)
void ESP8266_ReceiveData(uint16_t size) {
    while (size--) {
        rxBuffer[rxIndex++] = USART_ReceiveData(ESP_USARTx);
    }
    if (rxIndex >= RX_BUFFER_SIZE) {
        rxComplete = 1;
        rxIndex = 0;
    }
}

// 處理接收到的數(shù)據(jù)
void ProcessReceivedData() {
    // TODO: 根據(jù)接收到的數(shù)據(jù)進行處理
}

// ESP8266串口中斷處理函數(shù)
void USART1_IRQHandler(void) {
    if (USART_GetITStatus(ESP_USARTx, USART_IT_RXNE) != RESET) {
        ESP8266_ReceiveData(1);
    }
}

// 連接到MQTT服務器
void MQTT_Connect() {
    // 發(fā)送連接請求
    sprintf(txBuffer, "AT+CIPSTART="TCP","%s",%drn", MQTT_SERVER, MQTT_PORT);
    ESP8266_SendData(txBuffer);
    // 等待連接成功
    while (!strstr(rxBuffer, "CONNECTED")) {
        if (rxComplete) {
            ProcessReceivedData();
            rxComplete = 0;
        }
    }
    // 發(fā)送MQTT連接請求
    sprintf(txBuffer, "AT+MQTTCONNECT="%s","%s"rn", DEVICE_ID, DEVICE_PASSWORD);
    ESP8266_SendData(txBuffer);
    // 等待連接成功
    while (!strstr(rxBuffer, "CONNECTED")) {
        if (rxComplete) {
            ProcessReceivedData();
            rxComplete = 0;
        }
    }
}

// 訂閱主題
void MQTT_Subscribe() {
    sprintf(txBuffer, "AT+MQTTSUBSCRIBE="%s"rn", SUBSCRIBE_TOPIC);
    ESP8266_SendData(txBuffer);
}

// 發(fā)布消息
void MQTT_Publish(const char* message) {
    sprintf(txBuffer, "AT+MQTTPUBLISH="%s","%s"rn", PUBLISH_TOPIC, message);
    ESP8266_SendData(txBuffer);
}

int main(void) {
    // 初始化ESP8266的串口
    USART_InitTypeDef USART_InitStructure;
    USART_InitStructure.USART_BaudRate = 115200;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(ESP_USARTx, &USART_InitStructure);
    USART_Cmd(ESP_USARTx, ENABLE);
    USART_ITConfig(ESP_USARTx, USART_IT_RXNE, ENABLE);
    NVIC_EnableIRQ(USART1_IRQn);

    // 連接到MQTT服務器
    MQTT_Connect();

    // 訂閱主題
    MQTT_Subscribe();

    while (1) {
        if (rxComplete) {
            ProcessReceivedData();
            rxComplete = 0;
        }
        
        // TODO: 處理其他業(yè)務邏輯
        
        // 發(fā)布消息
        MQTT_Publish("Hello, MQTT!");
        
        // 延時一段時間
        delay_ms(1000);
    }
}

以上代碼用于演示使用STM32F103ZET6和ESP8266連接華為云物聯(lián)網(wǎng)平臺,通過MQTT協(xié)議實現(xiàn)設備登錄、主題訂閱和主題發(fā)布的基本功能。

5.2 ESP8266的MQTT協(xié)議指令

ESP8266通過MQTT協(xié)議連接到服務器的相關AT指令主要有以下幾個:

【1】AT+CIPSTART:建立TCP連接

  • 功能:使用TCP協(xié)議連接到遠程服務器
  • 用法:AT+CIPSTART=“TCP”,“<服務器地址>”,<服務器端口>
  • 示例:AT+CIPSTART=“TCP”,“mqtt.eclipse.org”,1883

【2】AT+MQTTCONNECT:連接到MQTT服務器

  • 功能:使用MQTT協(xié)議連接到MQTT服務器
  • 用法:AT+MQTTCONNECT=“<設備ID>”,“<設備密碼>”
  • 示例:AT+MQTTCONNECT=“your_device_id”,“your_device_password”

【3】AT+MQTTPUBLISH:發(fā)布消息

  • 功能:向指定主題發(fā)布消息
  • 用法:AT+MQTTPUBLISH=“<主題>”,“<消息內(nèi)容>”
  • 示例:AT+MQTTPUBLISH=“your_publish_topic”,“Hello, MQTT!”

【4】AT+MQTTSUBSCRIBE:訂閱主題

  • 功能:訂閱指定的主題
  • 用法:AT+MQTTSUBSCRIBE=“<主題>”
  • 示例:AT+MQTTSUBSCRIBE=“your_subscribe_topic”

【5】AT+CIPCLOSE:關閉TCP連接

  • 功能:關閉當前的TCP連接
  • 用法:AT+CIPCLOSE

這些AT指令可以通過串口與ESP8266進行通信,實現(xiàn)與MQTT服務器的連接、消息發(fā)布和訂閱等功能。通過這些指令,可以在嵌入式設備上實現(xiàn)與云端的通信和數(shù)據(jù)交換,從而實現(xiàn)物聯(lián)網(wǎng)應用。

5.3 讀取DHT11傳感器的溫濕度數(shù)據(jù)

以下是使用STM32F103ZET6讀取DHT11傳感器的溫濕度數(shù)據(jù)的實現(xiàn)代碼:

#include "stm32f10x.h"
#include "dht11.h"

int main(void)
{
    // 初始化DHT11傳感器
    DHT11_Init();

    while (1)
    {
        // 讀取DHT11傳感器的溫濕度數(shù)據(jù)
        DHT11_Result result = DHT11_Read();

        if (result.status == DHT11_OK)
        {
            // 溫度數(shù)據(jù)
            uint8_t temperature = result.temperature;
            // 濕度數(shù)據(jù)
            uint8_t humidity = result.humidity;

            // 在這里進行溫濕度數(shù)據(jù)的處理和使用
            // ...

            // 延時一段時間后再次讀取
            DelayMs(2000);
        }
        else
        {
            // 讀取失敗,可以進行相應的錯誤處理
            // ...
        }
    }
}

在主函數(shù)中,通過循環(huán)不斷讀取DHT11傳感器的溫濕度數(shù)據(jù)。如果讀取成功,可以從result結構體中獲取溫度和濕度數(shù)據(jù),并進行相應的處理。如果讀取失敗,可以根據(jù)需要進行錯誤處理。

5.4 DHT11.c和DHT11.h代碼

dht11.h:

#ifndef DHT11_H
#define DHT11_H

#include "stm32f10x.h"

typedef struct
{
    uint8_t status;      // 讀取狀態(tài),0表示成功,其他表示失敗
    uint8_t humidity;    // 濕度值
    uint8_t temperature; // 溫度值
} DHT11_Result;

void DHT11_Init(void);
DHT11_Result DHT11_Read(void);

#endif

dht11.c:

#include "dht11.h"

#define DHT11_PORT GPIOA
#define DHT11_PIN GPIO_Pin_0

static void DHT11_Delay(uint32_t us)
{
    uint32_t count = us * 8;
    while (count--)
    {
        __NOP();
    }
}

static void DHT11_SetOutput(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = DHT11_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(DHT11_PORT, &GPIO_InitStructure);
}

static void DHT11_SetInput(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = DHT11_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(DHT11_PORT, &GPIO_InitStructure);
}

static uint8_t DHT11_ReadByte(void)
{
    uint8_t byte = 0;
    for (uint8_t i = 0; i < 8; i++)
    {
        while (!GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN))
        {
            // 等待低電平結束
        }
        DHT11_Delay(30);
        if (GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN))
        {
            byte |= (1 << (7 - i));
        }
        while (GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN))
        {
            // 等待高電平結束
        }
    }
    return byte;
}

void DHT11_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_InitStructure.GPIO_Pin = DHT11_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(DHT11_PORT, &GPIO_InitStructure);

    GPIO_SetBits(DHT11_PORT, DHT11_PIN);
}

DHT11_Result DHT11_Read(void)
{
    DHT11_Result result;
    result.status = 1;

    DHT11_SetOutput();
    GPIO_ResetBits(DHT11_PORT, DHT11_PIN);
    DHT11_Delay(18000);
    GPIO_SetBits(DHT11_PORT, DHT11_PIN);
    DHT11_Delay(20);
    DHT11_SetInput();

    if (!GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN))
    {
        while (!GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN))
        {
            // 等待低電平結束
        }
        while (GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN))
        {
            // 等待高電平結束
        }

        uint8_t data[5];
        for (uint8_t i = 0; i < 5; i++)
        {
            data[i] = DHT11_ReadByte();
        }

        uint8_t sum = data[0] + data[1] + data[2] + data[3];
        if (sum == data[4])
        {
            result.status = 0;
            result.humidity = data[0];
            result.temperature = data[2];
        }
    }

    return result;
}

dht11.h文件定義了DHT11傳感器的初始化函數(shù)DHT11_Init()和讀取函數(shù)DHT11_Read(),以及DHT11_Result結構體用于存儲讀取結果。

dht11.c文件實現(xiàn)了DHT11傳感器的初始化和讀取函數(shù)。在初始化函數(shù)中,配置了DHT11引腳的GPIO模式和速度。在讀取函數(shù)中,通過發(fā)送開始信號和接收數(shù)據(jù)的方式讀取DHT11傳感器的溫濕度數(shù)據(jù),并進行校驗。

  • 更多詳細資料請聯(lián)系.docx
    下載
意法半導體

意法半導體

意法半導體(ST)集團于1987年6月成立,是由意大利的SGS微電子公司和法國Thomson半導體公司合并而成。1998年5月,SGS-THOMSON Microelectronics將公司名稱改為意法半導體有限公司。意法半導體是世界最大的半導體公司之一,公司銷售收入在半導體工業(yè)五大高速增長市場之間分布均衡(五大市場占2007年銷售收入的百分比):通信(35%),消費(17%),計算機(16%),汽車(16%),工業(yè)(16%)。 據(jù)最新的工業(yè)統(tǒng)計數(shù)據(jù),意法半導體是全球第五大半導體廠商,在很多市場居世界領先水平。例如,意法半導體是世界第一大專用模擬芯片和電源轉換芯片制造商,世界第一大工業(yè)半導體和機頂盒芯片供應商,而且在分立器件、手機相機模塊和車用集成電路領域居世界前列.

意法半導體(ST)集團于1987年6月成立,是由意大利的SGS微電子公司和法國Thomson半導體公司合并而成。1998年5月,SGS-THOMSON Microelectronics將公司名稱改為意法半導體有限公司。意法半導體是世界最大的半導體公司之一,公司銷售收入在半導體工業(yè)五大高速增長市場之間分布均衡(五大市場占2007年銷售收入的百分比):通信(35%),消費(17%),計算機(16%),汽車(16%),工業(yè)(16%)。 據(jù)最新的工業(yè)統(tǒng)計數(shù)據(jù),意法半導體是全球第五大半導體廠商,在很多市場居世界領先水平。例如,意法半導體是世界第一大專用模擬芯片和電源轉換芯片制造商,世界第一大工業(yè)半導體和機頂盒芯片供應商,而且在分立器件、手機相機模塊和車用集成電路領域居世界前列.收起

查看更多

相關推薦