一、項(xiàng)目背景
隨著智能家居技術(shù)的不斷發(fā)展,人們對(duì)于家居生活的需求也越來(lái)越高。智能窗簾作為智能家居領(lǐng)域的重要組成部分,為用戶提供了更便捷、舒適的生活體驗(yàn)。本項(xiàng)目基于STM32主控芯片和華為云物聯(lián)網(wǎng)平臺(tái),設(shè)計(jì)一款智能窗簾控制系統(tǒng),以滿足家庭和商業(yè)場(chǎng)所的需求。
在本項(xiàng)目中,選擇了STM32F103ZET6作為主控芯片具有強(qiáng)大的處理能力和豐富的外設(shè)接口,適合用于物聯(lián)網(wǎng)設(shè)備的控制和通信。通過(guò)與ESP8266-WIFI模塊的連接,可以實(shí)現(xiàn)智能窗簾與華為云物聯(lián)網(wǎng)平臺(tái)的互聯(lián)互通,實(shí)現(xiàn)遠(yuǎn)程控制和監(jiān)測(cè)。
為了方便用戶的操作和控制,使用Qt開(kāi)發(fā)了Android手機(jī)APP和Windows上位機(jī)軟件,用戶可以通過(guò)這些應(yīng)用程序進(jìn)行窗簾的遠(yuǎn)程控制。同時(shí),本地窗簾也支持手動(dòng)控制,用戶可以通過(guò)物理按鈕或開(kāi)關(guān)來(lái)操作窗簾的開(kāi)關(guān)、升降等功能。
在智能化方面,引入了語(yǔ)音識(shí)別技術(shù)(LD3320模塊),用戶可以通過(guò)語(yǔ)音指令來(lái)控制窗簾的運(yùn)行。這為用戶提供了更加便捷、智能的控制方式,使得窗簾的操作更加自然和智能化。
除了遠(yuǎn)程控制和智能化功能,還引入了自動(dòng)模式。在自動(dòng)模式下,系統(tǒng)會(huì)根據(jù)環(huán)境條件進(jìn)行智能判斷和控制。例如,當(dāng)檢測(cè)到陽(yáng)光強(qiáng)度超過(guò)設(shè)定閾值時(shí),系統(tǒng)會(huì)自動(dòng)關(guān)閉窗簾,以避免陽(yáng)光直射室內(nèi);在晚上時(shí),系統(tǒng)也會(huì)自動(dòng)拉上窗簾,提供更好的隱私和安全性。
本智能窗簾控制系統(tǒng)基于STM32主控芯片和華為云物聯(lián)網(wǎng)平臺(tái),結(jié)合語(yǔ)音識(shí)別、智能家居控制等功能,旨在為家庭和1商業(yè)場(chǎng)所提供便捷、舒適的智能化服務(wù)。通過(guò)遠(yuǎn)程控制、自動(dòng)模式和智能化功能,用戶可以實(shí)現(xiàn)對(duì)窗簾的靈活、智能的控制,提升生活質(zhì)量和用戶體驗(yàn)。
二、系統(tǒng)設(shè)計(jì)
2.1 硬件選型
在設(shè)計(jì)智能窗簾控制系統(tǒng)的硬件方案時(shí),需要考慮主控芯片、通信模塊和傳感器等關(guān)鍵組件的選型。
以下是當(dāng)前系統(tǒng)的具體硬件選型:
【1】主控芯片:采用STM32F103ZET6作為主控芯片。具備強(qiáng)大的處理能力和豐富的外設(shè)接口,適合用于物聯(lián)網(wǎng)設(shè)備的控制和通信??梢则?qū)動(dòng)各種傳感器和執(zhí)行器,并與ESP8266-WIFI模塊和LD3320語(yǔ)音識(shí)別模塊進(jìn)行通信。
【2】通信模塊:選擇了ESP8266-WIFI模塊作為通信模塊,用于連接華為云物聯(lián)網(wǎng)平臺(tái)。ESP8266-WIFI模塊具有低功耗、高集成度和穩(wěn)定的無(wú)線連接能力,能夠?qū)崿F(xiàn)智能窗簾與互聯(lián)網(wǎng)的互聯(lián)互通。
【3】光照傳感器:采用BH1750光照傳感器來(lái)檢測(cè)光照強(qiáng)度。BH1750是一種數(shù)字式光強(qiáng)度傳感器,能夠準(zhǔn)確測(cè)量環(huán)境光的強(qiáng)度。通過(guò)獲取光照強(qiáng)度數(shù)據(jù),系統(tǒng)可以根據(jù)設(shè)定的閾值來(lái)判斷是否需要自動(dòng)拉窗簾。
【4】語(yǔ)音識(shí)別模塊:選擇了LD3320語(yǔ)音識(shí)別模塊,用于實(shí)現(xiàn)語(yǔ)音控制功能。LD3320是一種高性能語(yǔ)音識(shí)別芯片,能夠?qū)崿F(xiàn)對(duì)語(yǔ)音指令的識(shí)別和解析。通過(guò)語(yǔ)音識(shí)別模塊,用戶可以通過(guò)語(yǔ)音指令來(lái)控制窗簾的開(kāi)合和模式切換。
【5】電機(jī)和驅(qū)動(dòng)模塊:選擇了28BYJ40步進(jìn)電機(jī)作為窗簾控制的電機(jī),并使用ULN2003驅(qū)動(dòng)模塊來(lái)驅(qū)動(dòng)電機(jī)。28BYJ40步進(jìn)電機(jī)具有較高的精度和穩(wěn)定性,適合用于窗簾的控制。ULN2003是一種高電壓、高電流驅(qū)動(dòng)芯片,能夠提供足夠的電流和電壓來(lái)驅(qū)動(dòng)步進(jìn)電機(jī)。
【6】用戶界面設(shè)備:采用Qt開(kāi)發(fā)Android手機(jī)APP和Windows上位機(jī)來(lái)實(shí)現(xiàn)用戶界面。通過(guò)這兩個(gè)界面,用戶可以進(jìn)行遠(yuǎn)程控制窗簾的操作,包括開(kāi)關(guān)窗簾、調(diào)整窗簾的開(kāi)合程度和切換窗簾的工作模式。
2.2 設(shè)計(jì)思路
智能窗簾控制系統(tǒng)的軟件設(shè)計(jì)主要包括主控程序、通信模塊驅(qū)動(dòng)、傳感器驅(qū)動(dòng)和用戶界面等部分。
以下是系統(tǒng)軟件設(shè)計(jì)的思路:
【1】主控程序:主控程序是系統(tǒng)的核心,負(fù)責(zé)控制窗簾的運(yùn)行、處理傳感器數(shù)據(jù)、與通信模塊進(jìn)行通信等。主控程序需要實(shí)現(xiàn)以下功能:
- 初始化各個(gè)硬件模塊,包括通信模塊、傳感器和電機(jī)驅(qū)動(dòng)等。
- 循環(huán)讀取傳感器數(shù)據(jù),根據(jù)數(shù)據(jù)進(jìn)行窗簾的控制和判斷。
- 處理用戶的控制指令,包括遠(yuǎn)程控制指令和本地控制指令。
- 與通信模塊進(jìn)行通信,實(shí)現(xiàn)與華為云物聯(lián)網(wǎng)平臺(tái)的互聯(lián)互通。
- 實(shí)現(xiàn)自動(dòng)模式下的智能判斷和控制邏輯。
【2】通信模塊驅(qū)動(dòng):通信模塊驅(qū)動(dòng)負(fù)責(zé)與華為云物聯(lián)網(wǎng)平臺(tái)進(jìn)行通信,實(shí)現(xiàn)遠(yuǎn)程控制和數(shù)據(jù)傳輸。通信模塊驅(qū)動(dòng)需要實(shí)現(xiàn)以下功能:
- 初始化通信模塊,建立與華為云物聯(lián)網(wǎng)平臺(tái)的連接。
- 接收來(lái)自云平臺(tái)的控制指令,解析指令內(nèi)容。
- 將傳感器數(shù)據(jù)和窗簾狀態(tài)等信息上傳到云平臺(tái),實(shí)現(xiàn)實(shí)時(shí)監(jiān)測(cè)和數(shù)據(jù)傳輸。
【3】傳感器驅(qū)動(dòng):傳感器驅(qū)動(dòng)負(fù)責(zé)與光敏傳感器、時(shí)間傳感器等傳感器進(jìn)行交互,獲取環(huán)境數(shù)據(jù)。傳感器驅(qū)動(dòng)需要實(shí)現(xiàn)以下功能:
- 初始化傳感器,配置傳感器的工作模式和參數(shù)。
- 定期讀取傳感器數(shù)據(jù),包括光敏傳感器的光強(qiáng)度和時(shí)間傳感器的時(shí)間信息。
- 將傳感器數(shù)據(jù)傳遞給主控程序,供其進(jìn)行判斷和控制。
【4】用戶界面:用戶界面是用戶與系統(tǒng)進(jìn)行交互的界面,可以通過(guò)Android手機(jī)APP或Windows上位機(jī)軟件實(shí)現(xiàn)。用戶界面需要實(shí)現(xiàn)以下功能:
- 顯示窗簾的狀態(tài)和實(shí)時(shí)數(shù)據(jù),如光強(qiáng)度、時(shí)間。
- 提供遠(yuǎn)程控制窗簾的功能,包括開(kāi)關(guān)、升降和自動(dòng)模式的切換。
- 接收用戶的控制指令,將指令傳遞給主控程序進(jìn)行處理。
三、部署華為云物聯(lián)網(wǎng)平臺(tái)
華為云官網(wǎng): https://www.huaweicloud.com/
打開(kāi)官網(wǎng),搜索物聯(lián)網(wǎng),就能快速找到 設(shè)備接入IoTDA
。
3.1 物聯(lián)網(wǎng)平臺(tái)介紹
華為云物聯(lián)網(wǎng)平臺(tái)(IoT 設(shè)備接入云服務(wù))提供海量設(shè)備的接入和管理能力,將物理設(shè)備聯(lián)接到云,支撐設(shè)備數(shù)據(jù)采集上云和云端下發(fā)命令給設(shè)備進(jìn)行遠(yuǎn)程控制,配合華為云其他產(chǎn)品,幫助我們快速構(gòu)筑物聯(lián)網(wǎng)解決方案。
使用物聯(lián)網(wǎng)平臺(tái)構(gòu)建一個(gè)完整的物聯(lián)網(wǎng)解決方案主要包括3部分:物聯(lián)網(wǎng)平臺(tái)、業(yè)務(wù)應(yīng)用和設(shè)備。
物聯(lián)網(wǎng)平臺(tái)作為連接業(yè)務(wù)應(yīng)用和設(shè)備的中間層,屏蔽了各種復(fù)雜的設(shè)備接口,實(shí)現(xiàn)設(shè)備的快速接入;同時(shí)提供強(qiáng)大的開(kāi)放能力,支撐行業(yè)用戶構(gòu)建各種物聯(lián)網(wǎng)解決方案。
設(shè)備可以通過(guò)固網(wǎng)、2G/3G/4G/5G、NB-IoT、Wifi等多種網(wǎng)絡(luò)接入物聯(lián)網(wǎng)平臺(tái),并使用LWM2M/CoAP、MQTT、HTTPS協(xié)議將業(yè)務(wù)數(shù)據(jù)上報(bào)到平臺(tái),平臺(tái)也可以將控制命令下發(fā)給設(shè)備。
業(yè)務(wù)應(yīng)用通過(guò)調(diào)用物聯(lián)網(wǎng)平臺(tái)提供的API,實(shí)現(xiàn)設(shè)備數(shù)據(jù)采集、命令下發(fā)、設(shè)備管理等業(yè)務(wù)場(chǎng)景。
3.2 開(kāi)通物聯(lián)網(wǎng)服務(wù)
地址: https://www.huaweicloud.com/product/iothub.html
進(jìn)來(lái)默認(rèn)會(huì)提示開(kāi)通標(biāo)準(zhǔn)版,在2023的1月1號(hào)年之后沒(méi)有基礎(chǔ)版了。
開(kāi)通之后,點(diǎn)擊總覽
,查看接入信息。 我們當(dāng)前設(shè)備準(zhǔn)備采用MQTT協(xié)議接入華為云平臺(tái),這里可以看到MQTT協(xié)議的地址和端口號(hào)等信息。
總結(jié):
端口號(hào): MQTT (1883)| MQTTS (8883)
接入地址: 7445c6bcd3.st1.iotda-app.cn-north-4.myhuaweicloud.com
根據(jù)域名地址得到IP地址信息:
Microsoft Windows [版本 10.0.19044.2728]
(c) Microsoft Corporation。保留所有權(quán)利。
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ù):
來(lái)自 117.78.5.125 的回復(fù): 字節(jié)=32 時(shí)間=42ms TTL=30
來(lái)自 117.78.5.125 的回復(fù): 字節(jié)=32 時(shí)間=35ms TTL=30
來(lái)自 117.78.5.125 的回復(fù): 字節(jié)=32 時(shí)間=36ms TTL=30
來(lái)自 117.78.5.125 的回復(fù): 字節(jié)=32 時(shí)間=36ms TTL=30
117.78.5.125 的 Ping 統(tǒng)計(jì)信息:
數(shù)據(jù)包: 已發(fā)送 = 4,已接收 = 4,丟失 = 0 (0% 丟失),
往返行程的估計(jì)時(shí)間(以毫秒為單位):
最短 = 35ms,最長(zhǎng) = 42ms,平均 = 37ms
C:Users11266>
MQTT協(xié)議接入端口號(hào)有兩個(gè),1883是非加密端口,8883是證書(shū)加密端口,單片機(jī)無(wú)法加載證書(shū),所以使用1883端口比較合適。 接下來(lái)的ESP8266就采用1883端口連接華為云物聯(lián)網(wǎng)平臺(tái)。
3.3 創(chuàng)建產(chǎn)品
(1)創(chuàng)建產(chǎn)品
點(diǎn)擊產(chǎn)品頁(yè),再點(diǎn)擊左上角創(chuàng)建產(chǎn)品。
(2)填寫產(chǎn)品信息
根據(jù)自己產(chǎn)品名字填寫。
(3)產(chǎn)品創(chuàng)建成功
(4)添加自定義模型
產(chǎn)品創(chuàng)建完成之后,點(diǎn)擊進(jìn)入產(chǎn)品詳情頁(yè)面,翻到最下面可以看到模型定義。
先點(diǎn)擊自定義模型。
再創(chuàng)建一個(gè)服務(wù)ID。
接著點(diǎn)擊新增屬性。
3.4 添加設(shè)備
產(chǎn)品是屬于上層的抽象模型,接下來(lái)在產(chǎn)品模型下添加實(shí)際的設(shè)備。添加的設(shè)備最終需要與真實(shí)的設(shè)備關(guān)聯(lián)在一起,完成數(shù)據(jù)交互。
(1)注冊(cè)設(shè)備
(2)根據(jù)自己的設(shè)備填寫
(3)保存設(shè)備信息
創(chuàng)建完畢之后,點(diǎn)擊保存并關(guān)閉,得到創(chuàng)建的設(shè)備密匙信息。該信息在后續(xù)生成MQTT三元組的時(shí)候需要使用。
(4) 設(shè)備創(chuàng)建完成
3.5 MQTT協(xié)議主題訂閱與發(fā)布
(1)MQTT協(xié)議介紹
當(dāng)前的設(shè)備是采用MQTT協(xié)議與華為云平臺(tái)進(jìn)行通信。
MQTT是一個(gè)物聯(lián)網(wǎng)傳輸協(xié)議,它被設(shè)計(jì)用于輕量級(jí)的發(fā)布/訂閱式消息傳輸,旨在為低帶寬和不穩(wěn)定的網(wǎng)絡(luò)環(huán)境中的物聯(lián)網(wǎng)設(shè)備提供可靠的網(wǎng)絡(luò)服務(wù)。MQTT是專門針對(duì)物聯(lián)網(wǎng)開(kāi)發(fā)的輕量級(jí)傳輸協(xié)議。MQTT協(xié)議針對(duì)低帶寬網(wǎng)絡(luò),低計(jì)算能力的設(shè)備,做了特殊的優(yōu)化,使得其能適應(yīng)各種物聯(lián)網(wǎng)應(yīng)用場(chǎng)景。目前MQTT擁有各種平臺(tái)和設(shè)備上的客戶端,已經(jīng)形成了初步的生態(tài)系統(tǒng)。
MQTT是一種消息隊(duì)列協(xié)議,使用發(fā)布/訂閱消息模式,提供一對(duì)多的消息發(fā)布,解除應(yīng)用程序耦合,相對(duì)于其他協(xié)議,開(kāi)發(fā)更簡(jiǎn)單;MQTT協(xié)議是工作在TCP/IP協(xié)議上;由TCP/IP協(xié)議提供穩(wěn)定的網(wǎng)絡(luò)連接;所以,只要具備TCP協(xié)議棧的網(wǎng)絡(luò)設(shè)備都可以使用MQTT協(xié)議。 本次設(shè)備采用的ESP8266就具備TCP協(xié)議棧,能夠建立TCP連接,所以,配合STM32代碼里封裝的MQTT協(xié)議,就可以與華為云平臺(tái)完成通信。
華為云的MQTT協(xié)議接入幫助文檔在這里: https://support.huaweicloud.com/devg-iothub/iot_02_2200.html
業(yè)務(wù)流程:
(2)華為云平臺(tái)MQTT協(xié)議使用限制
描述 | 限制 |
---|---|
支持的MQTT協(xié)議版本 | 3.1.1 |
與標(biāo)準(zhǔn)MQTT協(xié)議的區(qū)別 | 支持Qos 0和Qos 1支持Topic自定義不支持QoS2不支持will、retain msg |
MQTTS支持的安全等級(jí) | 采用TCP通道基礎(chǔ) + TLS協(xié)議(最高TLSv1.3版本) |
單帳號(hào)每秒最大MQTT連接請(qǐng)求數(shù) | 無(wú)限制 |
單個(gè)設(shè)備每分鐘支持的最大MQTT連接數(shù) | 1 |
單個(gè)MQTT連接每秒的吞吐量,即帶寬,包含直連設(shè)備和網(wǎng)關(guān) | 3KB/s |
MQTT單個(gè)發(fā)布消息最大長(zhǎng)度,超過(guò)此大小的發(fā)布請(qǐng)求將被直接拒絕 | 1MB |
MQTT連接心跳時(shí)間建議值 | 心跳時(shí)間限定為30至1200秒,推薦設(shè)置為120秒 |
產(chǎn)品是否支持自定義Topic | 支持 |
消息發(fā)布與訂閱 | 設(shè)備只能對(duì)自己的Topic進(jìn)行消息發(fā)布與訂閱 |
每個(gè)訂閱請(qǐng)求的最大訂閱數(shù) | 無(wú)限制 |
(3)主題訂閱格式
幫助文檔地址:https://support.huaweicloud.com/devg-iothub/iot_02_2200.html
對(duì)于設(shè)備而言,一般會(huì)訂閱平臺(tái)下發(fā)消息給設(shè)備 這個(gè)主題。
設(shè)備想接收平臺(tái)下發(fā)的消息,就需要訂閱平臺(tái)下發(fā)消息給設(shè)備 的主題,訂閱后,平臺(tái)下發(fā)消息給設(shè)備,設(shè)備就會(huì)收到消息。
如果設(shè)備想要知道平臺(tái)下發(fā)的消息,需要訂閱上面圖片里標(biāo)注的主題。
以當(dāng)前設(shè)備為例,最終訂閱主題的格式如下:
$oc/devices/{device_id}/sys/messages/down
最終的格式:
$oc/devices/6419627e40773741f9fbdac7_dev1/sys/messages/down
?
(4)主題發(fā)布格式
對(duì)于設(shè)備來(lái)說(shuō),主題發(fā)布表示向云平臺(tái)上傳數(shù)據(jù),將最新的傳感器數(shù)據(jù),設(shè)備狀態(tài)上傳到云平臺(tái)。
這個(gè)操作稱為:屬性上報(bào)。
幫助文檔地址:https://support.huaweicloud.com/usermanual-iothub/iot_06_v5_3010.html
根據(jù)幫助文檔的介紹, 當(dāng)前設(shè)備發(fā)布主題,上報(bào)屬性的格式總結(jié)如下:
發(fā)布的主題格式:
$oc/devices/{device_id}/sys/properties/report
最終的格式:
$oc/devices/6419627e40773741f9fbdac7_dev1/sys/properties/report
發(fā)布主題時(shí),需要上傳數(shù)據(jù),這個(gè)數(shù)據(jù)格式是JSON格式。
上傳的JSON數(shù)據(jù)格式如下:
{
"services": [
{
"service_id": <填服務(wù)ID>,
"properties": {
"<填屬性名稱1>": <填屬性值>,
"<填屬性名稱2>": <填屬性值>,
..........
}
}
]
}
根據(jù)JSON格式,一次可以上傳多個(gè)屬性字段。 這個(gè)JSON格式里的,服務(wù)ID,屬性字段名稱,屬性值類型,在前面創(chuàng)建產(chǎn)品的時(shí)候就已經(jīng)介紹了,不記得可以翻到前面去查看。
根據(jù)這個(gè)格式,組合一次上傳的屬性數(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,設(shè)備ID,設(shè)備密碼等信息,就像我們平時(shí)登錄QQ,微信一樣要輸入賬號(hào)密碼才能登錄。MQTT協(xié)議登錄的這3個(gè)參數(shù),一般稱為MQTT三元組。
接下來(lái)介紹,華為云平臺(tái)的MQTT三元組參數(shù)如何得到。
(1)MQTT服務(wù)器地址
要登錄MQTT服務(wù)器,首先記得先知道服務(wù)器的地址是多少,端口是多少。
幫助文檔地址:https://console.huaweicloud.com/iotdm/?region=cn-north-4#/dm-portal/home
MQTT協(xié)議的端口支持1883和8883,它們的區(qū)別是:8883 是加密端口更加安全。但是單片機(jī)上使用比較困難,所以當(dāng)前的設(shè)備是采用1883端口進(jìn)連接的。
根據(jù)上面的域名和端口號(hào),得到下面的IP地址和端口號(hào)信息: 如果設(shè)備支持填寫域名可以直接填域名,不支持就直接填寫IP地址。 (IP地址就是域名解析得到的)
華為云的MQTT服務(wù)器地址:114.116.232.138
域名:7445c6bcd3.st1.iotda-device.cn-north-4.myhuaweicloud.com
華為云的MQTT端口號(hào):1883
(2)生成MQTT三元組
華為云提供了一個(gè)在線工具,用來(lái)生成MQTT鑒權(quán)三元組: https://iot-tool.obs-website.cn-north-4.myhuaweicloud.com/
打開(kāi)這個(gè)工具,填入設(shè)備的信息(也就是剛才創(chuàng)建完設(shè)備之后保存的信息),點(diǎn)擊生成,就可以得到MQTT的登錄信息了。
下面是打開(kāi)的頁(yè)面:
填入設(shè)備的信息: (上面兩行就是設(shè)備創(chuàng)建完成之后保存得到的)
直接得到三元組信息。
得到三元組之后,設(shè)備端通過(guò)MQTT協(xié)議登錄鑒權(quán)的時(shí)候,填入?yún)?shù)即可。
ClientId 6419627e40773741f9fbdac7_dev1_0_0_2023032108
Username 6419627e40773741f9fbdac7_dev1
Password 861ac9e6a579d36888b2aaf97714be7af6c77017b017162884592bd68b086a6e
3.7 模擬設(shè)備登錄測(cè)試
經(jīng)過(guò)上面的步驟介紹,已經(jīng)創(chuàng)建了產(chǎn)品,設(shè)備,數(shù)據(jù)模型,得到MQTT登錄信息。 接下來(lái)就用MQTT客戶端軟件模擬真實(shí)的設(shè)備來(lái)登錄平臺(tái)。測(cè)試與服務(wù)器通信是否正常。
(1)填入登錄信息
打開(kāi)MQTT客戶端軟件,對(duì)號(hào)填入相關(guān)信息(就是上面的文本介紹)。然后,點(diǎn)擊登錄,訂閱主題,發(fā)布主題。
(2)打開(kāi)網(wǎng)頁(yè)查看
完成上面的操作之后,打開(kāi)華為云網(wǎng)頁(yè)后臺(tái),可以看到設(shè)備已經(jīng)在線了。
點(diǎn)擊詳情頁(yè)面,可以看到上傳的數(shù)據(jù)。
到此,云平臺(tái)的部署已經(jīng)完成,設(shè)備已經(jīng)可以正常上傳數(shù)據(jù)了。
四、上位機(jī)開(kāi)發(fā)
為了方便查看設(shè)備上傳的數(shù)據(jù),對(duì)設(shè)備進(jìn)行遠(yuǎn)程控制,接下來(lái)利用Qt開(kāi)發(fā)一款A(yù)ndroid和windows系統(tǒng)的上位機(jī)。
使用華為云平臺(tái)提供的API接口獲取設(shè)備上傳的數(shù)據(jù),也可以給設(shè)備下發(fā)指令,控制設(shè)備。
為了方便查看設(shè)備上傳的數(shù)據(jù),對(duì)設(shè)備進(jìn)行遠(yuǎn)程控制,接下來(lái)利用Qt開(kāi)發(fā)一款A(yù)ndroid和windows系統(tǒng)的上位機(jī)。
使用華為云平臺(tái)提供的API接口獲取設(shè)備上傳的數(shù)據(jù),也可以給設(shè)備下發(fā)指令,控制設(shè)備。
4.1 Qt開(kāi)發(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
打開(kāi)下載鏈接后選擇下面的版本進(jìn)行下載:
qt-opensource-windows-x86-5.12.6.exe 13-Nov-2019 07:28 3.7G Details
軟件安裝時(shí)斷網(wǎng)安裝,否則會(huì)提示輸入賬戶。
安裝的時(shí)候,第一個(gè)復(fù)選框里勾選一個(gè)mingw 32
編譯器即可,其他的不管默認(rèn)就行,直接點(diǎn)擊下一步繼續(xù)安裝。
說(shuō)明: 我這里只是介紹PC端的環(huán)境搭建(這個(gè)比較簡(jiǎn)單)。 Android的開(kāi)發(fā)環(huán)境比較麻煩,可以去我的博客里看詳細(xì)文章。
選擇MinGW 32-bit 編譯器:
4.2 創(chuàng)建IAM賬戶
創(chuàng)建一個(gè)IAM賬戶,因?yàn)榻酉聛?lái)開(kāi)發(fā)上位機(jī),需要使用云平臺(tái)的API接口,這些接口都需要token進(jìn)行鑒權(quán)。簡(jiǎn)單來(lái)說(shuō),就是身份的認(rèn)證。 調(diào)用接口獲取Token時(shí),就需要填寫IAM賬號(hào)信息。所以,接下來(lái)演示一下過(guò)程。
地址: https://console.huaweicloud.com/iam/?region=cn-north-4#/iam/users
獲取Token時(shí),除了AIM賬號(hào)外,還需要項(xiàng)目憑證:
faa0973835ab409ab48182e2590f4ad3
鼠標(biāo)點(diǎn)擊自己昵稱,點(diǎn)擊統(tǒng)一身份認(rèn)證。
點(diǎn)擊左上角創(chuàng)建用戶
。
創(chuàng)建成功:
4.3 獲取影子數(shù)據(jù)
幫助文檔:https://support.huaweicloud.com/api-iothub/iot_06_v5_0079.html
設(shè)備影子介紹:
設(shè)備影子是一個(gè)用于存儲(chǔ)和檢索設(shè)備當(dāng)前狀態(tài)信息的JSON文檔。
每個(gè)設(shè)備有且只有一個(gè)設(shè)備影子,由設(shè)備ID唯一標(biāo)識(shí)
設(shè)備影子僅保存最近一次設(shè)備的上報(bào)數(shù)據(jù)和預(yù)期數(shù)據(jù)
無(wú)論該設(shè)備是否在線,都可以通過(guò)該影子獲取和設(shè)置設(shè)備的屬性
簡(jiǎn)單來(lái)說(shuō):設(shè)備影子就是保存,設(shè)備最新上傳的一次數(shù)據(jù)。
我們?cè)O(shè)計(jì)的軟件里,如果想要獲取設(shè)備的最新?tīng)顟B(tài)信息,就采用設(shè)備影子接口。
如果對(duì)接口不熟悉,可以先進(jìn)行在線調(diào)試:https://apiexplorer.developer.huaweicloud.com/apiexplorer/doc?product=IoTDA&api=ShowDeviceShadow
在線調(diào)試接口,可以請(qǐng)求影子接口,了解請(qǐng)求,與返回的數(shù)據(jù)格式。
設(shè)備影子接口返回的數(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 修改設(shè)備屬性
地址: https://support.huaweicloud.com/api-iothub/iot_06_v5_0034.html
接口說(shuō)明
設(shè)備的產(chǎn)品模型中定義了物聯(lián)網(wǎng)平臺(tái)可向設(shè)備下發(fā)的屬性,應(yīng)用服務(wù)器可調(diào)用此接口向指定設(shè)備下發(fā)屬性。平臺(tái)負(fù)責(zé)將屬性以同步方式發(fā)送給設(shè)備,并將設(shè)備執(zhí)行屬性結(jié)果同步返回。
修改設(shè)備屬性的接口,可以讓服務(wù)器給設(shè)備下發(fā)指令,如果需要控制設(shè)備。
在線調(diào)試地址:
https://apiexplorer.developer.huaweicloud.com/apiexplorer/doc?product=IoTDA&api=UpdateProperties
修改設(shè)備屬性是屬于同步命令,需要設(shè)備在線才可以進(jìn)行調(diào)試,先使用MQTT客戶端登錄服務(wù)器,模擬設(shè)備上線。
然后進(jìn)行調(diào)試,測(cè)試數(shù)據(jù)遠(yuǎn)程下發(fā)給設(shè)備。
【1】利用MQTT客戶端先登錄設(shè)備 (這是同步命令,必須在線才能調(diào)試)
【2】點(diǎn)擊調(diào)試
{"services":{"temp_max":100}}
【4】可以看到,MQTT客戶端軟件上已經(jīng)收到了服務(wù)器下發(fā)的消息
由于是同步命令,服務(wù)器必須要收到設(shè)備的響應(yīng)才能順利完成一個(gè)流程,設(shè)備響應(yīng)了服務(wù)器才能確定數(shù)據(jù)下發(fā)成功。
MQTT設(shè)備端如何響應(yīng)呢?
設(shè)備響應(yīng)格式說(shuō)明:https://support.huaweicloud.com/api-iothub/iot_06_v5_3008.html
下面進(jìn)行實(shí)操:
當(dāng)服務(wù)器通過(guò)在線調(diào)試,發(fā)送指令下來(lái)之后,客戶端將請(qǐng)求ID復(fù)制下來(lái),添加到發(fā)布主題的格式里,再回復(fù)回去,服務(wù)器收到了響應(yīng),一次屬性修改就完美完成了。
就是成功的狀態(tài):
**下面是請(qǐng)求的總結(jié): ** (響應(yīng)服務(wù)器的修改設(shè)備屬性請(qǐng)求)
上報(bào)主題的格式:$oc/devices/{device_id}/sys/properties/set/response/request_id=
$oc/devices/6419627e40773741f9fbdac7_dev1/sys/properties/set/response/request_id=
響應(yīng)的數(shù)據(jù):
{"result_code": 0,"result_desc": "success"}
4.5 設(shè)計(jì)上位機(jī)
前面2講解了需要用的API接口,接下來(lái)就使用Qt設(shè)計(jì)上位機(jī),設(shè)計(jì)界面,完成整體上位機(jī)的邏輯設(shè)計(jì)。
【1】新建Qt工程
選擇工程路徑,放在英文路徑下。
創(chuàng)建完畢。
新建Android的模板:
【2】界面設(shè)計(jì)
【4】代碼設(shè)計(jì):配置參數(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ì):云端數(shù)據(jù)解析
//解析反饋結(jié)果
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響應(yīng)頭的數(shù)據(jù)
QList<QNetworkReply::RawHeaderPair> RawHeader=reply->rawHeaderPairs();
qDebug()<<"HTTP響應(yīng)頭數(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)
{
//判斷是否是對(duì)象,然后開(kāi)始解析數(shù)據(jù)
if(document.isObject())
{
QString error_str="";
QJsonObject obj = document.object();
QString error_code;
//解析錯(cuò)誤代碼
if(obj.contains("error_code"))
{
error_code=obj.take("error_code").toString();
error_str+="錯(cuò)誤代碼:";
error_str+=error_code;
error_str+="n";
}
if(obj.contains("error_msg"))
{
error_str+="錯(cuò)誤消息:";
error_str+=obj.take("error_msg").toString();
error_str+="n";
}
//顯示錯(cuò)誤代碼
QMessageBox::information(this,"提示",error_str,QMessageBox::Ok,QMessageBox::Ok);
}
}
return;
}
//設(shè)置屬性
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)
{
//判斷是否是對(duì)象,然后開(kāi)始解析數(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,"提示","遠(yuǎn)程命令操作完成.",QMessageBox::Ok,QMessageBox::Ok);
return;
}
else
{
//顯示狀態(tài)
QMessageBox::information(this,"提示","設(shè)備未正確回應(yīng).請(qǐng)檢查設(shè)備網(wǎng)絡(luò).",QMessageBox::Ok,QMessageBox::Ok);
return;
}
}
}
}
}
//查詢?cè)O(shè)備屬性
if(function_select==0)
{
//解析數(shù)據(jù)
QJsonParseError json_error;
QJsonDocument document = QJsonDocument::fromJson(replyData, &json_error);
if(json_error.error == QJsonParseError::NoError)
{
//判斷是否是對(duì)象,然后開(kāi)始解析數(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()<<"開(kāi)始解析數(shù)據(jù)....";
}
}
}
}
}
}
return;
}
}
五、代碼實(shí)現(xiàn)
5.1 ESP8266連接云平臺(tái)實(shí)現(xiàn)代碼
以下是使用STM32F103ZET6和ESP8266連接華為云物聯(lián)網(wǎng)平臺(tái),通過(guò)MQTT協(xié)議實(shí)現(xiàn)設(shè)備登錄、主題訂閱和主題發(fā)布的實(shí)現(xiàn)代碼:
#include "stm32f10x.h"
#include "stdio.h"
#include "string.h"
// 定義ESP8266的串口
USART_TypeDef* ESP_USARTx = USART1;
// 定義MQTT服務(wù)器的地址和端口
const char* MQTT_SERVER = "mqtt.eclipse.org";
const int MQTT_PORT = 1883;
// 定義設(shè)備ID和設(shè)備密碼
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ū)的索引和標(biāo)志位
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ù)進(jìn)行處理
}
// ESP8266串口中斷處理函數(shù)
void USART1_IRQHandler(void) {
if (USART_GetITStatus(ESP_USARTx, USART_IT_RXNE) != RESET) {
ESP8266_ReceiveData(1);
}
}
// 連接到MQTT服務(wù)器
void MQTT_Connect() {
// 發(fā)送連接請(qǐng)求
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連接請(qǐng)求
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服務(wù)器
MQTT_Connect();
// 訂閱主題
MQTT_Subscribe();
while (1) {
if (rxComplete) {
ProcessReceivedData();
rxComplete = 0;
}
// TODO: 處理其他業(yè)務(wù)邏輯
// 發(fā)布消息
MQTT_Publish("Hello, MQTT!");
// 延時(shí)一段時(shí)間
delay_ms(1000);
}
}
以上代碼用于演示使用STM32F103ZET6和ESP8266連接華為云物聯(lián)網(wǎng)平臺(tái),通過(guò)MQTT協(xié)議實(shí)現(xiàn)設(shè)備登錄、主題訂閱和主題發(fā)布的基本功能。
5.2 ESP8266的MQTT協(xié)議指令
ESP8266通過(guò)MQTT協(xié)議連接到服務(wù)器的相關(guān)AT指令主要有以下幾個(gè):
【1】AT+CIPSTART:建立TCP連接
- 功能:使用TCP協(xié)議連接到遠(yuǎn)程服務(wù)器
- 用法:AT+CIPSTART=“TCP”,“<服務(wù)器地址>”,<服務(wù)器端口>
- 示例:AT+CIPSTART=“TCP”,“mqtt.eclipse.org”,1883
【2】AT+MQTTCONNECT:連接到MQTT服務(wù)器
- 功能:使用MQTT協(xié)議連接到MQTT服務(wù)器
- 用法:AT+MQTTCONNECT=“<設(shè)備ID>”,“<設(shè)備密碼>”
- 示例: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:關(guān)閉TCP連接
- 功能:關(guān)閉當(dāng)前的TCP連接
- 用法:AT+CIPCLOSE
這些AT指令可以通過(guò)串口與ESP8266進(jìn)行通信,實(shí)現(xiàn)與MQTT服務(wù)器的連接、消息發(fā)布和訂閱等功能。通過(guò)這些指令,可以在嵌入式設(shè)備上實(shí)現(xiàn)與云端的通信和數(shù)據(jù)交換,從而實(shí)現(xiàn)物聯(lián)網(wǎng)應(yīng)用。
5.2 步進(jìn)電機(jī)控制代碼
以下是使用STM32F103ZET6單片機(jī)通過(guò)ULN2003驅(qū)動(dòng)芯片控制28BYJ-48步進(jìn)電機(jī)實(shí)現(xiàn)角度控制和速度控制的實(shí)現(xiàn)代碼:
#include "stm32f10x.h"
#include "delay.h"
// 定義步進(jìn)電機(jī)控制引腳
#define IN1_PIN GPIO_Pin_0
#define IN2_PIN GPIO_Pin_1
#define IN3_PIN GPIO_Pin_2
#define IN4_PIN GPIO_Pin_3
#define IN_PORT GPIOA
// 定義步進(jìn)電機(jī)角度和速度參數(shù)
#define ANGLE_1 512 // 控制步進(jìn)電機(jī)轉(zhuǎn)動(dòng)一圈的步數(shù)
#define SPEED_1 5 // 控制步進(jìn)電機(jī)轉(zhuǎn)動(dòng)的速度
// 步進(jìn)電機(jī)轉(zhuǎn)動(dòng)順序
const uint8_t stepSequence[8] = {0x01, 0x03, 0x02, 0x06, 0x04, 0x0C, 0x08, 0x09};
// 步進(jìn)電機(jī)當(dāng)前角度和速度
volatile uint16_t currentAngle = 0;
volatile uint8_t currentSpeed = 0;
// 初始化步進(jìn)電機(jī)控制引腳
void StepperMotor_Init() {
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = IN1_PIN | IN2_PIN | IN3_PIN | IN4_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(IN_PORT, &GPIO_InitStructure);
}
// 控制步進(jìn)電機(jī)轉(zhuǎn)動(dòng)一步
void StepperMotor_Step() {
static uint8_t stepIndex = 0;
GPIO_Write(IN_PORT, stepSequence[stepIndex]);
stepIndex = (stepIndex + 1) % 8;
}
// 控制步進(jìn)電機(jī)轉(zhuǎn)動(dòng)到指定角度
void StepperMotor_MoveToAngle(uint16_t targetAngle) {
uint16_t steps = targetAngle - currentAngle;
uint16_t absSteps = steps > 0 ? steps : -steps;
uint8_t direction = steps > 0 ? 1 : -1;
for (uint16_t i = 0; i < absSteps; i++) {
StepperMotor_Step();
delay_ms(2); // 控制步進(jìn)電機(jī)轉(zhuǎn)動(dòng)的速度
}
currentAngle = targetAngle;
}
// 控制步進(jìn)電機(jī)以指定速度連續(xù)轉(zhuǎn)動(dòng)
void StepperMotor_MoveWithSpeed(uint8_t speed) {
currentSpeed = speed;
while (1) {
StepperMotor_Step();
delay_ms(20 - currentSpeed); // 控制步進(jìn)電機(jī)轉(zhuǎn)動(dòng)的速度
}
}
int main(void) {
// 初始化步進(jìn)電機(jī)控制引腳
StepperMotor_Init();
// 控制步進(jìn)電機(jī)轉(zhuǎn)動(dòng)到指定角度
StepperMotor_MoveToAngle(ANGLE_1);
// 控制步進(jìn)電機(jī)以指定速度連續(xù)轉(zhuǎn)動(dòng)
StepperMotor_MoveWithSpeed(SPEED_1);
while (1) {
// 主循環(huán)中可以添加其他邏輯代碼
}
}
5.3 LD3320識(shí)別代碼
以下是使用STM32F103的串口2接收LD3320語(yǔ)音識(shí)別結(jié)果并進(jìn)行判斷控制的代碼:
#include "stm32f10x.h"
#include <stdio.h>
// 定義LD3320串口通信引腳
#define LD3320_RX_PIN GPIO_Pin_2
#define LD3320_RX_PORT GPIOA
#define LD3320_USART USART2
// 定義接收緩沖區(qū)大小
#define BUFFER_SIZE 128
// 接收緩沖區(qū)
volatile char rxBuffer[BUFFER_SIZE];
volatile uint8_t rxIndex = 0;
volatile uint8_t rxComplete = 0;
// 初始化LD3320串口通信引腳
void LD3320_UART_Init() {
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
GPIO_InitStructure.GPIO_Pin = LD3320_RX_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(LD3320_RX_PORT, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = 9600;
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_Init(LD3320_USART, &USART_InitStructure);
USART_ITConfig(LD3320_USART, USART_IT_RXNE, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_Cmd(LD3320_USART, ENABLE);
}
// 串口2中斷處理函數(shù)
void USART2_IRQHandler() {
if (USART_GetITStatus(LD3320_USART, USART_IT_RXNE) != RESET) {
char data = USART_ReceiveData(LD3320_USART);
if (rxIndex < BUFFER_SIZE - 1) {
rxBuffer[rxIndex++] = data;
}
if (data == 'n') {
rxComplete = 1;
}
}
}
// 處理接收到的LD3320識(shí)別結(jié)果
void ProcessLD3320Result() {
// 在這里進(jìn)行LD3320識(shí)別結(jié)果的判斷和控制邏輯
// 可以根據(jù)接收到的字符串進(jìn)行判斷,例如使用strcmp()函數(shù)進(jìn)行比較
// 示例:if (strcmp(rxBuffer, "ON") == 0) { // 執(zhí)行打開(kāi)操作 }
// 清空接收緩沖區(qū)
rxIndex = 0;
rxComplete = 0;
}
int main(void) {
// 初始化LD3320串口通信引腳
LD3320_UART_Init();
while (1) {
if (rxComplete) {
ProcessLD3320Result();
}
}
}
以上代碼使用STM32F103的串口2接收LD3320語(yǔ)音識(shí)別結(jié)果并進(jìn)行判斷控制。
代碼中使用了串口2的接收中斷來(lái)接收LD3320的識(shí)別結(jié)果。在中斷處理函數(shù)USART2_IRQHandler()
中,將接收到的數(shù)據(jù)存儲(chǔ)到接收緩沖區(qū)rxBuffer
中,并通過(guò)檢測(cè)換行符n
來(lái)判斷一條完整的識(shí)別結(jié)果是否接收完成。當(dāng)識(shí)別結(jié)果接收完成時(shí),調(diào)用ProcessLD3320Result()
函數(shù)進(jìn)行識(shí)別結(jié)果的判斷和控制邏輯處理。
在ProcessLD3320Result()
函數(shù)中,可以根據(jù)接收到的字符串進(jìn)行判斷和控制邏輯的實(shí)現(xiàn)。例如,使用字符串比較函數(shù)strcmp()
來(lái)比較接收到的字符串與預(yù)設(shè)的控制命令是否匹配,從而執(zhí)行相應(yīng)的操作。在這個(gè)函數(shù)中,可以添加你需要的控制邏輯,例如打開(kāi)或關(guān)閉某個(gè)設(shè)備,執(zhí)行特定的動(dòng)作等。