• 方案介紹
    • 一、前言
    • 二、搭建視頻監(jiān)控流媒體服務(wù)器
    • 三、華為云IOT服務(wù)器部署過(guò)程
    • 四、Android手機(jī)APP開(kāi)發(fā)
    • 五、STM32端代碼設(shè)計(jì)
    • 六、關(guān)于Android手機(jī)USB通信的問(wèn)題
    • 七、總結(jié)
  • 附件下載
  • 相關(guān)推薦
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

綠色再生·安卓4G智能遠(yuǎn)程操作巡視機(jī)器人小車(chē)

06/03 10:17
558
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

更多詳細(xì)資料請(qǐng)聯(lián)系.docx

共1個(gè)文件

一、前言

1.1 項(xiàng)目介紹

【1】項(xiàng)目功能介紹

隨著物聯(lián)網(wǎng)技術(shù)移動(dòng)通信技術(shù)的快速發(fā)展,遠(yuǎn)程遙控設(shè)備在日常生活及工業(yè)應(yīng)用中的普及度日益提高。無(wú)論是家用掃地機(jī)器人實(shí)現(xiàn)自主導(dǎo)航清掃,還是目前抖音平臺(tái)上展示的實(shí)景互動(dòng)小車(chē)等創(chuàng)新應(yīng)用,都體現(xiàn)了遠(yuǎn)程控制和實(shí)時(shí)視頻監(jiān)控技術(shù)對(duì)現(xiàn)代智能設(shè)備的重要性。

本項(xiàng)目設(shè)計(jì)并實(shí)現(xiàn)一款基于STM32微控制器的遠(yuǎn)程遙控安卓小車(chē)系統(tǒng)。該系統(tǒng)充分利用了淘汰下來(lái)的安卓舊手機(jī)作為車(chē)載信息處理單元,不僅實(shí)現(xiàn)了資源的有效再利用,還結(jié)合4G網(wǎng)絡(luò)技術(shù)以及先進(jìn)的流媒體服務(wù)和物聯(lián)網(wǎng)技術(shù),搭建起一套集遠(yuǎn)程操控、實(shí)時(shí)視頻音頻傳輸于一體的高效解決方案。

項(xiàng)目的小車(chē)搭載了STM32主控板以精確控制四個(gè)電機(jī)的動(dòng)作,通過(guò)L298N驅(qū)動(dòng)芯片確保了底座移動(dòng)的穩(wěn)定性和靈活性。同時(shí),小車(chē)的動(dòng)力源采用兩節(jié)18650鋰電池提供充足的電力支持。

車(chē)載的舊安卓手機(jī)通過(guò)USB線連接到STM32主控板上,接收并執(zhí)行來(lái)自遠(yuǎn)端手機(jī)APP的指令。這款由Qt開(kāi)發(fā)的Android APP能夠利用4G網(wǎng)絡(luò)實(shí)現(xiàn)實(shí)時(shí)在線,并通過(guò)攝像頭采集音視頻數(shù)據(jù),通過(guò)RTMP協(xié)議將這些數(shù)據(jù)推送到華為云ECS服務(wù)器上的NGINX流媒體服務(wù)器,從而實(shí)現(xiàn)高清流暢的遠(yuǎn)程視頻監(jiān)控。

為了實(shí)現(xiàn)雙向交互和低延遲控制,整個(gè)系統(tǒng)還借助MQTT協(xié)議連接至華為云IOT服務(wù)器。另一臺(tái)安裝了同樣由Qt開(kāi)發(fā)的Android手機(jī)APP的終端設(shè)備,可以通過(guò)該APP拉取小車(chē)端的實(shí)時(shí)音視頻流進(jìn)行播放,并通過(guò)方向鍵菜單實(shí)現(xiàn)對(duì)小車(chē)的精準(zhǔn)遠(yuǎn)程操控。這種設(shè)計(jì)不僅極大地拓展了傳統(tǒng)遙控小車(chē)的功能性與實(shí)用性,還為其他類似應(yīng)用場(chǎng)景提供了可借鑒的技術(shù)框架。

當(dāng)前設(shè)計(jì)的這種基于4G網(wǎng)絡(luò)設(shè)計(jì)的遠(yuǎn)程遙控巡檢小車(chē)的技術(shù)應(yīng)用場(chǎng)景主要是: 安全防護(hù)、環(huán)境監(jiān)測(cè)、設(shè)備巡檢、物料搬運(yùn)、應(yīng)急救援這些地方。

(1)遠(yuǎn)程監(jiān)控

  • 可應(yīng)用于安全防護(hù)、環(huán)境監(jiān)測(cè)、農(nóng)業(yè)監(jiān)控等領(lǐng)域,例如森林防火、農(nóng)田灌溉管理、危險(xiǎn)區(qū)域偵查等,通過(guò)實(shí)時(shí)視頻和音頻傳輸,工作人員可以在遠(yuǎn)程位置對(duì)現(xiàn)場(chǎng)情況進(jìn)行實(shí)時(shí)了解和操控。

(2)工業(yè)巡檢

  • 在工廠、倉(cāng)庫(kù)或大型設(shè)施內(nèi)部署此類小車(chē),用于設(shè)備巡檢、物料搬運(yùn)或生產(chǎn)流程監(jiān)控,尤其適合那些人員不易到達(dá)或者存在安全隱患的地方。

(3)應(yīng)急救援

  • 在地震、火災(zāi)等災(zāi)害現(xiàn)場(chǎng),遠(yuǎn)程遙控小車(chē)可用于進(jìn)入倒塌建筑內(nèi)部搜尋生命跡象,或是攜帶傳感器測(cè)量有害氣體濃度等,為救援決策提供及時(shí)信息

下面是當(dāng)前小車(chē)整體技術(shù)框架:

image-20240227102115213

小車(chē)的模型:

image-20240308094613156

本次設(shè)計(jì)里放在小車(chē)終端上的Android手機(jī)采用的是小米4C,一款普通的Android手機(jī):

2015年上市的小米4C。 從本身價(jià)值來(lái)講,現(xiàn)在在某魚(yú)上差不多是200塊,本身就是一個(gè)完整的系統(tǒng)。性價(jià)比非常高。比去單獨(dú)買(mǎi)Linux開(kāi)發(fā)板進(jìn)行模型開(kāi)發(fā)實(shí)驗(yàn)來(lái)說(shuō),成本低低很多。 主流的Linux、Android開(kāi)發(fā)板價(jià)格都比較貴的。

image-20240227102453689

image-20240227102527756

開(kāi)發(fā)過(guò)程中,測(cè)試遙控效果:

image-20240227103342355

【2】設(shè)計(jì)實(shí)現(xiàn)的功能

(1)STM32主控板功能:

  • 控制4個(gè)電機(jī):STM32通過(guò)L298N驅(qū)動(dòng)芯片驅(qū)動(dòng)4個(gè)電機(jī),實(shí)現(xiàn)小車(chē)的前進(jìn)、后退、左轉(zhuǎn)、右轉(zhuǎn)等動(dòng)作。
  • 數(shù)據(jù)通信:STM32通過(guò)USB接口與安卓手機(jī)通信,接收手機(jī)APP發(fā)送的控制指令,并將小車(chē)的狀態(tài)信息(如電量、速度、位置等)發(fā)送回手機(jī)。
  • 電源管理:管理2節(jié)18650鋰電池的供電,確保電壓穩(wěn)定并監(jiān)控電池電量。

(2)安卓手機(jī)APP功能

  • 控制指令下發(fā):手機(jī)APP通過(guò)USB接口向STM32發(fā)送控制指令,控制小車(chē)的動(dòng)作。
  • 視頻和音頻流獲?。篈PP從手機(jī)攝像頭麥克風(fēng)獲取視頻和音頻流,并進(jìn)行編碼處理。
  • 流媒體推流:通過(guò)RTMP協(xié)議將編碼后的視頻和音頻流推送到華為云ECS服務(wù)器+NGINX搭建的RTMP流媒體服務(wù)器。
  • MQTT連接:APP通過(guò)MQTT協(xié)議與華為云IOT服務(wù)器建立連接,實(shí)現(xiàn)雙向通信。

(3)華為云服務(wù)器功能:

  • RTMP流媒體服務(wù):接收并轉(zhuǎn)發(fā)安卓手機(jī)APP推送的視頻和音頻流。
  • MQTT服務(wù):作為MQTT消息代理,實(shí)現(xiàn)遠(yuǎn)程手機(jī)與STM32主控板之間的通信。

