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/,持續更新中。
可實戰項目:14個Linux+Qt綜合項目案例,8個MP1A物聯網拓展項目
關注公衆号“華清遠見V智能有料”,回複“mp157項目”,索取項目配套文檔及源碼。
1、Linux+Qt綜合項目案例:華清遠見stm32mp157開發闆優勢特色部分,包括音樂播放器、智慧家庭、智能工業電表、智能出行助手、智能貓眼、環境監測、智能安防、智能語音識别等10餘個項目案例,涉及家居、醫療、農業多種應用方向,在案例中使用了多種物聯網和嵌入式技術,包括OT開發、linux應用開發、linux驅動開發、物聯網雲端接入、MQTT協定、json字元串等知識點。
基于Linux+Qt的智慧家庭項目
項目簡介:
智慧家庭又可稱為智慧家庭服務平台,是智慧城市的最小單元,是以家庭為載體,以家庭成員之間的親情為紐帶。智慧家庭綜合利用物聯網、雲計算、移動網際網路和大資料等新一代資訊技術,結合自動控制技術,将家庭裝置智能控制、家庭環境感覺、家人健康感覺、家居安全感覺以及資訊交流、消費服務等家居生活有效地結合起來,創造出健康、安全、舒适、低碳、便捷、個性化和充滿關愛的家庭生活方式。
開發平台:
華清遠見stm32mp157開發闆豪華套餐(開發闆+仿真器+五寸屏+攝像頭+資源擴充闆+tf卡+讀卡器)
項目功能展示:
Wifi 子產品
點選重新整理按鈕,可以實時更新附近的 wifi,選擇要連接配接的 wifi,會彈出輸入密碼的頁面,輸入密碼,點選連接配接即可連接配接成功。
選擇要檢視天氣的城市,點選擷取按鈕
環境監測子產品
裝置控制子產品
選擇按鈕即可完成裝置的控制
開啟和關閉按鈕控制智能監測的開啟和關閉,送出按鈕上的輸入框輸入的是智能監測觸發的門檻值。
連接配接百度雲子產品
輸入百度雲連接配接三元組,點選擷取時間戳,點選計算、連接配接,即可實作向雲端發送環境進而轉發到微信小程式,并且通過微信小程式控制裝置。
在微信小程式顯示溫濕度。并且控制開發闆 led 燈
遮擋光電開關,會自動彈出門禁系統。
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 目錄。我們需要在終端中賦予安裝程式可執行的權限
我們可以使用圖形化的檔案管理器來檢視
輕按兩下“qt-creator-opensource-linux-x86_64-4.10.1.run”圖示運作安裝程式。
出現如下界面:
等待程式驗證完成後點選“Next”
這裡我們需要登入或者注冊一個賬号,如果我們之前已經注冊過直接登入就可以。如果沒有注冊過則需要新注冊有一個賬号後登入。這裡筆者已經注冊過賬号,是以直接登入。
登入成功後出現如下界面,點選 Next
這裡選擇安裝路徑
可以直接預設,Next
這路選擇安裝的元件,直接預設即可
這裡我們需要同意使用者協定
這個界面告訴我們安裝完成後需要占用的空間。點選”Install”按鈕後開始安裝。
安裝完成後出現如下界面
點選“Finish”按鈕後将彈出 Qt Creator 主界面
點選“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 ,一般按上面步驟執行後都會有
點選右側"Add"按鈕,彈出下拉清單後,選擇"GCC"的"C"
填寫資訊如下,"Name"為"Auto-GCC","Compiler path"點選旁邊的"Browse.."按鈕選擇編譯器的路徑,例子中的路徑是 “/usr/bin/gcc”
2)配置 G++
點選右側"Add"按鈕,彈出下拉清單後,選擇"GCC"的"C++",下面的文本框填寫"Name" 為"Auto-G++","Compiler path"點選旁邊的"Browse.."按鈕選擇編譯器的路徑,例子中的路徑是" /usr/bin/g++"。
填寫完成後,點選"Apply"。
3)配置 qmake
選擇"Qt Versions"标簽,如果有下面紅框中的文本,可以跳過下面步驟
如果沒有,在右側點選"Add..."
會彈出 qmake 路徑選擇對話框,這裡以"/home/linux/Qt5.14.1/5.14.1/gcc_64/bin/qmake"為例子。 選擇”qmake”檔案後,擊"Open"按鈕
"Version name"改為" Qt %{Qt:Version} GCC"。然後點選"Apply"按鈕。
4)配置 Kits
點選左側"Kits",右側選擇"Kits"标簽。檢查有沒有下圖紅框選中的文本,如果有可以跳過下面步驟
然後沒有,點選 Add:
在彈出的對話框中"Name"為"Desktop","Device Type"選擇"Desktop"選項, "Sysroot"選擇目标裝置的系統目錄,"Compiler"選擇之前配置的名稱
"Auto-GCC"和"Auto-G++","Qt version"選擇之前配 置的名稱"Qt 5.14.1
GCC",其它預設即可,最後點選"Apply"和"OK"按鈕
Qt Creator 建立工程
注意:工程路徑最好不要包含中文、特殊字元、空格等
我們可以建立一個“qt”檔案夾,該檔案夾用作我們以後存放源代碼。
打開 Qt Creator,在歡迎頁面點選 “New”按鈕,來建立一個工程。
在出現的建立項目視窗中,我們選則“Application”->“Qt Widgets
Application”,然後點選右下方“Choose…”按鈕,來建立一個桌面 Qt 應用。
我們在這裡設定項目介紹和源碼位置,我們這裡建立一個名為
“HelloWorld”的示例項目,設定完成之後點選 next
直接點選 next
随後進行細節設定,主要設定要建立的源碼檔案的基本類資訊,包括類名等。這裡我們可以根據自己的項目特點進行設定。需要說明的一點就是基類的選擇,這裡基類有 QMainWindow、QWidget、QDialog 三種,它們的不同之處如下:
⚫ QMainWindow 類提供一個帶有菜單條,工具條和一個狀态條的主應用程式視窗。主視窗通常提供一個大的中央視窗部件,以及周圍菜單,工具條,和一個狀态欄。QMainWindow 視窗經常被繼承,使得封裝中央部件,菜單,工具條,狀态欄等都變得很容易,當使用者點選它的時候,相應的槽就會被調用;
⚫ QWidget 類是所有使用者界面對象的基類,視窗部件是使用者界面的一個基本單元,它從視窗系統接收滑鼠,鍵盤和其他消息,并在螢幕上繪制自己。一個視窗部件可以被他的父視窗或者是其他視窗擋住一部分;
⚫ QDialog 類是對話框視窗的基類,對話框視窗主要用于短期任務和使用者進行短期通訊的頂級視窗,QDialog 可以是模态對話框或者是非模态對話框。QDialog 支援擴充并帶有傳回值,他們可以帶有預設值;我們在這裡選擇 QDialog 類即可,點選 next 完成類資訊設定
直接點選 next 按鈕即可。
然後進行工具選擇,該頁面可以選擇我們建立的工程可以使用的工具,選擇想要使用的編譯器子產品,例如下圖 。點選 next
最後我們設定彙總資訊,如果不需要版本控制等功能,直接點選完成finish 即可
随後我們就進入到了主界面,這時候 Qt 已經幫我們做好了一些準備工作,包括建立了一些檔案,寫好了一些前置代碼等等。
我們可以點選左邊 protect 欄,來檢視我們的編譯選項
我們可以在左下角選擇編譯 Debug 版或者 Release 版,即調試版或發行版。
左下角綠色剪頭是編譯并運作,錘子是僅編譯,我們可以直接點選綠色小箭頭将我們導入的工程編譯并運作起來。
點選運作按鈕後,我們可以看到 HelloWorld 視窗運作起來了。
導入工程
我們可以将已存在的 Qt 程式項目直接打開,這裡以上一章節的HelloWorld 程式為例。首先我們确定源碼存在的位置,如 HelloWorld 程式源碼在 /home/linux/qt/helloworld 路徑下。
點選歡迎頁面的“Open” 按鈕可以打開已有的工程。
找到我們剛才解壓好的源碼,選擇“helloworld.pro”檔案并點選打開
接下來我們就可以進入到代碼編輯界面了。
左上角是項目欄,點選項目名稱左邊的小箭頭可以展開項目目錄。
我們可以點選左邊項目欄,來檢視我們的編譯選項。需注意的是建構設定中的路徑應與工程路徑處于同級目錄下。
我們可以在左下角選擇編譯 Debug 版或者 Release 版,即調試版或發行版。
左下角綠色剪頭是編譯并運作,錘子是僅編譯,我們可以直接點選綠色小箭頭将我們導入的工程編譯并運作起來。
點選運作按鈕後,我們可以看到 HelloWorld 視窗運作起來了。
檔案說明
通過上面兩個章節,我們學習到了 Qt 程式的建立與導入的方法,也知道了Qt 會幫我們做一些基礎工作,比如幫我們建立了一些檔案,那麼這些檔案都是幹什麼用的呢?我們以HelloWorld 程式來說明一下。
以“.pro”為字尾名的檔案,為 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 對窗體進行可視化設計;
UI 設計器有以下一些功能區域:
元件面闆:視窗左側是界面設計元件面闆,分為多個組,如 Layouts、Buttons、Display Widgets 等,界面設計的常見元件都可以在元件面闆裡找到。
中間主要區域是待設計的窗體。如果要将某個元件放置到窗體上時,從元件面闆上拖放一個元件到窗體上即可。
Signals 和 Slots 編輯器與 Action 編輯器是位于待設計窗體下方的兩個編輯器。Signals 和 Slots 編輯器用于可視化地進行信号與槽的關聯,Action 編輯器用于可視化設計 Action。
布局和界面設計工具欄:視窗上方的一個工具欄,工具欄上的按鈕主要實作布局和界面設計。
對象浏覽器(Object Inspector):視窗右上方是 Object Inspector,用樹狀視圖顯示窗體上各元件之間的布局包含關系,視圖有兩列,顯示每個元件的對象名稱(ObjectName)和類名稱。
屬性編輯器(Property Editor):視窗右下方是屬性編輯器,是界面設計時最常用到的編輯器。屬性編輯器顯示某個選中的元件或窗體的各種屬性及其取值,可以在屬性編輯器裡修改這些屬性的值。屬性編輯器的内容分為兩列,左側為屬性的名稱,右側為屬性的值。屬性又分為多個組,實際上表示了類的繼承關系,位于下方的類屬性組繼承自位于上方的類屬性組;如果我們需要建立資源檔案、源碼檔案等,可以在項目檔案夾出點選滑鼠右鍵,選擇 Add New;如果我們有新的檔案需要添加,可以在項目檔案夾出點選滑鼠右鍵,選擇 Add Existing Files。
幫助文檔
Qt 的幫助文檔是伴随我們學習 Qt 開發的好夥伴。在 Qt 開發過程中,我們會面臨圖形接口使用的問題,它不像 C 語言那樣就那麼幾個函數接口,圖形接口的接口數量可以用海量來形容,常用的我們可能能記住,其它的就沒有必要去記了,用到什麼就去幫助文檔檢視用法是比較友善的。我們可以按 F1 按鍵,或通過上方導航欄的“help->contects”來進入幫助文檔。
上方的前進後退按鈕友善我們檢視文檔,如傳回到上一步,傳回到下一步。
我們可以通過幫助文檔來檢視以下幾個部分:
類使用的相關介紹;
檢視相關類的使用介紹,我們可以先進入到幫助文檔,然後在左上角選擇“Search”。筆者這裡以 QWidget 類為例,輸入我們想要查找的類的名字,然後輕按兩下查找結果來檢視說明
也可以先将滑鼠移動到想要查詢的類的位置,如圖所示,将滑鼠移動至“QWidget”處,然後按“F1”鍵,即可跳轉到相應的幫助文檔
我們可以通過再按一次“F1”鍵來全視窗檢視幫助文檔,按“Esc”鍵可以退出。
部分常用的成員元素包括以下幾項:
⚫ 公有成員函數:操作部件屬性的相關函數;
⚫ 公有槽函數:Qt 類中已經定義好的槽函數,直接可與信号相連接配接;
⚫ 信号:軟中斷,如按下按鈕觸發 pressed() 信号等;
⚫ 保護成員函數:通常事件所對應的虛函數放在此處;
⚫ 事件:常用事件,如操作滑鼠觸發的滑鼠事件;
滾動滑鼠滾輪,向下即可看到“Qwdget Class”類的相關說明了。
1) 檢視所用的部件的相應成員函數。
我們可以查找到該類所用部件的相應成員函數的使用方法、功能、參數、
傳回值等等,我們以“按鈕”控件,即“QPushButton Class”類為例,我們通
過索引搜尋的方式,來找到這個類
我們可以通過點選“Public Functions” 來檢視“QPushButton”這個類中的成員函數。
這裡以“QPushButton(const QString &text, QWidget *parent =Q_NULLPTR)”為例,我們點選函數名字可以進入到函數詳情中。我們可以看到相應的描述為:以“text”為顯示内容,以“parent”為父對象,構造一個push 按鈕。“text”“parent”為函數參數,由于是構造函數,是以此函數沒有傳回值。
還有一些函數是繼承自其它類的,例如“Public Functions”中有 21 個繼承自“QAbstractButton”類的函數,我們點選“QAbstractButton”即可檢視。
點選“QAbstractButton”即可檢視。
同樣我們可以點選相應的函數進入檢視詳情。如檢視“void setText(const QString &text)”。
2) 檢視所用的部件的信号。
我們這裡還是以“PushButton”為例,我們點選“Public Slots”。
可以看到“PushButton”本身有一個“void showMenu()”的信号,并且有很多繼承自其他類的信号。
一般來說我們用的“PushButton”的信号,最多的是用到其繼承自基類“
QAbstractButton”中的幾個信号,分别是點選(按下後擡起)、按壓(單按下)、釋放(單擡起)等。
我們可以點選相應信号檢視詳情
3) 檢視所用的部件的事件(所對應的虛函數如何編寫)。部件常用事件主要在 “QWidget”中聲明,選擇“Events”即可檢視相關說明。
每個事件都對應着事件函數。
點選事件函數可檢視詳情
微信小程式開發環境搭建
微信小程式開發工具簡介
微信小程式是小程式中的一種,英文名 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/
按照步驟依次注冊,輸入郵箱,密碼,驗證碼等,同意協定進行注冊。
然後登入自己的郵箱,查閱郵件,點選連結進行激活。進入步驟 3,資訊登記,按照網頁要求,依次輸入資訊,身份資訊,管理者微信資訊,即可激活成功。
傳回微信公衆平台,輸入剛剛注冊的賬戶密碼,會需要用管理者微信掃碼登入,登入後,下載下傳普通小程式開發者工具。
點選開發,選擇開發設定,擷取小程式 ID,以備後續開發需求。
微信小程式開發工具下載下傳完成後,進行預設安裝即可
建立新項目工程
打開微信小程式開發者工具,點選建立新工程,填寫自己的 APPID,選擇預設模闆,語言選擇 JavaScript,點選建立。
建立工程完成為如下界面:
基本環境配置
打開主界面的右上角的詳情按鈕,找到本地設定,将“增強編譯”和“不校驗合法域名”這兩個選項進行勾選,因為在小程式的開發階段,盡量把這兩勾選上。
編譯、調試
打開 app.json 檔案,可以更改微信小程式的标題,改為“工程 demo”。然後按下 Ctrl + S 快捷鍵進行儲存,即可完成編譯,調試輸出。檢視現象。
項目總體設計介紹
總體架構
智慧家庭系統的設計基于物聯網的思想,物聯網是新一代資訊技術的重要組成部分,其英文名稱是“The Internet of things”。其基本思想是以網際網路為媒介,實作遠端監督、控制。它在各個領域有着非常廣泛的應用。
總體架構如下:
該項目分為 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 的配置
建立重新整理按鈕信号槽連接配接,實作點選重新整理按鈕界面顯示附近 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,增加了滑鼠點選事件,當觸摸屏點選輸入框的時候,會彈出軟鍵盤,用來輸入密碼
這裡連接配接 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("連接配接成
功"));
}
……
智能門禁子產品
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 為溫度标尺,計算公式如下,公式來自與驅動對應代碼:
=
175.72 × 1000 × 4
65535 = 10.725097656
in_ temp_offset 為資料偏移,計算公式如下,公式來自于驅動對應代碼
=
−46.85 × 65536
4 × 175.72 = −4368
in_ temp_raw 為原始資料,計算公式如下,公式來自于驅動對應代碼:
=
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
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(); //銷毀請求對象
}
百度雲互動子產品
通過 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
修改微信小程式代碼
修改 app.json 檔案
下載下傳支援 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 目錄下
編寫 index.wxml,這個檔案是用來編寫頁面的布局。
編寫 index.wxss,這個檔案是用來配置頁面的屬性。
編寫 index.js,修改裝置資訊三元組。這個檔案用來主要邏輯的編寫
修改 socket 合法域名
所有的程式編寫完成之後,進入調試視窗,就會看到下面這種情況:
說明沒有在微信小程式的開發管理中添加這個域名所導緻的。
打開小程式開發網頁:
https://mp.weixin.qq.com/wxamp/devprofile/get_profile?token=584612979&lang=zh_CN
打開開發管理->開發設定->伺服器域名,修改 socket 合法域名,添加這個域名即可。
實驗源碼
源碼路徑【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/
2.如果使用 mipi 五寸屏運作此項目,需要進行螢幕旋轉以适應螢幕,具體步驟如下:
在/etc/profile.d/qt-eglfs.sh 添加環境變量如下:
下面變量的 event0 裝置需要填實際的觸摸屏裝置
這裡即填 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