硬件环境
- 任意支持MQTT透传DTU,DTU支持4G、Wifi或网口都可以,只要能和服务器通讯即可。
- 串口接Modbus设备,需要知道Modbus设备的设备地址、寄存器地址、串口相关信息。
软件环境
- EMQX自建服务器,该项可自行百度搭建方法。
- 配置DTU连接MQTT服务器,需要知道MQTT服务器地址、端口、账户、密码、ClientId。
- VistualStudio用于编写测试采集程序本次测试采用ModbusRTU报文进行测试。
- MQTTX客户端,用于测试报文收发,确认串口能在MQTT上实现数据收发。
- 软件库,本人采用了Hsl,只是因为库中既有MQTT又有Modbus比较方便,可以自行选择MQTTnet、NModbus4之类的库。
设计架构
采集程序
完整代码如下:
using HslCommunication.Core.Address;
using HslCommunication.ModBus;
using HslCommunication;
using System;
using System.Text;
using HslCommunication.Core;
using HslCommunication.MQTT;
using System.Linq;
using System.Threading;
class Program
{
static void Main()
{
byte deviceAddress = 0x02; // 设备地址
ushort registerAddress = 0x000D; // 寄存器地址
ushort length = 0x0001; // 数据长度
OperateResult<byte[]> command = ModbusMqtt.BuildReadModbusCommandBytes(registerAddress.ToString(), length, deviceAddress, true, ModbusInfo.ReadRegister, true);
string hexString = BitConverter.ToString(command.Content).Replace("-", " ");
Console.WriteLine("MQTT发送ModbusRtu读取指令:" + hexString);
byte[] byteArray = hexString.Split(' ')
.Select(s => Convert.ToByte(s, 16))
.ToArray();
MqttConnectionOptions options = new MqttConnectionOptions
{
CleanSession = true,
IpAddress = "broker.emqx.io",
Port = 1883,
ClientId = "MqttTest_1"
};
MqttClient mqttClient = new MqttClient(options);
mqttClient.ConnectServer();
if (mqttClient.SubscribeMessage("sub").IsSuccess == true && mqttClient.SubscribeMessage("pub").IsSuccess == true)
{
Thread.Sleep(1000);
if (mqttClient.PublishMessage(new MqttApplicationMessage()
{
Topic = "sub",
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce,
Payload = byteArray,
Retain = false
}).IsSuccess)
{
Console.WriteLine("连接MQTT客户端成功...");
Console.WriteLine("订阅sub、pub主题成功...");
}
Thread.Sleep(1000);
//订阅MQTT数据接收事件,添加对应事件处理函数
mqttClient.OnMqttMessageReceived += onMqttMessageReceived;
Thread.Sleep(Timeout.Infinite);
}
mqttClient.ConnectClose();
}
private static void onMqttMessageReceived(MqttClient client, string topic, byte[] payload)
{
if (topic == "pub")
{
// 解析收到的消息
byte[] receivedData = payload;
string hexString = BitConverter.ToString(receivedData).Replace("-", "");
Console.WriteLine("MQTT接收ModbusRtu返回指令:" + hexString);
short value = ModbusMqtt.ReadShortValue(receivedData);
Console.WriteLine("点位Short值:" + value);
}
}
}
两个红框处为本人修改源码添加的方法,大家也可以自行编写,此处将两个封装方法贴出
public static OperateResult<byte[]> BuildReadModbusCommandBytes(string address, ushort length, byte station, bool isStartWithZero, byte defaultFunction, bool crcFlag)
{
try
{
ModbusAddress mAddress = new ModbusAddress(address, station, defaultFunction);
CheckModbusAddressStart(mAddress, isStartWithZero);
OperateResult<byte[][]> operateResult = BuildReadModbusCommand(mAddress, length);
if (operateResult.IsSuccess)
{
if (crcFlag)
{
byte[] array = CalculateCRC(operateResult.Content[0]);
byte[] array2 = new byte[operateResult.Content[0].Length + array.Length];
Array.Copy(operateResult.Content[0], array2, operateResult.Content[0].Length);
Array.Copy(array, 0, array2, operateResult.Content[0].Length, array.Length);
return OperateResult.CreateSuccessResult(array2);
}
return OperateResult.CreateSuccessResult(operateResult.Content[0]);
}
return null;
}
catch (Exception ex)
{
return new OperateResult<byte[]>(ex.Message);
}
}
public static short ReadShortValue(byte[] data)
{
if (data.Length >= 5 && data[1] == 3 && data[2] == 2)
{
byte[] array = new byte[2]
{
data[3],
data[4]
};
string value = BitConverter.ToString(array).Replace("-", "");
int value2 = Convert.ToInt16(value, 16);
return Convert.ToInt16(value2);
}
throw new ArgumentException("Invalid Modbus RTU read register data");
}