(4)遠(yuǎn)程Android手機(jī)APP功能:

  • 實(shí)時(shí)視頻和音頻播放:從華為云ECS服務(wù)器拉取視頻和音頻流,并實(shí)時(shí)顯示在APP界面上。
  • MQTT連接:與華為云IOT服務(wù)器建立連接,接收STM32主控板發(fā)送的小車(chē)狀態(tài)信息。
  • 遠(yuǎn)程控制:提供方向鍵控制菜單,允許用戶遠(yuǎn)程控制小車(chē)前進(jìn)、后退、轉(zhuǎn)彎等動(dòng)作。

【3】項(xiàng)目硬件模塊組成

(1)電源模塊

  • 電池組:采用兩節(jié)18650鋰電池作為供電源,它們具有高能量密度、體積小的特點(diǎn),能夠?yàn)檎麄€(gè)系統(tǒng)提供穩(wěn)定的直流電能。

(2)主控模塊

  • STM32微控制器:這是整個(gè)小車(chē)的核心控制單元,負(fù)責(zé)處理所有的邏輯運(yùn)算和數(shù)據(jù)通信任務(wù)。通過(guò)編程實(shí)現(xiàn)對(duì)電機(jī)驅(qū)動(dòng)、USB通信、網(wǎng)絡(luò)連接等功能的控制。

(3)電機(jī)驅(qū)動(dòng)模塊

  • L298N驅(qū)動(dòng)芯片:用于驅(qū)動(dòng)底座上的四個(gè)電機(jī),L298N是一個(gè)高性能的H橋電機(jī)驅(qū)動(dòng)器,可以接收來(lái)自STM32的信號(hào),轉(zhuǎn)換為足夠驅(qū)動(dòng)電機(jī)工作的電流和電壓,并且支持電機(jī)正反轉(zhuǎn)及速度調(diào)節(jié)。

(4)移動(dòng)平臺(tái)模塊

  • 四個(gè)直流電機(jī):直接安裝在小車(chē)底座上,通過(guò)L298N驅(qū)動(dòng)進(jìn)行精確的速度和方向控制,以實(shí)現(xiàn)小車(chē)前進(jìn)、后退、左右轉(zhuǎn)彎等運(yùn)動(dòng)功能。

(5)通信模塊

  • USB接口:STM32主控板通過(guò)USB線與安卓手機(jī)物理連接,實(shí)現(xiàn)數(shù)據(jù)傳輸,接收來(lái)自手機(jī)APP的控制指令。
  • 4G模組:集成在安卓手機(jī)內(nèi)部,插入SIM卡后可實(shí)現(xiàn)高速無(wú)線網(wǎng)絡(luò)連接,使小車(chē)能夠在遠(yuǎn)程環(huán)境下通過(guò)互聯(lián)網(wǎng)與其他設(shè)備通信。

(6)多媒體采集模塊

  • 安卓手機(jī)攝像頭:用于捕捉實(shí)時(shí)視頻和音頻信息,是小車(chē)端環(huán)境感知的關(guān)鍵組件。

(7)云服務(wù)交互模塊

  • 華為云ECS服務(wù)器+NGINX RTMP流媒體服務(wù)器:小車(chē)端將采集到的音視頻流推送到華為云服務(wù)器上,通過(guò)RTMP協(xié)議實(shí)現(xiàn)實(shí)時(shí)音視頻的低延遲傳輸和分發(fā)。
  • 華為云IOT服務(wù)器:小車(chē)和遠(yuǎn)端控制手機(jī)均通過(guò)MQTT協(xié)議與之建立連接,實(shí)現(xiàn)遠(yuǎn)程數(shù)據(jù)交換和控制命令的下發(fā)。

【3】功能總結(jié)

(1)電機(jī)驅(qū)動(dòng)與控制:通過(guò)STM32微控制器和L298N驅(qū)動(dòng)芯片,實(shí)現(xiàn)對(duì)小車(chē)上四個(gè)電機(jī)的精確控制,包括前進(jìn)、后退、左轉(zhuǎn)、右轉(zhuǎn)等動(dòng)作,從而控制小車(chē)的移動(dòng)方向和速度。

(2)無(wú)線通信與數(shù)據(jù)傳輸:STM32與安卓手機(jī)之間通過(guò)USB接口建立通信,實(shí)現(xiàn)控制指令的下發(fā)和小車(chē)狀態(tài)信息的上傳。同時(shí),安卓手機(jī)通過(guò)4G網(wǎng)絡(luò)連接到華為云服務(wù)器,實(shí)現(xiàn)了遠(yuǎn)程控制命令的遠(yuǎn)程傳輸和視頻音頻流的推送。

(3)流媒體推流與播放:安卓手機(jī)APP能夠捕獲手機(jī)攝像頭和麥克風(fēng)的視頻和音頻流,通過(guò)RTMP協(xié)議推送到華為云服務(wù)器。另一臺(tái)安卓手機(jī)APP則從服務(wù)器拉取這些流,實(shí)現(xiàn)實(shí)時(shí)播放,從而允許用戶遠(yuǎn)程觀看小車(chē)的實(shí)時(shí)畫(huà)面和音頻。

(4)華為云服務(wù)器支持:華為云ECS服務(wù)器和NGINX搭建的RTMP流媒體服務(wù)器負(fù)責(zé)接收、轉(zhuǎn)發(fā)視頻和音頻流,確保流媒體的穩(wěn)定性和實(shí)時(shí)性。同時(shí),華為云IOT服務(wù)器通過(guò)MQTT協(xié)議提供消息代理服務(wù),實(shí)現(xiàn)遠(yuǎn)程手機(jī)與STM32之間的雙向通信。

(5)用戶界面與交互設(shè)計(jì):安卓手機(jī)APP提供了直觀的用戶界面,包括控制按鈕、狀態(tài)顯示、視頻播放器等,使用戶能夠方便地對(duì)小車(chē)進(jìn)行控制、觀看視頻、監(jiān)聽(tīng)音頻,以及監(jiān)控小車(chē)的狀態(tài)信息。

(6)遠(yuǎn)程控制:通過(guò)結(jié)合STM32的電機(jī)控制、華為云服務(wù)器的數(shù)據(jù)處理和傳輸,以及安卓手機(jī)的用戶界面和交互設(shè)計(jì),實(shí)現(xiàn)了從遠(yuǎn)程手機(jī)到小車(chē)的遠(yuǎn)程控制功能。用戶可以在遠(yuǎn)離小車(chē)的地點(diǎn),通過(guò)手機(jī)APP發(fā)出控制指令,實(shí)時(shí)觀察小車(chē)的動(dòng)作和周?chē)h(huán)境。

1.2 設(shè)計(jì)思路

1.3 系統(tǒng)功能總結(jié)

自主供電與移動(dòng)控制 采用2節(jié)18650鋰電池為小車(chē)提供電力供應(yīng);STM32微控制器結(jié)合L298N驅(qū)動(dòng)芯片,精準(zhǔn)控制4個(gè)電機(jī)動(dòng)作,實(shí)現(xiàn)前進(jìn)、后退、轉(zhuǎn)彎等移動(dòng)功能
手機(jī)APP通信與指令傳輸 STM32通過(guò)USB線與安卓手機(jī)連接,接收并解析來(lái)自手機(jī)APP的控制指令,實(shí)現(xiàn)人機(jī)交互和遠(yuǎn)程指令執(zhí)行
實(shí)時(shí)視頻音頻流傳輸 安卓手機(jī)利用4G網(wǎng)絡(luò)上網(wǎng),搭載Qt開(kāi)發(fā)的Android APP采集攝像頭視頻和音頻數(shù)據(jù),并通過(guò)RTMP協(xié)議將音視頻流推送到華為云ECS服務(wù)器+NGINX搭建的流媒體服務(wù)器
物聯(lián)網(wǎng)(IoT)連接與遠(yuǎn)程監(jiān)控 小車(chē)端及遠(yuǎn)端控制手機(jī)均通過(guò)MQTT協(xié)議連接華為云IOT服務(wù)器,實(shí)現(xiàn)車(chē)輛狀態(tài)信息實(shí)時(shí)上傳及遠(yuǎn)程音視頻流拉取顯示;遠(yuǎn)端手機(jī)APP提供方向鍵菜單以遠(yuǎn)程操控小車(chē)
數(shù)據(jù)交互與低延遲控制 利用MQTT協(xié)議確保在4G網(wǎng)絡(luò)環(huán)境下高效、低延遲的數(shù)據(jù)交互,實(shí)現(xiàn)對(duì)小車(chē)的實(shí)時(shí)遠(yuǎn)程控制,提升整體系統(tǒng)的響應(yīng)速度和操作體驗(yàn)

