qt c++ modeBus通信实例

前言:最近要编一个modbus的客户端,利用TCP网络通信。在编写之前我脑海中的默认想法是:

1、建立一个客户端Client上位机,与设备通信,可以下发命令,也可以读取参数。————但是最开始我坚定不移的认为,客户端是从站。于是我写好基本的连接代码后,就尝试与modbus主站模拟器连接,怎么都连不上。我都疯魔了,代码我检查了数十遍。

然后我才开始搜索主从站,服务器,客户端定义,恍然大悟。

我的错误想法:想当然的以为,服务器Sever是老大,当然是主站,老大当然是服务器;客户端Client听着就比较小,当然是从站。

我一直在用客户端(主站)连接另一个客户端(主站),能连上才有鬼!!!!

重点:

客户端(Client),是主站(Master)

服务器(Server),是从站(Slave)

对于上位机,看具体协议要求,上位机自己没有主从站之分。如果拿到的通信协议让你建立Sever那你就是服务器,是从站;如果是要求你建立Client,那就是主站。

2、modbus简介

modbus通信时候,有多个功能码,刚开始可以了解一下。用qt开发的时候,完全没必要关注这些功能码,qt自己打包的modbus类,建立好实例后,例如:QModbusRtuSerialMaster modbusMaster,接下来就用该实例来发送读取请求、和写入请求。这个阶段,只需要关注,你需要操作的地址是什么:

1、线圈寄存器---------Coils-----地址1~9999

2、Discreteinputs(InputStatus)---离散输入(输入线圈、离散寄存器)------地址10001~19999

3、HoldingRegisters---------保持寄存器-------------地址40001~49999

4、InputRegisters----------输入寄存器地址:30001~39999

这里面,是我在查资料过程中确认的,尤其是第二种,不同的地方,叫法不一。

红色部分,是qt中的名称,在编码过程中,是那个寄存器,到时候就用那个。

目录

一、新建项目,模块包含。

二、代码

三、实现效果

3.1保持寄存器数据写

在前期学习中,就开始根据协议手搓通讯程序。编写过程中对modebus协议理解总是不到位,后来不断查资料,才知道qt有实现好的modebus类不用自己从头开始根据协议手搓。不过手搓过程虽然显得蠢,对协议理解还是有帮助。后来利用Qt中有的modebus类就得心应手了。

总的来说和有两中模式,一种是modeBus串口通信,一种是modeBus网口通信,都用QT封装好的类实现。

 modebus通讯协议理解推荐:详解Modbus通信协议---清晰易懂-CSDN博客

本文实现modeBus串口通信,末尾有网口RTU介绍,掌握一种之后,第二种就不难。

环境,VS2015,qt5.9.6.

一、新建项目,模块包含。

        新建qt widgets项目,包含需要用到的两个类,在属性中添加,然后构建程序,重新扫描就行。

        然后,ui界面布局如下。我们的程序逻辑是,点击“查询”按钮——获取“设备ID”、“模式”、“起始地址”、“长度”,对设备发起读取请求,然后接收设备反馈,在界面显示。

这时候需要用到一个modebus设备模拟器,推荐:modbus模拟器(可模拟Master和Slave)_modbus模拟器,modbus模拟资源-CSDN文库

二、代码

.h头文件

#pragma once

#include <QtWidgets/QWidget>
#include "ui_myModeBus.h"
#include <QSerialPort>
#include <QtSerialBus/QtSerialBus>
#include <QtSerialBus/QModbusDataUnit>
#include <QtSerialBus/qmodbusrtuserialmaster.h>
#include <qserialportinfo.h>
#include <unordered_map> //非排序图
#include <qmessagebox.h>

class myModeBus : public QWidget
{
    Q_OBJECT

public:
    myModeBus(QWidget *parent = Q_NULLPTR);

	private slots:
	void on_pushButton_conn_clicked();
	void on_pushButton_query_clicked();
	void query_out();
	void on_lineEdit_data1_returnPressed();
	void on_lineEdit_data2_returnPressed();
	void on_lineEdit_data3_returnPressed();
	void set_data();

private:
    Ui::myModeBusClass ui;
	QModbusRtuSerialMaster modbusMaster;//主机类

