hi,你好!欢迎访问本站!登录
本站由网站地图腾讯云宝塔系统阿里云强势驱动
当前位置:首页 - 教程 - 杂谈 - 正文 君子好学,自强不息!

IoTClient开辟3 - ModBusTcp协定客户端完成

2019-11-18杂谈搜奇网35°c
A+ A-

媒介

进过前面两章的引见,本日最先正式的实战。

进制转换

许多朋侪关于进制转换多是在刚学盘算机的时刻有打仗,厥后做高等言语开辟能够就逐步忘记了。我们做工控开辟的时刻须要常常举行进制转换,这里和人人一同温习下。
一个字节等8位(1byte = 8bit),能够存储2^8(0-255)总计256个数字。所以我们要对8、256等数字要敏感。
int16(short), int32(int), int64(long) 分别是占用2个字节、4个字节、8个字节,Single(float)也是占用4个字节。

bool       System.Boolean (布尔型,其值为 true 或许 false)
byte       System.Byte    (字节型,占 1 字节,示意 8 位正整数,局限 0 ~ 255)
sbyte      System.SByte   (带标记字节型,占 1 字节,示意 8 位整数,局限 -128 ~ 127)
char       System.Char    (字符型,占领 2 个字节,示意 1 个 Unicode 字符)
short      System.Int16   (短整型,占 2 字节,示意 16 位整数,局限 -32,768 ~ 32,767)
ushort     System.UInt16  (无标记短整型,占 2 字节,示意 16 位正整数,局限 0 ~ 65,535)
uint       System.UInt32  (无标记整型,占 4 字节,示意 32 位正整数,局限 0 ~ 4,294,967,295)
int        System.Int32   (整型,占 4 字节,示意 32 位整数,局限 -2,147,483,648 到 2,147,483,647)
float      System.Single  (单精度浮点型,占 4 个字节)
ulong      System.UInt64  (无标记长整型,占 8 字节,示意 64 位正整数)
long       System.Int64   (长整型,占 8 字节,示意 64 位整数)
double     System.Double  (双精度浮点型,占8 个字节)

接着我们来看其他进制转十进制的盘算

十进制转十进制  
1263 = 1*10^3 + 2*10^2 + 6*10^1 + 3*10^0 = 1000 + 200 + 60 + 3 = 1263
二进制转十进制 
1001 = 1*2^3 + 0*2^2 + 0*2^1 + 1*2^0 = 8 + 0 + 0 + 1 = 9 
十六进制转十进制 
3245 = 3*16^3 + 2*16^2 + 4*16^1 + 5*16^0 = 3*4096 + 2*256 + 4*16 + 5 = 12869

十进制转二进制

第八位 第七位 第六位 第五位 第四位 第三位 第二位 第一位
2^7     2^6    2^5    2^4     2^3    2^2    2^1   2^0
128     64     32     16      8      4      2      1

以上位二进制位能存储最大十进制数,所以我们反过来也能够对比把十进制转二进制。比方86,
86小于128多以第八位是0,86大于64所以第七位是1。86-64=22,22小于32所以第六位是0,22大于16所以第五位是1。。。 


所以最好转成二进制是:0101 0110

二进制转十六进制

我们用二进制 0101 0110来演示,也就是上面十进制的86。

固然,你最好用盘算器考证下

ModBusTcp协定引见

我们在对进制转换举行温习事后,接下来说ModBusTcp协定。
ModBus协定是如今工控内里用的比较多比较通用的一种协定,什么牢靠啊、简朴啊等等一些长处就不说了,直接入正题。
ModBus分为RTU、ASCII、TCP三种体式格局举行通信,本日我们只讲TCP。
在ModBus内里有站号、功用码、寄存器地点等观点。

  • 站号:多装备的标识号
  • 功用码:一些功用的标识号
    功用码详解:
01:读线圈
02:读离散量
03:读坚持寄存器(每一个寄存器含有两个字节)
04:读输入寄存器
05:写单个线圈
06:写单个寄存器
15:用于写多个线圈
16:写多个寄存器

ModBusTcp报文剖析