二、搭建視頻監(jiān)控流媒體服務(wù)器

2.1 購(gòu)買(mǎi)云服務(wù)器

如果之前沒(méi)有使用過(guò)華為云的ECS服務(wù)器,可以先申請(qǐng)?jiān)囉?個(gè)月,了解服務(wù)器的基本使用然后再購(gòu)買(mǎi),不得不說(shuō)提供這個(gè)試用服務(wù)還是非常不錯(cuò)。

產(chǎn)品試用領(lǐng)取地址: https://activity.huaweicloud.com/free_test/index.html

image-20220611130453218

每天9:30開(kāi)搶,每天限量100份,這個(gè)頁(yè)面不僅有云服務(wù)器可以領(lǐng)取試用,還有云數(shù)據(jù)庫(kù)、沙盒等其他產(chǎn)品。

image-20220611130511064

image-20220611130612334

我這里領(lǐng)取了 2核4G S6云服務(wù)器,這個(gè)服務(wù)器是配套華為自研25GE智能高速網(wǎng)卡,適用于網(wǎng)站和Web應(yīng)用等中輕載企業(yè)應(yīng)用。

image-20220611130700250

選擇配置的時(shí)候發(fā)現(xiàn)S6規(guī)格的已經(jīng)沒(méi)有了,來(lái)晚了。

image-20220611130956075

S6規(guī)格沒(méi)有了,就選擇了S3,2核,4GB的配置結(jié)算。

image-20220611131022494

頁(yè)面向下翻,下面選擇系統(tǒng)預(yù)裝的系統(tǒng),我這里選擇ubuntu 20.04,安裝NGINX,來(lái)搭建流媒體服務(wù)器。

image-20220611131123954

頁(yè)面繼續(xù)下翻,設(shè)置云服務(wù)器名稱,設(shè)置系統(tǒng)的root密碼。

image-20220611131148126

接著就可以繼續(xù)去支付了。

image-20220611131218881

image-20220611131250941

image-20220611131306457

購(gòu)買(mǎi)后等待一段時(shí)間,系統(tǒng)資源就即可分配完成。

image-20220611141408922

2.2 登錄云服務(wù)器

云服務(wù)器的管理控制臺(tái)從這里進(jìn)入: https://www.huaweicloud.com/product/ecs.html

在官網(wǎng)主頁(yè),點(diǎn)擊產(chǎn)品,找到計(jì)算選項(xiàng),就可以看到彈性云服務(wù)器ECS,點(diǎn)擊進(jìn)去就可以看到管理控制臺(tái)的選項(xiàng)。

image-20220611151527835

image-20220611151444306

彈性云服務(wù)器的選項(xiàng)頁(yè)面可以看到剛才購(gòu)買(mǎi)的云服務(wù)器,如果點(diǎn)擊進(jìn)去提示該區(qū)域沒(méi)有可用的服務(wù)器,說(shuō)明區(qū)域選擇的不對(duì),在下面截圖紅色框框的位置可以看到可用的區(qū)域切換按鈕,切換之后就行了。

image-20220611151650793

點(diǎn)擊服務(wù)器右邊的更多,可以對(duì)服務(wù)器重裝系統(tǒng)、切換操作系統(tǒng)、關(guān)機(jī)、開(kāi)機(jī)、重啟、重置密碼等操作。

image-20220611151918159

接下來(lái)先點(diǎn)擊登錄,了解一下登錄的流程,看看系統(tǒng)進(jìn)去的效果。

image-20220611152037313

云服務(wù)器支持SSH協(xié)議遠(yuǎn)程登錄,可以在瀏覽器上直接使用CloudShell方式或者VNC方式登錄,如果本身你自己也是使用Linux系統(tǒng),可以在Linux系統(tǒng)里通過(guò)ssh命令直接登錄,如果是在windows下可以使用SecureCRT登錄。

image-20220611160601965

其他登錄方式。

image-20220611152254866

最方便的登錄方式,直接在控制臺(tái)使用VNC在瀏覽器里登錄,點(diǎn)擊立即登錄。

image-20220611152727005

根據(jù)提示輸入用戶名,密碼后,按下回車(chē)鍵即可登錄。

用戶名直接輸入root,密碼就是剛才配置云服務(wù)器時(shí),填入的root密碼。

注意: Linux下輸入密碼默認(rèn)都是隱藏的,也就是在鍵盤(pán)上輸入密碼界面上是不會(huì)有反應(yīng)的,自己按照正確的密碼輸入即可。

image-20220611152929951

用戶名、密碼輸入正確后,登錄成功。

可以點(diǎn)擊全屏模式,更好的操作。

image-20220611153239527

2.3 使用CloudShell登錄云服務(wù)器

在頁(yè)面上直接點(diǎn)擊CloudShell登錄按鈕。CloudShell方式比VNC方式方便、流暢多了,也支持命令的復(fù)制粘貼。

image-20220611160821704

image-20220611160729806

輸入密碼點(diǎn)擊連接。

image-20220611160944543

登錄成功進(jìn)入終端。

image-20220611161046853

2.4 使用SecureCRT登錄云服務(wù)器

上面演示了兩種登錄方式,都是直接在瀏覽器里登錄,這種兩種方式比較來(lái)看,VNC方式效率最低,CloudShell相對(duì)來(lái)說(shuō)要方便很多。一般我自己正常開(kāi)發(fā)時(shí),都是使用第三方工具來(lái)登錄的,如果本身自己開(kāi)發(fā)環(huán)境的電腦就是Linux,MAC等,可以直接使用ssh命令登錄,這種方式操作流暢方便。如果在windows下,可以使用第三方軟件登錄。

我現(xiàn)在使用的系統(tǒng)是win10,在windows系統(tǒng)下有很多遠(yuǎn)程終端軟件支持SSH登錄到Linux云服務(wù)器,我當(dāng)前采用的使用SecureCRT 6.5來(lái)作為登錄終端,登錄云服務(wù)器。

注意: SecureCRT 6.5 登錄高版本Linux系統(tǒng)會(huì)出現(xiàn)Key exchange failed問(wèn)題,導(dǎo)致登錄失敗,比如: 登錄ubuntu 20.04 系統(tǒng)。 出現(xiàn)這種問(wèn)題需要對(duì)系統(tǒng)ssh配置文件進(jìn)行添加配置。

添加配置的流程:

命令行輸入:
vim /etc/ssh/sshd_config

在文件最后添加:
KexAlgorithms curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group14-sha1,diffie-hellman-group-exchange-sha1,diffie-hellman-group1-sha1

保存退出。
    
重啟ssh服務(wù)
service ssh restart

如果不想這么麻煩的去修改配置文件,那么最簡(jiǎn)單的辦法就是: 切換操作系統(tǒng),換一個(gè)低版本的,比如:ubuntu18.04 即可。

在云服務(wù)器的控制臺(tái),找到自己的服務(wù)器,然后選擇切換操作系統(tǒng)。

image-20220611155915095

根據(jù)界面上的引導(dǎo)流程,切換即可。更換新的系統(tǒng)需要1~4分鐘時(shí)間,稍微等待一下即可。

image-20220611160010714

如果要使用遠(yuǎn)程SSH協(xié)議方式登錄云服務(wù)器,需要具備以下幾個(gè)前提條件。