	//波特率
	std::unordered_map<int, QSerialPort::BaudRate> baudRateMap =
	{
		{ 0, QSerialPort::Baud1200 },{ 1, QSerialPort::Baud2400 },{ 2, QSerialPort::Baud4800 },{ 3, QSerialPort::Baud9600 },
		{ 4, QSerialPort::Baud19200 },{ 5, QSerialPort::Baud38400 },{ 6, QSerialPort::Baud57600 },{ 7, QSerialPort::Baud115200 }
	};
	//数据位
	std::unordered_map<int, QSerialPort::DataBits>dataMap =
	{
		{ 0,QSerialPort::Data5 },{ 1,QSerialPort::Data6 },{ 2,QSerialPort::Data7 },{ 3,QSerialPort::Data8 }
	};
	//
	//校验位
	std::unordered_map<int, QSerialPort::Parity>parityMap =
	{
		{ 0,QSerialPort::NoParity },{ 1,QSerialPort::OddParity },{ 2,QSerialPort::EvenParity }
	};
	//停止位
	std::unordered_map<int, QSerialPort::StopBits>stopMap =
	{
		{ 0,QSerialPort::OneStop },{ 1,QSerialPort::OneAndHalfStop },{ 2,QSerialPort::TwoStop }
	};
	//状态
	std::unordered_map<int,QModbusDataUnit::RegisterType>RegMap=
	{    //线圈                     //离散输入                             //保持寄存器                               输入寄存器
		{0,QModbusDataUnit::Coils},{ 1,QModbusDataUnit::DiscreteInputs },{ 2,QModbusDataUnit::HoldingRegisters },{ 3,QModbusDataUnit::InputRegisters }
	};
};

.cpp文件

#include "myModeBus.h"

myModeBus::myModeBus(QWidget *parent)
    : QWidget(parent)
{
    ui.setupUi(this);
	ui.comboBox_baudrate->addItem(QString::number(1200));
	ui.comboBox_baudrate->addItem(QString::number(2400));
	ui.comboBox_baudrate->addItem(QString::number(4800));
	ui.comboBox_baudrate->addItem(QString::number(9600));
	ui.comboBox_baudrate->addItem(QString::number(19200));
	ui.comboBox_baudrate->addItem(QString::number(38400));
	ui.comboBox_baudrate->addItem(QString::number(57600));
	ui.comboBox_baudrate->addItem(QString::number(115200));
	ui.comboBox_baudrate->setCurrentIndex(3);

	QStringList dataList = (QStringList() << QString::fromLocal8Bit("5位") << QString::fromLocal8Bit("6位") << QString::fromLocal8Bit("7位") << QString::fromLocal8Bit("8位"));
	ui.comboBox_datasize->addItems(dataList);
	ui.comboBox_datasize->setCurrentIndex(3);

	QStringList checklist = (QStringList() << QString::fromLocal8Bit("无校验") << QString::fromLocal8Bit("奇校验") << QString::fromLocal8Bit("偶校验"));
	ui.comboBox_check->addItems(checklist);
	ui.comboBox_check->setCurrentIndex(1);

	QStringList stoplist = (QStringList() << QString::fromLocal8Bit("1位") << QString::fromLocal8Bit("1.5位") << QString::fromLocal8Bit("2位"));
	ui.comboBox_stop->addItems(stoplist);
	ui.comboBox_stop->setCurrentIndex(0);

	QStringList modlist = (QStringList() << QString("Coils") << QString("DiscreteInputs") << QString("HoldingRegisters") << QString("InputRegisters"));
	ui.comboBox_mode->addItems(modlist);
	ui.comboBox_mode->setCurrentIndex(0);

	QStringList portNameList;
	QList<QSerialPortInfo> serialPortInfos = QSerialPortInfo::availablePorts();
	ui.comboBox_com->clear();
	for (int i = 0; i < serialPortInfos.count(); i++)
	{
		QSerialPort serial;
		serial.setPort(serialPortInfos[i]);
		if (serial.open(QIODevice::ReadWrite))
		{
			portNameList.append(serial.portName());
			serial.close();
		}
	}
	ui.comboBox_com->addItems(portNameList);
	ui.comboBox_com->setCurrentIndex(0);

}