协定的明白和完成重要就是要对协定报文明白。(注重:以下报文数据都是十六进制)

数据【读取-要求报文】:19 B2 00 00 00 06 02 03 00 04 00 01

  • 19 B2 是客户端发的磨练信息,随便定义。
  • 00 00 代表是基于tcp/ip协定的modbus
  • 00 06 标识背面另有多长的字节
  • 02 示意站号地点
  • 03 为功用码(读坚持寄存器)
  • 00 04 为寄存器地点
  • 00 01 为寄存器的长度(寄存器个数)

数据【读取-相应报文】(分两次猎取)

第一次猎取前八个字节(Map报文头):19 B2 00 00 00 05 02 03 02 00 20

  • 19 B2 磨练考证信息(复制的客户端发的,配件磨练)
  • 00 00 代表是基于tcp/ip协定的modbus(复制的客户端发的)
  • 00 05 为当前位置到最后的长度
  • 02 示意站号地点(复制的客户端发的)
  • 03 为功用码(复制的客户端发的)

第二次猎取的报文:02 00 20

  • 02 字节个数
  • 00 20 相应的数据

数据【写入-要求报文】:19 B2 00 00 00 09 02 10 00 04 00 01 02 00 20

  • 19 B2 是客户端发的磨练信息,随便定义。
  • 00 00 代表是基于tcp/ip协定的modbus
  • 00 09 从本字节下一个到最后
  • 02 站号
  • 10 功用码(转十进制就是16)
  • 00 04 寄存器地点
  • 00 01 寄存器的长度(寄存器个数)
  • 02 写字节的个数
  • 00 20 要写入的值(转十进制为32)

数据【写入-相应报文】:19 B2 00 00 00 06 02 10 00 04 00 01

和要求报文的区分

  • 没有了要求报文的数据值
  • 00 09 变成了00 06 由于报文长度变了
  • 其他的报文意义和要求报文一致

ModBusTcp对寄存器的读取

有了上面的三个报文做参考,我们就能够用Socket来完成ModBusTcp协定了。实在协定就是根据报文的划定来,也没有想的那末庞杂,和我们前面完成的谈天通信软件区分不大。
第一步,我们先完成数据读取报文的组装:

/// <summary>
/// 猎取读取敕令(此要领传入参数后就能够获得相似19 B2 00 00 00 06 02 03 00 04 00 01如许的要求报文)
/// </summary>
/// <param name="address">寄存器肇端地点</param>
/// <param name="stationNumber">站号</param>
/// <param name="functionCode">功用码</param>
/// <param name="length">读取长度</param>
/// <returns></returns>
public static byte[] GetReadCommand(ushort address, byte stationNumber, byte functionCode, ushort length)
{
    byte[] buffer = new byte[12];
    buffer[0] = 0x19;
    buffer[1] = 0xB2;//Client发出的磨练信息
    buffer[2] = 0x00;
    buffer[3] = 0x00;//示意tcp/ip 的协定的modbus的协定
    buffer[4] = 0x00;
    buffer[5] = 0x06;//示意的是该字节今后的字节长度

    buffer[6] = stationNumber;  //站号
    buffer[7] = functionCode;   //功用码
    buffer[8] = BitConverter.GetBytes(address)[1];
    buffer[9] = BitConverter.GetBytes(address)[0];//寄存器地点
    buffer[10] = BitConverter.GetBytes(length)[1];
    buffer[11] = BitConverter.GetBytes(length)[0];//示意request 寄存器的长度(寄存器个数)
    return buffer;
}

第二步,就是竖立我们的Socket衔接,并发送要求报文

//1 建立Socket
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

//2 竖立衔接
socket.Connect(new IPEndPoint(IPAddress.Parse(ip), 端口));

//3 猎取敕令[组装要求报文](寄存器肇端地点:4、站号:2、功用码:3、读取寄存器长度:1)
byte[] command = GetReadCommand(4, 2, 3, 1);

//4 发送敕令
socket.Send(command);

第三步,剖析相应报文,获得数据值

