基于TCP协议的Modbus协议,应用于电子控制器上的一种通用语言。
ModusBus协议数据格式:
写多个寄存器
97 79 00 00 00 09 04 10 00 00 00 01 02 00 01 | ||||
| 示例 | 长度 | 说明 | 备注 |
Map报文头 | 0x97 | 1 | 事务处理标识符Hi | 客户机发起,服务器复制,用于事务处理配对 |
0x79 | 1 | 事务处理标识符Lo | ||
0x0000 | 2 | 协议标识符号 | 客户机发起,服务器复制 Modbus协议 = 0. | |
0x0009 | 2 | 长度 | 从本字节下一个到最后
| |
0x04 | 1 | 单元标识符 | 客户机发起,服务器复制 串口链路或其他总线上远程终端标识 | |
功能码 | 0x10 | 1 | 功能码,读寄存器 | 参考标准modbus协议 |
数据 | 0x0000 | 2 | 起始地址 |
|
0x 0001 | 2 | 写寄存器数量 |
| |
0x 02 | 1 | 写字节的个数 |
| |
00 01 | 2 | 目标值 |
| |
校验 |
|
数据请求回复
97 96 00 00 00 FD 04 04 FA AB 9E 41 18 7A E1 3F 94 7A E1 3F 94 0A 3D 3F 97 51 EC 3F 98 CC CD C0 6C 33 33 C0 E3 CC CD C0 EC EB 85 41 F1 D7 0A 41 E9 47 AE 41 ED EB 85 41 F1 19 9A 43 D0 E6 66 43 C9 4C CD 43 CF EB 85 41 F3 66 66 42 0F CC CD 41 C2 E6 66 44 0A 1E B8 41 FB A3 D7 42 0C CC CD 41 BC C0 00 44 0A B8 52 41 F6 5C 29 42 0F 47 AE 41 D1 C6 66 44 0A 00 00 00 00 C9 9E FF 7F C9 9E FF 7F C9 9E FF 7F C9 9E FF 7F C9 9E FF 7F C9 9E FF 7F C9 9E FF 7F C9 9E FF 7F C9 9E FF 7F 05 16 00 00 04 11 00 00 05 16 00 00 04 11 00 00 05 16 00 00 04 11 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0A 00 0A 00 0A 00 0A 00 04 00 04 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0F | ||||
| 示例 | 长度 | 说明 | 备注 |
Map报文头 | 0x97 | 1 | 事务处理标识符Hi | 客户机发起,服务器复制,用于事务处理配对 |
0x96 | 1 | 事务处理标识符Lo | ||
0x0000 | 2 | 协议标识符号 | 客户机发起,服务器复制 Modbus协议 = 0. | |
0x00FD | 2 | 长度 | 从本字节下一个到最后
| |
0x04 | 1 | 单元标识符 | 客户机发起,服务器复制 串口链路或其他总线上远程终端标识 | |
功能码 | 0x04 | 1 | 功能码,读寄存器 | 参考标准modbus协议 |
数据 | 0x FA | 1 | 字节个数 |
|
0x---- |
| 数据 | ||
校验 |
|
部分功能码
代码 | 中文名称 | 寄存器PLC地址 | 位操作/字操作 | 操作数量 |
01 | 读线圈状态 | 00001-09999 | 位操作 | 单个或多个 |
02 | 读离散输入状态 | 10001-19999 | 位操作 | 单个或多个 |
03 | 读保持寄存器 | 40001-49999 | 字操作 | 单个或多个 |
04 | 读输入寄存器 | 30001-39999 | 字操作 | 单个或多个 |
05 | 写单个线圈 | 00001-09999 | 位操作 | 单个 |
06 | 写单个保持寄存器 | 40001-49999 | 字操作 | 单个 |
15 | 写多个线圈 | 00001-09999 | 位操作 | 多个 |
16 | 写多个保持寄存器 | 40001-49999 | 字操作 | 多个 |
代码
//PlcModusTcp.h SocketClient为第一章中的tcp通讯
#pragma once
#include "SocketClient.h"
class PlcModbusTcp
{
public:
PlcModbusTcp();
~PlcModbusTcp();
ErrorCode Connect(char* ip,int port);
ErrorCode Disconnect();
ErrorCode ReadRegister(int addr, int len, short* buf);
ErrorCode WriteRegister(int addr, int len, short* buf);
private:
CRITICAL_SECTION cs;
char m_ip[20];
int m_port;
unsigned short m_cmdID;
SocketClient m_socketClient;
};
//PlcModusTcp.cpp
#include "PlcModbusTcp.h"
/********************
@date:2019/03/16
@author: yuyang
@info:与PLC通讯Modbus协议
********************/
PlcModbusTcp::PlcModbusTcp()
{
m_cmdID = 0;
InitializeCriticalSection(&cs);
}
PlcModbusTcp::~PlcModbusTcp()
{
DeleteCriticalSection(&cs);
}
ErrorCode PlcModbusTcp::Connect(char* ip, int port)
{
ErrorCode res = ERRORCODE_OK;
memset(m_ip, 0, 20);
strcpy(m_ip, ip);
m_port = port;
res=m_socketClient.Connect(m_ip, port, 100, 100);
if (res != ERRORCODE_OK)
{
goto ExitLine;
}
ExitLine:
if (res != ERRORCODE_OK)
{
m_socketClient.Disconnect();
}
return res;
}
ErrorCode PlcModbusTcp::Disconnect()
{
return m_socketClient.Disconnect();
}
ErrorCode PlcModbusTcp::ReadRegister(int addr, int len, short* buf)
{
EnterCriticalSection(&cs);
ErrorCode res = ERRORCODE_OK;
byte cmd[12];
byte bufIn[1000];
int cmdlen;
if (!m_socketClient.IsConnected())
{
res=m_socketClient.Connect(m_ip, m_port, 100, 100);
if (res != ERRORCODE_OK)
{
goto ExitLine;
}
}
cmd[0] = m_cmdID/256;
cmd[1] = m_cmdID%256;
cmd[2] = 0x00;
cmd[3] = 0x00;
cmd[4] = 0x00;
cmd[5] = 0x06;
cmd[6] = 0x01;
cmd[7] = 0x03;
cmd[8] = addr / 256;
cmd[9] = addr % 256;
cmd[10] = len / 256;
cmd[11] = len % 256;
cmdlen = 12;
res = m_socketClient.Write(cmd, cmdlen);
if (res != ERRORCODE_OK)
{
goto ExitLine;
}
cmdlen = 1000;
res = m_socketClient.Read(bufIn, cmdlen);
if (res != ERRORCODE_OK)
{
goto ExitLine;
}
if (cmdlen !=9+2* len)
{
res = ERRORCODE_FAIL;
goto ExitLine;
}
for (int i = 0; i < 8; i++)
{
if (i!=5&&bufIn[i] != cmd[i])
{
res = ERRORCODE_FAIL;
goto ExitLine;
}
}
for (int i = 0; i < len; i++)//响应从第10个字节开始为数据
{
buf[i] = bufIn[9 + 2 * i] * 256 + bufIn[9 + 2 * i + 1];//高位在前
}
ExitLine:
if (res != ERRORCODE_OK)
{
m_socketClient.Disconnect();
}
LeaveCriticalSection(&cs);
return res;
}
ErrorCode PlcModbusTcp::WriteRegister(int addr, int len, short* buf)
{
EnterCriticalSection(&cs);
ErrorCode res = ERRORCODE_OK;
byte cmd[1000];
byte bufIn[100];
int cmdlen;
if (!m_socketClient.IsConnected())
{
res = m_socketClient.Connect(m_ip, m_port, 100, 100);
if (res != ERRORCODE_OK)
{
goto ExitLine;
}
}
m_cmdID++;
cmd[0] = m_cmdID / 256; //为此次通信事务处理标识符,一般每次通信之后将被要求加1以区别不同的通信数据报文;
cmd[1] = m_cmdID % 256;
cmd[2] = 0x00; //协议标识符,00 00为modbus协议;
cmd[3] = 0x00;
cmd[4] = 0x00; //数据长度,用来指示接下来数据的长度,单位字节;
cmd[5] = 7+2*len;
cmd[6] = 0x01; //设备地址
//用以标识连接在串行线或者网络上的远程服务端的地址。以上七个字节也被称为modbus报文头;
cmd[7] = 0x10; //功能码 03读 16写 保持寄存器数据
cmd[8] = addr / 256; //起始地址
cmd[9] = addr % 256;
cmd[10] = len / 256; //请求寄存器数量 word数量
cmd[11] = len % 256;
cmd[12] = 2*len;
for (int i = 0; i < len; i++)
{
cmd[13 + i * 2] = (buf[i]>>8) & 0xFF; //负数 右移为算术位移 左端补1 结果为255 (-100 short转byte为:11111111 10011100)
cmd[13 + i * 2 +1] = buf[i] & 0xFF;
}
cmdlen = 13 + 2 * len;
res = m_socketClient.Write(cmd, cmdlen);
if (res != ERRORCODE_OK)
{
goto ExitLine;
}
cmdlen = 100;
res = m_socketClient.Read(bufIn, cmdlen);
if (res != ERRORCODE_OK)
{
goto ExitLine;
}
if (cmdlen != 12)
{
res = ERRORCODE_FAIL;
goto ExitLine;
}
for (int i = 0; i < 12; i++)
{
if (i != 5 && bufIn[i] != cmd[i])
{
res = ERRORCODE_FAIL;
goto ExitLine;
}
}
ExitLine:
if (res != ERRORCODE_OK)
{
m_socketClient.Disconnect();
}
LeaveCriticalSection(&cs);
return res;
}