概念
Modbus是一种串行通信协议,Modbus协议目前存在用于串口、以太网以及其他支持互联网协议的网络的版本。
大多数Modbus设备通信通过串口EIA-485物理层进行。
通讯格式
地址域 | 功能码 | 数据 | CRC校验(低字节在前) |
---|---|---|---|
1字节 | 1字节 | N字节 | 2字节 |
在单片机硬件通讯串口行业,很多厂家都把485串口参数设置为:
波特率9600,无奇偶校验,1停止位
功能码说明
实际用途中常用的功能码有:
- 0x03用来读取单片机寄存器的数据
- 0x06用来给单片机写入数据
功能码0x03:读单个或多个保持寄存器
设备地址(1字节) | 功能码(1字节) | 寄存器起始地址(2字节) | 寄存器数量(2字节) | CRC校验(2字节) |
---|---|---|---|---|
XX | 03 | XX XX | XX XX | XX XX |
响应:
设备地址(1字节) | 功能码(1字节) | 数据长度(2字节) | 数据(N字节) | CRC校验(2字节) |
---|---|---|---|---|
XX | 03 | XX XX | N字节 | XX XX |
示例
查询设备类型
设备地址(1字节) | 功能码(1字节) | 数据长度(2字节) | 数据(N字节) | CRC校验(2字节) |
---|---|---|---|---|
01 | 03 | 00 01 | 00 01 | D5 CA |
查询设备类型响应
设备地址(1字节) | 功能码(1字节) | 数据长度(2字节) | 数据(N字节) | CRC校验(2字节) |
---|---|---|---|---|
01 | 03 | 02 | 00 21 | 78 5C |
功能码0x06:写单个寄存器
设备地址(1字节) | 功能码(1字节) | 寄存器地址(2字节) | 数据(N字节) | CRC校验(2字节) |
---|---|---|---|---|
XX | 06 | XX XX | N字节 | XX XX |
响应:
设备地址(1字节) | 功能码(1字节) | 寄存器地址(2字节) | 数据(N字节) | CRC校验(2字节) |
---|---|---|---|---|
XX | 06 | XX XX | N字节 | XX XX |
示例
写数据
设备地址(1字节) | 功能码(1字节) | 寄存器地址(2字节) | 数据(N字节) | CRC校验(2字节) |
---|---|---|---|---|
01 | 06 | 00 14 | 00 00 | C9 CE |
Java版modbus协议代码:
package com.wrs.project.modbus;
import java.util.ArrayList;
import java.util.List;
public class ModbusUtils {
/**
* 读指令
* @param deviceAddress 设备地址
* @param registerAddress 寄存器起始地址
* @param registerCount 寄存器个数
* @return
*/
public static byte[] getReadModbus(int deviceAddress, int registerAddress, int registerCount) {
int registerLow = registerAddress & 0xFF;
int registerHigh = registerAddress >>> 8;
int countLow = registerCount & 0xFF;
int countHigh = registerCount >>> 8;
int[] data = new int[]{registerHigh, registerLow, countHigh, countLow};
byte[] sendData = getModbusData(deviceAddress, 0x03, data);
return sendData;
}
/**
* 写指令
* @param deviceAddress 设备地址
* @param registerAddress 寄存器起始地址
* @param writeData 寄存器个数
* @return
*/
public static byte[] getWriteModbus(int deviceAddress, int registerAddress, int[] writeData) {
int registerLow = registerAddress & 0xFF;
int registerHigh = registerAddress >>> 8;
int[] data = new int[writeData.length + 2];
data[0] = registerHigh;
data[1] = registerLow;
for (int i = 0; i < writeData.length; i ++) {
data[i + 2] = writeData[i];
}
byte[] sendData = getModbusData(deviceAddress, 0x06, data);
return sendData;
}
public static List<Byte> isValidModbusResponseData(List<Byte> list) {
if (null != list && list.size() >= 4) {
int size = list.size();
int leng = ByteUtils.byteToInt(list.get(2));
int totalSize = leng + 5;
if (size < totalSize) {
return null;
}
List tempList = list.subList(0, totalSize - 2);
byte[] tempData = ArrayUtils.listTobyte(tempList);
int crc = CRCUtils.getCRC(tempData);
int low = crc & 0xFF;
int high = crc >>> 8;
byte lowData = list.get(totalSize - 2);
byte hightData = list.get(totalSize - 1);
if (low == ByteUtils.byteToInt(lowData) && high == ByteUtils.byteToInt(hightData)) {
return list.subList(0, totalSize);
} else {
return null;
}
} else {
return null;
}
}
public static byte[] getModbusData(int address, int function, int[] data) {
int leng = 2;
if (null != data) {
leng += data.length;
}
byte[] tempData = new byte[leng];
tempData[0] = ByteUtils.intToByte(address);
tempData[1] = ByteUtils.intToByte(function);
if (null != data) {
for (int i = 0; i < data.length; i++) {
tempData[i + 2] = ByteUtils.intToByte(data[i]);
}
}
int crc = CRCUtils.getCRC(tempData);
int low = crc & 0xFF;
int high = crc >>> 8;
List list = new ArrayList();
list.addAll(ArrayUtils.byteToList(tempData));
list.add(ByteUtils.intToByte(low));
list.add(ByteUtils.intToByte(high));
return ArrayUtils.listTobyte(list);
}
}
项目源码下载
网友评论