void myModeBus::on_pushButton_conn_clicked()
{
	QString comstr = ui.comboBox_com->currentText();
	modbusMaster.setConnectionParameter(QModbusDevice::SerialPortNameParameter, comstr); // 设置串口名称

	int baudindex = ui.comboBox_baudrate->currentIndex();
	modbusMaster.setConnectionParameter(QModbusDevice::SerialBaudRateParameter, baudRateMap[baudindex]);

	int dataindex = ui.comboBox_datasize->currentIndex();
	modbusMaster.setConnectionParameter(QModbusDevice::SerialDataBitsParameter, dataMap[dataindex]);    // 设置数据位

	int parityindex = ui.comboBox_check->currentIndex();
	modbusMaster.setConnectionParameter(QModbusDevice::SerialParityParameter, parityMap[parityindex]); // 设置奇校验位

	int stopindex = ui.comboBox_stop->currentIndex();
	modbusMaster.setConnectionParameter(QModbusDevice::SerialStopBitsParameter, stopMap[stopindex]); // 设置停止位

	if (modbusMaster.state() != QModbusDevice::ConnectedState)
	{
		if (!modbusMaster.connectDevice())
		{
			qDebug() << "Failed to connect to Modbus device!";
		}
		else
		{
			QMessageBox::information(this, QString::fromLocal8Bit("连接"), QString::fromLocal8Bit("确认"));
			ui.pushButton_conn->setText(QString::fromLocal8Bit("断开"));
		}
	}
	else
	{
		QMessageBox::information(this, QString::fromLocal8Bit("断开"), QString::fromLocal8Bit("确认"));
		ui.pushButton_conn->setText(QString::fromLocal8Bit("连接"));
		modbusMaster.disconnectDevice();	
	}
}

void myModeBus::on_pushButton_query_clicked()
{
	int moIndex = ui.comboBox_mode->currentIndex();
	int deviceID = ui.lineEdit_Deviceaddress->text().toInt();
	int address = ui.lineEdit_address->text().toInt();
	int length = ui.lineEdit_length->text().toInt();

	QModbusDataUnit readUnit(RegMap[moIndex], address-1, length); // 读取保持寄存器,起始地址为100,长度读3
	auto *reply1 = modbusMaster.sendReadRequest(readUnit, deviceID);//获取温度值
	if (reply1)
	{
		// 等待读取完成
		connect(reply1, &QModbusReply::finished, this, &myModeBus::query_out);//
	}
	else
	{
		qDebug() << "Failed to send Modbus read request!";
	}
}
void myModeBus::query_out()
{
	ui.textEdit_receive->clear();
	auto reply = qobject_cast<QModbusReply *>(sender());
	//这是 QObject 类的一个方法。在Qt的信号和槽机制中,当一个信号被触发时,sender() 方法就提供了这样的功能,
	//它返回触发信号的对象的指针(如果可用)
	if (!reply)
		return;
	if (reply->error() == QModbusDevice::NoError)
	{// 处理读取到的数据
		const auto values = reply->result().values();
		int value;
		for (int i = 0; i < values.size(); i++)
		{
			QString str = QString("<%1>:%2").arg(ui.lineEdit_address->text().toInt()+i).arg(values[i]);
			ui.textEdit_receive->append(str);
		}
	}
	else
	{
		qDebug() << "Read error:" << reply->errorString();
	}
	reply->deleteLater();
}
void myModeBus::on_lineEdit_data1_returnPressed()
{
	set_data();
}
void myModeBus::on_lineEdit_data2_returnPressed()
{
	set_data();
}
void myModeBus::on_lineEdit_data3_returnPressed()
{
	set_data();
}
void myModeBus::set_data()
{
	bool ok;
	QVector<quint16> data;
	//data.resize(3);
	quint16 data1 = ui.lineEdit_data1->text().toUShort(&ok);
	if(ok)
	data.append(data1);

	quint16 data2 = ui.lineEdit_data2->text().toUShort(&ok);
	if(ok)
	data.append(data2);

	quint16 data3 = ui.lineEdit_data3->text().toUShort(&ok);
	if(ok)
	data.append(data3);

	qDebug() << "write1:"<< data;

	int moIndex = ui.comboBox_mode->currentIndex();
	int deviceID = ui.lineEdit_Deviceaddress->text().toInt();
	int address = ui.lineEdit_address->text().toInt();
	int length = ui.lineEdit_length->text().toInt();

	QModbusDataUnit writeUnit(RegMap[moIndex], address-1, length); // 写入保持寄存器0,	
	writeUnit.setValues(data);

	if (auto *reply2 = modbusMaster.sendWriteRequest(writeUnit, deviceID))
	{
		// 等待写入完成
		if (!reply2->isFinished())
		{
			QEventLoop loop;
			QObject::connect(reply2, &QModbusReply::finished, &loop, &QEventLoop::quit);
			loop.exec();
		}

		// 检查写入结果
		if (reply2->error() == QModbusDevice::NoError)
		{
			qDebug() << "Write request completed successfully!";
		}
		else
		{
			qDebug() << "Write error:" << reply2->errorString();
		}

		// 释放Modbus回复对象
		reply2->deleteLater();
	}
	else
	{
		qDebug() << "Failed to send Modbus write request!";
	}
}

