Modbus由MODICON公司(现施耐德公司)于1979年开发,是一种工业现场总线协议标准。主要分为了RTU,ASCII,TCP三种协议类型。本文我们只探讨Qt框架下modbusTCP的应用。 modbusTcp协议采用master/slave模型。在modbus总线中是以"一主多从"关系存在的。通讯方式是主站发出请求(广播或者单播),从站收到请求后应答。
请求 :19 15 00 00 00 06 01 03 05 4A 00 01
应答:19 15 00 00 00 05 01 03 02 11 22
请求:
19 15 为报文标识符,一般每次通信加1来表示区别不同报文
00 00 表示modbusTCP协议
00 06 表示后面的数据长度
01 从站号
03 功能码。
05 4A 读取数据的起始地址
00 01 读几位地址(读连续的地址)
应答:
19 15 为报文标识符
00 00 表示modbusTCP协议
00 05 表示后面的数据长度
01 从站号
03 功能码
02 表示读到的数据长度
11 22 表示读到的数据
不同于RTU协议,不需要进行CRC校验
功能码 | 含义 |
---|---|
01 | 读线圈 |
05 | 写单个线圈 |
0F | 写多个线圈 |
02 | 读离散量输入 |
04 | 读输入寄存器 |
03 | 读保持寄存器 |
06 | 写单个保持寄存器 |
10 | 写多个保持寄存器 |
一般来说上位机作为客户端和主站进行连接
QT += serialport serialbus
#include //modbusTCP客户端
#include //数据容器
#include
//创建套接字
QModbusTcpClient* m_MyClient;
//判断是否已连接
if(m_Myclient->state() != QModbusDevice::ConnectedState)
{
//连接ip和端口 ip为QStirng类型,port为uint类型
m_Myclient->setConnectionParameter(QModbusDevice::NetworkAddressParameter,IP_address);
m_Myclient->setConnectionParameter(QModbusDevice::NetworkPortParameter,Port);
//连接超时和重连次数
m_Myclient->setTimeout(2000);
m_Myclient->setNumberOfRetries(3);
//连接设备
m_Myclient->connectDevice();
}
//参数:读线圈 地址 地址长度
QModbusDataUnit ReadUnit(QModbusDataUnit::QModbusDataUnit::Coils,510,1);
//参数:读寄存器 地址 地址长度
QModbusDataUnit ReadUnit(QModbusDataUnit::HoldingRegisters,510,22);
if (auto *reply = m_Myclient->sendReadRequest(ReadUnit, 1)) //1是从站号(不设置默认1)
{
if (!reply->isFinished())
{
connect(reply, SIGNAL(finished()),this,SLOT(readReady()));
}
else
{
delete reply;
}
}
void ModbusTcpClient::readReady()
{
QModbusReply *reply = qobject_cast<QModbusReply *>(sender());
if (!reply){
return ;
}
if (reply->error() == QModbusDevice::NoError)
{
//接收数据
const QModbusDataUnit unit = reply->result();
for(int i=0; i< unit.valueCount();i++ )
{
//解析数据
quint16 data =unit.value(i);
}
}
reply->deleteLater();
}
5.写线圈和保持寄存器
//参数:写线圈 地址 地址长度
QModbusDataUnit WriteUnit(QModbusDataUnit::QModbusDataUnit::Coils,510,1);
//参数:写寄存器 地址 地址长度
QModbusDataUnit WriteUnit(QModbusDataUnit::HoldingRegisters,510,22);
//设置发送要数据
writeUnit.setValue(i,data);//i 第几个数据 data数据 qunit16类型
//也可以
writeUnit.setValues(QVector<qint16>);
//发送数据
if (auto *reply = m_boxclient->sendWriteRequest(writeUnit, 1))
{
if (!reply->isFinished())
{
connect(reply,SIGNAL(finished()),this,SLOT(slot_WriteData()));
}
else
{
reply->deleteLater();
}
}
void ModbusTcpClient::slot_WriteData()
{
QModbusReply *reply = qobject_cast<QModbusReply *>(sender());
if (!reply){
return ;
}
if (reply->error() == QModbusDevice::NoError)
{
//写入成功
}
reply->deleteLater();
}