天天看點

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

作者:華清遠見研發中心

stm32mp157開發闆FS-MP1A是華清遠見自主研發的一款高品質、高成本效益的Linux+單片機二合一的嵌入式教學級開發闆。開發闆搭載ST的STM32MP157高性能微處理器,內建2個Cortex-A7核和1個Cortex-M4 核,A7核上可以跑Linux作業系統,M4核上可以跑FreeRTOS、RT-Thread等實時作業系統。開發闆搭配仿真器、顯示屏、攝像頭、資源擴充闆等豐富的擴充子產品,可拓展物聯網、人工智能等相關技術學習,還可以拓展豐富的項目實戰,非常貼合企業當下開發需求,是一款嵌入式Linux入門進階必備開發闆!

可學習技術:嵌入式Linux應用/系統/驅動開發、ARM裸機開發、Qt界面程式設計、STM32單片機、FreeRTOS、人工智能機器視覺等。其中ARM Cortex-A7裸機開發課程是華清遠見獨有特色課程,可關注:https://www.bilibili.com/video/BV1Xe4y1i7vm/,持續更新中。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

可實戰項目:14個Linux+Qt綜合項目案例,8個MP1A物聯網拓展項目

關注公衆号“華清遠見V智能有料”,回複“mp157項目”,索取項目配套文檔及源碼。

1、Linux+Qt綜合項目案例:華清遠見stm32mp157開發闆優勢特色部分,包括音樂播放器、智慧家庭、智能工業電表、智能出行助手、智能貓眼、環境監測、智能安防、智能語音識别等10餘個項目案例,涉及家居、醫療、農業多種應用方向,在案例中使用了多種物聯網和嵌入式技術,包括OT開發、linux應用開發、linux驅動開發、物聯網雲端接入、MQTT協定、json字元串等知識點。

基于Linux+Qt的智慧家庭項目

項目簡介:

智慧家庭又可稱為智慧家庭服務平台,是智慧城市的最小單元,是以家庭為載體,以家庭成員之間的親情為紐帶。智慧家庭綜合利用物聯網、雲計算、移動網際網路和大資料等新一代資訊技術,結合自動控制技術,将家庭裝置智能控制、家庭環境感覺、家人健康感覺、家居安全感覺以及資訊交流、消費服務等家居生活有效地結合起來,創造出健康、安全、舒适、低碳、便捷、個性化和充滿關愛的家庭生活方式。

開發平台:

華清遠見stm32mp157開發闆豪華套餐(開發闆+仿真器+五寸屏+攝像頭+資源擴充闆+tf卡+讀卡器)

項目功能展示:

Wifi 子產品

點選重新整理按鈕,可以實時更新附近的 wifi,選擇要連接配接的 wifi,會彈出輸入密碼的頁面,輸入密碼,點選連接配接即可連接配接成功。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭
STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

選擇要檢視天氣的城市,點選擷取按鈕

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

環境監測子產品

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

裝置控制子產品

選擇按鈕即可完成裝置的控制

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

開啟和關閉按鈕控制智能監測的開啟和關閉,送出按鈕上的輸入框輸入的是智能監測觸發的門檻值。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

連接配接百度雲子產品

輸入百度雲連接配接三元組,點選擷取時間戳,點選計算、連接配接,即可實作向雲端發送環境進而轉發到微信小程式,并且通過微信小程式控制裝置。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

在微信小程式顯示溫濕度。并且控制開發闆 led 燈

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

遮擋光電開關,會自動彈出門禁系統。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

Qt開發環境搭建

主機開發環境說明

1) 本文檔主要介紹 linux 環境下的 Qt 程式開發;

2) 主機 Qt 版本為 5.14.1;

主機 Qt 環境搭建及使用

Qt Creator 安裝

将 qt-creator-opensource-linux-x86_64-4.10.1.run(Qt 實驗源碼\工具軟體) 複制到 ubuntu 主機中,可以采用共享檔案夾的方式也可以使用 tfp方式将文 件存入家目錄下的 Downloads 目錄。我們需要在終端中賦予安裝程式可執行的權限

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

我們可以使用圖形化的檔案管理器來檢視

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

輕按兩下“qt-creator-opensource-linux-x86_64-4.10.1.run”圖示運作安裝程式。

出現如下界面:

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

等待程式驗證完成後點選“Next”

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

這裡我們需要登入或者注冊一個賬号,如果我們之前已經注冊過直接登入就可以。如果沒有注冊過則需要新注冊有一個賬号後登入。這裡筆者已經注冊過賬号,是以直接登入。

登入成功後出現如下界面,點選 Next

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

這裡選擇安裝路徑

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

可以直接預設,Next

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

這路選擇安裝的元件,直接預設即可

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

這裡我們需要同意使用者協定

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

這個界面告訴我們安裝完成後需要占用的空間。點選”Install”按鈕後開始安裝。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

安裝完成後出現如下界面

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

點選“Finish”按鈕後将彈出 Qt Creator 主界面

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

點選“Cancel”按鈕後即可正常使用

Qt5.14.1 安裝

複制到 qt-opensource-linux-x64-5.14.1.run(Qt 實驗源碼\工具軟體)到 ubuntu 主機中,可以采用共享檔案夾的方式也可以使用 tfp 方式将檔案存入家目錄下的 Downloads 目錄。進入所在檔案夾,先給執行權限輸入指令

chmod +x ./qt-opensource-linux-x64-5.14.1.run

安裝在指令行輸入

./qt-opensource-linux-x64-5.14.1.run

會有可視化引導安裝,一直 next 就行了

在選擇安裝元件的時候要是不知道選擇那些就全選了 大概有 4 個 G 左右

下載下傳 gcc 和 g++

sudo apt-get install gcc g++

下載下傳 cmake

sudo apt-get install cmake

下載下傳連結庫

sudo apt-get install libgl1-mesa-dev libglu1-mesa-dev

Qt Creator 配置

1)配置 GCC

運作 QtCreator 後,依次點選"Tool"->"Options",出現選項對話框,在左側點選"Kits",右 邊選擇"Compilers"标簽。 檢查有沒有下圖示注的 C++和 C ,一般按上面步驟執行後都會有

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

點選右側"Add"按鈕,彈出下拉清單後,選擇"GCC"的"C"

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

填寫資訊如下,"Name"為"Auto-GCC","Compiler path"點選旁邊的"Browse.."按鈕選擇編譯器的路徑,例子中的路徑是 “/usr/bin/gcc”

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

2)配置 G++

點選右側"Add"按鈕,彈出下拉清單後,選擇"GCC"的"C++",下面的文本框填寫"Name" 為"Auto-G++","Compiler path"點選旁邊的"Browse.."按鈕選擇編譯器的路徑,例子中的路徑是" /usr/bin/g++"。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭
STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

填寫完成後,點選"Apply"。

3)配置 qmake

選擇"Qt Versions"标簽,如果有下面紅框中的文本,可以跳過下面步驟

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

如果沒有,在右側點選"Add..."

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

會彈出 qmake 路徑選擇對話框,這裡以"/home/linux/Qt5.14.1/5.14.1/gcc_64/bin/qmake"為例子。 選擇”qmake”檔案後,擊"Open"按鈕

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

"Version name"改為" Qt %{Qt:Version} GCC"。然後點選"Apply"按鈕。

4)配置 Kits

