modbus在qt中分为服务器与客户端,再细分为串口和TCP,如图:
服务器与客户端:
客户端分为串口与TCP:
服务器分为串口与TCP:
添加相应模块:
添加相应类:
QModbusRtuSerialMaster/QModbusTcpClient/QModbusRtuSerialSlave/QModbusTcpServer
以QModbusRtuSerialMaster为例子:
构建对象:
初始化:
找不到就向父类找,这里是qmodbusdevice有一个定义链接参数的函数,自行查阅手册看下详细
这个函数形参分为对象和值,对象如图:
有串口的名字、校验位、波特率、数据位、停止位;还有网口的端口号与IP地址
值得自己添加。
#ifndef MODBUS_H
#define MODBUS_H
#include <QWidget>
#include <QModbusRtuSerialMaster>
namespace Ui {
class ModBus;
}
class ModBus : public QWidget
{
Q_OBJECT
public:
explicit ModBus(QWidget *parent = nullptr);
~ModBus();
private:
Ui::ModBus *ui;
QModbusRtuSerialMaster *m_modbusrtumaster;
};
#endif // MODBUS_H
初始化:
//初始化
m_modbusrtumaster = new QModbusRtuSerialMaster(this);
m_modbusrtumaster->setConnectionParameter(QModbusDevice::SerialPortNameParameter, "COM3");
m_modbusrtumaster->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::EvenParity);
m_modbusrtumaster->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, 9600);
m_modbusrtumaster->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8);
m_modbusrtumaster->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop);
记得添加类:
#include <QModbusDevice>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QTcpServer>
#include <QTcpSocket>
接下来就是读写,首先是数据单元:
modbus协议可以了解下,不知道的以为也可以用ai搜索下,以下为ai查询结果:
QModbusDataUnit
类在Qt的Modbus模块中是用来表示Modbus协议中不同类型数据单元的。以下是这几个枚举值的区别:
-
QModbusDataUnit::Invalid
- 这是一个无效的数据单元类型,通常用于表示未定义或错误的状态。
-
QModbusDataUnit::DiscreteInputs
- 表示Modbus协议中的离散输入寄存器(DI, Discrete Inputs)。这些寄存器用于读取远程设备上的数字输入状态,如开关、传感器等的开/关状态,每个输入占用1个字节,只能读取,不能写入。
-
QModbusDataUnit::Coils
- 表示Modbus协议中的线圈(Coils)。线圈类似于离散输入,但它们是可以读写的数字输出,也是用来表示开/关状态。每个线圈也占用1个字节。
-
QModbusDataUnit::InputRegisters
- 输入寄存器(IR, Input Registers)用于读取远程设备上的模拟量输入或更复杂的状态信息,每个寄存器通常占用2个字节,也是只读的。
-
QModbusDataUnit::HoldingRegisters
- 持续寄存器(HR, Holding Registers)既可以读取也可以写入,通常用于存储远程设备的设定值、计数值或其它需要保持的数据。每个寄存器也占用2个字节。
在Modbus协议中,这些数据单元类型对应于不同的功能码(Function Codes),例如:
- 功能码02用于读取输入寄存器(InputRegisters)。
- 功能码01用于读取线圈状态(Coils)。
- 功能码04用于读取离散输入状态(DiscreteInputs)。
- 功能码03和16用于读取和写入持续寄存器(HoldingRegisters)。
如果是用来与PLC通信的话,一般用的最多的是QModbusDataUnit::DiscreteInputs与QModbusDataUnit::InputRegisters。像西门子的PLC,模拟量一般留两个字节,开关量一个字节。
这里我们做测试选个持续的寄存器QModbusDataUnit::HoldingRegisters。
//链接设备
if(m_modbusrtumaster->state() != QModbusRtuSerialMaster::ConnectedState)
{
m_modbusrtumaster->connectDevice();
}
//起始位
QModbusDataUnit unit(QModbusDataUnit::HoldingRegisters, ui->lineEdit_startaddr->text().toInt(), 1);
//发送请求响应
QModbusReply *reply = m_modbusrtumaster->sendReadRequest(unit, 2);
然后就是响应了,数据内容在响应里面:
//发送请求响应
QModbusReply *reply = m_modbusrtumaster->sendReadRequest(unit, 1);
if(reply && !reply->isFinished())
{
connect(reply, &QModbusReply::finished, this, [=]()
{
QModbusReply *reply_1 = qobject_cast<QModbusReply *>(sender());//等同于reply_1 = reply
if(reply_1)
{
QModbusDataUnit unit_1 = reply_1->result();//数据存入unit_1
reply_1->deleteLater();
QVector<quint16> data = unit_1.values();
QString s;
Q_FOREACH(quint16 i, data)
{
s.append(QString::number(i)).append(" ");
}
ui->lineEdit_readwrite->setText(s);
}
});
}
源码如下:
头文件:
#ifndef MODBUS_H
#define MODBUS_H
#include <QWidget>
#include <QModbusRtuSerialMaster>
namespace Ui {
class ModBus;
}
class ModBus : public QWidget
{
Q_OBJECT
public:
explicit ModBus(QWidget *parent = nullptr);
~ModBus();
private slots:
void on_pushButton_read_clicked();
void on_pushButton_write_clicked();
private:
Ui::ModBus *ui;
QModbusRtuSerialMaster *m_modbusrtumaster;
};
#endif // MODBUS_H
cpp:
#include "modbus.h"
#include "ui_modbus.h"
#include <QModbusDevice>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QTcpServer>
#include <QTcpSocket>
ModBus::ModBus(QWidget *parent) :
QWidget(parent),
ui(new Ui::ModBus)
{
ui->setupUi(this);
//初始化
m_modbusrtumaster = new QModbusRtuSerialMaster(this);
m_modbusrtumaster->setConnectionParameter(QModbusDevice::SerialPortNameParameter, "COM3");
m_modbusrtumaster->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::EvenParity);
m_modbusrtumaster->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, 9600);
m_modbusrtumaster->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8);
m_modbusrtumaster->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop);
}
ModBus::~ModBus()
{
delete ui;
}
void ModBus::on_pushButton_read_clicked()
{
//链接设备
if(m_modbusrtumaster->state() != QModbusRtuSerialMaster::ConnectedState)
{
m_modbusrtumaster->connectDevice();
}
//起始位
QModbusDataUnit unit(QModbusDataUnit::HoldingRegisters, ui->lineEdit_startaddr->text().toInt(), 2);//读2个位
//发送请求响应
QModbusReply *reply = m_modbusrtumaster->sendReadRequest(unit, 1);
if(reply && !reply->isFinished())
{
connect(reply, &QModbusReply::finished, this, [=]()
{
QModbusReply *reply_1 = qobject_cast<QModbusReply *>(sender());//等同于reply_1 = reply
if(reply_1)
{
QModbusDataUnit unit_1 = reply_1->result();//数据存入unit_1
reply_1->deleteLater();
QVector<quint16> data = unit_1.values();
QString s;
Q_FOREACH(quint16 i, data)
{
s.append(QString::number(i)).append(" ");
}
ui->lineEdit_readwrite->setText(s);
}
});
}
}
void ModBus::on_pushButton_write_clicked()
{
if(m_modbusrtumaster->state() != QModbusRtuSerialMaster::ConnectedState)
{
m_modbusrtumaster->connectDevice();
}
QVector<quint16> data;
data << 1 << 89;
QModbusDataUnit unit(QModbusDataUnit::HoldingRegisters,
ui->lineEdit_startaddr->text().toInt(), data);
m_modbusrtumaster->sendWriteRequest(unit, 1);
}