Modbus通信协议
介绍
Modbus是一种串行通信协议,是Modicon公司(现在的施耐德电气 Schneider Electric)于1979年为使用可编程逻辑控制器(PLC)通信而发表。Modbus已经成为工业领域通信协议的业界标准(De facto),并且现在是工业电子设备之间常用的连接方式。
- 公开发表并且无版权要求
- 易于部署和维护
- 对供应商来说,修改移动本地的比特或字节没有很多限制
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进制过来
谁要数据,谁解析
主从的,发的是请求,会有回应