工控 modbusTCP 报文

Tx 发送报文:00 C9 00 00 00 06 01 03 00 00 00 02

Rx 接收报文:00 C9 00 00 00 07 01 03 04 01 4D 00 01

Tx 发送报文:00 C9 00 00 00 06 01 03 00 00 00 02

00 C9 事务处理标识符 2字节

00 00 协议标识符 2字节 固定 00 00

00 06 长度 2字节 表示之后的字节总数 (01 03 00 00 00 02 6个字节)

01 单元标识符 Unit ID 1字节

00 C9 00 00 00 06 01 合起来就是 MPAP报文头

03 功能码 1字节

00 00 00 02 数据部分

00 00 开始地址 2个字节

00 02 数据的长度 2个字节, 此处是读取2个寄存器

Rx 接收报文:00 C9 00 00 00 07 01 03 04 01 4D 00 01

00 C9 事务处理标识符 2字节

00 00 协议标识符 2字节 固定 00 00

00 07 长度 2字节 表示之后的字节总数 ( 01 03 04 01 4D 00 01 7个字节)

01 单元标识符 Unit ID 1字节

00 C9 00 00 00 07 01 合起来就是 MPAP报文头

03 功能码 1字节

04 01 4D 00 01 数据部分

04 字节数 1个字节 2个寄存器 占4个字节

01 4D 数据1

00 01 数据2

保持寄存器的值可以读取也可以修改 ,而输入寄存器的值对于master来说就只能读取。所谓保持寄存器,指的是可以通过通信命令读或者写的寄存器;通常是一些功能控制寄存器或者输出寄存器等。

