最新案例動態,請查閲【案例共創】在開發者空間快速開發MQTT客户端實現硬件仿真上雲。小夥伴們快來領取華為開發者空間進行實操吧!
本案例由開發者:DS小龍哥提供
1 概述
1.1 背景介紹
隨着物聯網技術的不斷髮展,越來越多的設備和應用依賴於實時數據交換和遠程控制。在物聯網生態系統中,設備與雲平台之間的通信是核心環節之一,然而對於許多開發者來説,進行這種設備與雲平台之間的通信往往涉及到硬件的配置與調試,這對於一些不熟悉硬件的開發者,尤其是那些處於軟件開發領域的人員,可能是一大挑戰。傳統的物聯網開發往往需要開發者擁有一定的硬件基礎,或者至少具備與硬件設備進行調試和交互的能力,這使得一些開發者在沒有硬件設備的情況下,難以快速上手和測試物聯網應用,開發者迫切需要一種能夠模擬硬件設備並與雲平台進行交互的工具。
本案例通過開發一款基於MQTT協議的客户端調試助手,旨在為開發者提供一個簡單易用的工具,模擬硬件設備與雲平台的通信交互。這款工具通過軟件模擬了物聯網設備的行為,支持主題的訂閲與發佈,能夠與華為雲物聯網平台(IoTDA)進行實時通信。對於不熟悉硬件的開發者,或者暫時沒有硬件設備的開發者而言,這款調試助手可以讓他們在沒有物理硬件的前提下,體驗完整的物聯網設備上雲過程。開發者可以通過該工具快速瞭解設備如何連接雲平台,如何進行數據傳輸,並學習MQTT協議的基本操作。
1.2 適用對象
- 企業
- 個人開發者
- 高校學生
1.3 案例時間
本案例總時長預計60分鐘。
1.4 案例流程
説明:
- 登錄開發者空間,配置開發環境;
- 編輯MQTT客户端源碼;
- CodeArts IDE運行MQTT客户端源碼文件;
- MQTT客户端實現與MQTT服務端通信,實現連接MQTT服務器、發佈和訂閲消息;
- MQTT服務器與華為雲IoTA通信,像註冊的設備發佈和訂閲消息。
1.5 資源總覽
本案例預計花費總計0元。
|
資源名稱 |
規格 |
單價(元) |
時長(分鐘) |
|
雲主機 |
2 vCPUs | 4 GB Ubuntu 22.04 64bit Python工具集
|
0
|
60
|
|
華為雲物聯網平台(IoTDA) |
免費單元
|
0
|
60
|
2 開發者空間開發環境準備
本案例中,實現MQTT客户端與雲端註冊的設備進行交互,需要開通IoTA服務以及安裝開發客户端所需依賴庫。
2.1 配置雲主機
登錄開發者空間,登錄後頁面如下:
點擊“配置雲主機”,在彈出的對話框中進行雲主機配置。
- 按如規格下配置雲主機:
- 雲主機名稱:默認/自定義
- CPU架構:X86
- 規格:2 vCPUs 4 GB
- 操作系統:Ubuntu
- 系統鏡像:公共鏡像 Ubuntu 22.04 server 64bit (xfce4 desktop)
- 工具:Python工具集(CodeArts IDE+ Python +Git)
確認以上配置無誤,點擊“安裝”,進行雲主機操作系統安裝。
安裝完畢之後,點擊“進入桌面”。
環境準備中,大約需要3-5分鐘,請您耐心等待…
進入桌面後的默認效果如下:
點擊左下角的“所有應用程序”-\>“開發”-\>“CodeArts IDE for Python”,打開IDE。
CodeArts IDE for Python 啓動後,在彈框界面,選擇“新建工程”。
在新建工程頁面,自定義輸入工程名稱,點擊“創建”。
在CodeArts IDE for Python 中,在新建的工程文件目錄中,選擇“venv/lib/python3.10/site-packages”路徑下的任一文件,鼠標右鍵後,選擇“打開所在文件夾”。
複製被打開的文件夾路徑。
在CodeArts IDE for Python 中,點擊下方的“終端”,輸入以下命令後回車,安裝paho-mqtt庫(paho-mqtt是一個提供MQTT協議功能的Python庫,通過這個庫,開發者可以快速實現MQTT客户端的功能,包括連接到MQTT代理服務器、發佈消息到主題、訂閲感興趣的主題以及接收並處理消息。):
pip install paho-mqtt --target={package-path}
其中{package-path}用上面複製的文件夾路徑替換。
按上面的方式執行以下命令,安裝PyQt5庫(PyQt5是基於paho-mqtt庫,實現MQTT通信。):
pip install PyQt5 --target={package-path}
其中{package-path}也用上面複製的文件夾路徑替換。
到此,雲主機的開發環境已經配置完成。
2.2 開通IoTA服務
登錄設備接入IoTA服務控制枱,點擊“開通免費單元”。
實例配置保持默認,點擊“立即創建”按鈕。
需要等待標準版實例創建完成。
創建IoT設備
2.3 創建產品
實例創建完成之後,點擊實例名稱,進入實例。點擊左側“產品”菜單欄,點擊“創建產品”按鈕。
在“創建產品”彈窗中,自定義填寫產品名稱,設備類型選擇“自定義類型”,自定義填寫設備類型(例:dev),點擊“確定”,完成產品創建。
在“創建產品成功”提示窗中點擊“查看詳情”。
在產品詳情頁面,點擊“自定義模型”,在“添加服務”彈窗中,填寫服務ID(例:stm32),點擊“確定”。
説明:模型就是存放設備上傳到雲平台的數據,你可以根據自己的產品進行創建。
在新增的服務中,點擊“新增屬性”,在“新增屬性”彈窗中,填寫屬性名稱,點擊“確定”。設備屬性是指與物聯網設備相關的各種參數和設置,這些屬性通常以鍵值對的形式存在,用於描述設備的各種特徵和行為。
點擊左上角“\<”回到上一級頁面。
2.4 添加設備
產品是屬於上層的抽象模型,接下來在產品模型下添加實際的設備。添加的設備最終需要與真實的設備關聯在一起,完成數據交互。
在左側菜單欄選擇“設備-\>所有設備”,點擊“註冊設備”。
在“單設備註冊”彈窗中,選擇所屬資源空間,所屬產品選擇步驟3.1中創建的產品,自定義輸入設備標識碼(例:dev1)、設備名稱和秘鑰,點擊“確定”。
在“設備創建成功”提示窗中,點擊“保存並關閉”。可以看到,剛剛註冊的設備處於“未激活”狀態,待真實設備接入平台才會變成“在線”狀態。
2.5 生成MQTT三元組
華為雲提供了一個在線工具,用來生成MQTT鑑權三元組。
打開這個MQTT ClientId生成工具,DeviceId填入剛剛註冊設備的設備ID,DeviceSecret填入步驟3.2中註冊設備時設置的秘鑰,點擊“Generate”,就可以得到MQTT的登錄信息了。
圖形化界面開發MQTT客户端
下面我們開發完成 MQTT 客户端調試助手,模擬真實設備接入IoTA平台,整體開發,基於 paho-mqtt 庫來實現以下功能:
連接到 MQTT 服務器:通過提供的 IP、端口、客户端 ID、用户名和密碼連接到 MQTT 服務器。
訂閲主題:從用户輸入的訂閲主題中接收消息。
發佈消息:發佈主題消息到指定的發佈主題。
每個按鈕添加相應的功能:Connect、訂閲和發佈 。在此基礎上,日誌框將顯示與 MQTT 連接和消息傳輸相關的調試信息。
在雲主機的CodeArts IDE for Python中,點擊“文件”-\>“新建”-\>“文件”。
點擊“文件”-\>“保存”。
輸入文件名稱為“MQTT.py”,點擊“保存”。
在MQTT.py文件中輸入以下代碼(複製文檔中python代碼時,可能會導致格式錯誤,可以點擊下載獲取MQTT.py文件內容!),用於實現MQTT客户端:
import sys
import json
import paho.mqtt.client as mqtt # 導入 paho-mqtt 庫
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QFormLayout, QLabel, QLineEdit, QSpinBox, QPushButton, QGridLayout, QGroupBox, QPlainTextEdit, QSpacerItem, QSizePolicy, QMenuBar, QStatusBar
import time
class MQTTClientDebugger(QMainWindow):
def __init__(self):
super().__init__()
self.connected = False
self.setWindowTitle("MQTT 客户端調試助手")
self.setGeometry(100, 100, 1019, 772) # 設置窗口大小
self.centralWidget = QWidget(self)
self.setCentralWidget(self.centralWidget)
self.client = None # MQTT 客户端實例
# 主佈局
self.mainLayout = QVBoxLayout(self.centralWidget)
# 連接設置佈局
self.connectionLayout = QHBoxLayout()
self.host = "117.78.5.125"
self.clientId = ""
self.username = ""
self.passWord = ""
self.formLayout = QFormLayout()
self.hostLineEdit = QLineEdit()
self.hostLineEdit.setText(self.host) \# 默認服務器IP地址
self.formLayout.addRow(QLabel("服務器域名或者IP地址:"), self.hostLineEdit)
# 端口號
self.spinBoxPort = QSpinBox()
self.spinBoxPort.setMaximum(99999)
self.spinBoxPort.setValue(1883) # 默認端口號
self.formLayout.addRow(QLabel("服務器端口:"), self.spinBoxPort)
self.clientIdLineEdit = QLineEdit()
self.clientIdLineEdit.setText(self.clientId) #客户端ID
self.formLayout.addRow(QLabel("ClientId"), self.clientIdLineEdit)
self.usernameLineEdit = QLineEdit()
self.usernameLineEdit.setText(self.username) # 設備用户名
self.formLayout.addRow(QLabel("Username"), self.usernameLineEdit)
self.passwordLineEdit = QLineEdit()
self.passwordLineEdit.setText(self.passWord) # 默認密碼
self.formLayout.addRow(QLabel("Password"), self.passwordLineEdit)
self.connectionLayout.addLayout(self.formLayout)
# 連接按鈕
self.connectButton = QPushButton("Connect")
self.connectButton.clicked.connect(self.connect_to_server)
self.connectionLayout.addWidget(self.connectButton)
self.mainLayout.addLayout(self.connectionLayout)
# MQTT 主題和消息佈局
self.gridLayout = QGridLayout()
self.gridLayout.addWidget(QLabel("訂閲主題:"), 0, 0)
self.subscribeTopicLineEdit = QLineEdit("\$oc/devices/"+ self.username +"/sys/messages/down")
self.gridLayout.addWidget(self.subscribeTopicLineEdit, 0, 1)
self.gridLayout.addWidget(QPushButton("訂閲"), 0, 2)
self.gridLayout.addWidget(QLabel("發佈主題:"), 1, 0)
self.publishTopicLineEdit = QLineEdit("\$oc/devices/"+ self.username +"/sys/properties/report")
self.gridLayout.addWidget(self.publishTopicLineEdit, 1, 1)
self.gridLayout.addWidget(QLabel("主題消息:"), 2, 0)
self.messageLineEdit = QLineEdit('{"services": [{"service_id": "stm32","properties":{"DHT11_T":18.1,"DHT11_H":16.2,"SOIL":12.4,"BH1750":124.5,"MOTOR_SW":1,"SOIL_MAX":30,"run_mode":1}}]}')
self.gridLayout.addWidget(self.messageLineEdit, 2, 1)
self.publishButton = QPushButton("發佈")
self.publishButton.clicked.connect(self.publish_message)
self.gridLayout.addWidget(self.publishButton, 2, 2)
self.mainLayout.addLayout(self.gridLayout)
# 日誌區域
self.logGroupBox = QGroupBox("日誌消息:")
self.logLayout = QHBoxLayout()
self.logTextEdit = QPlainTextEdit()
self.logTextEdit.setReadOnly(True)
self.logLayout.addWidget(self.logTextEdit)
self.logGroupBox.setLayout(self.logLayout)
self.mainLayout.addWidget(self.logGroupBox)
# 底部按鈕佈局
self.bottomLayout = QHBoxLayout()
self.testButton = QPushButton("測試按鈕(一鍵填充MQTT信息)")
self.clearButton = QPushButton("一鍵清除MQTT信息")
self.clearLogButton = QPushButton("清除日誌消息")
self.viewTutorialButton = QPushButton("【查看物聯網項目開發教程】")
self.quitButton = QPushButton("退出軟件")
self.testButton.clicked.connect(self.fillMQTTInfo)
self.clearButton.clicked.connect(self.clearMQTTInfo)
self.clearLogButton.clicked.connect(self.clear_logs)
self.bottomLayout.addWidget(self.testButton)
self.bottomLayout.addWidget(self.clearButton)
self.bottomLayout.addWidget(self.clearLogButton)
self.bottomLayout.addWidget(self.viewTutorialButton)
self.bottomLayout.addWidget(self.quitButton)
self.mainLayout.addLayout(self.bottomLayout)
# 菜單欄
self.menuBar = self.menuBar()
self.fileMenu = self.menuBar.addMenu("File")
quitAction = self.fileMenu.addAction("Quit")
quitAction.triggered.connect(self.close)
# 狀態欄
self.statusBar = QStatusBar()
self.setStatusBar(self.statusBar)
# 連接到MQTT服務器
def connect_to_server(self):
host = self.hostLineEdit.text()
port = self.spinBoxPort.value()
client_id = self.clientIdLineEdit.text()
username = self.usernameLineEdit.text()
password = self.passwordLineEdit.text()
try:
self.client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1,client_id)
self.client.username_pw_set(username, password) # 設置用户名和密碼
# 設置連接成功、消息接收、連接丟失等回調函數
self.client.on_connect = self.on_connect
self.client.on_message = self.on_message
self.client.on_disconnect = self.on_disconnect
# 連接到服務器
self.client.connect(host, port, 60)
# 啓動 MQTT 客户端
self.client.loop_start()
while not self.connected and not self._stop_event.is_set():
time.sleep(0.1)
self.connected = True
except Exception as e:
return f"{e}"
def on_connect(self, client, userdata, flags, rc):
"""當連接到MQTT服務器時調用"""
self.log(f"連接成功,返回碼:{rc}")
# 連接成功後訂閲主題
self.subscribeTopicLineEdit = QLineEdit("\$oc/devices/"+ self.usernameLineEdit.text() +"/sys/messages/down")
subscribe_topic = self.subscribeTopicLineEdit.text()
try:
client.subscribe(subscribe_topic)
except Exception as e:
self.log(f"訂閲失敗:{e}")
def on_message(self, client, userdata, msg):
"""當接收到MQTT消息時調用"""
self.log(f"接收到消息:{msg.topic} {msg.payload.decode()}")
def on_disconnect(self, client, userdata, rc):
"""當斷開連接時調用"""
self.log(f"MQTT服務器斷開連接,返回碼:{rc}")
# 發佈消息
def publish_message(self):
self.publishTopicLineEdit = QLineEdit("\$oc/devices/"+ self.usernameLineEdit.text() +"/sys/properties/report")
topic = self.publishTopicLineEdit.text()
message = self.messageLineEdit.text()
if self.client:
self.client.publish(topic, message)
self.log(f"發佈消息:{topic} {message}")
# 發送心跳包
def send_heartbeat(self):
if self.client:
self.client.ping()
self.log("發送心跳包")
# 日誌輸出
def log(self, message):
"""向日志框輸出信息"""
self.logTextEdit.appendPlainText(message)
# 填充MQTT信息
def fillMQTTInfo(self):
self.hostLineEdit.setText(self.host)
self.spinBoxPort.setValue(1883)
self.clientIdLineEdit.setText(self.clientId)
self.usernameLineEdit.setText(self.username)
self.passwordLineEdit.setText(self.passWord)
self.subscribeTopicLineEdit.setText("\$oc/devices/"+ self.username +"/sys/messages/down")
self.publishTopicLineEdit.setText("\$oc/devices/"+ self.username +"/sys/properties/report")
self.messageLineEdit.setText('{"services": [{"service_id": "stm32","properties":{"DHT11_T":18.1,"DHT11_H":16.2,"SOIL":12.4,"BH1750":124.5,"MOTOR_SW":1,"SOIL_MAX":30,"run_mode":1}}]}')
# 清除MQTT信息
def clearMQTTInfo(self):
self.hostLineEdit.clear()
self.spinBoxPort.clear()
self.clientIdLineEdit.clear()
self.usernameLineEdit.clear()
self.passwordLineEdit.clear()
self.subscribeTopicLineEdit.clear()
self.publishTopicLineEdit.clear()
# 清除日誌信息
def clear_logs(self):
self.logTextEdit.clear()
if \__name_\_ == '__main__':
app = QApplication(sys.argv)
mainWin = MQTTClientDebugger()
mainWin.show()
sys.exit(app.exec_())
將代碼27\~29行的3個參數值,填入步驟3.3中獲取的MQTT三元組的“ClientId”、“Username”、“Password”的值,如下圖所示:
説明:三元組數據會定時刷新,為確保鏈接數據有效,在填入參數值前請再次點擊“Generate”刷新,獲取最新數據後再填寫(請參考步驟3.3)。
按上述步驟編寫好代碼後,在CodeArts IDE for Python中,MQTT.py文件頁面,點擊右上角的綠色三角形按鈕,運行代碼。
在MQTT客户端調試助手窗口,依次點擊“Connect”、“訂閲”、“發佈”按鈕,可在“日誌消息”區域查看打印的日誌。
再次登錄設備接入IoTA服務控制枱,點擊步驟2.2中開通的實例,進入實例,點擊“設備-\>所有設備”,可以看到在步驟3.2中添加的設備已處於“在線”狀態,這説明我們開發的MQTT客户端已成功與雲端註冊的設備進行通信。
點擊設備的“詳情”,點擊“消息跟蹤”,可進一步查看我們開發的MQTT客户端發佈過來的消息詳情。
説明:如果您的消息跟蹤還未開啓,請點擊“啓動消息跟蹤”,在彈出的“消息跟蹤”彈窗中點擊“確定”,即可查看MQTT客户端發佈過來的消息詳情。
至此,利用雲主機快速開發MQTT客户端實現硬件仿真上雲全部完成。