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
























相关推荐
拓朗工控1 天前
视觉检测行业工控机选型指南:核心要素与避坑策略
人工智能·数码相机·视觉检测·工控机·工业电脑
peixiuhui1 天前
GoView 和 FUXA对比差异
工控机·ubuntu22.04·组态软件·goview·rk3506·fuxa·大屏显示
深圳英康仕1 天前
一款面向AGV智能搬运机器人的RK3588工控机的数据资料整理
嵌入式硬件·rk3588·工控机·agv·智能搬运机器人
派勤电子5 天前
AI 加速卡与工控机集成优化 2026 软硬件协同实操指南
边缘计算·机器视觉·工控机·ai推理·工业主机·ai工控机·ai工业应用
派勤电子7 天前
2026 支持 FPGA 工控机 AI 加速应用场景详解
gpu·fpga·npu·工控机·ai工控机·fpga工控机·工业级工控机
拓朗工控8 天前
工业视觉AI边缘计算解决方案
人工智能·深度学习·边缘计算·工控机·工业电脑·拓朗工控
peixiuhui12 天前
ARM工控机与边缘计算网关:工业现场的算力革命与选型实践
网关·边缘计算·数据采集·开发板·工控机·rk3506·工控板
派勤电子12 天前
高防护工控机如何抵御机器人车间的粉尘、水汽与油污侵蚀?
工控机·机器人工控机·高防护工控机·防尘工控机·防水工控机·防油污工控机·喷涂机器人工控机
派勤电子12 天前
宽温工控机如何适应机器人 - 40°C~85°C 的极端工作环境?
工控机·agv机器人·机器人工控机·宽温工控机·agv工控机·耐低温工控机·宽温工业主机
peixiuhui14 天前
RK3506 ubuntu22.04系统编译
ubuntu·开发板·核心板·工控机·ubuntu22.04·瑞芯微·rk3506