點選左側"Kits",右側選擇"Kits"标簽。檢查有沒有下圖紅框選中的文本,如果有可以跳過下面步驟

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

然後沒有,點選 Add:

在彈出的對話框中"Name"為"Desktop","Device Type"選擇"Desktop"選項, "Sysroot"選擇目标裝置的系統目錄,"Compiler"選擇之前配置的名稱

"Auto-GCC"和"Auto-G++","Qt version"選擇之前配 置的名稱"Qt 5.14.1

GCC",其它預設即可,最後點選"Apply"和"OK"按鈕

Qt Creator 建立工程

注意:工程路徑最好不要包含中文、特殊字元、空格等

我們可以建立一個“qt”檔案夾,該檔案夾用作我們以後存放源代碼。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

打開 Qt Creator,在歡迎頁面點選 “New”按鈕,來建立一個工程。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

在出現的建立項目視窗中,我們選則“Application”->“Qt Widgets

Application”,然後點選右下方“Choose…”按鈕,來建立一個桌面 Qt 應用。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

我們在這裡設定項目介紹和源碼位置,我們這裡建立一個名為

“HelloWorld”的示例項目,設定完成之後點選 next

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

直接點選 next

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

随後進行細節設定,主要設定要建立的源碼檔案的基本類資訊,包括類名等。這裡我們可以根據自己的項目特點進行設定。需要說明的一點就是基類的選擇,這裡基類有 QMainWindow、QWidget、QDialog 三種,它們的不同之處如下:

⚫ QMainWindow 類提供一個帶有菜單條,工具條和一個狀态條的主應用程式視窗。主視窗通常提供一個大的中央視窗部件,以及周圍菜單,工具條,和一個狀态欄。QMainWindow 視窗經常被繼承,使得封裝中央部件,菜單,工具條,狀态欄等都變得很容易,當使用者點選它的時候,相應的槽就會被調用;

⚫ QWidget 類是所有使用者界面對象的基類,視窗部件是使用者界面的一個基本單元,它從視窗系統接收滑鼠,鍵盤和其他消息,并在螢幕上繪制自己。一個視窗部件可以被他的父視窗或者是其他視窗擋住一部分;

⚫ QDialog 類是對話框視窗的基類,對話框視窗主要用于短期任務和使用者進行短期通訊的頂級視窗,QDialog 可以是模态對話框或者是非模态對話框。QDialog 支援擴充并帶有傳回值,他們可以帶有預設值;我們在這裡選擇 QDialog 類即可,點選 next 完成類資訊設定

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

直接點選 next 按鈕即可。

然後進行工具選擇,該頁面可以選擇我們建立的工程可以使用的工具,選擇想要使用的編譯器子產品,例如下圖 。點選 next

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

最後我們設定彙總資訊,如果不需要版本控制等功能,直接點選完成finish 即可

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

随後我們就進入到了主界面,這時候 Qt 已經幫我們做好了一些準備工作,包括建立了一些檔案,寫好了一些前置代碼等等。

我們可以點選左邊 protect 欄,來檢視我們的編譯選項

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

我們可以在左下角選擇編譯 Debug 版或者 Release 版,即調試版或發行版。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

左下角綠色剪頭是編譯并運作,錘子是僅編譯,我們可以直接點選綠色小箭頭将我們導入的工程編譯并運作起來。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

點選運作按鈕後,我們可以看到 HelloWorld 視窗運作起來了。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

導入工程

我們可以将已存在的 Qt 程式項目直接打開,這裡以上一章節的HelloWorld 程式為例。首先我們确定源碼存在的位置,如 HelloWorld 程式源碼在 /home/linux/qt/helloworld 路徑下。

點選歡迎頁面的“Open” 按鈕可以打開已有的工程。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

找到我們剛才解壓好的源碼,選擇“helloworld.pro”檔案并點選打開

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

接下來我們就可以進入到代碼編輯界面了。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

左上角是項目欄,點選項目名稱左邊的小箭頭可以展開項目目錄。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

我們可以點選左邊項目欄,來檢視我們的編譯選項。需注意的是建構設定中的路徑應與工程路徑處于同級目錄下。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

我們可以在左下角選擇編譯 Debug 版或者 Release 版,即調試版或發行版。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

左下角綠色剪頭是編譯并運作,錘子是僅編譯,我們可以直接點選綠色小箭頭将我們導入的工程編譯并運作起來。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

點選運作按鈕後,我們可以看到 HelloWorld 視窗運作起來了。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

檔案說明

通過上面兩個章節,我們學習到了 Qt 程式的建立與導入的方法,也知道了Qt 會幫我們做一些基礎工作,比如幫我們建立了一些檔案,那麼這些檔案都是幹什麼用的呢?我們以HelloWorld 程式來說明一下。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

以“.pro”為字尾名的檔案,為 Qt 的項目管理檔案,存儲項目設定的檔案;

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

“Qt += core gui”表示項目中加入 core gui 子產品。core gui 是 Qt 用于GUI 設計的類庫子產品,如果建立的是控制台(console)應用程式,就不需要添加 core gui。

Qt 類庫以子產品的形式組織各種功能的類,根據項目涉及的功能需求,在項目中添加适當的類庫子產品支援。例如,如果項目中使用到了涉及資料庫操作的類就需要用到 sql(資料庫)子產品,在 pro 檔案中需要在後面加上 sql:

1 Qt += core gui sql

“greaterThan(QT_MAJOR_VERSION, 4): QT += widgets”,這是個條件執行語句,表示當 Qt 主版本大于 4 時,才加入 widgets 子產品。

“TARGET = HelloWorld”表示生成的目标可執行檔案的名稱,即編譯後生成的可執行檔案是 HelloWorld.exe。

“TEMPLATE = app”表示項目使用的模闆是 app,是一般的應用程式。

後面的 SOURCES、HEADERS、FORMS 記錄了項目中包含的源程式檔案、頭檔案和窗體檔案(.ui 檔案)的名稱。這些檔案清單是 Qt Creator 自動添加到項目管理檔案裡面的,使用者不需要手動修改。當添加一個檔案到項目,或從項目裡删除一個檔案時,項目管理檔案裡的條目會自動修改。

檔案夾“Header”中,存放的是所設計的窗體類的頭檔案;

檔案夾“Sources”中,存放着源碼檔案。main.cpp 是實作 main()函數的程式檔案,HelloWorld.cpp 是 widget.h 裡定義類的實作檔案。C++中,任何窗體或界面元件都是用類封裝的,一個類一般有一個頭檔案(.h 檔案)和一個源程式檔案(.cpp 檔案);

檔案夾“Forms”中,存放着界面設計檔案,“.ui”檔案是一個 XML 格式存儲的窗體上的元件及其布局的檔案,輕按兩下項目檔案目錄樹中的檔案 ui,會打開一個內建在 Qt Creator 中的 Qt Designer 對窗體進行可視化設計;

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

UI 設計器有以下一些功能區域:

元件面闆:視窗左側是界面設計元件面闆,分為多個組,如 Layouts、Buttons、Display Widgets 等,界面設計的常見元件都可以在元件面闆裡找到。

中間主要區域是待設計的窗體。如果要将某個元件放置到窗體上時,從元件面闆上拖放一個元件到窗體上即可。

