基础知识:

1.功能码

modbus完整支持很多功能码,但是实际在应用的时候常用的也就那么几个。具体如下:

0x01: 读线圈寄存器
0x02: 读离散输入寄存器
0x03: 读保持寄存器
0x04: 读输入寄存器
0x05: 写单个线圈寄存器
0x06: 写单个保持寄存器
0x0f: 写多个线圈寄存器
0x10: 写多个保持寄存器
如上所示一共8种功能码。这其中有涉及到线圈、离散输入、保持、输入四种寄存器。这名字也不知道谁起的,让人看了一点不通俗易懂,搞得晕晕乎乎。实际上你要是看清他的本质就很简单了。下面分别解释一下:

线圈寄存器:实际上就可以类比为开关量,每个bit都对应一个信号的开关状态。所以一个byte就可以同时控制8路的信号。比如控制外部8路io的高低。 线圈寄存器支持读也支持写,写在功能码里面又分为写单个线圈寄存器和写多个线圈寄存器。对应上面的功能码也就是:0x01 0x05 0x0f

离散输入寄存器:如果线圈寄存器理解了这个自然也明白了。离散输入寄存器就相当于线圈寄存器的只读模式,他也是每个bit表示一个开关量,而他的开关量只能读取输入的开关信号,是不能够写的。比如我读取外部按键的按下还是松开。所以功能码也简单就一个读的 0x02

保持寄存器:这个寄存器的单位不再是bit而是两个byte,也就是可以存放具体的数据量的,并且是可读写的。比如我我设置时间年月日,不但可以写也可以读出来现在的时间。写也分为单个写和多个写,所以功能码有对应的三个:0x03 0x06 0x10

输入寄存器:只剩下这最后一个了,这个和保持寄存器类似,但是也是只支持读而不能写。一个寄存器也是占据两个byte的空间。类比我我通过读取输入寄存器获取现在的AD采集值。对应的功能码也就一个 0x04

对应的错误返回:
在对应功能码基础上加上0x80

————————————————
版权声明:本文为CSDN博主「JiaoCL」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/liboxiu/article/details/86473516

https://blog.csdn.net/kissgoodbye2012/article/details/112105418?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522162701004216780271551414%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=162701004216780271551414&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_v2~rank_v29-2-112105418.pc_v2_rank_blog_default&utm_term=QT+modbus%E5%86%99&spm=1018.2226.3001.4450

2.使用QT实现