//5 读取相应
byte[] buffer1 = new byte[8];//先读取前面八个字节(Map报文头)
socket.Receive(buffer1, 0, buffer1.Length, SocketFlags.None);

//5.1 猎取将要读取的数据长度
int length = buffer1[4] * 256 + buffer1[5] - 2;//减2是由于这个长度数据包含了单位标识符和功用码,占两个字节

//5.2 读取数据
byte[] buffer2 = new byte[length];
var readLength2 = socket.Receive(buffer2, 0, buffer2.Length, SocketFlags.None);

byte[] buffer3 = new byte[readLength2 - 1];
//5.3  过滤第一个字节(第一个字节代表数据的字节个数)
Array.Copy(buffer2, 1, buffer3, 0, buffer3.Length);
var buffer3Reverse = buffer3.Reverse().ToArray();
var value = BitConverter.ToInt16(buffer3Reverse, 0);

//6 封闭衔接
socket.Shutdown(SocketShutdown.Both);
socket.Close();

ModBusTcp对寄存器的写入

关于数据写入就更简朴了。
第一步,组装要求报文

/// <summary>
/// 猎取写入敕令
/// </summary>
/// <param name="address">寄存器地点</param>
/// <param name="values"></param>
/// <param name="stationNumber">站号</param>
/// <param name="functionCode">功用码</param>
/// <returns></returns>
public static byte[] GetWriteCommand(ushort address, byte[] values, byte stationNumber, byte functionCode)
{
    byte[] buffer = new byte[13 + values.Length];
    buffer[0] = 0x19;
    buffer[1] = 0xB2;//磨练信息,用来考证response是不是串数据了           
    buffer[4] = BitConverter.GetBytes(7 + values.Length)[1];
    buffer[5] = BitConverter.GetBytes(7 + values.Length)[0];//示意的是header handle背面另有多长的字节

    buffer[6] = stationNumber; //站号
    buffer[7] = functionCode;  //功用码
    buffer[8] = BitConverter.GetBytes(address)[1];
    buffer[9] = BitConverter.GetBytes(address)[0];//寄存器地点
    buffer[10] = (byte)(values.Length / 2 / 256);
    buffer[11] = (byte)(values.Length / 2 % 256);//写寄存器数目(除2是一个寄存器两个字节,寄存器16位。除以256是byte最大存储255。)              
    buffer[12] = (byte)(values.Length);          //写字节的个数
    values.CopyTo(buffer, 13);                   //把目标值附加到数组背面
    return buffer;
}

第二步,竖立Socket衔接,并发送报文

//1 建立Socket
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

//2 竖立衔接
socket.Connect(new IPEndPoint(IPAddress.Parse(ip), 端口));

//值的转换
short value = 32;
var values = BitConverter.GetBytes(value).Reverse().ToArray();

//3 猎取并发送敕令(寄存器肇端地点、站号、功用码)
var command = GetWriteCommand(4, values, 2, 16);
socket.Send(command);

//4 封闭衔接
socket.Shutdown(SocketShutdown.Both);
socket.Close();

完毕

  • 同步至索引目次:《物联网基本组件IoTClient开辟系列》
  • 参考1:https://www.cnblogs.com/any91/p/3530540.html
  • 参考2:https://www.cnblogs.com/DreamRecorder/p/9081134.html
  • demo:https://github.com/zhaopeiym/BlogDemoCode/tree/master/IoTClient/ModbusTCP
  • 完全完成:https://github.com/zhaopeiym/IoTClient
  选择打赏方式
微信赞助

打赏

QQ钱包

打赏

支付宝赞助

打赏

  移步手机端
IoTClient开辟3 - ModBusTcp协定客户端完成

1、打开你手机的二维码扫描APP
2、扫描左则的二维码
3、点击扫描获得的网址
4、可以在手机端阅读此文章
未定义标签

本文来源:搜奇网

本文地址:https://www.sou7.cn/282223.html

关注我们:微信搜索“搜奇网”添加我为好友

版权声明: 本文仅代表作者个人观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。请记住本站网址https://www.sou7.cn/搜奇网。

发表评论

选填

必填

必填

选填

请拖动滑块解锁
>>