Signals 和 Slots 編輯器與 Action 編輯器是位于待設計窗體下方的兩個編輯器。Signals 和 Slots 編輯器用于可視化地進行信号與槽的關聯,Action 編輯器用于可視化設計 Action。

布局和界面設計工具欄:視窗上方的一個工具欄,工具欄上的按鈕主要實作布局和界面設計。

對象浏覽器(Object Inspector):視窗右上方是 Object Inspector,用樹狀視圖顯示窗體上各元件之間的布局包含關系,視圖有兩列,顯示每個元件的對象名稱(ObjectName)和類名稱。

屬性編輯器(Property Editor):視窗右下方是屬性編輯器,是界面設計時最常用到的編輯器。屬性編輯器顯示某個選中的元件或窗體的各種屬性及其取值,可以在屬性編輯器裡修改這些屬性的值。屬性編輯器的内容分為兩列,左側為屬性的名稱,右側為屬性的值。屬性又分為多個組,實際上表示了類的繼承關系,位于下方的類屬性組繼承自位于上方的類屬性組;如果我們需要建立資源檔案、源碼檔案等,可以在項目檔案夾出點選滑鼠右鍵,選擇 Add New;如果我們有新的檔案需要添加,可以在項目檔案夾出點選滑鼠右鍵,選擇 Add Existing Files。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

幫助文檔

Qt 的幫助文檔是伴随我們學習 Qt 開發的好夥伴。在 Qt 開發過程中,我們會面臨圖形接口使用的問題,它不像 C 語言那樣就那麼幾個函數接口,圖形接口的接口數量可以用海量來形容,常用的我們可能能記住,其它的就沒有必要去記了,用到什麼就去幫助文檔檢視用法是比較友善的。我們可以按 F1 按鍵,或通過上方導航欄的“help->contects”來進入幫助文檔。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

上方的前進後退按鈕友善我們檢視文檔,如傳回到上一步,傳回到下一步。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

我們可以通過幫助文檔來檢視以下幾個部分:

類使用的相關介紹;

檢視相關類的使用介紹,我們可以先進入到幫助文檔,然後在左上角選擇“Search”。筆者這裡以 QWidget 類為例,輸入我們想要查找的類的名字,然後輕按兩下查找結果來檢視說明

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭
STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

也可以先将滑鼠移動到想要查詢的類的位置,如圖所示,将滑鼠移動至“QWidget”處,然後按“F1”鍵,即可跳轉到相應的幫助文檔

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭
STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

我們可以通過再按一次“F1”鍵來全視窗檢視幫助文檔,按“Esc”鍵可以退出。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

部分常用的成員元素包括以下幾項:

⚫ 公有成員函數:操作部件屬性的相關函數;

⚫ 公有槽函數:Qt 類中已經定義好的槽函數,直接可與信号相連接配接;

⚫ 信号:軟中斷,如按下按鈕觸發 pressed() 信号等;

⚫ 保護成員函數:通常事件所對應的虛函數放在此處;

⚫ 事件:常用事件,如操作滑鼠觸發的滑鼠事件;

滾動滑鼠滾輪,向下即可看到“Qwdget Class”類的相關說明了。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

1) 檢視所用的部件的相應成員函數。

我們可以查找到該類所用部件的相應成員函數的使用方法、功能、參數、

傳回值等等,我們以“按鈕”控件,即“QPushButton Class”類為例,我們通

過索引搜尋的方式,來找到這個類

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

我們可以通過點選“Public Functions” 來檢視“QPushButton”這個類中的成員函數。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

這裡以“QPushButton(const QString &text, QWidget *parent =Q_NULLPTR)”為例,我們點選函數名字可以進入到函數詳情中。我們可以看到相應的描述為:以“text”為顯示内容,以“parent”為父對象,構造一個push 按鈕。“text”“parent”為函數參數,由于是構造函數,是以此函數沒有傳回值。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

還有一些函數是繼承自其它類的,例如“Public Functions”中有 21 個繼承自“QAbstractButton”類的函數,我們點選“QAbstractButton”即可檢視。

點選“QAbstractButton”即可檢視。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭
STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

同樣我們可以點選相應的函數進入檢視詳情。如檢視“void setText(const QString &text)”。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

2) 檢視所用的部件的信号。

我們這裡還是以“PushButton”為例,我們點選“Public Slots”。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

可以看到“PushButton”本身有一個“void showMenu()”的信号,并且有很多繼承自其他類的信号。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

一般來說我們用的“PushButton”的信号,最多的是用到其繼承自基類“

QAbstractButton”中的幾個信号,分别是點選(按下後擡起)、按壓(單按下)、釋放(單擡起)等。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

我們可以點選相應信号檢視詳情

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

3) 檢視所用的部件的事件(所對應的虛函數如何編寫)。部件常用事件主要在 “QWidget”中聲明,選擇“Events”即可檢視相關說明。

每個事件都對應着事件函數。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

點選事件函數可檢視詳情

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

微信小程式開發環境搭建

微信小程式開發工具簡介

微信小程式是小程式中的一種,英文名 Wechat Mini Program,是一種不需要下載下傳安裝即可使用的應用,它實作了應用“觸手可及”的夢想,使用者掃一掃或搜一下即可打開應用。全面開放申請後,主體類型為企業、政府、媒體、其他組織或個人的開發者,均可申請注冊小程式。微信小程式、微信訂閱号、微信服務号、微信企業号是并行的體系。以下是小程式所涉及的技術概括:

⚫ JSON

JSON(JavaScript Object Notation)是一種輕量級的資料交換格式。它是基于 ECMAScript(w3c 制定的 js 規範)的一個子集,采用完全獨立于程式設計語言的文本格式來存儲和表示資料。簡潔和清晰的層次結構使得 JSON 成為理想的資料交換語言。易于人的閱讀和編寫,同時也易于機器解析和生成,并有效提升網絡傳輸效率。

⚫ XML

XML(Extensible Markup Language),中文名為可擴充标記語言,标準通用标記語言的子集,是一種用于标記電子檔案使其具有結構性的标記語言。

⚫ CSS

層疊樣式表(Cascading Style Sheets)是一種用來表現 HTML(标準通用标記語言的一個應用)或 XML(标準通用标記語言的一個子集)等檔案樣式的計算機語言。CSS 不僅可以靜态的修飾網頁,還可以配合各種腳本語言動态的對網頁各元素進行格式化。

⚫ JavaScript

JavaScript,是一種直譯式腳本語言,是一種動态類型、弱類型、基于原型的語言,内置支援類型。它的解釋器被稱為 JavaScript 引擎,是浏覽器的一部分,廣泛用于用戶端的腳本語言。

申請微信小程式

登入微信公衆平台,新增賬號,選擇小程式。https://mp.weixin.qq.com/

按照步驟依次注冊,輸入郵箱,密碼,驗證碼等,同意協定進行注冊。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭
STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

然後登入自己的郵箱,查閱郵件,點選連結進行激活。進入步驟 3,資訊登記,按照網頁要求,依次輸入資訊,身份資訊,管理者微信資訊,即可激活成功。

傳回微信公衆平台,輸入剛剛注冊的賬戶密碼,會需要用管理者微信掃碼登入,登入後,下載下傳普通小程式開發者工具。

點選開發,選擇開發設定,擷取小程式 ID,以備後續開發需求。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

微信小程式開發工具下載下傳完成後,進行預設安裝即可

