• 沒有找到結果。

接收下发命令

4.3.7 C# Demo 使用说明

概述

本文以C#语言为例,介绍设备通过MQTTS/MQTT协议接入平台,通过平台接口实现

“数据上报”、“命令下发”的功能。如需其他语言接入,请参考资源获取。

前提条件

● 已安装Microsoft Visual Studio,若未安装请参考安装Microsoft Visual Studio。

● 已在控制台获取设备接入地址。获取地址的操作步骤,请参考平台对接信息。

● 已在控制台创建产品和设备。创建产品和设备的具体操作细节,请参考创建产 品、注册单个设备或批量注册设备。

准备工作

● 访问Microsoft官网,选择合适系统的版本下载Microsoft Visual Studio。(本文 以windows 64-bit系统,Microsoft Visual Studio 2017和.NET Framework 4.5.1 为例)。

● 下载完成后,运行安装文件,根据界面提示安装。

导入代码样例

步骤1 下载quickStart(C#)样例。

步骤2 运行Microsoft Visual Studio 2017,单击“打开项目/解决方案”,选择步骤1中下载 的样例。

设备接入

开发指南 4 设备侧开发

步骤3 完成代码导入。

代码目录简述:

App.config:Server地址和设备信息配置文件;

C#:项目C#代码;

EncryptUtil.cs:设备密钥加密辅助类;

FrmMqttDemo.cs:窗体界面;

Program.cs:Demo程序启动入口。

dll:项目中使用到了第三方库

MQTTnet:v3.0.11,是一个基于 MQTT 通信的高性能 .NET 开源库,它同时支持 MQTT 服务器端和客户端,引用库文件包含MQTTnet.dll。

MQTTnet.Extensions.ManagedClient:v3.0.11,这是一个扩展库,它使用 MQTTnet为托管MQTT客户机提供附加功能。

步骤4 Demo里的工程配置参数。

● App.config:需要配置服务器地址、设备ID和设备密钥,用于启动Demo程序的时 候,程序将此信息自动写到Demo主界面。

<add key="serverUri" value="serveruri"/>

<add key="deviceId" value="deviceid"/>

设备接入

开发指南 4 设备侧开发

<add key="deviceSecret" value="secret"/>

<add key="PortIsSsl" value="8883"/>

<add key="PortNotSsl" value="1883"/>

----结束

界面展示

1. FrmMqttDemo主要提供了界面显示,默认启动后自动从App.config中获取Server 地址、设备ID、设备密钥。请根据实际创建的设备信息填写。

– Server地址:即域名,参考平台对接信息获取;

– 设备ID和设备密钥:在物联网平台注册设备或调用创建设备接口后获取。

2. 示例中App.config默认写了设备侧接入的Server地址(SSL加密接入时该Server地 址要与对应的证书文件匹配使用)。

<add key="serverUri" value="iot-mqtts.cn-north-4.myhuaweicloud.com

"/>;

3. 用户可以选择设备侧建链时是否为SSL加密,选择Qos方式是0还是1,当前不支持 Qos2,可参考使用限制。

int portIsSsl = int.Parse(ConfigurationManager.AppSettings["PortIsSsl"]);

int portNotSsl = int.Parse(ConfigurationManager.AppSettings["PortNotSsl"]);

if (client == null)

{ client = new MqttFactory().CreateManagedMqttClient();

}

string timestamp = DateTime.Now.ToString("yyyyMMddHH");

string clientID = txtDeviceId.Text + "_0_0_" + timestamp;

// 对密码进行HmacSHA256加密 string secret = string.Empty;

if (!string.IsNullOrEmpty(txtDeviceSecret.Text))

{ secret = EncryptUtil.HmacSHA256(txtDeviceSecret.Text, timestamp);

}

// 判断是否为安全连接 if (!cbSSLConnect.Checked)

{ options = new ManagedMqttClientOptionsBuilder()

.WithAutoReconnectDelay(TimeSpan.FromSeconds(RECONNECT_TIME)) .WithClientOptions(new MqttClientOptionsBuilder()

.WithTcpServer(txtServerUri.Text, portNotSsl)

.WithCommunicationTimeout(TimeSpan.FromSeconds(DEFAULT_CONNECT_TIMEOUT)) .WithCredentials(txtDeviceId.Text, secret)

.WithClientId(clientID)

.WithKeepAlivePeriod(TimeSpan.FromSeconds(DEFAULT_KEEPLIVE)) .WithCleanSession(false)

.WithProtocolVersion(MqttProtocolVersion.V311) .Build())

.Build();

}else

{ string caCertPath = Environment.CurrentDirectory + @"\certificate\rootcert.pem";

X509Certificate2 crt = new X509Certificate2(caCertPath);

options = new ManagedMqttClientOptionsBuilder()

.WithAutoReconnectDelay(TimeSpan.FromSeconds(RECONNECT_TIME)) .WithClientOptions(new MqttClientOptionsBuilder()

.WithTcpServer(txtServerUri.Text, portIsSsl)

.WithCommunicationTimeout(TimeSpan.FromSeconds(DEFAULT_CONNECT_TIMEOUT)) .WithCredentials(txtDeviceId.Text, secret)

.WithClientId(clientID)

.WithKeepAlivePeriod(TimeSpan.FromSeconds(DEFAULT_KEEPLIVE)) .WithCleanSession(false)

.WithTls(new MqttClientOptionsBuilderTlsParameters() {

.WithProtocolVersion(MqttProtocolVersion.V311) .Build())

.Build();

}

2. FrmMqttDemo类提供了Mqtt客户端建立连接的的方法StartAsync,连接成功后 会通过回调函数OnMqttClientConnected打印连接成功日志。

Invoke((new Action(() =>

{ ShowLogs($"{"try to connect to server " + txtServerUri.Text}{Environment.NewLine}");

})));

if (client.IsStarted)

设备接入

开发指南 4 设备侧开发

{ await client.StopAsync();

}

// 注册事件

client.ApplicationMessageProcessedHandler = new ApplicationMessageProcessedHandlerDelegate(new

Action<ApplicationMessageProcessedEventArgs>(ApplicationMessageProcessedHandlerMethod)); // 消 息发布回调

client.ApplicationMessageReceivedHandler = new MqttApplicationMessageReceivedHandlerDelegate(new

Action<MqttApplicationMessageReceivedEventArgs>(MqttApplicationMessageReceived)); // 命令下发 回调

client.ConnectedHandler = new MqttClientConnectedHandlerDelegate(new Action<MqttClientConnectedEventArgs>(OnMqttClientConnected)); // 连接成功回调 client.DisconnectedHandler = new MqttClientDisconnectedHandlerDelegate(new Action<MqttClientDisconnectedEventArgs>(OnMqttClientDisconnected)); // 连接断开回调 // 连接平台设备

await client.StartAsync(options);

注:如果连接失败,在OnMqttClientDisconnected函数中已实现退避重连,代 码样例如下:

private void OnMqttClientDisconnected(MqttClientDisconnectedEventArgs e) { try {

Invoke((new Action(() =>

{

ShowLogs("mqtt server is disconnected" + Environment.NewLine);

txtSubTopic.Enabled = true;

long backOffWithJitter = (int)(Math.Pow(2.0, retryTimes)) * (randomBackOff + lowBound);

long waitTImeUtilNextRetry = (int)(minBackoff + backOffWithJitter) > maxBackoff ? maxBackoff : (minBackoff + backOffWithJitter);

Invoke((new Action(() =>

{

ShowLogs("mqtt demo error: " + ex.Message + Environment.NewLine);

})));

} }

订阅 Topic

订阅某topic的设备才能接收broker发布的关于该topic的消息,关于平台预置topic可参 考Topic定义。

在FrmMqttDemo类中提供了订阅命令下发topic的功能:

List<MqttTopicFilter> listTopic = new List<MqttTopicFilter>();

var topicFilterBulderPreTopic = new MqttTopicFilterBuilder().WithTopic(topic).Build();

listTopic.Add(topicFilterBulderPreTopic);

// 订阅Topic

client.SubscribeAsync(listTopic.ToArray()).Wait();

建链后,如果成功订阅Topic,主界面日志栏显示如下信息:

接收下发命令

在FrmMqttDemo类中提供了接收平台下发命令的功能,在MQTT建链完成并成功订阅 Topic后,可以在控制台设备详情中命令下发或使用应用侧Demo对该设备ID进行命令 下发。下发成功后,在MQTT的回调函数中接收到平台下发给设备的命令。

private void MqttApplicationMessageReceived(MqttApplicationMessageReceivedEventArgs e) { Invoke((new Action(() =>

{

ShowLogs($"received message is {Encoding.UTF8.GetString(e.ApplicationMessage.Payload)}

{Environment.NewLine}");

设备接入

开发指南 4 设备侧开发

string msg = "{\"result_code\": 0,\"response_name\": \"COMMAND_RESPONSE\",\"paras\": {\"result\":

\"success\"}}";

string topic = "$oc/devices/" + txtDeviceId.Text + "/sys/commands/response/request_id=" + e.ApplicationMessage.Topic.Split('=')[1];

ShowLogs($"{"response message msg = " + msg}{Environment.NewLine}");

var appMsg = new MqttApplicationMessage();

appMsg.Payload = Encoding.UTF8.GetBytes(msg);

appMsg.Topic = topic;

appMsg.QualityOfServiceLevel = int.Parse(cbOosSelect.SelectedValue.ToString()) == 0 ? MqttQualityOfServiceLevel.AtMostOnce : MqttQualityOfServiceLevel.AtLeastOnce;

appMsg.Retain = false;

// 上行响应

client.PublishAsync(appMsg).Wait();

})));

}

例如下发参数名为smokeDetector: SILENCE,参数值为50的命令。

命令下发成功后,Demo界面显示如下: