Modbus通信协议和Java实现

Modbus通信协议

介绍

Modbus是一种串行通信协议,是Modicon公司(现在的施耐德电气 Schneider Electric)于1979年为使用可编程逻辑控制器(PLC)通信而发表。Modbus已经成为工业领域通信协议的业界标准(De facto),并且现在是工业电子设备之间常用的连接方式。

  1. 公开发表并且无版权要求
  2. 易于部署和维护
  3. 对供应商来说,修改移动本地的比特或字节没有很多限制

Modbus允许多个 (大约240个) 设备连接在同一个网络上进行通信,举个例子,一个测量温度和湿度的装置,并且将结果发送给计算机。在数据采集与监视控制系统(SCADA)中,Modbus通常用来连接监控计算机和远程终端控制系统(RTU)。

Modbus协议是一种应用层的报文传输协议
同一个父亲,儿子有三种形式

  • RTU
  • ASCII
  • TCP

RTU

目的:通信,本质是读写

存储区

存储区:输入线圈、输出线圈、输入寄存器、输出寄存器
线圈和寄存器表示最小单位,存布尔用线圈 1位 0|1,存数据用寄存器 16位
1位等于1bit
16位代表二进制的16个位,无符号 最小为0, 最大为 FFFF。有符号 最小为-8000 最大为7FFF

比如
输出线圈 0
输入线圈 1
输出寄存器 4
输入寄存器 3

存储区范围:
**标准地址 5位 ** XXXXX 第一位表示哪个存储区
Y XXXX

输出线圈 00001-09999
输入线圈 10001-19999
输出寄存器 40001-49999
输入寄存器 30001-39999
通过第一位就知道是哪个存储区,后面表示第多少个寄存器或者线圈

**扩展地址 6位 **
Y XXXXX

输出线圈 000001-065535
输入线圈 100001-165536
输出寄存器 400001-465536
输入寄存器 300001-365536

最多到65536

通信

批注:
输入端,也就是电源进去的一端
输出端,也就是电源出去的一端
读和写

读输出线圈 01
读输入线圈 02
读输出寄存器 03
读输入寄存器 04

写单个输出线圈 05
写单个输出寄存器 06

写多个输出线圈 15
写多个输出寄存器 16

6种动作-功能-功能码

协议

ModbusRTU/ASCII

规定报文格式:
从站地址(设备编号)(1byte)+ 功能码(1byte) + 数据(N byte) + CRC校验 (2byte)

对于读取来说:
从站地址(设备编号) —区分设备(多个人,先喊名字再说话)找谁
功能码 — 干嘛
数据 — 具体干嘛的细节
校验 —验证

对于写入来说:
从站地址(设备编号) —区分设备(多个人,先喊名字再说话)找谁
功能码 — 干嘛
数据 — 具体干嘛的细节(细节更多,多了写入的具体数值)
校验 —验证

举个栗子:
01 03 00 00 00 02 C4 0B (发送)
01 03 04 01 46 01 3B 5A 59 (回)

01 站地址
03 读输出寄存器
00 00 起始寄存器
00 02 寄存器长度
C4 0B 校验码
从站地址1,读输出寄存器,从0开始,读2个

01 站地址
03 读输出寄存器
04 字节计数
01 46 01 3B 具体4个字节
0146 16进制转换成10进制的结果 326 说明书上会说 0表示湿度 得到后除以10就行,也就是32.6
013B 315温度也就是31.5

5A 59 校验码