csharp 复制代码
 public class ModBusTCP
 {
     public  int ushorts2int(ushort[] res)
     {
         int high = res[0];
         int low = res[1];
         int value = (high << 16) + low;
         return value;
     }

     public  ushort[] int2ushorts(int res)
     {
         ushort ust1 = (ushort)(res >> 16);
         ushort ust2 = (ushort)res;          
         return new ushort[] { ust1, ust2 };
     }
     //声明一个Socket对象
     private Socket tcpClient;
     //单元标识符
     public byte SlaveId { get; set; } = 0x01;
     /// <summary>
     /// 建立连接
     /// </summary>
     /// <param name="ip">IP地址  </param>
     /// <param name="port">端口号</param>
     /// <returns></returns>
     public bool Connect(string ip, int port)
     { 
         //实例化Socket对象
         tcpClient =new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);
         try
         {
             //建立连接   三次握手
             tcpClient.Connect(IPAddress.Parse(ip), port);
             return true;
         }
         catch (Exception)
         {

            return false;
         }
       
     }
     /// <summary>
     /// 断开连接
     /// </summary>
     public void DisConnect() 
     { 
         //  4次握手
         tcpClient?.Close();
     }
     /// <summary>
     /// 读取多个数据
     /// </summary>
     /// <param name="startAdr"></param>
     /// <param name="length"></param>
     /// <returns></returns>
     public byte[] ReadOutputRegisters(ushort startAdr, ushort length) 
     {
         #region 1 拼接报文
         
         List<byte> SendCommand = new List<byte>();
         //ModBusTCP 报文 格式     MBAP  + 功能码+  数据部分
         // MBAP  事务标识符(2字节) 协议标识符(2字节) 长度(2字节) 单元标识符(1字节)
         // 功能码 (1字节)
         // 数据部分  起始寄存器地址(2字节)   寄存器长度(2字节) 
         //=======================报文拼接===============================
         //事务标识符  设置固定  0x00 0x00
         // 等价 SendCommand.Add(0x00); SendCommand.Add(0x00);
         SendCommand.AddRange(new byte[] { 0x00, 0x00 });

         // 协议标识符  设置固定  0x00 0x00
         SendCommand.AddRange(new byte[] { 0x00, 0x00 });

         // 长度  
         SendCommand.AddRange(new byte[] {0x00,0x06});

         // 单元标识符 1字节
         SendCommand.Add(SlaveId);

         //功能码  固定 0x03
         SendCommand.Add(0x03);
         //数据部分 起始寄存器地址(2字节)
         #region ushort 转换字节数组
         //  %  /
         //byte Hbyte = (byte)(startAdr / 255);
         //byte Lbyte = (byte)(startAdr % 255);
         //SendCommand.AddRange(new byte[] { Hbyte,Lbyte});
         #endregion
         //BitConverter 大小端是颠倒
         byte[] temp = BitConverter.GetBytes(startAdr);
         SendCommand.AddRange(new byte[] { temp[1], temp[0] });
         //数据部分 寄存器长度(2字节)
         temp = BitConverter.GetBytes(length);
         SendCommand.AddRange(new byte[] { temp[1], temp[0] });
         #endregion
         #region 2 发送报文
         tcpClient.Send(SendCommand.ToArray());
         #endregion
         #region 3 接收报文
         Thread.Sleep(50);
         int count = tcpClient.Available;
         byte[] buffer= new byte[count];
         tcpClient.Receive(buffer,buffer.Length,SocketFlags.None);
         #endregion
         #region 4 验证报文
         //Tx  发送报文: 00 C9 00 00 00 06 01 03 00 00 00 02
         //Rx  接收报文: 00 C9 00 00 00 07 01 03 04 01 4D 00 01
         // 校验 00 07 01 03 04  这几位
         // 00 C9 00 00 00 07 01 03 04   这9位是长度固定的,  +   获取寄存器长度length*2   每个寄存器占2字节
         if (buffer.Length == 9+length*2) 
         {
             //两位字节转换整数  result_h   高位字节 result_1  地位字节
             //result = result_h * 256 + result_1
             //   00 07     
             //   3+length*2   3来源于01 03 04  固定的  
             if (buffer[4] * 256 + buffer[5]==3+length*2) 
             {
                 //   单元标识符  和   功能码
                 if (buffer[6] == SlaveId && buffer[7] == 0x03)
                 {
                     #region 5 解析报文
                     //  返回的数据字节数,就是 要读取寄存器*2
                     byte[] result = new byte[length*2];
                     Array.Copy(buffer,9,result,0,length*2);
                     return result;
                     #endregion
                 }
             }

         }
         return null;
         #endregion
        
       
     }

     /// <summary>
     /// 写入多数据
     /// </summary>
     /// <param name="startAdr">开始寄存器地址</param>
     /// <param name="length">寄存器长度</param>
     /// <param name="dates">具体数据</param>
     /// <returns></returns>
     public void WriteResisters(ushort startAdr, ushort length,ushort datecount, ushort[] dates)
     {
         #region 1 拼接报文
         List<byte> SendCommand = new List<byte>();
         // 事务标识符 2字节  固定  0x00 0x00
         SendCommand.AddRange(new byte[] { 0x00, 0x00 });
         // 协议标识符 2字节  固定  0x00 0x00
         SendCommand.AddRange(new byte[] { 0x00, 0x00 });
         // 长度  2字节       7:  单元标识符1字节+功能码1字节+ 开始寄存器地址2字节 寄存器长度2字节 + 字节长度1字节
         ushort[] lens=int2ushorts(2*length+7);
         //byte[] start1 = BitConverter.GetBytes(lens[0]);
         byte[] len = BitConverter.GetBytes(lens[1]);
         if (BitConverter.IsLittleEndian)
         {
             Array.Reverse(len);
         }
         SendCommand.AddRange(len);
         // 单元标识符  1字节  固定 SlaveId
         SendCommand.Add(SlaveId);
         // 功能码  写入 多个保持寄存器
         SendCommand.Add(0x10);
         // 数据部分     开始寄存器地址 寄存器长度  字节长度   具体数据
         // 开始寄存器地址
         byte[] start = BitConverter.GetBytes(startAdr);
         //数据值
         List<byte> valueBytes = new List<byte>();
         byte[] value;
         foreach (ushort b in dates)
         {
             value= BitConverter.GetBytes(b);
             if (BitConverter.IsLittleEndian)
             {
                 Array.Reverse(value);
             }
             valueBytes.AddRange(value);
         }            
         //根据计算机大小端存储方式进行高低字节转换
         if (BitConverter.IsLittleEndian)
         {
             Array.Reverse(start);              
         }
         SendCommand.AddRange(start);
         //寄存器长度  2字节
         byte[] registLen = BitConverter.GetBytes(length);         
         if (BitConverter.IsLittleEndian)
         {
             Array.Reverse(registLen);
           
         }
         SendCommand.AddRange(registLen);
         //字节长度 1字节
         SendCommand.Add((byte)(datecount%255));
         
         //SendCommand.AddRange(new byte[] { 0,1});
        
         SendCommand.AddRange(valueBytes);
       
         #endregion
         #region 2 发送报文
         Thread.Sleep(50);
         //tcpClient.Send(SendCommand.ToArray(),0,SendCommand.Count(),SocketFlags.Partial);
         //tcpClient.Send(SendCommand.ToArray());
         //tcpClient.Send(new byte[] { 00, 01, 00, 00, 00, 08, 0xFF, 0x05, 00, 05 , 00 ,01, 01, 01 });
         //Rx: 000002 - 00 01 00 00 00 09 01 0A 00 05 00 01 01 00 0F
         //事务标识符2字节           1F 97
         //协议标识符2字节           00 00
         //长度 2字节                00 11
         //单元标识符                01
         //功能码                    10
         //起始地址 2字节            00 04
         //寄存器长度 2字节          00 02            读2个寄存器
         //字节数    寄存器长度*2    04
         //字节数组N*2字节           00 06 00 09
         //1F 97 00 00 00 11 01 10 00 04 00 02 04 00 06 00 09
         // 00 01 00 00 00 11 01 10 00 03 00 02 04 00 02 01 09
         //tcpClient.Send(new byte[] { 00, 01, 00, 00, 00, 11, 01, 0x10, 00, 00 ,00 ,02 ,04,01,02, 30 ,39 });
         //tcpClient.Send(new byte[] { 00, 01, 00, 00, 00, 11, 01, 0x10, 00, 00 ,00 ,02 ,04,01,02, 30 ,39 });
         tcpClient.Send(SendCommand.ToArray());
         #endregion

     }
 }
位操作,写写数据需要吧数据转换 2进制

4种寄存器

线圈寄存器 按位操作 可读可写

离散输入寄存器 按位 读取寄存器

保持寄存器

输入寄存器


》》 按位操作 所以 返回 1个字节
























相关推荐
Industio_触觉智能3 个月前
新品|瑞芯微RK3588工控机IPC8801适用AI算力、边缘计算、工业视觉
人工智能·rk3588·openharmony·工控机
Ares-Wang5 个月前
上位机 运动控制
工控机
Mico工控6 个月前
Centos/Ubuntu等Linux系统下增加扩展4个串口以上配置操作
linux·ubuntu·centos·电脑·软件·工控机·串行通信
emdoorrugged9 个月前
工控机在机器人领域的应用丨工业一体机的应用
人工智能·机器人·工业平板电脑·工控机·工控一体机
emdoorrugged9 个月前
工控机丨丨工业电脑丨工控计算机丨工业一体机丨什么是工业一体机
电脑·工业平板电脑·工控机·工业一体机·工控一体机
疾风铸境1 年前
QT5.14.2开发的Mysql8.0系统安装部署过程
开发语言·qt·mysql·安装包·工控机
掌心天涯1 年前
H110主板搭配魔改QNCW升级小记
工控机