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. Modbus TCP floating point conversion
Forum Updated to NodeBB v4.3 + New Features

Modbus TCP floating point conversion

Scheduled Pinned Locked Moved Unsolved General and Desktop
8 Posts 4 Posters 2.3k Views 2 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 Offline
    M Offline
    Mark81
    wrote on last edited by
    #1

    It seems there are not ready-to-use converter functions for QModbus classes. I need to convert a 32-bit floating point to 2 16-bit registers. I tried the following:

    QVector<quint16> reg;
    QByteArray bytes(reinterpret_cast<const char*>(&value), sizeof(value));
    
    quint16 high = static_cast<quint16>(bytes[1] + (bytes[0] << 8));
    quint16 low = static_cast<quint16>(bytes[3] + (bytes[2] << 8));
    
    reg.append(high);
    reg.append(low);
    

    and there write the reg values, but the device does not acknowledge (it works for sure because using a third party Modbus client it return OK).

    As far as I know the required modbus endiannes is:

    MSB and LSB for MSW, then MSB and LSB for LSW
    

    is there a more reliable way to achieve this?

    aha_1980A 1 Reply Last reply
    0
    • M Mark81

      It seems there are not ready-to-use converter functions for QModbus classes. I need to convert a 32-bit floating point to 2 16-bit registers. I tried the following:

      QVector<quint16> reg;
      QByteArray bytes(reinterpret_cast<const char*>(&value), sizeof(value));
      
      quint16 high = static_cast<quint16>(bytes[1] + (bytes[0] << 8));
      quint16 low = static_cast<quint16>(bytes[3] + (bytes[2] << 8));
      
      reg.append(high);
      reg.append(low);
      

      and there write the reg values, but the device does not acknowledge (it works for sure because using a third party Modbus client it return OK).

      As far as I know the required modbus endiannes is:

      MSB and LSB for MSW, then MSB and LSB for LSW
      

      is there a more reliable way to achieve this?

      aha_1980A Offline
      aha_1980A Offline
      aha_1980
      Lifetime Qt Champion
      wrote on last edited by
      #2

      Hi @Mark81,

      one word in advance: you can use Wireshark to capture and debug TCP connections, and so you can probably compare your code with the third party one.

      I don't know which Endianess Modbus uses (and I'm too lazy to look that up), but what you describe is Big Endian and that is typically used in networks (e.g. Ethernet) - so that could really be correct. Please note that your Intel computer in contrast uses Little Endian - which means, the data in memory is ordered in reverse.

      But as you give words (aka quint16) to the transport layer, I think you only need to make sure high- and low word are correctly ordered.

      If it's like this, then the following could work:

      float value = 1234.56f;
      
      // interpret the memory consumed by the float value as unsigned int
      // we need to use pointers so the compiler does not type conversion
      const quint32 *raw = reinterpret_cast<const quint32 *>(&value);
      
      QVector<quint16> reg;
      // append the high word first, then the low word
      // the endianess of the words should be handled by the modbus layer
      reg.append(*raw >> 16);
      reg.append(*raw & 0xFFFF);
      

      Disclaimer: only brain compiled, not tested.

      Qt has to stay free or it will die.

      1 Reply Last reply
      5
      • J.HilkJ Offline
        J.HilkJ Offline
        J.Hilk
        Moderators
        wrote on last edited by J.Hilk
        #3

        A code example for one of my earlier Modbus projects.

        enum byteOrder{
                ABCD,
                BADC,
                CDAB,
                DCBA
            };
        
        quint16 fromCharArray(const unsigned *data)
        {
            uint16_t value(0);
            for(int i(0); i < 2; ++i){
                value += data[i] << 8 *i;
            }
            return value;
        }
        
        QVector<quint16> fromFloat(float abcd, byteOrder order)
        {
            short A(0),B(0),C(0),D(0);
            switch (order) {
            case ABCD:A = 0; B = 1; C = 2; D = 3;break;
            case BADC:A = 1; B = 0; C = 3; D = 2;break;
            case CDAB:A = 2; B = 3; C = 0; D = 1;break;
            case DCBA:A = 3; B = 2; C = 1; D = 0;break;
            }
        
            unsigned char *cArray = reinterpret_cast<unsigned char *>(&abcd);
            unsigned value1[] = {cArray[A],cArray[B]};
            unsigned value2[] = {cArray[C],cArray[D]};
        
            QVector<quint16> values;
            values.append(fromCharArray(value1));
            values.append(fromCharArray(value2));
        
            return values;
        }
        

        maybe not the most efficient of codes one, but it gets the job done :-)


        Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


        Q: What's that?
        A: It's blue light.
        Q: What does it do?
        A: It turns blue.

        aha_1980A 1 Reply Last reply
        2
        • J.HilkJ J.Hilk

          A code example for one of my earlier Modbus projects.

          enum byteOrder{
                  ABCD,
                  BADC,
                  CDAB,
                  DCBA
              };
          
          quint16 fromCharArray(const unsigned *data)
          {
              uint16_t value(0);
              for(int i(0); i < 2; ++i){
                  value += data[i] << 8 *i;
              }
              return value;
          }
          
          QVector<quint16> fromFloat(float abcd, byteOrder order)
          {
              short A(0),B(0),C(0),D(0);
              switch (order) {
              case ABCD:A = 0; B = 1; C = 2; D = 3;break;
              case BADC:A = 1; B = 0; C = 3; D = 2;break;
              case CDAB:A = 2; B = 3; C = 0; D = 1;break;
              case DCBA:A = 3; B = 2; C = 1; D = 0;break;
              }
          
              unsigned char *cArray = reinterpret_cast<unsigned char *>(&abcd);
              unsigned value1[] = {cArray[A],cArray[B]};
              unsigned value2[] = {cArray[C],cArray[D]};
          
              QVector<quint16> values;
              values.append(fromCharArray(value1));
              values.append(fromCharArray(value2));
          
              return values;
          }
          

          maybe not the most efficient of codes one, but it gets the job done :-)

          aha_1980A Offline
          aha_1980A Offline
          aha_1980
          Lifetime Qt Champion
          wrote on last edited by
          #4

          @J.Hilk

          But does your code take the byte order of the system you are running in account?

          I.e. do you need to give a different byteOrder parameter depending on the CPU architecture?

          (I know, in practice I does not matter that much, but I'm curious).

          Qt has to stay free or it will die.

          J.HilkJ 1 Reply Last reply
          1
          • aha_1980A aha_1980

            @J.Hilk

            But does your code take the byte order of the system you are running in account?

            I.e. do you need to give a different byteOrder parameter depending on the CPU architecture?

            (I know, in practice I does not matter that much, but I'm curious).

            J.HilkJ Offline
            J.HilkJ Offline
            J.Hilk
            Moderators
            wrote on last edited by
            #5

            @aha_1980
            It should be able to cover the whole scope. Might however need a different byteorder parameter, if your cpu architecture changes.

            IIRC I used it to bruteforce my way to connection, because I has no idea what target used. But it worked and is, I think, still running to day,


            Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


            Q: What's that?
            A: It's blue light.
            Q: What does it do?
            A: It turns blue.

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

              I'm not sure about this, honestly. My application will run only under Windows and desktop PC. Do I need to take care of endianness?

              aha_1980A kshegunovK 2 Replies Last reply
              0
              • M Mark81

                I'm not sure about this, honestly. My application will run only under Windows and desktop PC. Do I need to take care of endianness?

                aha_1980A Offline
                aha_1980A Offline
                aha_1980
                Lifetime Qt Champion
                wrote on last edited by aha_1980
                #7

                @Mark81 if my version works, then it is compatible with both endianess.

                Qt has to stay free or it will die.

                1 Reply Last reply
                2
                • M Mark81

                  I'm not sure about this, honestly. My application will run only under Windows and desktop PC. Do I need to take care of endianness?

                  kshegunovK Offline
                  kshegunovK Offline
                  kshegunov
                  Moderators
                  wrote on last edited by kshegunov
                  #8

                  Alternatively C99 compliant compilers allow you to do (if ensured a packed structure):

                  union  {
                      struct  {
                          quint16 high, low;
                       } reg;
                       float value;
                  } data;
                  
                  data.value = 1234.56f;
                  
                  quint16 high = data.reg.high;
                  quint16 low = data.reg.low;
                  

                  or the oddly looking conversion should also do:

                  float value = 1234.56f
                  quint16 (*reg)[2] = reinterpret_cast<quint16(*)[2]>(&value);
                  quint16 high = (*reg)[0], low = (*reg)[1];
                  

                  Read and abide by the Qt Code of Conduct

                  1 Reply Last reply
                  2

                  • Login

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