Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. How to Use QModbus Class to get the data on a DAQ card?
Forum Updated to NodeBB v4.3 + New Features

How to Use QModbus Class to get the data on a DAQ card?

Scheduled Pinned Locked Moved Solved General and Desktop
modbus
11 Posts 3 Posters 13.0k Views 3 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • M MartinChan

    Recently I am using Qt to do some job on getting digital signals on an DAQ card.The DAQ card supports Modbus agreement and I have already known the ip and port of the DAQ card,and the register address which holds the signal that I want to read.

    So, anyone can provide a example to initialize the QModbus variables and tell me how to open and read the data from the device??(The documentary has provided some examples about serial port,but it doesn't provide too much about Modbus.)

    Thx

    beeckscheB Offline
    beeckscheB Offline
    beecksche
    wrote on last edited by beecksche
    #2

    @MartinChan

    Hi,
    is your DAQ card provide a modbus server or client?
    Normally they're modbus server, so you have to send request to it from a client.

    With the TCP protocol you can use the QModbusTcpClient (see http://doc.qt.io/qt-5/qmodbustcpclient.html).
    To connect your client to the server call:

    QModbusTcpClient *client = new QModbusTcpClient();
    client->setConnectionParameter(QModbusDevice::NetworkAddressParameter, "192.168.0.1");
    client->setConnectionParameter(QModbusDevice::NetworkPortParameter, 502);
    client->connectDevice();
    

    Now you can call read and/or write requests to the server (see http://doc.qt.io/qt-5/qmodbusdataunit.html)
    Write request:

    QModbusDataUnit writeUnit(QModbusDataUnit::HoldingRegisters, 40003, 1); // write 1 value in address 40003
    writeUnit.setValue(0, 0x253); 
    
    if (auto *reply = client->sendWriteRequest(writeUnit, 1))
    {
    	if (!reply->isFinished())
    	{
    		connect(reply, &QModbusReply::finished, this, [this, reply]() 
    		{
    			if (reply->error() != QModbusDevice::NoError) 				
    					// error in reply
    											
    				reply->deleteLater();
    			});
    	}
    	else
    	{
    		if (reply->error() != QModbusDevice::NoError)			
    			// error in reply
    			
    		// broadcast replies return immediately
    		reply->deleteLater();
    	}
    }
    else
    {
    	// error in request
    }
    

    Read request:

    QModbusDataUnit readUnit(QModbusDataUnit::InputRegisters, 40006, 1); // just read input register 40006 
    
    if (auto *reply = client->sendReadRequest(readUnit, 255)) // client id 255
    {
    	if (!reply->isFinished())
    	{
            // connect the finished signal of the request to your read slot
    		connect(reply, &QModbusReply::finished, this, &YourClass::readReady);
    	}
    	else
    	{
    		delete reply; // broadcast replies return immediately
    	}
    }
    else
    {
    	// request error
    }
    
    YourClass::readReady
    {
    	QModbusReply *reply = qobject_cast<QModbusReply *>(sender());
    	if (!reply)
    		return;
    
    	if (reply->error() == QModbusDevice::NoError)
    	{
    		const QModbusDataUnit unit = reply->result();
    		int startAddress = unit.startAddress(); // the start address, here 40006
    		int value = unit.value(0); // value of the start address + 0
    		... 
    
    	}
    	else
    	{
    		// reply error
    	}
    
    	reply->deleteLater(); // delete the reply 
    }
    

    Note that the API of the QModbusTcpClient class is asynchronous.

    In the source of Qt5.6 there are also some examples of modbus master/clients and slaves/server.

    Hope this helps!

    1 Reply Last reply
    1
    • M Offline
      M Offline
      MartinChan
      wrote on last edited by
      #3

      Thx first and I'm sorry to bother you because I have some question.

      The sendWrtieRequest func didn't work correctly.May I ask what is the second parameter--server address??I have known the device ip address and its port,but I have no more parameter like server address or something.

      beeckscheB 1 Reply Last reply
      0
      • N Offline
        N Offline
        nolden
        wrote on last edited by
        #4

        @MartinChan

        The server address is what used to be the slave ID on the serial bus. Usually on TCP those can be 0 or 255 by default, but can be any other in between. That way, multiple "devices" can be emulated by Modbus TCP which is used in e.g. some VFDs and power control units.

        You need to check your documentation of your device to know which value to set this to.

        1 Reply Last reply
        2
        • M MartinChan

          Thx first and I'm sorry to bother you because I have some question.

          The sendWrtieRequest func didn't work correctly.May I ask what is the second parameter--server address??I have known the device ip address and its port,but I have no more parameter like server address or something.

          beeckscheB Offline
          beeckscheB Offline
          beecksche
          wrote on last edited by
          #5

          @MartinChan

          What's your error message?

          1 Reply Last reply
          0
          • M Offline
            M Offline
            MartinChan
            wrote on last edited by MartinChan
            #6

            Thx @nolden and @beecksche

            About error message,my device supports two connection ways:first is use Modbus TCP ways and second is to use "Virtual Serial Port" to make connection with the virtual port.

            When I use the first way,the code is like

            QModbusTcpClient *client=new QModbusTcpClient();
            client>setConnectionParameter(QModbusDevice::NetworkAddressParameter,"192.168.0.201");
            client->setConnectionParameter(QModbusDevice::NetworkPortParameter,502);
             client->connectDevice();
            
            QModbusDataUnit readUnit(QModbusDataUnit::Coils,00000,4);
             if (auto *reply=client->sendReadRequest(readUnit,255)){
            

            it will show "Cannot conncet the device"on cmd when you execute the last line

            however,it worked when you tried the second way like:

            QModbusRtuSerialMaster *client=new QModbusRtuSerialMaster();
                    client->setConnectionParameter(QModbusDevice::SerialPortNameParameter,"COM5");
                    client->setConnectionParameter(QModbusDevice::SerialParityParameter,QSerialPort::NoParity);
                    client->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,QSerialPort::Baud115200);
                    client->setConnectionParameter(QModbusDevice::SerialDataBitsParameter,QSerialPort::Data8);
                    client->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,QSerialPort::OneStop);
                   client->connectDevice();
            
                QModbusDataUnit readUnit(QModbusDataUnit::Coils,00000,4);
                if (auto *reply=client->sendReadRequest(readUnit,255)){
            

            It can return the value,but it seems I never get the value i want (I use some modbus software like< Modbus Pull> to get the every register value of my device).

            beeckscheB 1 Reply Last reply
            0
            • M MartinChan

              Thx @nolden and @beecksche

              About error message,my device supports two connection ways:first is use Modbus TCP ways and second is to use "Virtual Serial Port" to make connection with the virtual port.

              When I use the first way,the code is like

              QModbusTcpClient *client=new QModbusTcpClient();
              client>setConnectionParameter(QModbusDevice::NetworkAddressParameter,"192.168.0.201");
              client->setConnectionParameter(QModbusDevice::NetworkPortParameter,502);
               client->connectDevice();
              
              QModbusDataUnit readUnit(QModbusDataUnit::Coils,00000,4);
               if (auto *reply=client->sendReadRequest(readUnit,255)){
              

              it will show "Cannot conncet the device"on cmd when you execute the last line

              however,it worked when you tried the second way like:

              QModbusRtuSerialMaster *client=new QModbusRtuSerialMaster();
                      client->setConnectionParameter(QModbusDevice::SerialPortNameParameter,"COM5");
                      client->setConnectionParameter(QModbusDevice::SerialParityParameter,QSerialPort::NoParity);
                      client->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,QSerialPort::Baud115200);
                      client->setConnectionParameter(QModbusDevice::SerialDataBitsParameter,QSerialPort::Data8);
                      client->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,QSerialPort::OneStop);
                     client->connectDevice();
              
                  QModbusDataUnit readUnit(QModbusDataUnit::Coils,00000,4);
                  if (auto *reply=client->sendReadRequest(readUnit,255)){
              

              It can return the value,but it seems I never get the value i want (I use some modbus software like< Modbus Pull> to get the every register value of my device).

              beeckscheB Offline
              beeckscheB Offline
              beecksche
              wrote on last edited by beecksche
              #7

              @MartinChan

              Do you call the code above in one function? Like

              YourClass::connect()
              {
              QModbusTcpClient *client=new QModbusTcpClient();
              client->setConnectionParameter(QModbusDevice::NetworkAddressParameter,"192.168.0.201");
              client->setConnectionParameter(QModbusDevice::NetworkPortParameter,502);
              client->connectDevice();
              
              QModbusDataUnit readUnit(QModbusDataUnit::Coils,00000,4);
              if (auto *reply=client->sendReadRequest(readUnit,255)){
              
              ...
              }
              

              The API of the QModbusDevice classes are asynchronous. That means when you call
              client->connectDevice() your client is not "really" connected to your server. The function just send an event to the event loop. When the application enters the event loop the event will be executed.

              The QModbusDevice class has some signals to control your connection state and error message. I would built my class something like that:

              MyClass::connectToModbusServer()
              {
                 QModbusTcpClient *client=new QModbusTcpClient();
                 client->setConnectionParameter(QModbusDevice::NetworkAddressParameter,"192.168.0.201");
                 client->setConnectionParameter(QModbusDevice::NetworkPortParameter,502);
              
                 if (client->connectDevice())
                 {
                    connect(client, &QModbusTcpClient::stateChanged, this, &MyClass::onStateChanged); 
                    connect(client, &QModbusTcpClient::errorOccurred, this, &MyClass::onErrorOccurred); 
                 }
                 else
                    qDebug() << client->errorString();
              }
              
              MyClass::readCoil()
              {
                 QModbusDataUnit readUnit(QModbusDataUnit::Coils,00000,4);
              
                 if (auto *reply = client->sendReadRequest(readUnit, 255) 
                 {
                     if (!reply->isFinished())
                     {
                         // connect the finished signal of the request to your read slot
                         connect(reply, &QModbusReply::finished, this, &MyClass::readReady);
                     }
                     else
                     {
                         delete reply; // broadcast replies return immediately
                     }
                 }
                 else
                 {
                  // request error
                 }
              }
              

              Check the client state with yout slot. Before reading registers ensure that the client is in connected state.

              Haven't tested the above code.

              Finally i found the Qt Modbus Master example: https://doc-snapshots.qt.io/qt5-5.6/qtserialbus-modbus-master-example.html

              It is a good example of how the API works!

              M 2 Replies Last reply
              1
              • beeckscheB beecksche

                @MartinChan

                Do you call the code above in one function? Like

                YourClass::connect()
                {
                QModbusTcpClient *client=new QModbusTcpClient();
                client->setConnectionParameter(QModbusDevice::NetworkAddressParameter,"192.168.0.201");
                client->setConnectionParameter(QModbusDevice::NetworkPortParameter,502);
                client->connectDevice();
                
                QModbusDataUnit readUnit(QModbusDataUnit::Coils,00000,4);
                if (auto *reply=client->sendReadRequest(readUnit,255)){
                
                ...
                }
                

                The API of the QModbusDevice classes are asynchronous. That means when you call
                client->connectDevice() your client is not "really" connected to your server. The function just send an event to the event loop. When the application enters the event loop the event will be executed.

                The QModbusDevice class has some signals to control your connection state and error message. I would built my class something like that:

                MyClass::connectToModbusServer()
                {
                   QModbusTcpClient *client=new QModbusTcpClient();
                   client->setConnectionParameter(QModbusDevice::NetworkAddressParameter,"192.168.0.201");
                   client->setConnectionParameter(QModbusDevice::NetworkPortParameter,502);
                
                   if (client->connectDevice())
                   {
                      connect(client, &QModbusTcpClient::stateChanged, this, &MyClass::onStateChanged); 
                      connect(client, &QModbusTcpClient::errorOccurred, this, &MyClass::onErrorOccurred); 
                   }
                   else
                      qDebug() << client->errorString();
                }
                
                MyClass::readCoil()
                {
                   QModbusDataUnit readUnit(QModbusDataUnit::Coils,00000,4);
                
                   if (auto *reply = client->sendReadRequest(readUnit, 255) 
                   {
                       if (!reply->isFinished())
                       {
                           // connect the finished signal of the request to your read slot
                           connect(reply, &QModbusReply::finished, this, &MyClass::readReady);
                       }
                       else
                       {
                           delete reply; // broadcast replies return immediately
                       }
                   }
                   else
                   {
                    // request error
                   }
                }
                

                Check the client state with yout slot. Before reading registers ensure that the client is in connected state.

                Haven't tested the above code.

                Finally i found the Qt Modbus Master example: https://doc-snapshots.qt.io/qt5-5.6/qtserialbus-modbus-master-example.html

                It is a good example of how the API works!

                M Offline
                M Offline
                MartinChan
                wrote on last edited by
                #8

                @beecksche

                Thx!~~ I will try it soon~~

                1 Reply Last reply
                0
                • beeckscheB beecksche

                  @MartinChan

                  Do you call the code above in one function? Like

                  YourClass::connect()
                  {
                  QModbusTcpClient *client=new QModbusTcpClient();
                  client->setConnectionParameter(QModbusDevice::NetworkAddressParameter,"192.168.0.201");
                  client->setConnectionParameter(QModbusDevice::NetworkPortParameter,502);
                  client->connectDevice();
                  
                  QModbusDataUnit readUnit(QModbusDataUnit::Coils,00000,4);
                  if (auto *reply=client->sendReadRequest(readUnit,255)){
                  
                  ...
                  }
                  

                  The API of the QModbusDevice classes are asynchronous. That means when you call
                  client->connectDevice() your client is not "really" connected to your server. The function just send an event to the event loop. When the application enters the event loop the event will be executed.

                  The QModbusDevice class has some signals to control your connection state and error message. I would built my class something like that:

                  MyClass::connectToModbusServer()
                  {
                     QModbusTcpClient *client=new QModbusTcpClient();
                     client->setConnectionParameter(QModbusDevice::NetworkAddressParameter,"192.168.0.201");
                     client->setConnectionParameter(QModbusDevice::NetworkPortParameter,502);
                  
                     if (client->connectDevice())
                     {
                        connect(client, &QModbusTcpClient::stateChanged, this, &MyClass::onStateChanged); 
                        connect(client, &QModbusTcpClient::errorOccurred, this, &MyClass::onErrorOccurred); 
                     }
                     else
                        qDebug() << client->errorString();
                  }
                  
                  MyClass::readCoil()
                  {
                     QModbusDataUnit readUnit(QModbusDataUnit::Coils,00000,4);
                  
                     if (auto *reply = client->sendReadRequest(readUnit, 255) 
                     {
                         if (!reply->isFinished())
                         {
                             // connect the finished signal of the request to your read slot
                             connect(reply, &QModbusReply::finished, this, &MyClass::readReady);
                         }
                         else
                         {
                             delete reply; // broadcast replies return immediately
                         }
                     }
                     else
                     {
                      // request error
                     }
                  }
                  

                  Check the client state with yout slot. Before reading registers ensure that the client is in connected state.

                  Haven't tested the above code.

                  Finally i found the Qt Modbus Master example: https://doc-snapshots.qt.io/qt5-5.6/qtserialbus-modbus-master-example.html

                  It is a good example of how the API works!

                  M Offline
                  M Offline
                  MartinChan
                  wrote on last edited by MartinChan
                  #9

                  @beecksche

                  Finally I succeed and get the right value of the device.The core question of the connection is what u said:The connectionDevice progress is asynchronous and you can't just use the read order after you use the connectionDevice order,its relationship should be more like a trigger and a bullet(Sorry I don't know too much about QEvent/QThread,I used to think it insignificant but now I didn't) ,you should use the connect function to make sure the right process of connection and read function.

                  I will put my total code here,although it has a lot of unsteady sentences and problems ,but it is simple and I hope it will help those who want to use it in QT5.

                  My DAQ card uses TCP agreement and the Coils(Digital Input)starts from Address 00000 and there're 4 coils,the third is 1 the other should be 0.

                  modclient.h:

                  #ifndef MODCLIENT_H
                  #define MODCLIENT_H
                  #include <QModbusTcpClient>
                  #include <QModbusDevice>
                  #include <QObject>
                  class modclient:public QObject
                  {
                      Q_OBJECT
                  public:
                      modclient();
                      void connecttoModbusServer();
                  private:
                      QModbusTcpClient *client;
                  
                  private slots:
                      void readready() const;
                      void query();
                  };
                  
                  #endif // MODCLIENT_H
                  

                  modclient.cpp:

                  #include "modclient.h"
                  #include <QVariant>
                  #include <QModbusDataUnit>
                  #include <QDebug>
                  modclient::modclient()
                  {
                      client=new QModbusTcpClient();
                      client->setConnectionParameter(QModbusDevice::NetworkAddressParameter,"192.168.0.201");
                      client->setConnectionParameter(QModbusDevice::NetworkPortParameter,502);
                      client->setTimeout(200);
                      client->setNumberOfRetries(3);
                      
                      connecttoModbusServer();
                  }
                  
                  void modclient::connecttoModbusServer(){
                      if(!client->connectDevice()){
                          qDebug()<<QString("There's something wrong with the device");
                      }else {
                          qDebug()<<QString("Right connection");
                          connect(client,&QModbusTcpClient::stateChanged,this,&modclient::query);
                      }
                      
                      
                  }
                  void modclient::readready() const{
                      QModbusReply *reply=qobject_cast<QModbusReply *>(sender());
                      const QModbusDataUnit result=reply->result();
                      for (int j=0;j<4;j++)
                          qDebug()<<QString("The value of %1 is %2").arg(j).arg(result.value(j));
                  }
                  
                  void modclient::query(){
                      QModbusDataUnit readUnit(QModbusDataUnit::Coils,00000,4);
                      
                      if (auto *reply = client->sendReadRequest(readUnit,1))
                      {
                          if (!reply->isFinished())
                          {
                              // connect the finished signal of the request to your read slot
                              connect(reply, &QModbusReply::finished,this,&modclient::readready);
                          }
                          else
                          {
                              delete reply; // broadcast replies return immediately
                          }
                      }
                      else
                      {
                          // request error
                      }
                  }
                  
                  

                  main.cpp

                  #include <QCoreApplication>
                  #include "modclient.h"
                  int main(int argc, char *argv[])
                  {
                      QCoreApplication a(argc, argv);
                      modclient d;
                      return a.exec();
                  }
                  

                  Thx for u all again~

                  beeckscheB 1 Reply Last reply
                  0
                  • M MartinChan

                    @beecksche

                    Finally I succeed and get the right value of the device.The core question of the connection is what u said:The connectionDevice progress is asynchronous and you can't just use the read order after you use the connectionDevice order,its relationship should be more like a trigger and a bullet(Sorry I don't know too much about QEvent/QThread,I used to think it insignificant but now I didn't) ,you should use the connect function to make sure the right process of connection and read function.

                    I will put my total code here,although it has a lot of unsteady sentences and problems ,but it is simple and I hope it will help those who want to use it in QT5.

                    My DAQ card uses TCP agreement and the Coils(Digital Input)starts from Address 00000 and there're 4 coils,the third is 1 the other should be 0.

                    modclient.h:

                    #ifndef MODCLIENT_H
                    #define MODCLIENT_H
                    #include <QModbusTcpClient>
                    #include <QModbusDevice>
                    #include <QObject>
                    class modclient:public QObject
                    {
                        Q_OBJECT
                    public:
                        modclient();
                        void connecttoModbusServer();
                    private:
                        QModbusTcpClient *client;
                    
                    private slots:
                        void readready() const;
                        void query();
                    };
                    
                    #endif // MODCLIENT_H
                    

                    modclient.cpp:

                    #include "modclient.h"
                    #include <QVariant>
                    #include <QModbusDataUnit>
                    #include <QDebug>
                    modclient::modclient()
                    {
                        client=new QModbusTcpClient();
                        client->setConnectionParameter(QModbusDevice::NetworkAddressParameter,"192.168.0.201");
                        client->setConnectionParameter(QModbusDevice::NetworkPortParameter,502);
                        client->setTimeout(200);
                        client->setNumberOfRetries(3);
                        
                        connecttoModbusServer();
                    }
                    
                    void modclient::connecttoModbusServer(){
                        if(!client->connectDevice()){
                            qDebug()<<QString("There's something wrong with the device");
                        }else {
                            qDebug()<<QString("Right connection");
                            connect(client,&QModbusTcpClient::stateChanged,this,&modclient::query);
                        }
                        
                        
                    }
                    void modclient::readready() const{
                        QModbusReply *reply=qobject_cast<QModbusReply *>(sender());
                        const QModbusDataUnit result=reply->result();
                        for (int j=0;j<4;j++)
                            qDebug()<<QString("The value of %1 is %2").arg(j).arg(result.value(j));
                    }
                    
                    void modclient::query(){
                        QModbusDataUnit readUnit(QModbusDataUnit::Coils,00000,4);
                        
                        if (auto *reply = client->sendReadRequest(readUnit,1))
                        {
                            if (!reply->isFinished())
                            {
                                // connect the finished signal of the request to your read slot
                                connect(reply, &QModbusReply::finished,this,&modclient::readready);
                            }
                            else
                            {
                                delete reply; // broadcast replies return immediately
                            }
                        }
                        else
                        {
                            // request error
                        }
                    }
                    
                    

                    main.cpp

                    #include <QCoreApplication>
                    #include "modclient.h"
                    int main(int argc, char *argv[])
                    {
                        QCoreApplication a(argc, argv);
                        modclient d;
                        return a.exec();
                    }
                    

                    Thx for u all again~

                    beeckscheB Offline
                    beeckscheB Offline
                    beecksche
                    wrote on last edited by beecksche
                    #10

                    @MartinChan
                    i don't understand your connection (it also should shown an error while compiling because the arguments of the both functions missmatch)

                    connect(client,&QModbusTcpClient::stateChanged,this,&modclient::query);
                    

                    The doc says:

                    void QModbusDevice::stateChanged(QModbusDevice::State state):

                    This signal is emitted every time the state of the device changes. The new state is represented by state.

                    In your code every time the state changed the query slot is called, which send a read request to the server.

                    I connect the stateChanged signal to something like that:

                    onStateChanged(const QModbusDevice::State & newState)
                    {
                       switch(newState)
                       {
                       case QModbusDevice::UnconnectedState:
                          ...
                          break;
                       case QModbusDevice::ConnectingState:
                          ...
                          break;
                       case QModbusDevice::ConnectedState:
                          ...
                          break;
                       case QModbusDevice::ClosingState: 
                          ...
                          break;
                       }
                    }
                    
                    M 1 Reply Last reply
                    0
                    • beeckscheB beecksche

                      @MartinChan
                      i don't understand your connection (it also should shown an error while compiling because the arguments of the both functions missmatch)

                      connect(client,&QModbusTcpClient::stateChanged,this,&modclient::query);
                      

                      The doc says:

                      void QModbusDevice::stateChanged(QModbusDevice::State state):

                      This signal is emitted every time the state of the device changes. The new state is represented by state.

                      In your code every time the state changed the query slot is called, which send a read request to the server.

                      I connect the stateChanged signal to something like that:

                      onStateChanged(const QModbusDevice::State & newState)
                      {
                         switch(newState)
                         {
                         case QModbusDevice::UnconnectedState:
                            ...
                            break;
                         case QModbusDevice::ConnectingState:
                            ...
                            break;
                         case QModbusDevice::ConnectedState:
                            ...
                            break;
                         case QModbusDevice::ClosingState: 
                            ...
                            break;
                         }
                      }
                      
                      M Offline
                      M Offline
                      MartinChan
                      wrote on last edited by
                      #11

                      @beecksche
                      Hi,I have to say you're right,however,it didn't mean my code couldn't run--because in my code. once you use connectDevice command,it did change the state from "Unconnected"to "Connected"(Your code discuss the problem it could trigger during the connectDevice() progress,it's necessary in practice,in my case I have checked the parameters like IP,Port Address etc,so it didn't show error when I compile the file).

                      1 Reply Last reply
                      0

                      • Login

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • Users
                      • Groups
                      • Search
                      • Get Qt Extensions
                      • Unsolved