建立新項目工程

打開微信小程式開發者工具,點選建立新工程,填寫自己的 APPID,選擇預設模闆,語言選擇 JavaScript,點選建立。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

建立工程完成為如下界面:

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

基本環境配置

打開主界面的右上角的詳情按鈕,找到本地設定,将“增強編譯”和“不校驗合法域名”這兩個選項進行勾選,因為在小程式的開發階段,盡量把這兩勾選上。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

編譯、調試

打開 app.json 檔案,可以更改微信小程式的标題,改為“工程 demo”。然後按下 Ctrl + S 快捷鍵進行儲存,即可完成編譯,調試輸出。檢視現象。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭
STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

項目總體設計介紹

總體架構

智慧家庭系統的設計基于物聯網的思想,物聯網是新一代資訊技術的重要組成部分,其英文名稱是“The Internet of things”。其基本思想是以網際網路為媒介,實作遠端監督、控制。它在各個領域有着非常廣泛的應用。

總體架構如下:

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

該項目分為 WIFI 連接配接子產品、智能門禁子產品、資料采集子產品、智能檢測子產品、裝置控制子產品、天氣預報子產品、與百度雲互動子產品:下面具體介紹幾個子產品的功能。

WIFI 連接配接子產品

該子產品實作的原理是使用 wpa_supplicant 工具對無線網絡進行管理和控制的功能。wpa_supplicant 是一個開源項目,已經被移植到 Linux,Windows 以及很多嵌入式系統上。它是 WPA 的應用層認證用戶端,負責完成認證相關的登入、加密等工作。

wpa_supplicant 工具包含 wpa_supplicant 和 wpa_cli 這 2 個程式,其中wpa_supplicant 程式作為服務端在背景運作,服務 wpa_cli 用戶端的請求,進而實作 WiFi 的配置連接配接。下面是通過 shell 指令去進行 WIFI 的配置及連接配接。

1.打開 wlan0 接口:

root@fsmp1c:~# ifconfig wlan0 up

2.啟動 wpa_supplicant 程序并在背景運作

root@fsmp1c:~# wpa_supplicant -D nl80211 -i wlan0 -c /etc/wpa_supplicant.conf-B

3.掃描周邊 WiFi 熱點:

wpa_cli -i wlan0 scan

4.檢視掃描結果:

root@fsmp1c:~# wpa_cli -i wlan0 scan_results

5.添加一個網絡連接配接

root@fsmp1c:~# wpa_cli -i wlan0 add_network

6.配置 WiFi 熱點的名稱 ssid:

root@fsmp1c:~# wpa_cli -i wlan0 set_network 1 ssid '"FARSIGHT"'

7.配置 WiFi 熱點的密碼 psk:

root@fsmp1c:~# wpa_cli -i wlan0 set_network 1 psk '"fs123456"'

8.列舉所有儲存的連接配接

root@fsmp1c:~# wpa_cli -i wlan0 list_network

9.連接配接第 1 個儲存的連接配接

root@fsmp1c:~# wpa_cli -i wlan0 select_network 1

10.啟動 wpa_supplicant 應用

root@fsmp1c:~#wpa_supplicant -B -c wifi.conf -i wlan0

11.使用 udhcpc 指令動态擷取 IP

root@fsmp1c:~#udhcpc -i wlan0

智能門禁子產品

該子產品是根據關電開關智能識别有沒有人來,當有人觸發光電開關,會自動彈出登陸界面,輸入使用者名和密碼,程式會自動比對資料庫,如果使用者名密碼錯誤超過三次,會自動報警;如果輸入正确,則開門。

資料采集子產品

另開一個線程,實時去讀取溫濕度驅動裝置檔案的資料,進行計算得出溫濕度的數值同樣的方式得到光照的數值,通過信号傳參的方式傳給主線程,将資料設定到 ui 界面上。

智能檢測子產品

開啟智能檢測後,程式會根據你設定的門檻值進行檢測,假如溫度超過你設定的溫度門檻值,會自動開啟風扇;或者當光照低于你設定的門檻值,會開燈提高照明的亮度。

裝置控制子產品

通過 ui 界面的按鍵去開關燈或者風扇,以及蜂鳴器。

天氣預報子產品

連接配接 WIFI 之後,通過 get 方法從網上擷取資訊,得到 Json 類型的資料,對這個資料進行解析,将解析到的資料設定到 ui 界面上面。

百度雲互動子產品

在 ui 界面輸入在百度雲建立裝置時的 IoTCoreld、DeviceKey、DeviceSecret(三元組)通過組合生成 addr 和使用者名,使用 MD5 加密算法計算得到密碼,用于連接配接,連接配接成功後開啟定時器,自動向指定好的 topic 釋出采集到的溫度濕度和光照,并且訂閱雲端控制裝置的 topic,使用者向雲端釋出json 資料,開發闆接收到雲端轉發的 json 資料會做出響應。如{“led1”,1},開發闆收到資料後 led1 會亮

源碼分析

WIFI 連接配接子產品

在 Qt 程式裡使用 system 函數來執行 5.2 的指令來實作 WIFI 的配置

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

建立重新整理按鈕信号槽連接配接,實作點選重新整理按鈕界面顯示附近 wifi。實作原理是啟動 wpa_supplicant 程序并在背景運作,掃描周邊 WiFi 熱點;

使用wpa_cli -i wlan0 scan_results 指令檢視掃描結果,并将掃描結果重定向到wifilist 檔案中,對 wifilist 檔案進行讀操作,将讀到的資料直接顯示到 ui界面上面。核心代碼如下

……