image.png
image.png

  1. #ifndef WIDGET_H
  2. #define WIDGET_H
  3. #include <QWidget>
  4. #include <QModbusRtuSerialMaster>
  5. #include <QModbusDataUnit>
  6. #include <QModbusReply>
  7. #include <QVariant>
  8. #include <QSerialPort>
  9. #include <QDebug>
  10. namespace Ui {
  11. class Widget;
  12. }
  13. class Widget : public QWidget
  14. {
  15. Q_OBJECT
  16. public:
  17. explicit Widget(QWidget *parent = nullptr);
  18. void connectSerial();
  19. void disconnectSerial();
  20. void readData(int startaddr, int num, int addr);
  21. bool writeData(int startaddr, int size, QVector<int> value, int addr);
  22. void onReadReady();
  23. QString fillNumber(quint16 number);
  24. ~Widget();
  25. private:
  26. Ui::Widget *ui;
  27. QModbusClient *modbusDevice = nullptr;
  28. };
  29. #endif // WIDGET_H
  30. #-------------------------------------------------
  31. #
  32. # Project created by QtCreator 2021-07-22T18:34:43
  33. #
  34. #-------------------------------------------------
  35. QT += core gui serialbus serialport
  36. greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
  37. TARGET = untitled5
  38. TEMPLATE = app
  39. # The following define makes your compiler emit warnings if you use
  40. # any feature of Qt which has been marked as deprecated (the exact warnings
  41. # depend on your compiler). Please consult the documentation of the
  42. # deprecated API in order to know how to port your code away from it.
  43. DEFINES += QT_DEPRECATED_WARNINGS
  44. # You can also make your code fail to compile if you use deprecated APIs.
  45. # In order to do so, uncomment the following line.
  46. # You can also select to disable deprecated APIs only up to a certain version of Qt.
  47. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
  48. CONFIG += c++11
  49. SOURCES += \
  50. main.cpp \
  51. widget.cpp
  52. HEADERS += \
  53. widget.h
  54. FORMS += \
  55. widget.ui
  56. # Default rules for deployment.
  57. qnx: target.path = /tmp/$${TARGET}/bin
  58. else: unix:!android: target.path = /opt/$${TARGET}/bin
  59. !isEmpty(target.path): INSTALLS += target
  1. #ifndef WIDGET_H
  2. #define WIDGET_H
  3. #include <QWidget>
  4. #include <QModbusRtuSerialMaster>
  5. #include <QModbusDataUnit>
  6. #include <QModbusReply>
  7. #include <QVariant>
  8. #include <QSerialPort>
  9. #include <QDebug>
  10. namespace Ui {
  11. class Widget;
  12. }
  13. class Widget : public QWidget
  14. {
  15. Q_OBJECT
  16. public:
  17. explicit Widget(QWidget *parent = nullptr);
  18. void connectSerial();
  19. void disconnectSerial();
  20. void readData(int startaddr, int num, int addr);
  21. bool writeData(int startaddr, int size, QVector<int> value, int addr);
  22. void onReadReady();
  23. QString fillNumber(quint16 number);
  24. ~Widget();
  25. private:
  26. Ui::Widget *ui;
  27. QModbusClient *modbusDevice = nullptr;
  28. };
  29. #endif // WIDGET_H
  1. #include "widget.h"
  2. #include "ui_widget.h"
  3. #include <QPushButton>
  4. #include <QTimer>
  5. #include <QEventLoop>
  6. Widget::Widget(QWidget *parent) :
  7. QWidget(parent),
  8. ui(new Ui::Widget)
  9. {
  10. ui->setupUi(this);
  11. connect(ui->btnConnect, &QPushButton::clicked, this, &Widget::connectSerial);
  12. connect(ui->btnQuit, &QPushButton::clicked, this, &Widget::disconnectSerial);
  13. QTimer *mytimer = new QTimer(this);
  14. connect(mytimer, &QTimer::timeout, this, [=](){
  15. // 0A 03 00 01 00 02
  16. readData(0x01, 0x02, 0x0A);
  17. // 0A 06 00 36 00 00
  18. writeData(0x36, 1, ,0, 0x0A);
  19. });
  20. connect(ui->pushButton, &QPushButton::clicked, [=](){
  21. mytimer->start(1000);
  22. });
  23. }
  24. void Widget::connectSerial()
  25. {
  26. //1. 创建QModbusDevice对象
  27. modbusDevice = new QModbusRtuSerialMaster(this);
  28. //2. 如果处于连接状态,则返回
  29. if (modbusDevice->state() == QModbusDevice::ConnectedState)
  30. {
  31. return;
  32. }
  33. //3. 设置串口相关参数
  34. //设置串口信息
  35. modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter, QVariant("ttyUSB0"));
  36. //设置校验 无校验
  37. modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::NoParity);
  38. //设置波特率
  39. modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QSerialPort::Baud9600);
  40. //设置停止位
  41. modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop);
  42. //设置数据位
  43. modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8);
  44. //其他设置
  45. //设置超时时间
  46. modbusDevice->setTimeout(300);
  47. //失败次数
  48. modbusDevice->setNumberOfRetries(3);
  49. bool ok = modbusDevice->connectDevice();
  50. if (!ok)
  51. {
  52. qDebug() << "连接到串口失败: " << modbusDevice->errorString();
  53. ui->labelState->setText("串口设置有误,连接失败");
  54. }
  55. else
  56. {
  57. qDebug() << "连接到串口成功";
  58. ui->labelState->setText("串口连接成功");
  59. }
  60. }
  61. void Widget::disconnectSerial()
  62. {
  63. if(modbusDevice->state() != QModbusDevice::UnconnectedState)
  64. {
  65. modbusDevice->disconnectDevice();
  66. ui->labelState->setText("关闭连接");
  67. }
  68. }
  69. void Widget::readData(int startaddr, int num, int addr)
  70. {
  71. if(modbusDevice->state() == QModbusDevice::UnconnectedState) {
  72. return;
  73. }
  74. //读取2个保存寄存器的值 寄存器起始地址为1
  75. QModbusDataUnit data(QModbusDataUnit::HoldingRegisters, startaddr, num);
  76. data.setValue(0, 0);
  77. QModbusReply* reply = modbusDevice->sendWriteRequest(data, addr);
  78. if (nullptr == reply)
  79. {
  80. qDebug() << "发送请求数据失败: " << modbusDevice->errorString();
  81. return;
  82. }
  83. else
  84. {
  85. if (!reply->isFinished())
  86. {
  87. QEventLoop loop;
  88. connect(reply, &QModbusReply::finished,&loop,&QEventLoop::quit);
  89. loop.exec();
  90. }
  91. }
  92. if (reply->error() == QModbusDevice::NoError)
  93. {
  94. //读取响应数据
  95. const auto responseData = reply->rawResult();
  96. auto values = responseData.data().toHex();
  97. qDebug() << values;
  98. if(values.size() == 2) {
  99. bool ok;
  100. QString mystring = fillNumber(values.at(1)) + fillNumber(values.at(0));
  101. int getFLoat = mystring.toUInt(&ok, 16);
  102. //转换为浮点数
  103. float floatdata = *(float*)&getFLoat;
  104. ui->textEdit->append(QString("%1").arg(floatdata));
  105. }
  106. if(values.size() == 1) {
  107. ui->textEdit->append(QString("%1").arg(values.at(0)));
  108. }
  109. }
  110. else if (reply->error() == QModbusDevice::ProtocolError)
  111. {
  112. qDebug() << "Read response Protocol error: " << reply->errorString();
  113. }
  114. else
  115. {
  116. qDebug() << "Read response Error: " << reply->errorString();
  117. }
  118. }
  119. bool Widget::writeData(int startaddr, int size, QVector<int> value, int addr)
  120. {
  121. if(modbusDevice->state() == QModbusDevice::UnconnectedState) {
  122. return;
  123. }
  124. //读取2个保存寄存器的值 寄存器起始地址为1
  125. QModbusDataUnit data(QModbusDataUnit::HoldingRegisters, startaddr, size);
  126. for(int i = 0; i < size; i++) {
  127. data.setValue(i, value.at(i));
  128. }
  129. data.setValue(0, value);
  130. QModbusReply* reply = modbusDevice->sendWriteRequest(data, addr);
  131. if (nullptr == reply)
  132. {
  133. qDebug() << "发送请求数据失败: " << modbusDevice->errorString();
  134. return;
  135. }
  136. else
  137. {
  138. if (!reply->isFinished())
  139. {
  140. QEventLoop loop;
  141. connect(reply, &QModbusReply::finished,&loop,&QEventLoop::quit);
  142. loop.exec();
  143. }
  144. }
  145. if (reply->error() == QModbusDevice::NoError)
  146. {
  147. return true;
  148. }
  149. else
  150. {
  151. qDebug() << "Read response Error: " << reply->errorString();
  152. return false;
  153. }
  154. }
  155. void Widget::onReadReady()
  156. {
  157. auto reply = qobject_cast<QModbusReply*>(sender());
  158. if (nullptr == reply)
  159. {
  160. return;
  161. }
  162. //删除reply
  163. reply->deleteLater();
  164. }
  165. QString Widget::fillNumber(quint16 number)
  166. {
  167. QString str = QString::number(number, 16);
  168. while (str.size() < 4) {
  169. str += "0";
  170. }
  171. return str;
  172. }
  173. Widget::~Widget()
  174. {
  175. delete ui;
  176. }