三、实现效果

3.1保持寄存器数据写入

3.2读取寄存器

四、modebus RTU网口通信

网口与串口通信没有很大的区别,就是在连接时候,使用QModbusTcpClient,类建立一个私有成员。

client = new QModbusTcpClient(this);
然后在ui界面放置一个编辑框用于输入网口:端口
如100.10.0.00:502,冒号前是ip,冒号后是端口。
然后,点击按钮,其中的参数设置如下。连接完成之后,寄存器的读写与串口没有大的区别。就是在

const QUrl url = QUrl::fromUserInput(ui.lineEdit_IPaddress->text());

    client->setConnectionParameter(QModbusDevice::NetworkPortParameter, url.port());
    client->setConnectionParameter(QModbusDevice::NetworkAddressParameter, url.host());
    client->setTimeout(500); 
    client->setNumberOfRetries(3);

结束语:代码已经全部放在文中了,没有详细解释,项目正常建立的话,程序应该可以正常运行。有疑问的话,可以评论,我会回复的。

要在QT6中使用Modbus连接设备,可以按照以下步骤进行操作: 1. 安装Qt Modbus模块。在Qt Creator中,选择菜单栏上的“工具”,然后选择“选项”,在“构建和运行”中找到“Qt版本”选项卡,然后勾选“Qt Modbus”模块并安装。 2. 创建一个新的Qt项目。在Qt Creator中,选择“文件”菜单,然后选择“新建文件或项目”,选择“应用程序”,然后选择“Qt Widgets应用程序”。 3. 在Qt Creator中创建Modbus设备。在Qt Creator中,选择菜单栏上的“工具”,然后选择“Modbus调试器”,在弹出的窗口中选择“创建设备”,然后设置设备参数,例如IP地址、端口号等。 4. 在Qt代码中使用Modbus连接设备。使用Qt Modbus类库中的QModbusTcpClient类或QModbusRtuSerialMaster类,可以在QT中轻松实现Modbus连接设备的功能。在代码中,可以使用类库中的函数来读取或写入Modbus寄存器。 例如,以下代码可以连接Modbus设备并读取寄存器值: ``` QModbusTcpClient *client = new QModbusTcpClient(this); client->setConnectionParameter(QModbusDevice::NetworkPortParameter, 502); client->setConnectionParameter(QModbusDevice::NetworkAddressParameter, "192.168.1.1"); client->connectDevice(); QModbusDataUnit readUnit = QModbusDataUnit(QModbusDataUnit::HoldingRegisters, 0, 10); if (auto *reply = client->sendReadRequest(readUnit, 1)) { if (!reply->isFinished()) connect(reply, &QModbusReply::finished, this, &MyClass::readReady); else delete reply; // broadcast replies return immediately } else { qDebug() << "Read error: " << client->errorString(); } ``` 这是一个简单的例子,它连接到一个IP地址为192.168.1.1的Modbus设备,并读取从地址0开始的10个保持寄存器的值。在这个例子中,如果读取操作成功,将调用readReady()槽函数,在该函数中可以处理返回的数据。如果读取操作失败,将输出错误信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值