校验码代码

 private static readonly byte[] aucCRCHi = {
             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
             0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
             0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
             0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
             0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
             0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
             0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
             0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
             0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
             0x00, 0xC1, 0x81, 0x40
         };
        private static readonly byte[] aucCRCLo = {
             0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7,
             0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E,
             0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9,
             0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC,
             0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
             0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32,
             0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D,
             0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38,
             0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF,
             0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
             0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1,
             0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4,
             0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB,
             0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA,
             0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
             0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0,
             0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97,
             0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E,
             0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89,
             0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
             0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83,
             0x41, 0x81, 0x80, 0x40
         };

        /// <summary>
        /// CRC校验
        /// </summary>
        /// <param name="pucFrame">字节数组</param>
        /// <param name="usLen">验证长度</param>
        /// <returns>2个字节</returns>
        public  static  byte[] CalculateCRC(byte[] pucFrame, int usLen)
        {
            int i = 0;
            byte[] res = new byte[2] { 0xFF, 0xFF };
            ushort iIndex;
            while (usLen-- > 0)
            {
                iIndex = (ushort)(res[0] ^ pucFrame[i++]);
                res[0] = (byte)(res[1] ^ aucCRCHi[iIndex]);
                res[1] = aucCRCLo[iIndex];
            }
            return res;
        }

栗子

在这里插入图片描述

在这里插入图片描述

说明书的地址

温度的Modbus地址是多少?40001
湿度?40002
绝对地址

通讯报文的地址,是相对地址。不需要给绝对值了,会出现重复和冲突。从0开始
对于输出寄存器来说 0对应-40001
对于输入寄存器 0对应-30001

交流的时候:
温度数据给我,地址是多少?
直接说40001,既告诉了哪个区,有说了多少个

要知道10进制还是16进制过来

谁要数据,谁解析
主从的,发的是请求,会有回应

实现

Java实现ModbusTCP通信

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Modbus是一种通信协议,用于在不同设备之间进行数据传输和通信。Modbus RTU是Modbus协议的一种变体,它使用二进制编码方式传输数据,并且常用于串行通信。 要在Java实现Modbus RTU协议,你可以使用一些现有的Java库来简化开发过程。以下是一个基本的示例,使用Jamod库实现Modbus RTU通信: 1. 首先,你需要添加Jamod库的依赖。你可以在pom.xml文件中添加以下代码来引入Jamod库: ```xml <dependencies> <dependency> <groupId>net.wimpi</groupId> <artifactId>jamod</artifactId> <version>1.2</version> </dependency> </dependencies> ``` 2. 创建一个Java类来实现Modbus通信。下面是一个简单的例子: ```java import net.wimpi.modbus.Modbus; import net.wimpi.modbus.io.ModbusSerialTransaction; import net.wimpi.modbus.msg.ReadInputRegistersRequest; import net.wimpi.modbus.msg.ReadInputRegistersResponse; import net.wimpi.modbus.net.SerialConnection; import net.wimpi.modbus.util.SerialParameters; public class ModbusRTUExample { public static void main(String[] args) { // 创建串口参数 SerialParameters parameters = new SerialParameters(); parameters.setPortName("/dev/ttyUSB0"); // 设置串口名称 parameters.setBaudRate(9600); // 设置波特率,根据实际情况修改 parameters.setDatabits(8); // 设置数据位 parameters.setParity("None"); // 设置校验位 parameters.setStopbits(1); // 设置停止位 // 创建串口连接 SerialConnection connection = new SerialConnection(parameters); try { // 打开串口连接 connection.open(); // 创建Modbus请求 ReadInputRegistersRequest request = new ReadInputRegistersRequest(0, 10); // 读取寄存器地址0-9的数据 // 创建Modbus事务 ModbusSerialTransaction transaction = new ModbusSerialTransaction(connection); transaction.setRequest(request); // 执行Modbus请求 transaction.execute(); // 获取Modbus响应 ReadInputRegistersResponse response = (ReadInputRegistersResponse) transaction.getResponse(); if (response != null) { // 处理Modbus响应数据 int[] registers = response.getRegisters(); for (int register : registers) { System.out.println("Register value: " + register); } } else { System.out.println("No response received."); } } catch (Exception e) { e.printStackTrace(); } finally { // 关闭串口连接 connection.close(); } } } ``` 请注意,上述示例中的串口参数和寄存器地址需要根据你的实际情况进行调整。你还需要根据你的操作系统和串口连接的实际情况修改串口名称。 这只是一个简单的示例,你可以根据自己的需求进一步扩展和定制。希望这可以帮助你开始使用Java实现Modbus RTU协议
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值