system("wpa_supplicant -D nl80211 -i wlan0 -c /etc/wpa_supplicant.conf -

B");

system("wpa_cli -i wlan0 scan");

system("wpa_cli -i wlan0 scan_results > ./wifilist");

usleep(50000);

QString fileName = "./wifilist";

QFile file(fileName);

int f = 0;

j = 0;

if(!file.open(QIODevice::ReadOnly | QIODevice::Text))

{

QMessageBox::warning(this,"Warnning","can't

open",QMessageBox::Yes);

}

QTextStream in(&file);

QString str;

while (!((str = in.readLine()).isEmpty ()))

……

選中重新整理後顯示的 wifi 名後,點選連接配接會進入二級界面。二級界面輸入密碼正确後點選連接配接,等待幾 s 後即可完成連接配接。這裡輸入框使用自定義類MylineEdit,繼承自 QlineEdit,增加了滑鼠點選事件,當觸摸屏點選輸入框的時候,會彈出軟鍵盤,用來輸入密碼

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

這裡連接配接 wifi 的核心代碼如下:

……

sprintf(set_ssid,"wpa_cli -i wlan0 set_network %d ssid

'\"%s\"'",i,wifiName.toLatin1().data());

sprintf(set_password, "wpa_cli -i wlan0 set_network %d psk '\"%s\"' >

TorF.ini",i,password_edit->text().toLatin1().data());

sprintf(select_wlan, "wpa_cli -i wlan0 select_network %d ",i);

system(set_ssid);

system(set_password);

system("wpa_cli -i wlan0 list_network");

system(select_wlan);

system("wpa_supplicant -B -c wifi.conf -i wlan0");

qDebug()<< get_TorF().data()->toUpper();

if(get_TorF().data()->toUpper()=="F")

{

QMessageBox::warning(this,tr("Connect information"), tr("密碼

錯誤"));

return ;

}

system("udhcpc -i wlan0 -B");

char echo_1[64];

char echo_2[64];

sprintf(echo_1,"echo \"nameserver 114.114.114.114\" >

/etc/resolv.conf");

system(echo_1);

sprintf(echo_2,"echo \"nameserver 8.8.8.8\" > /etc/resolv.conf");

system(echo_2);

close();

QMessageBox::information(this,tr("Connect information"), tr("連接配接成

功"));

}

……

智能門禁子產品

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

PanGu 開發闆有多個 GPIO 組,檢視 GPIO 組資訊,可以使用 gpiodetect 指令。

# gpiodetect

gpiochip0 [GPIOA] (16 lines)

gpiochip1 [GPIOB] (16 lines)

gpiochip10 [GPIOK] (16 lines)

gpiochip11 [GPIOZ] (16 lines)

gpiochip2 [GPIOC] (16 lines)

gpiochip3 [GPIOD] (16 lines)

gpiochip4 [GPIOE] (16 lines)

gpiochip5 [GPIOF] (16 lines)

gpiochip6 [GPIOG] (16 lines)

gpiochip7 [GPIOH] (16 lines)

gpiochip8 [GPIOI] (16 lines)

gpiochip9 [GPIOJ] (16 lines)

通過查擴充闆的原理圖可以看到 :光電開關的 GPIO 管腳是 PE15

讀取 PE15 的狀态

# gpioget gpiochip4 15

當有東西遮擋光電開關時,執行以上指令得到的是 0;沒有東西遮擋。得

到的是 1.

是以我們開一個線程讓它一直去讀取光 PE15 的狀态,當讀到為 0 時,說明

有人,發送信号給主線程,使主線程開啟登陸界面。

void ReadPE15Thread::run()

{

system("touch pe15.txt");

system("gpioget gpiochip4 15 > pe15.txt");

int fd;

char buf[32];

while (1) {

fd = open("./pe15.txt",O_RDONLY);

system("gpioget gpiochip4 15 > pe15.txt");

read(fd,buf,sizeof(buf));

if(strcmp(buf,"1\n")==0)

{

qDebug()<<tr("login !!");

emit pesig();

}

sleep(1);

close(fd);

}

}

建立信号槽連接配接

void MainWindow::loginSlot()

{

disconnect(&pe15thread,SIGNAL(pesig()),this,SLOT(loginSlot()));

login->show();

connect(login,SIGNAL(loginsuccess()),this,SLOT(loginsuccessSlot()));

connect(login,SIGNAL(loginfailed()),this,SLOT(loginfailedSlot()));

connect(login,SIGNAL(loginclose()),this,SLOT(logincloseSlot()));

}

登陸界面的編寫

編譯 UI 界面。

建立 usr.db 資料庫檔案

# sqlit3 usr.db

建立表

sqlite> CREATE TABLE usr(

usrname TEXT PRIMARY KEY ,

password NOT NULL

);

向資料庫中添加使用者名和密碼

sqlite> INSERT INTO usr values(“usr”,“123”);

login.cpp

打開資料庫 驗證密碼

bool Login::openDb()

{

QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");

db.setDatabaseName("usr.db");

if(!db.open())

{

QMessageBox::warning(0, tr("Warning"), db.lastError().text());

return false;

}

QSqlQuery query(db);

if(!query.exec("select usrname,password from usr"))

{

db.close();

return false;

}

while(query.next())

{

QString UserName = query.value(0).toString();

QString Password = query.value(1).toString();

// qDebug()<< UserName;

// qDebug()<< Password;

if(UserName == usr_edit->text() &&Password ==password_edit->text())

return true;

}

db.close();

return false;

}

登陸成功發送成功信号,登陸失敗發送失敗信号

void MainWindow::loginSlot()

{

disconnect(&pe15thread,SIGNAL(pesig()),this,SLOT(loginSlot()));

login->show();

connect(login,SIGNAL(loginsuccess()),this,SLOT(loginsuccessSlot()));

connect(login,SIGNAL(loginfailed()),this,SLOT(loginfailedSlot()));

connect(login,SIGNAL(loginclose()),this,SLOT(logincloseSlot()));

}

void MainWindow::loginsuccessSlot()

{

QMessageBox::information(this, tr("information"),"密碼正确,門鎖已打開");

// 重置錯誤次數

Numberoferrors =3;

login->close();

beepunring();

connect(&pe15thread,SIGNAL(pesig()),this,SLOT(loginSlot()));

}

void MainWindow::loginfailedSlot()

{

QString info;

Numberoferrors--;

switch(Numberoferrors)

{

case 2:

info ="密碼錯誤,還有 3 次機會";

break;

case 1:

info ="密碼錯誤,還有 2 次機會";

break;

case 0:

info ="密碼錯誤,還有 1 次機會";

break;

default:

info ="即将報警";

break;

}

QMessageBox::warning(this, tr("warning"),info);

if(Numberoferrors <0)

{

qDebug() << Numberoferrors;

beepring();

}

}

驗證密碼錯誤三次後蜂鳴器報警

資料采集和智能檢測子產品

系統啟動後可以檢視目錄/sys/bus/iio/devices/

root@fsmp1c:~# ls /sys/bus/iio/devices/ iio:device0

如果系統中有多個 iio 裝置,這裡可能會有很多個 iio 目錄,确定哪個

目錄是我們的裝置對應目錄,可以通過檢視

/sys/bus/iio/devices/iio\:device0/name 資訊确認:

root@fsmp1c:~# cat /sys/bus/iio/devices/iio\:device0/name

0-0040

由顯示資訊每個驅動對應裝置可能有所不同,目前顯示内容為裝置的實體

位址,與裝置樹中位址一緻,可以确認 iio:device0 是目前裝置對應目錄檢視

當目錄下内容:

root@fsmp1c:~# ls -l /sys/bus/iio/devices/iio\:device0/

total 0

-r--r--r-- 1 root root 4096 Feb 7 15:51 dev

-rw-r--r-- 1 root root 4096 Feb 7 15:51 in_humidityrelative_offset

-rw-r--r-- 1 root root 4096 Feb 7 15:51 in_humidityrelative_raw

-rw-r--r-- 1 root root 4096 Feb 7 15:51 in_humidityrelative_scale

-rw-r--r-- 1 root root 4096 Feb 7 15:51 in_temp_offset

-rw-r--r-- 1 root root 4096 Feb 7 15:51 in_temp_raw

-rw-r--r-- 1 root root 4096 Feb 7 15:51 in_temp_scale

-r--r--r-- 1 root root 4096 Feb 7 15:51 name

drwxr-xr-x 2 root root 0 Feb 7 15:51 power

lrwxrwxrwx 1 root root 0 Feb 7 15:50 subsystem -> ../../../../../../../bus/iio

-rw-r--r-- 1 root root 4096 Feb 7 15:50 uevent

檔案說明:

檔案 in_ temp_scale 為溫度标尺,計算公式如下,公式來自與驅動對應代碼:

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

=

175.72 × 1000 × 4

65535 = 10.725097656

in_ temp_offset 為資料偏移,計算公式如下,公式來自于驅動對應代碼

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

=

−46.85 × 65536

4 × 175.72 = −4368

in_ temp_raw 為原始資料,計算公式如下,公式來自于驅動對應代碼:

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

=

4

閱讀 SI7006 晶片手冊可以看到溫度的計算公式為:

(℃) =

175.72 ×

65535 − 46.85

上述公式與驅動傳回值看不出直接對應關系,是以我們按照驅動提供的 scale、

offset 及 Raw 的計算公式對公式進行處理,得到最終公式計算過程如下:

(℃) × 65536 = 175.72 × − 46.85 × 65536

(℃) × 65536 = 175.72 × ( +

−46.85 × 65536

175.72 )

(℃) =

175.72

65536 × ( +

−46.85 × 65536

175.72 )

(℃) =

175.72 × 1000

65536 × ( +

−46.85 × 65536

175.72 ) ÷ 1000

(℃) =

175.72 × 1000 × 4

65536 × (

4

−46.85 × 65536

175.72 × 4

) ÷ 1000

最終确定溫度的計算公式為:

(℃) = ( + ) × ÷ 1000

同理濕度的計算公式為:

(% ) = ( ℎ + ℎ ) × ℎ ÷ 1000

主要代碼如下:

開一個線程去讀取裝置檔案的資訊并計算,得到溫度濕度光照,通過信号傳參的方式

傳給主線程。

子線程:

……

void CollentdataThread::run()

{

int temp_raw = 0;

int temp_offset = 0;

float temp_scale = 0;

int hum_raw = 0;

int hum_offset = 0;

float hum_scale = 0;

float tem_float =0;

float hum_float =0;

float ill_float =0;

QString hum;

QString tem;

QString ill;

const char *device1 ="iio:device0";//溫濕度

const char *device2 ="iio:device1";//光照

while (1)

{

/*read temp data*/

read_sysfs_int(device1, "in_temp_raw", &temp_raw);

read_sysfs_int(device1, "in_temp_offset", &temp_offset);

read_sysfs_float(device1, "in_temp_scale", &temp_scale);

tem_float =(temp_raw + temp_offset) * temp_scale / 1000;

tem =QString::number(tem_float,'f', 2);

read_sysfs_int(device1, "in_humidityrelative_raw", &hum_raw);

read_sysfs_int(device1, "in_humidityrelative_offset", &hum_offset);

read_sysfs_float(device1, "in_humidityrelative_scale", &hum_scale);

hum_float = (hum_raw + hum_offset) * hum_scale / 1000;

hum =QString::number(hum_float,'f', 2);

read_sysfs_float(device2, "in_illuminance_input", &ill_float);

ill =QString::number(ill_float,'f', 2);

emit send(tem,hum,ill);

sleep(2);

}

}

int CollentdataThread::read_sysfs_float(const char *device, const char *filename, float

*val)

{

int ret = 0;

FILE *sysfsfp;

char temp[128];

memset(temp, '0', 128);

ret = sprintf(temp, "/sys/bus/iio/devices/%s/%s", device, filename);

if (ret < 0)

goto error;

sysfsfp = fopen(temp, "r");

if (!sysfsfp)

{

ret = -errno;

goto error;

}

errno = 0;

if (fscanf(sysfsfp, "%f\n", val) != 1)

{

ret = errno ? -errno : -ENODATA;

if (fclose(sysfsfp))

perror("read_sysfs_float(): Failed to close dir");

goto error;

}

if (fclose(sysfsfp))

ret = -errno;

error:

return ret;

}

int CollentdataThread::read_sysfs_int(const char *device, const char *filename, int *val)

{

int ret = 0;

FILE *sysfsfp;

char temp[128];

memset(temp, '0', 128);

ret = sprintf(temp, "/sys/bus/iio/devices/%s/%s", device, filename);

if (ret < 0)

goto error;

sysfsfp = fopen(temp, "r");

if (!sysfsfp)

{

ret = -errno;

goto error;

}

errno = 0;

if (fscanf(sysfsfp, "%d\n", val) != 1)

{

ret = errno ? -errno : -ENODATA;

if (fclose(sysfsfp))

perror("read_sysfs_float(): Failed to close dir");

goto error;

}

if (fclose(sysfsfp))

ret = -errno;

error:

return ret;

}

主線程:

信号槽連接配接

connect(&thread_collentdata,SIGNAL(send(QString,QString,QString)),this,SLOT(set_

humAdte

mAdill(QString,QString,QString)));

槽函數:

void MainWindow::set_humAdtemAdill(QString tem,QString hum,QString ill)

{

// 将線程采集的資料指派給成員變量

this->tem =tem;

this->hum =hum;

this->ill =ill;

/************** 異常處理 ********/

if(abnormalSwitch == true)

{

if(this->tem.toFloat() >tem_max.toFloat())

{

system("echo 255 > /sys/class/hwmon/hwmon1/pwm1");

}

else

system("echo 0 > /sys/class/hwmon/hwmon1/pwm1");

if(this->ill.toFloat() < ill_lv1.toFloat()&&this->ill.toFloat() > ill_lv2.toFloat())

{

system("echo 1 > /sys/class/leds/led1/brightness");

}

else if(this->ill.toFloat() < ill_lv2.toFloat()&&this->ill.toFloat() > ill_lv3.toFloat())

{

system("echo 1 > /sys/class/leds/led1/brightness");

system("echo 1 > /sys/class/leds/led2/brightness");

}

else if(this->ill.toFloat() < ill_lv3.toFloat())

{

system("echo 1 > /sys/class/leds/led1/brightness");

system("echo 1 > /sys/class/leds/led2/brightness");

system("echo 1 > /sys/class/leds/led3/brightness");

}

else

{

system("echo 0 > /sys/class/leds/led1/brightness");

system("echo 0 > /sys/class/leds/led2/brightness");

system("echo 0 > /sys/class/leds/led3/brightness");

}

}

else

{

/*************************************************************************/

tem.append("℃");

hum.append("%");

ill.append("Candela");

ui->illTextBrowser->setText(ill);

ui->humTextBrowser_2->setText(hum);

ui->temTextBrowser_2->setText(tem);

}

}

//開啟/關閉智能檢測

void MainWindow::abn_pushbutton_ONSlot()

{

abnormalSwitch = true;

QMessageBox::information(this, tr("information"),"開啟智能檢測成功");

}

void MainWindow::abn_pushbutton_OFFSlot()

{

abnormalSwitch =false;

QMessageBox::information(this, tr("information"),"關閉智能檢測成功");

}

其中 abnormalSwitch 這個變量是全局變量,通過兩個按鈕來改變這個變量的值,如果關

閉按鈕點選這個值變成 false

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

If 語句就不會執行。直接将資訊顯示到 ui 界面

裝置控制子產品

這個子產品很簡單,直接上指令。

Led1 亮/滅

Led2 亮/滅

Led3 亮/滅

root@fsmp1c:~# echo 1 > /sys/class/leds/user1/brightness

root@fsmp1c:~# echo 0 > /sys/class/leds/ user1/brightness

root@fsmp1c:~# echo 1> /sys/class/leds/ user2/brightness

root@fsmp1c:~# echo 0 > /sys/class/leds/ user2/brightness

root@fsmp1c:~# echo 1 > /sys/class/leds/ user3/brightness

root@fsmp1c:~# echo 0 > /sys/class/leds/ user3/brightness

蜂鳴器響

void MainWindow::beepring()

{

int fd;

struct input_event event;

struct timeval time;

fd = open("/dev/input/by-path/platform-beeper-event", O_RDWR);

event.type = EV_SND;

event.code = SND_TONE;

event.value = 1000;

time.tv_sec = 1;

time.tv_usec = 0;

event.time = time;

write(fd, &event, sizeof(struct input_event));

}

讓蜂鳴器關的話把 event.value 的值置成 0,将結構體對象再寫到

/dev/input/by-path/platform-beeper-event 檔案裡

天氣預報子產品

使用 QNetworkAccessManager 類定義一個請求句柄;使用 QNetworkRequest 類

定義一個操作請求。

QNetworkAccessManager *manager;

QNetworkRequest quest;

QNetworkAccessManager 是用來協調網絡操作的,即用來操作 QNetworkRequest

請求的 。QNetworkRequest 是 Network Access API 的一部分,是在網絡上保

存着發送一個請求的必要資訊.它包含一個 URL 和一些輔助資訊,可以被用來去

修改請求。打個比方: 你想要帶一些水給朋友,這個水就好比

QNetWorkRequest 你的請求,而裝水的容器就是 QNetworkAccessManager.。有

了容器你才可以帶走水,同理你想要發送請求就需要

QNetworkAccessManager。

這裡,在 ui 界面的 comboBox 選擇要檢視的城市,填充到 QnetWorkRequest

裡,使用 get 方法發送請求。

/*********************** 天氣子產品 *********************/

//點選查詢請求天氣資料

void MainWindow::weather_cilcked_Slot()

{

QString local_city = ui->comboBox->currentText().trimmed(); //獲得需要查詢

天氣的城市名稱

sendQuest(local_city);

}

//get 方法擷取資訊

void MainWindow::sendQuest(QString cityStr)

{

char quest_array[256] = "http://wthrcdn.etouch.cn/weather_mini?city=";

QNetworkRequest quest;

sprintf(quest_array, "%s%s", quest_array, cityStr.toUtf8().data());

quest.setUrl(QUrl(quest_array));

quest.setHeader(QNetworkRequest::UserAgentHeader, "RT-Thread ART");

manager->get(quest); /*發送 get 網絡請求*/

}

建立信号槽連接配接

connect(manager, SIGNAL(finished(QNetworkReply*)), this,

SLOT(replyFinished(QNetworkReply*)));

當發送請求後,會收到網絡的回複信号,隻需要使用 QJsonDocument 解析 Json

類型的資料就好了。具體代碼如下。

//天氣資料處理槽函數

void MainWindow::replyFinished(QNetworkReply *reply)

{

QString all = reply->readAll();

QJsonParseError err;

//解析 json 對象

QJsonDocument json_recv = QJsonDocument::fromJson(all.toUtf8(), &err);

qDebug() << "recv weather data! error:"<< err.error;

if (!json_recv.isNull())

{

QJsonObject object = json_recv.object();

if (object.contains("data"))

{

QJsonValue value = object.value("data"); // 擷取指定 key 對應的 value

if (value.isObject())

{

QJsonObject object_data = value.toObject();

if (object_data.contains("forecast"))

{

QJsonValue value = object_data.value("forecast");

if (value.isArray())

{

QJsonObject today_weather = value.toArray().at(0).toObject();

QString weather_type = today_weather.value("type").toString();

QString tuijian = object.value("data").toObject().value("ganmao").toString();

QString low = today_weather.value("low").toString();

QString high = today_weather.value("high").toString();

QString wendu = low.mid(low.length() - 4, 4) + "~" + high.mid(high.length()

- 4, 4);

QString strength = today_weather.value("fengli").toString();

strength.remove(0, 8);

strength.remove(strength.length() - 2, 2);

QString fengli = today_weather.value("fengxiang").toString() + strength;

ui->label_weather_2->setText(weather_type); //顯示天氣類型

ui->label_temperature_2->setText(wendu);

ui->label_wind_2->setText(fengli);

ui->label_recommend_2->setText(tuijian);

}

}

}

}

}

else

ui->label_recommend_2->setText( "json_recv is NULL or is not a object !");

reply->deleteLater(); //銷毀請求對象

}

百度雲互動子產品

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

通過 ui 界面輸入建立雲端裝置時生成的 ioTcoreid、DeviceKety、DeviceSecret。使用代碼進行組合生成 brokerAddr、以及使用者名密碼,再使用

MD5 加密算法對密碼進行加密得到連接配接需要的資訊。點選計算按鈕就将計算的結果複制給成員變量,便于連接配接時使用。

void MainWindow::pushButton_calculateSlot()

{

username.clear();

password.clear();

password_md5.clear();

brokerAddr.clear();

ioTCoreld = lineEdit_coreid->text();

deviceKey = lineEdit_devkey->text();

deviceSecret = lineEdit_devsecret->text();

brokerPort = "1883";

if(ui->comboBox_city->currentText() =="廣州")

brokerAddr =

brokerAddr.append(ioTCoreld).append(".iot.").append("gz").append(".http://baidubce.com"

);

else

brokerAddr =

brokerAddr.append(ioTCoreld).append(".iot.").append("bj").append(".http://baidubce.com");

clientId = "zhjt123";

username =

username.append("thingidp").append("@").append(ioTCoreld).append("|").append(de

viceKey)\

.append("|").append(currentTimestamp).append("|").append("MD5");

password =

password.append(deviceKey).append("&").append(currentTimestamp).append("&").a

ppend("MD5")\

.append(deviceSecret);

QByteArray password_md5result;

QCryptographicHash md(QCryptographicHash::Md5);

md.addData(password.toUtf8());

password_md5result = md.result();

password_md5.append(password_md5result.toHex());

}

在 pro 檔案裡加入

QT += mqtt

然後在 mainwindow.h 加入頭檔案

#include <QtMqtt/qmqttclient.h>

使用 QmqttClient 定義 m_client 對象用于連接配接。

QMqttClient *m_client;//mqtt client 指針

将上一步計算好的資訊設定到 m_client 對象中,使用

m_client->connectToHost();

進行連接配接。

void MainWindow::pushButton_connectmqttSlot()

{

//未連接配接伺服器則連接配接

if (m_client->state() == QMqttClient::Disconnected) {

ui->pushButton_connectmqtt->setText(tr("斷開"));

m_client->setHostname(brokerAddr);

m_client->setPort(brokerPort.toInt());

m_client->setUsername(username);

m_client->setPassword(password_md5);

m_client->connectToHost();

// 定時器初始化

InitTimer();

connect(m_client,SIGNAL(connected()),this,SLOT(mqttconnectSlot()));

connect(m_client, SIGNAL(messageReceived(const QByteArray, const

QMqttTopicName)),

this, SLOT(messageReceived(const QByteArray, const

QMqttTopicName)));

} else {//斷開連接配接

ui->pushButton_connectmqtt->setText(tr("連接配接"));

m_client->disconnectFromHost();

}

}

釋出資訊

使用定時器設定定時時間,這裡設定的是 5s,每隔 5s 會向雲端指定的

topic 釋出溫濕度的資料。

void MainWindow::InitTimer()

{

m_timer = new QTimer;

//設定定時器是否為單次觸發。預設為 false 多次觸發

m_timer->setSingleShot(false);

//啟動或重新開機定時器, 并設定定時器時間:毫秒

m_timer->start(5000);

//定時器觸發信号槽

connect(m_timer,SIGNAL(timeout()),this,SLOT(TimertimeOut()));

}

void MainWindow::TimertimeOut()

{

mqttTopic ="$iot/zhjt/user/fortest";

mqttMessage.clear();

mqttMessage.append("{\"temperature").append("\"").append(":").append(tem)\

.append(",").append("\"humidity").append("\"").append(":").append(hu

m)\

.append(",").append("\"illumination").append("\"").append(":").append

(ill)\

.append("}");

//執行定時器觸發時需要處理的業務

// 釋出

if(m_client->publish(mqttTopic,mqttMessage.toUtf8()) == -1)

{

QMessageBox::critical(this,"Error","連接配接斷開或輸入的 topic 有誤,無法

釋出",QMessageBox::Yes);

m_timer->stop(); //停止定時器

}

}

訂閱資訊

訂閱 Topic 為 $iot/zhjt/user/control

當有用戶端發送 Topic 為 $iot/zhjt/user/control 的資料時,會接收到

資料并進行處理。

void MainWindow::mqttconnectSlot()

{

QString subScribeTopic ="$iot/zhjt/user/control";

m_client->subscribe(subScribeTopic);

}

void MainWindow::messageReceived(const QByteArray &message, const

QMqttTopicName &topic)

{

qDebug()<<"messageReceived:"<<topic.name()<<QString(message);

QJsonObject json_object = QJsonDocument::fromJson(message).object();

if(json_object.value("led1").toInt() ==1)

system("echo 1 > /sys/class/leds/led1/brightness");

else

system("echo 0 > /sys/class/leds/led1/brightness");

if(json_object.value("led2").toInt() ==1)

system("echo 1 > /sys/class/leds/led2/brightness");

else

system("echo 0 > /sys/class/leds/led2/brightness");

if(json_object.value("led3").toInt() ==1)

system("echo 1 > /sys/class/leds/led3/brightness");

else

system("echo 0 > /sys/class/leds/led3/brightness");

if(json_object.value("fan").toInt() ==0)

system("echo 0 > /sys/class/hwmon/hwmon1/pwm1");

else

{

char fan_on[64];

sprintf(fan_on,"echo %d >

/sys/class/hwmon/hwmon1/pwm1",json_object.value("fan").toInt());

system(fan_on);

}

if(json_object.value("beep").toInt() ==1)

beepring();

else

beepunring();

}

微信小程式設計

建立新項目

打開微信開發者工具,建立項目,填寫自己的 AppID,建立。

注意:建立工程的路徑一定不能有中文。

準備圖檔

下載下傳幾個圖示,可以去阿裡巴巴矢量圖示庫進行下載下傳。下載下傳好的圖檔放在

pages 檔案夾下得 image 檔案夾中。(沒有可以自己建立一個檔案夾)

https://www.iconfont.cn/home/index?spm=a313x.7781069.1998910419.2

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

修改微信小程式代碼

修改 app.json 檔案

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

下載下傳支援 MQTT 協定和 sha1 加密的 js 庫

下載下傳 mqtt.js https://github.com/mqttjs/MQTT.js

下載下傳 hex_hmac_sha1.js https://github.com/xihu-fm/aliyun-iotclient-sdk/tree/master/lib

将這兩個檔案存放到 utils 目錄下

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

編寫 index.wxml,這個檔案是用來編寫頁面的布局。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

編寫 index.wxss,這個檔案是用來配置頁面的屬性。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

編寫 index.js,修改裝置資訊三元組。這個檔案用來主要邏輯的編寫

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

修改 socket 合法域名

所有的程式編寫完成之後,進入調試視窗,就會看到下面這種情況:

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

說明沒有在微信小程式的開發管理中添加這個域名所導緻的。

打開小程式開發網頁:

https://mp.weixin.qq.com/wxamp/devprofile/get_profile?token=584612979&lang=zh_CN

打開開發管理->開發設定->伺服器域名,修改 socket 合法域名,添加這個域名即可。

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

實驗源碼

源碼路徑【4_智慧家庭\實驗源碼\4_zhjt】

【4_智慧家庭\實驗源碼\WeChat_zhjt_pro】

注意事項

1.在開發闆運作時,需要導入中文字庫,否則會因為識别不了中文。

将【4_智慧家庭\工具軟體\wqy-zenhei-0.9.47-nightlybuild.tar.gz 或 wqyzenhei-0.8.38-1.tar.gz】複制到 ubuntu 下。并使用 scp 指令将檔案拷貝到開發闆

的 usr/share/fonts 目錄下,使用 tar 指令解壓後即可。

linux@ubuntu:~$ scp wqy-zenhei-0.8.38-1.tar.gz

[email protected]:/usr/share/fonts/

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

2.如果使用 mipi 五寸屏運作此項目,需要進行螢幕旋轉以适應螢幕,具體步驟如下:

在/etc/profile.d/qt-eglfs.sh 添加環境變量如下:

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭
STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

下面變量的 event0 裝置需要填實際的觸摸屏裝置

STM32MP157開發闆Linux+Qt項目實戰:智慧家庭

這裡即填 event0

export QT_QPA_EGLFS_ROTATION=90

export QT_QPA_EGLFS_NO_LIBINPUT=1

export

QT_QPA_EVDEV_TOUCHSCREEN_PARAMETERS=/dev/input/event0:rotate=90

時間顯示的時候 ARM 系統的時間要和目前時間進行同步需要使用 ntp 服務。

ntpd

ntpd 是一個時間服務。采用柔性時間調整政策,讓時間的變化和調整盡量減少對業務的影響。

ntpd 不盲目相信遠端時鐘,伺服器時間和遠端時鐘超過恐慌門檻值(預設 1000 秒),ntpd 甚至會停止時間同步。

ntpd 自己會思考。它相信本地時間可能不對,但是不會忽快忽慢甚至停滞。ntpd 通過多次收發包選擇權威穩定的時間源,算出雙方間的網絡延遲,

然後才會采信新的遠端時鐘進行時間同步。

ntpd 在和時間伺服器的同步過程中,會把 BIOS 計時器的振蕩頻率偏差——或者說 Local Clock 的自然漂移 (drift) ——記錄下來。這樣即使網絡有問題,本機仍然能維持一個相當精确的走時。

在 ubuntu 主機下載下傳安裝 ntp 服務

linux@ubuntu:~$ sudo apt-get install ntp

linux@ubuntu:~$ vi /etc/ntp.conf

将裡面的文本複制下來,修改 ntpd 配置檔案

root@fsmp1c:~# vi /etc/ntp.conf

将剛才複制的粘貼到這個檔案下,重新開機 ntpd 服務

root@fsmp1c:~# systemctl restart ntpd.service

為開發闆增加時區,在開發闆建立檔案夾

root@fsmp1c:~# mkdir /usr/share/zoneinfo

root@fsmp1c:~# mkdir /usr/share/zoneinfo/Asia

進入 ubuntu 時區目錄

linux@ubuntu:~$ cd /usr/share/zoneinfo/Asia/

拷貝目前目錄下的 shanghai 檔案到開發闆的/usr/share/zoneinfo/Asia/

linux@ubuntu:~$ scp Shanghai [email protected]:/usr/share/zoneinfo/Asia/

root@fsmp1c:~#ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

繼續閱讀