[1]彈性云服務(wù)器狀態(tài)為“運(yùn)行中”。
[2]彈性云服務(wù)器已經(jīng)綁定彈性公網(wǎng)IP,綁定方式請(qǐng)參見(jiàn)綁定彈性公網(wǎng)IP。
[3]所在安全組入方向已開(kāi)放22端口,配置方式請(qǐng)參見(jiàn)配置安全組規(guī)則。
[4]使用的登錄工具(如PuTTY,SecureCRT`)與待登錄的彈性云服務(wù)器之間網(wǎng)絡(luò)連通。例如,默認(rèn)的22端口沒(méi)有被防火墻屏蔽。

但是這些配置不用擔(dān)心,在購(gòu)買(mǎi)服務(wù)器后,根據(jù)引導(dǎo)一套走完,上面的這些配置都已經(jīng)默認(rèn)配置好了,不用自己再去單獨(dú)配置。

系統(tǒng)切換成功后,打開(kāi)SecureCRT 6.5軟件,進(jìn)行登錄。SecureCRT 6.5整體而言還是挺好用的。

如果自己沒(méi)有``SecureCRT,自己下載一個(gè)即可。當(dāng)然,不一定非要使用SecureCRT`,其他還有很多SSH遠(yuǎn)程登錄工具,喜歡哪個(gè)使用哪個(gè)。

下面介紹``SecureCRT `登錄的流程。

image-20220611160216821

選擇新建會(huì)話。

image-20220611161730570

選擇SSH2協(xié)議。

image-20220611161757991

主機(jī)名就填服務(wù)器的公網(wǎng)IP地址,端口號(hào)默認(rèn)是22,用戶名填root。

image-20220611161831158

自己云服務(wù)器的公網(wǎng)IP地址,在控制臺(tái)可以看到。

image-20220611161945275

軟件點(diǎn)擊下一步之后,可以填充描述信息,備注這個(gè)鏈接是干什么的。

image-20220611162046191

選擇剛才新建的協(xié)議端口點(diǎn)擊連接。

image-20220611162148248

云服務(wù)器連接上之后,軟件界面會(huì)彈出對(duì)話框填充用戶名、密碼。

image-20220611162228021

登錄成功的效果如下。

image-20220611162246059

軟件可以配置一些選項(xiàng),讓界面符合Linux終端配色,可以修改字體大小、字體編碼等等。

image-20220611162407116

image-20220611162522751

配置后的效果。

image-20220611162543936

2.5 安裝NFS服務(wù)器

為了方便向服務(wù)器上拷貝文件,可以采用NFS服務(wù)器、或者FTP服務(wù)器 這些方式。 我本地有一臺(tái)ubuntu 18.04 系統(tǒng)筆記本,我這里采用NFS方式拷貝文件上去。

(1)安裝NFS服務(wù)器

root@ecs-348470:~# sudo apt-get install nfs-kernel-server

(2)創(chuàng)建一個(gè)work目錄方便當(dāng)做共享目錄使用

root@ecs-348470:~# mkdir work

(3)編寫(xiě)NFS配置文件

NFS 服務(wù)的配置文件為/etc/exports,如果系統(tǒng)沒(méi)有默認(rèn)值,這個(gè)文件就不一定會(huì)存在,可以使用 vim 手動(dòng)建立,然后在文件里面寫(xiě)入配置內(nèi)容。

/home/work *(rw,no_root_squash,sync,no_subtree_check,insecure)    

image-20220611182221690

配置文件里參數(shù)的含義:

image-20220611180438643

image-20220611180457992

image-20220611180909866

image-20220611180933623

(4)NFS服務(wù)器相關(guān)指令

/etc/init.d/nfs-kernel-server start #啟動(dòng) NFS 服務(wù)
ufw disable     #關(guān)閉防火墻
/etc/init.d/nfs-kernel-server restart  #重啟NFS服務(wù)
exportfs -arv   #共享路徑生效

(5)本地客戶機(jī)掛載服務(wù)器的目錄

wbyq@wbyq:~$ sudo mount -t nfs -o nolock 122.112.212.171:/home/work /home/wbyq/mnt/

(6)設(shè)置華為云服務(wù)器的安全策略

需要把華為云服務(wù)器的端口號(hào)開(kāi)放出來(lái),不然其他客戶端是無(wú)法訪問(wèn)服務(wù)器的。

我這里比較粗暴直接,直接將所有端口號(hào),所有IP地址都開(kāi)放出來(lái)了。

image-20220611185514553

image-20220611185254027

image-20220611185204400

**(7)本地客戶機(jī)掛載服務(wù)器測(cè)試 **

掛載指令:

sudo mount -t nfs -o nolock 122.112.212.171:/home/work /home/wbyq/mnt/

image-20220611185744008

2.6 安裝NGINX(配置RTMP服務(wù))

(1)下載編譯時(shí)需要依賴的一些工具

root@ecs-348470:~# sudo apt-get install build-essential libpcre3 libpcre3-dev libssl-dev

image-20220611170102729

(2)下載NGINX編譯需要的軟件包

root@ecs-348470:~# mkdir nginx      
root@ecs-348470:~# cd nginx/
root@ecs-348470:~# wget http://nginx.org/download/nginx-1.10.3.tar.gz
root@ecs-348470:~# wget http://zlib.net/zlib-1.2.11.tar.gz
root@ecs-348470:~# wget https://ftp.pcre.org/pub/pcre/pcre-8.40.tar.gz
root@ecs-348470:~# wget https://www.openssl.org/source/openssl-1.0.2k.tar.gz
root@ecs-348470:~# wget https://github.com/arut/nginx-rtmp-module/archive/master.zip

image-20220611190538685

(3)下載后的文件全部解壓

root@ecs-348470:~# tar xvf openssl-1.0.2k.tar.gz
root@ecs-348470:~# tar xvf nginx-rtmp-module-master.tar.gz
root@ecs-348470:~# tar xvf nginx-1.8.1.tar.gz
root@ecs-348470:~# tar xvf pcre-8.40.tar.gz
root@ecs-348470:~# tar xvf zlib-1.2.11.tar.gz

image-20220611190905353

(4)配置NGINX源碼,生成Makefile文件

root@ecs-348470:~# cd nginx-1.8.1/
root@ecs-348470:~# ./configure --prefix=/usr/local/nginx --with-debug --with-pcre=../pcre-8.40 --with-zlib=../zlib-1.2.11 --with-openssl=../openssl-1.0.2k --add-module=../nginx-rtmp-module-master

執(zhí)行完上一步之后,打開(kāi)objs/Makefile文件,找到-Werror選項(xiàng)并刪除。

(5)編譯并安裝NGINX

 root@ecs-348470:~/nginx/nginx-1.8.1# make && make install

安裝之后NGINX的配置文件存放路徑:

/usr/local/nginx/nginx:主程序

(6)查看NGINX的版本號(hào)

root@ecs-348470:/usr/local/nginx/sbin# /usr/local/nginx/sbin/nginx -v
nginx version: nginx/1.8.1

(5)在配置文件里加入RTMP服務(wù)器的配置

root@ecs-348470:~# vim /usr/local/nginx/conf/nginx.conf 
打開(kāi)文件后,在文件最后加入以下配置:

rtmp {  
    server {  
        listen 8888;   
        application live {  
            live on;  
			record all;
    		record_unique on;
    		record_path "./video";  #視頻緩存的路徑
    		record_suffix -%Y-%m-%d-%H_%M_%S.flv;
        	}
         } 		 
}

這樣配置之后,服務(wù)器收到RTMP流會(huì)在NGINX的當(dāng)前目錄下,創(chuàng)建一個(gè)video目錄用來(lái)緩存視頻。

客戶端向服務(wù)器推流之后,服務(wù)器就會(huì)緩存視頻到設(shè)置的目錄。

(5)檢查配置文件是否正確

root@ecs-348470:/usr/local/nginx/sbin# /usr/local/nginx/sbin/nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful

(6)NGINX常用的3個(gè)命令

sudo service nginx start
sudo service nginx stop
sudo service nginx restart

(7)啟動(dòng)NGINX服務(wù)器

sudo service nginx start

2.7 攝像頭推流音視頻到服務(wù)器

為了模擬攝像頭實(shí)時(shí)監(jiān)控推流,我這使用QT+FFMPEG編寫(xiě)了一個(gè)小軟件,在windows下運(yùn)行,推流本地筆記本電腦的數(shù)據(jù)到服務(wù)器。軟件運(yùn)行之后,將本地音頻、視頻編碼之后通過(guò)RTMP協(xié)議推流到NGINX服務(wù)器。

軟件運(yùn)行效果:

image-20220611200146361

推流工具運(yùn)行過(guò)程中效果。

image-20220611200428359

2.8 編寫(xiě)本地RTMP流播放器

在上面通過(guò)推流客戶端模擬攝像頭,已經(jīng)將本地的攝像頭數(shù)據(jù)實(shí)時(shí)推流到服務(wù)器了,那么還差一個(gè)播放器,為了方便能夠在任何有網(wǎng)的地方實(shí)時(shí)查看攝像頭的音頻和圖像,還需要編寫(xiě)一個(gè)RTMP流媒體播放器。

我這里的播放器內(nèi)核是采用libvlc開(kāi)發(fā)的,使用QT作為GUI框架,開(kāi)發(fā)了一個(gè)流媒體播放器,可以實(shí)時(shí)拉取服務(wù)器上的流數(shù)據(jù),并且還支持回放。因?yàn)榉?wù)器上的NGINX配置了自動(dòng)保存的參數(shù),可以將推上去的流按時(shí)間段保存下來(lái)。

輸入服務(wù)器地址之后就可以拉取流進(jìn)行播放。

image-20220611201255709

點(diǎn)擊獲取回放列表,可以查看服務(wù)器上保存的歷史視頻回放播放。

image-20220611201443927

三、華為云IOT服務(wù)器部署過(guò)程

在華為云IOT平臺(tái)上,需要進(jìn)行設(shè)備接入、數(shù)據(jù)模型定義、規(guī)則引擎配置和應(yīng)用開(kāi)發(fā)等四個(gè)核心模塊的開(kāi)發(fā)。其中,設(shè)備接入模塊包括設(shè)備注冊(cè)、獲取設(shè)備證書(shū)、建立連接等步驟,以保障設(shè)備與云平臺(tái)之間的安全通信;數(shù)據(jù)模型定義模塊需要根據(jù)實(shí)際需求定義相應(yīng)的數(shù)據(jù)模型,包括上傳數(shù)據(jù)格式、設(shè)備屬性和服務(wù)等。規(guī)則引擎配置模塊需要完成實(shí)時(shí)消息推送、遠(yuǎn)程控制和告警等功能。應(yīng)用開(kāi)發(fā)模塊則是將完整的智能井蓋系統(tǒng)進(jìn)行打包,為用戶提供統(tǒng)一的操作接口。

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

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

image-20221204193824815

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)景。

img

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

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

image-20221204194233414

開(kāi)通標(biāo)準(zhǔn)版免費(fèi)單元。

image-20230420181306316

image-20230420181322092

開(kāi)通之后,點(diǎn)擊總覽,查看接入信息。 我們當(dāng)前設(shè)備準(zhǔn)備采用MQTT協(xié)議接入華為云平臺(tái),這里可以看到MQTT協(xié)議的地址和端口號(hào)等信息。

image-20230423111235524

總結(jié):

端口號(hào):   MQTT (1883)| MQTTS (8883)	
接入地址: a3433ab133.iot-mqtts.cn-north-4.myhuaweicloud.com

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

Microsoft Windows [版本 10.0.19044.2846]
(c) Microsoft Corporation。保留所有權(quán)利。

C:Users11266>ping a3433ab133.iot-mqtts.cn-north-4.myhuaweicloud.com

正在 Ping a3433ab133.iot-mqtts.cn-north-4.myhuaweicloud.com [121.36.42.100] 具有 32 字節(jié)的數(shù)據(jù):
來(lái)自 121.36.42.100 的回復(fù): 字節(jié)=32 時(shí)間=37ms TTL=31
來(lái)自 121.36.42.100 的回復(fù): 字節(jié)=32 時(shí)間=37ms TTL=31
來(lái)自 121.36.42.100 的回復(fù): 字節(jié)=32 時(shí)間=36ms TTL=31
來(lái)自 121.36.42.100 的回復(fù): 字節(jié)=32 時(shí)間=37ms TTL=31

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

C:Users11266>

image-20230423111213624

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)擊右上角創(chuàng)建產(chǎn)品。

image-20230420181503524

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

根據(jù)自己產(chǎn)品名字填寫(xiě),設(shè)備類型選擇自定義類型。

(3)添加自定義模型

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

image-20230420181615129

模型簡(jiǎn)單來(lái)說(shuō): 就是存放設(shè)備上傳到云平臺(tái)的數(shù)據(jù)。比如:環(huán)境溫度、環(huán)境濕度、環(huán)境煙霧濃度、火焰檢測(cè)狀態(tài)圖等等,這些我們都可以單獨(dú)創(chuàng)建一個(gè)模型保存。

3.4 添加設(shè)備

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

(1)注冊(cè)設(shè)備

點(diǎn)擊右上角注冊(cè)設(shè)備。

image-20230421091842025

(2)根據(jù)自己的設(shè)備填寫(xiě)

在彈出的對(duì)話框里填寫(xiě)自己設(shè)備的信息。根據(jù)自己設(shè)備詳細(xì)情況填寫(xiě)。

(3)保存設(shè)備信息

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

比如我當(dāng)前設(shè)備的信息如下:

{
    "device_id": "64000697352830580e48df07_dev1",
    "secret": "12345678"
}

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是專門(mén)針對(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

img

業(yè)務(wù)流程:

img

(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

image-20221207153310037

對(duì)于設(shè)備而言,一般會(huì)訂閱平臺(tái)下發(fā)消息給設(shè)備 這個(gè)主題。

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

比如:我創(chuàng)建的設(shè)備信息如下

以當(dāng)前設(shè)備為例,最終訂閱主題的格式如下:
$oc/devices/{device_id}/sys/messages/down
    
最終的格式:
$oc/devices/64000697352830580e48df07_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

image-20221207153637391

根據(jù)幫助文檔的介紹, 當(dāng)前設(shè)備發(fā)布主題,上報(bào)屬性的格式總結(jié)如下:

發(fā)布的主題格式:
$oc/devices/{device_id}/sys/properties/report
 
最終的格式:
$oc/devices/64000697352830580e48df07_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)介紹了,不記得可以翻到前面去查看。

//Up, Down, Left, Right, Stop
    
根據(jù)這個(gè)格式,組合一次上傳的屬性數(shù)據(jù):
{"services": [{"service_id": "stm32","properties":{"Up":1,"Down":1,"Left":1,"Right":1,"Stop":1}}]}

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

image-20230411141412090

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

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

華為云的MQTT服務(wù)器地址:117.78.5.125
華為云的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è)面:

image-20221207154917230

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

直接得到三元組信息。

image-20230302140101884

image-20230302140153743

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

DeviceId      64000697352830580e48df07_dev1
DeviceSecret  12345678
ClientId      64000697352830580e48df07_dev1_0_0_2023030206
Username      64000697352830580e48df07_dev1
Password      a695af9883c5d0e3817bc6971beeecadf8c7c595677c461b1fe75882ed2bf449

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ā)布主題。

image-20230302142915034

(2)打開(kāi)網(wǎng)頁(yè)查看

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

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

(3)MQTT登錄測(cè)試參數(shù)總結(jié)

IP地址:117.78.5.125
端口號(hào):1883
DeviceId      64000697352830580e48df07_dev1
DeviceSecret  12345678
ClientId      64000697352830580e48df07_dev1_0_0_2023030206
Username      64000697352830580e48df07_dev1
Password      a695af9883c5d0e3817bc6971beeecadf8c7c595677c461b1fe75882ed2bf449
訂閱主題:$oc/devices/64000697352830580e48df07_dev1/sys/messages/down
發(fā)布主題:$oc/devices/64000697352830580e48df07_dev1/sys/properties/report
發(fā)布的消息:{"services": [{"service_id": "stm32","properties":{"Up":1,"Down":1,"Left":1,"Right":1,"Stop":1}}]}

四、Android手機(jī)APP開(kāi)發(fā)

4.1 開(kāi)發(fā)環(huán)境介紹

在當(dāng)前項(xiàng)目中,用于遠(yuǎn)程遙控安卓小車(chē)的Android手機(jī)APP是基于Qt框架開(kāi)發(fā)的。Qt是一個(gè)功能強(qiáng)大且高度靈活的跨平臺(tái)應(yīng)用程序開(kāi)發(fā)框架,特別適合構(gòu)建具有豐富圖形用戶界面(GUI)的應(yīng)用程序,同時(shí)也支持開(kāi)發(fā)非GUI程序。在開(kāi)發(fā)這款遠(yuǎn)程遙控APP時(shí),Qt的優(yōu)勢(shì)在于其跨平臺(tái)性,使得同一份代碼可以輕松部署在不同操作系統(tǒng)平臺(tái)上,包括Android。

Android開(kāi)發(fā)必備的工具鏈包括:Java JDK  、Android SDK 、Android NDK。

NDK下載地址:https://dl.google.com/android/repository/android-ndk-r19c-linux-x86_64.zip
SDK下載: https://www.androiddevtools.cn/
JDK下載地址:https://www.oracle.com/java/technologies/javase-jdk8-downloads.html

image-20240308101952890

image-20240308102004096

4.2 ffmpeg介紹

說(shuō)起ffmpeg,只要是搞音視頻相關(guān)的開(kāi)發(fā)應(yīng)該都是聽(tīng)過(guò)的。FFmpeg提供了非常先進(jìn)的音頻/視頻編解碼庫(kù),并且支持跨平臺(tái)。

現(xiàn)在互聯(lián)網(wǎng)上ffmpeg相關(guān)的文章、教程也非常的多,ffmpeg本身主要是用來(lái)對(duì)視頻、音頻進(jìn)行解碼、編碼,對(duì)音視頻進(jìn)行處理。

其中主要是解碼和編碼。 解碼的應(yīng)用主要是視頻播放器制作、音樂(lè)播放器制作,解碼視頻文件得到視頻畫(huà)面再渲染顯示出來(lái)就是播放器的基本模型了。 編碼主要是用于視頻錄制保存,就是將攝像頭的畫(huà)面或者屏幕的畫(huà)面編碼后寫(xiě)入文件保存為視頻,比如:行車(chē)記錄儀錄制視頻,監(jiān)控?cái)z像頭錄制視頻等等。 當(dāng)然也可以編碼推流到服務(wù)器,現(xiàn)在的直播平臺(tái)、智能家居里的視頻監(jiān)控、智能安防攝像頭都是這樣的應(yīng)用。

在本項(xiàng)目里,通過(guò)ffmpeg技術(shù)將手機(jī)采集的視頻圖像編碼后,推流到搭建好的流媒體服務(wù)器,實(shí)現(xiàn)遠(yuǎn)程監(jiān)控。

4.3 Linux下編譯安裝ffmpeg

(1)安裝依賴項(xiàng):

sudo apt-get update
sudo apt-get install build-essential nasm yasm cmake libx264-dev libx265-dev libvpx-dev libfdk-aac-dev libmp3lame-dev libopus-dev libssl-dev

(2)下載FFmpeg源碼:

wget https://ffmpeg.org/releases/ffmpeg-4.2.2.tar.gz
tar -zxvf ffmpeg-x.y.z.tar.gz
cd ffmpeg-x.y.z

注意替換 4.2.2 為實(shí)際的版本號(hào)。

(3)配置編譯選項(xiàng):

./configure --enable-gpl --enable-libx264 --enable-libx265 --enable-libvpx --enable-libfdk-aac --enable-libmp3lame --enable-libopus --enable-nonfree

如果需要其他編碼器或功能,可以根據(jù)需要添加或修改配置選項(xiàng)。

(4)編譯和安裝:

make -j$(nproc)
sudo make install

-j$(nproc) 表示使用多個(gè)CPU核心進(jìn)行并行編譯,可以根據(jù)實(shí)際情況調(diào)整。

(5)完成后,可以通過(guò)運(yùn)行以下命令來(lái)驗(yàn)證FFmpeg是否正確安裝:

ffmpeg -version

這樣就完成了在Linux下編譯FFmpeg源碼的過(guò)程。

4.4 Qt攝像頭采集

下面代碼實(shí)現(xiàn),通過(guò)子線程采集攝像頭畫(huà)面,并通過(guò)信號(hào)槽機(jī)制將圖像傳遞給主線程顯示。

// mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QCamera>
#include <QCameraViewfinder>
#include <QCameraImageCapture>
#include <QThread>

class CameraThread;

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_pushButton_start_clicked();
    void on_pushButton_stop_clicked();
    void onNewImageAvailable(const QImage &image);

private:
    Ui::MainWindow *ui;
    QCamera *camera;
    QCameraViewfinder *viewfinder;
    QCameraImageCapture *imageCapture;
    CameraThread *cameraThread;
};

#endif // MAINWINDOW_H


// mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QThread>

class CameraThread : public QThread
{
    Q_OBJECT

public:
    explicit CameraThread(QObject *parent = nullptr);

signals:
    void newImageAvailable(const QImage &image);

protected:
    void run() override;

private:
    QCamera *camera;
    QCameraImageCapture *imageCapture;
};

CameraThread::CameraThread(QObject *parent) : QThread(parent)
{
    camera = new QCamera(this);
    imageCapture = new QCameraImageCapture(camera, this);
}

void CameraThread::run()
{
    camera->setCaptureMode(QCamera::CaptureStillImage);
    camera->start();

    connect(imageCapture, &QCameraImageCapture::imageCaptured, this, [&](int id, const QImage &preview) {
        emit newImageAvailable(preview);
    });

    exec();
}

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    camera = new QCamera(this);
    viewfinder = new QCameraViewfinder(this);
    imageCapture = new QCameraImageCapture(camera, this);
    cameraThread = new CameraThread(this);

    cameraThread->start();

    connect(cameraThread, &CameraThread::newImageAvailable, this, &MainWindow::onNewImageAvailable);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_pushButton_start_clicked()
{
    camera->setViewfinder(viewfinder);
    camera->start();
    ui->verticalLayout->addWidget(viewfinder);
}

void MainWindow::on_pushButton_stop_clicked()
{
    camera->stop();
    ui->verticalLayout->removeWidget(viewfinder);
    viewfinder->deleteLater();
}

void MainWindow::onNewImageAvailable(const QImage &image)
{
    // 在這里處理接收到的圖像,例如將其顯示在 QLabel 上
    ui->label_image->setPixmap(QPixmap::fromImage(image));
}

// main.cpp

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

(1)CameraThread 類繼承自 QThread,在子線程中負(fù)責(zé)采集攝像頭畫(huà)面。在 run() 方法中,首先創(chuàng)建了一個(gè) QCamera 對(duì)象和一個(gè) QCameraImageCapture 對(duì)象,然后設(shè)置攝像頭的捕獲模式為靜態(tài)圖像,啟動(dòng)攝像頭。通過(guò)連接 imageCaptureimageCaptured 信號(hào)到 lambda 函數(shù),當(dāng)捕獲到新圖像時(shí),將圖像通過(guò)自定義信號(hào) newImageAvailable 發(fā)送出去。

(2)MainWindow 類是程序的主窗口,其中包含了攝像頭的視圖控件、開(kāi)始按鈕、停止按鈕以及用于顯示圖像的標(biāo)簽。在構(gòu)造函數(shù)中,創(chuàng)建了攝像頭對(duì)象、視圖控件對(duì)象、圖像捕獲對(duì)象,并創(chuàng)建了一個(gè) CameraThread 對(duì)象作為子線程來(lái)處理攝像頭畫(huà)面的采集。

(3)當(dāng)用戶點(diǎn)擊開(kāi)始按鈕時(shí),調(diào)用 on_pushButton_start_clicked() 槽函數(shù),將攝像頭視圖控件添加到界面上并啟動(dòng)攝像頭,開(kāi)始顯示攝像頭畫(huà)面。

(4)當(dāng)用戶點(diǎn)擊停止按鈕時(shí),調(diào)用 on_pushButton_stop_clicked() 槽函數(shù),停止攝像頭捕獲并移除視圖控件。

(5)當(dāng)子線程采集到新的圖像時(shí),通過(guò) onNewImageAvailable() 槽函數(shù)接收到圖像,并在標(biāo)簽上顯示該圖像。

(6)main.cpp 文件是程序的入口,創(chuàng)建了 QApplication 對(duì)象和 MainWindow 對(duì)象,并執(zhí)行主事件循環(huán)。

4.6 ffmpeg視頻編碼推流代碼

使用Qt(C++)結(jié)合FFmpeg庫(kù)來(lái)采集攝像頭畫(huà)面,進(jìn)行編碼,并通過(guò)子線程將視頻推送到RTMP流媒體服務(wù)器。

// mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QThread>

extern "C" {
#include <libavformat/avformat.h>
#include <libavdevice/avdevice.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
}

class VideoCaptureThread;

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_pushButton_start_clicked();
    void on_pushButton_stop_clicked();
    void onNewFrameAvailable(const QImage &frame);

private:
    Ui::MainWindow *ui;
    VideoCaptureThread *videoCaptureThread;
};

#endif // MAINWINDOW_H


// mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

class VideoCaptureThread : public QThread
{
    Q_OBJECT

public:
    explicit VideoCaptureThread(QObject *parent = nullptr);
    ~VideoCaptureThread();

protected:
    void run() override;

signals:
    void newFrameAvailable(const QImage &frame);

private:
    AVFormatContext *formatContext;
    AVCodecContext *codecContext;
    AVStream *videoStream;
    SwsContext *swsContext;
    bool stop;
};

VideoCaptureThread::VideoCaptureThread(QObject *parent) : QThread(parent), stop(false)
{
    avformat_network_init();

    formatContext = avformat_alloc_context();
    AVInputFormat *inputFormat = av_find_input_format("dshow");
    avformat_open_input(&formatContext, "video=YourCameraDevice", inputFormat, NULL);

    // 省略了初始化視頻編碼器的部分,需要根據(jù)實(shí)際情況添加

    swsContext = sws_getContext(codecContext->width, codecContext->height, codecContext->pix_fmt,
                                codecContext->width, codecContext->height, AV_PIX_FMT_RGB32,
                                SWS_BICUBIC, NULL, NULL, NULL);
}

VideoCaptureThread::~VideoCaptureThread()
{
    stop = true;
    wait();

    sws_freeContext(swsContext);
    avcodec_free_context(&codecContext);
    avformat_close_input(&formatContext);
    avformat_free_context(formatContext);
}

void VideoCaptureThread::run()
{
    while (!stop) {
        AVPacket packet;
        av_init_packet(&packet);
        if (av_read_frame(formatContext, &packet) >= 0) {
            // 省略了視頻編碼的部分,需要根據(jù)實(shí)際情況添加

            QImage frameImage(codecContext->width, codecContext->height, QImage::Format_RGB32);
            sws_scale(swsContext, codecContext->coded_frame->data, codecContext->coded_frame->linesize,
                      0, codecContext->height, reinterpret_cast<uint8_t **>(frameImage.bits()), frameImage.bytesPerLine());

            emit newFrameAvailable(frameImage);
        }
        av_packet_unref(&packet);
    }
}

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    videoCaptureThread = new VideoCaptureThread(this);
    connect(videoCaptureThread, &VideoCaptureThread::newFrameAvailable, this, &MainWindow::onNewFrameAvailable);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_pushButton_start_clicked()
{
    videoCaptureThread->start();
}

void MainWindow::on_pushButton_stop_clicked()
{
    videoCaptureThread->quit();
}

void MainWindow::onNewFrameAvailable(const QImage &frame)
{
    // 將QImage轉(zhuǎn)換為AVFrame
    AVFrame *avFrame = av_frame_alloc();
    avFrame->width = frame.width();
    avFrame->height = frame.height();
    avFrame->format = AV_PIX_FMT_RGB32;
    av_frame_get_buffer(avFrame, 0);
    for (int y = 0; y < frame.height(); ++y) {
        memcpy(avFrame->data[0] + y * avFrame->linesize[0], frame.scanLine(y), frame.width() * 4);
    }

    // 編碼視頻幀
    AVPacket packet;
    av_init_packet(&packet);
    int ret = avcodec_send_frame(codecContext, avFrame);
    if (ret < 0) {
        qDebug() << "Failed to send frame to encoder";
        return;
    }
    while (ret >= 0) {
        ret = avcodec_receive_packet(codecContext, &packet);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
            break;
        } else if (ret < 0) {
            qDebug() << "Error during encoding";
            return;
        }

        // 推送到RTMP服務(wù)器
        RTMP *rtmp = RTMP_Alloc();
        RTMP_Init(rtmp);
        if (!RTMP_SetupURL(rtmp, "rtmp://your_rtmp_server_url")) {
            qDebug() << "Failed to set RTMP URL";
            RTMP_Close(rtmp);
            RTMP_Free(rtmp);
            return;
        }
        RTMP_EnableWrite(rtmp);
        if (!RTMP_Connect(rtmp, NULL)) {
            qDebug() << "Failed to connect to RTMP server";
            RTMP_Close(rtmp);
            RTMP_Free(rtmp);
            return;
        }
        if (!RTMP_ConnectStream(rtmp, 0)) {
            qDebug() << "Failed to connect to RTMP stream";
            RTMP_Close(rtmp);
            RTMP_Free(rtmp);
            return;
        }

        packet.stream_index = videoStream->index;
        ret = RTMP_SendPacket(rtmp, reinterpret_cast<char*>(packet.data), packet.size, TRUE);
        if (ret < 0) {
            qDebug() << "Failed to send packet to RTMP server";
            RTMP_Close(rtmp);
            RTMP_Free(rtmp);
            return;
        }

        av_packet_unref(&packet);
        RTMP_Close(rtmp);
        RTMP_Free(rtmp);
    }

    av_frame_free(&avFrame);
}

代碼中創(chuàng)建了一個(gè) VideoCaptureThread 類作為子線程,負(fù)責(zé)采集攝像頭畫(huà)面并進(jìn)行視頻編碼。在 run() 方法中,利用FFmpeg庫(kù)讀取攝像頭畫(huà)面,進(jìn)行視頻編碼,并通過(guò)自定義信號(hào) newFrameAvailable 發(fā)送每一幀圖像。主界面中的 MainWindow 類負(fù)責(zé)開(kāi)始和停止視頻采集線程,并處理接收到的視頻幀,可以在 onNewFrameAvailable() 槽函數(shù)中將視頻幀編碼為RTMP流并推送到服務(wù)器。將QImage轉(zhuǎn)換為AVFrame,使用avcodec_send_frameavcodec_receive_packet函數(shù)對(duì)視頻幀進(jìn)行編碼。創(chuàng)建一個(gè)RTMP連接,并將編碼后的視頻包發(fā)送到RTMP服務(wù)器。

五、STM32端代碼設(shè)計(jì)

STM32端的代碼主要是控制小車(chē)的移動(dòng),代碼比較少。

5.1 STM32小車(chē)底座驅(qū)動(dòng)代碼

#include "stm32f10x.h"

#define MOTOR1_PIN1 GPIO_Pin_0
#define MOTOR1_PIN2 GPIO_Pin_1
#define MOTOR2_PIN1 GPIO_Pin_2
#define MOTOR2_PIN2 GPIO_Pin_3
#define MOTOR3_PIN1 GPIO_Pin_4
#define MOTOR3_PIN2 GPIO_Pin_5
#define MOTOR4_PIN1 GPIO_Pin_6
#define MOTOR4_PIN2 GPIO_Pin_7

void delay_ms(uint32_t ms) {
    uint32_t i, j;
    for (i = 0; i < ms; i++)
        for (j = 0; j < 7200; j++);
}

void motor_init() {
    GPIO_InitTypeDef GPIO_InitStructure;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitStructure.GPIO_Pin = MOTOR1_PIN1 | MOTOR1_PIN2 | MOTOR2_PIN1 | MOTOR2_PIN2 | MOTOR3_PIN1 | MOTOR3_PIN2 | MOTOR4_PIN1 | MOTOR4_PIN2;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    
    GPIO_Init(GPIOA, &GPIO_InitStructure);
}

void forward() {
    GPIO_ResetBits(GPIOA, MOTOR1_PIN2 | MOTOR2_PIN2 | MOTOR3_PIN2 | MOTOR4_PIN2);
    GPIO_SetBits(GPIOA, MOTOR1_PIN1 | MOTOR2_PIN1 | MOTOR3_PIN1 | MOTOR4_PIN1);
}

void backward() {
    GPIO_ResetBits(GPIOA, MOTOR1_PIN1 | MOTOR2_PIN1 | MOTOR3_PIN1 | MOTOR4_PIN1);
    GPIO_SetBits(GPIOA, MOTOR1_PIN2 | MOTOR2_PIN2 | MOTOR3_PIN2 | MOTOR4_PIN2);
}

void left() {
    GPIO_ResetBits(GPIOA, MOTOR1_PIN2 | MOTOR2_PIN1 | MOTOR3_PIN1 | MOTOR4_PIN2);
    GPIO_SetBits(GPIOA, MOTOR1_PIN1 | MOTOR2_PIN2 | MOTOR3_PIN2 | MOTOR4_PIN1);
}

void right() {
    GPIO_ResetBits(GPIOA, MOTOR1_PIN1 | MOTOR2_PIN2 | MOTOR3_PIN2 | MOTOR4_PIN1);
    GPIO_SetBits(GPIOA, MOTOR1_PIN2 | MOTOR2_PIN1 | MOTOR3_PIN1 | MOTOR4_PIN2);
}

int main(void) {
    motor_init();
    
    while (1) {
        forward();
        delay_ms(1000);
        
        backward();
        delay_ms(1000);
        
        left();
        delay_ms(1000);
        
        right();
        delay_ms(1000);
    }
}

5.2 小車(chē)控制代碼

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

#define MOTOR1_PIN1 GPIO_Pin_0
#define MOTOR1_PIN2 GPIO_Pin_1
#define MOTOR2_PIN1 GPIO_Pin_2
#define MOTOR2_PIN2 GPIO_Pin_3
#define MOTOR3_PIN1 GPIO_Pin_4
#define MOTOR3_PIN2 GPIO_Pin_5
#define MOTOR4_PIN1 GPIO_Pin_6
#define MOTOR4_PIN2 GPIO_Pin_7

void delay_ms(uint32_t ms) {
    uint32_t i, j;
    for (i = 0; i < ms; i++)
        for (j = 0; j < 7200; j++);
}

void motor_init() {
    GPIO_InitTypeDef GPIO_InitStructure;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitStructure.GPIO_Pin = MOTOR1_PIN1 | MOTOR1_PIN2 | MOTOR2_PIN1 | MOTOR2_PIN2 | MOTOR3_PIN1 | MOTOR3_PIN2 | MOTOR4_PIN1 | MOTOR4_PIN2;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    
    GPIO_Init(GPIOA, &GPIO_InitStructure);
}

void forward() {
    GPIO_ResetBits(GPIOA, MOTOR1_PIN2 | MOTOR2_PIN2 | MOTOR3_PIN2 | MOTOR4_PIN2);
    GPIO_SetBits(GPIOA, MOTOR1_PIN1 | MOTOR2_PIN1 | MOTOR3_PIN1 | MOTOR4_PIN1);
}

void backward() {
    GPIO_ResetBits(GPIOA, MOTOR1_PIN1 | MOTOR2_PIN1 | MOTOR3_PIN1 | MOTOR4_PIN1);
    GPIO_SetBits(GPIOA, MOTOR1_PIN2 | MOTOR2_PIN2 | MOTOR3_PIN2 | MOTOR4_PIN2);
}

void left() {
    GPIO_ResetBits(GPIOA, MOTOR1_PIN2 | MOTOR2_PIN1 | MOTOR3_PIN1 | MOTOR4_PIN2);
    GPIO_SetBits(GPIOA, MOTOR1_PIN1 | MOTOR2_PIN2 | MOTOR3_PIN2 | MOTOR4_PIN1);
}

void right() {
    GPIO_ResetBits(GPIOA, MOTOR1_PIN1 | MOTOR2_PIN2 | MOTOR3_PIN2 | MOTOR4_PIN1);
    GPIO_SetBits(GPIOA, MOTOR1_PIN2 | MOTOR2_PIN1 | MOTOR3_PIN1 | MOTOR4_PIN2);
}

void usart_init() {
    USART_InitTypeDef USART_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);

    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(USART1, &USART_InitStructure);
    USART_Cmd(USART1, ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
}

void usart_send(USART_TypeDef* USARTx, uint8_t data) {
    while (USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
    USART_SendData(USARTx, data);
}

uint8_t usart_receive(USART_TypeDef* USARTx) {
    while (USART_GetFlagStatus(USARTx, USART_FLAG_RXNE) == RESET);
    return USART_ReceiveData(USARTx);
}

void usart_puts(USART_TypeDef* USARTx, char* str) {
    while (*str) {
        usart_send(USARTx, *str++);
    }
}

void control(char* cmd) {
    if (strcmp(cmd, "forward") == 0) {
        forward();
        usart_puts(USART1, "OKn");
    } else if (strcmp(cmd, "backward") == 0) {
        backward();
        usart_puts(USART1, "OKn");
    } else if (strcmp(cmd, "left") == 0) {
        left();
        usart_puts(USART1, "OKn");
    } else if (strcmp(cmd, "right") == 0) {
        right();
        usart_puts(USART1, "OKn");
    } else {
        usart_puts(USART1, "Invalid commandn");
    }
}

int main(void) {
    motor_init();
    usart_init();
    
    while (1) {
        char cmd[10];
        memset(cmd, 0, sizeof(cmd));
        int i = 0;
        while (1) {
            char c = usart_receive(USART1);
            if (c == 'r' || c == 'n') {
                break;
            }
            cmd[i++] = c;
        }
        control(cmd);
    }
}

六、關(guān)于Android手機(jī)USB通信的問(wèn)題

在Qt中開(kāi)發(fā)Android手機(jī)APP并利用USB線進(jìn)行串口通信,需要啟用權(quán)限。

(1)添加權(quán)限:在AndroidManifest.xml文件中添加USB權(quán)限,并在Qt項(xiàng)目中的Android配置文件中聲明需要的權(quán)限。例如,在AndroidManifest.xml中添加以下代碼:

<uses-permission android:name="android.permission.USB_PERMISSION" />

(2)檢測(cè)USB連接:通過(guò)Qt的Android JNI接口(Java Native Interface)來(lái)檢測(cè)USB設(shè)備的插拔狀態(tài),并獲取USB設(shè)備的信息。

(3)打開(kāi)和關(guān)閉USB串口:使用Qt的QSerialPort類來(lái)打開(kāi)和關(guān)閉USB串口,并進(jìn)行數(shù)據(jù)的讀寫(xiě)操作??梢酝ㄟ^(guò)檢測(cè)到的USB設(shè)備路徑來(lái)打開(kāi)對(duì)應(yīng)的串口。

(4)處理串口數(shù)據(jù):接收到的串口數(shù)據(jù)可以通過(guò)信號(hào)槽機(jī)制或者其他方式傳遞給界面進(jìn)行顯示或進(jìn)一步處理。

下面是測(cè)試的代碼:

#include <QSerialPort>
#include <QSerialPortInfo>

void detectUsbDevices() {
    QList<QSerialPortInfo> usbDevices = QSerialPortInfo::availablePorts();
    
    foreach (const QSerialPortInfo &info, usbDevices) {
        qDebug() << "USB Device Name: " << info.portName();
        qDebug() << "Description: " << info.description();
    }
}

void openUsbSerialPort(const QString &portName) {
    QSerialPort serialPort;
    serialPort.setPortName(portName);
    serialPort.setBaudRate(QSerialPort::Baud9600);
    
    if (serialPort.open(QIODevice::ReadWrite)) {
        qDebug() << "USB Serial Port opened successfully!";
        
        // Read or write data here
        
        serialPort.close();
    } else {
        qDebug() << "Failed to open USB Serial Port!";
    }
}

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    
    // Detect USB devices
    detectUsbDevices();
    
    // Open USB serial port
    openUsbSerialPort("/dev/ttyUSB0"); // Replace with the actual port name
    
    return app.exec();
}

七、總結(jié)

本文詳細(xì)介紹了一款創(chuàng)新且環(huán)保的基于4G網(wǎng)絡(luò)設(shè)計(jì)的遠(yuǎn)程遙控安卓小車(chē)系統(tǒng)的開(kāi)發(fā)與實(shí)現(xiàn)過(guò)程。該項(xiàng)目巧妙地將被淘汰的安卓舊手機(jī)升級(jí)轉(zhuǎn)化為車(chē)載信息處理單元,賦予其新的生命力,同時(shí)融入先進(jìn)的4G網(wǎng)絡(luò)技術(shù)、流媒體服務(wù)以及物聯(lián)網(wǎng)技術(shù),成功打造出一個(gè)集遠(yuǎn)程操控與實(shí)時(shí)音視頻傳輸功能于一身的高效率解決方案。

智能小車(chē)以其獨(dú)特的設(shè)計(jì)思路和強(qiáng)大的功能特性,展現(xiàn)出廣泛的應(yīng)用潛力。無(wú)論是作為教育科研領(lǐng)域的實(shí)踐平臺(tái),還是在遠(yuǎn)程監(jiān)控、工業(yè)巡檢、應(yīng)急救援、無(wú)人駕駛技術(shù)驗(yàn)證,甚至智能家居與物流等方面,均能發(fā)揮重要作用,顯著提升了工作效率,降低了人力成本,并有效保障了作業(yè)的安全性。

該項(xiàng)目積極響應(yīng)可持續(xù)發(fā)展號(hào)召,通過(guò)資源循環(huán)利用,成功展示了科技如何助力環(huán)保,彰顯了技術(shù)創(chuàng)新的社會(huì)價(jià)值。展望未來(lái),隨著5G網(wǎng)絡(luò)技術(shù)的廣泛應(yīng)用,這款基于4G網(wǎng)絡(luò)的遠(yuǎn)程遙控安卓小車(chē)將進(jìn)一步優(yōu)化性能,拓展應(yīng)用場(chǎng)景,為社會(huì)各領(lǐng)域帶來(lái)更加智能化、便捷化的技術(shù)服務(wù)。

  • 更多詳細(xì)資料請(qǐng)聯(lián)系.docx
    下載
意法半導(dǎo)體

意法半導(dǎo)體

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

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

查看更多

相關(guān)推薦

方案定制

去合作
方案開(kāi)發(fā)定制化,2000+方案商即時(shí)響應(yīng)!