【案例共创】在开发者空间快速开发MQTT客户端实现硬件仿真上云

最新案例动态,请查阅【案例共创】在开发者空间快速开发MQTT客户端实现硬件仿真上云。小伙伴们快来领取华为开发者空间进行实操吧!

本案例由开发者:DS小龙哥提供

1 概述

1.1 背景介绍

随着物联网技术的不断发展,越来越多的设备和应用依赖于实时数据交换和远程控制。在物联网生态系统中,设备与云平台之间的通信是核心环节之一,然而对于许多开发者来说,进行这种设备与云平台之间的通信往往涉及到硬件的配置与调试,这对于一些不熟悉硬件的开发者,尤其是那些处于软件开发领域的人员,可能是一大挑战。传统的物联网开发往往需要开发者拥有一定的硬件基础,或者至少具备与硬件设备进行调试和交互的能力,这使得一些开发者在没有硬件设备的情况下,难以快速上手和测试物联网应用,开发者迫切需要一种能够模拟硬件设备并与云平台进行交互的工具。

本案例通过开发一款基于MQTT协议的客户端调试助手,旨在为开发者提供一个简单易用的工具,模拟硬件设备与云平台的通信交互。这款工具通过软件模拟了物联网设备的行为,支持主题的订阅与发布,能够与华为云物联网平台(IoTDA)进行实时通信。对于不熟悉硬件的开发者,或者暂时没有硬件设备的开发者而言,这款调试助手可以让他们在没有物理硬件的前提下,体验完整的物联网设备上云过程。开发者可以通过该工具快速了解设备如何连接云平台,如何进行数据传输,并学习MQTT协议的基本操作。

1.2 适用对象

  • 企业
  • 个人开发者
  • 高校学生

1.3 案例时间

本案例总时长预计60分钟。

1.4 案例流程

说明:

  1. 登录开发者空间,配置开发环境;
  2. 编辑MQTT客户端源码;
  3. CodeArts IDE运行MQTT客户端源码文件;
  4. MQTT客户端实现与MQTT服务端通信,实现连接MQTT服务器、发布和订阅消息;
  5. MQTT服务器与华为云IoTA通信,像注册的设备发布和订阅消息。

1.5 资源总览

本案例预计花费总计0元。

资源名称规格单价(元)时长(分钟)
云主机2 vCPUs | 4 GB Ubuntu 22.04 64bit Python工具集060
华为云物联网平台(IoTDA)免费单元060

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客户端实现硬件仿真上云全部完成。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值