工控 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个字节
























相关推荐
拓朗工控3 天前
工业视觉检测:从像素到决策的智能制造之眼
人工智能·视觉检测·制造·工控机·工业电脑
兴华恒成加固计算机4 天前
户外工程为什么更推荐使用加固笔记本呢?
笔记本·工控机·三防笔记本·加固笔记本·加固计算机·加固本·户外工程
派勤电子6 天前
风力发电巡检机器人中的工控机如何选型与应用
机器人·工控机·工控主板·机器人工控主板·机器人工控机·工控主机·风力发电巡检机器人
深圳英康仕6 天前
龙芯2K3000嵌入式工控机的技术拆解:算力、接口与国产系统适配
嵌入式硬件·工控机·工业计算机·国产工控机·龙芯2k3000
拓朗工控8 天前
机器视觉落地有多难?看拓朗工控如何重新定义工控机的“硬核”标准
数码相机·视觉检测·工控机
拓朗工控10 天前
深度学习工控机部署实战:从硬件选型到稳定运行的避坑指南
人工智能·深度学习·智能电视·工控机
派勤电子18 天前
高性能工控机在切割机器人中的应用
工控机·工控主板·机器人工控机·机器人控制工控机·切割机器人工控主板·切割机器人工控机·机器人主板
派勤电子19 天前
多轴控制工控机在喷涂机器人中的应用及产品解决方案
工控机·工控主板·机器人工控机·工业机器人工控机·多轴控制工控机·机器人控制主板·机器人控制主机
拓朗工控24 天前
工控机上电开机:工业自动化的脉搏启动瞬间
运维·自动化·工控机
拓朗工控25 天前
视觉革命:独立显卡工控机在医疗领域的深度应用
人工智能·智慧医疗·工控机