• Register
    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search

    How to Read M103 Modbus Doc?

    UniPi Neuron Series
    2
    9
    972
    Loading More Posts
    • 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.
    • K
      knebb last edited by

      Hi all,

      ok, I manged to use the relays through modbus with libmodbus with my C-code. Now I am trying to use the DI/DO/AO/AI interfaces.

      I see the Neuron Registers Map but I am only able to understand it partially...

      I am aware the function modbus_read_bits() can be usedd to read the DIs. So I open a modbus_tcp connection with the address of DI2.1 for example:

      modbus_read_bits(modbus_handle, 108, nb, *dest);
      

      *dest is supposed to be uint16_t.

      I was able to read the above- finally I have some data in *dest. But how do I have to read the data there? I expected something like "bit 1 tells if the DI is currently ON or OFF and bit2 is the counter while writing bit3 will reset the counter" or stuff like this.

      So I am a little bit lost here to interpret the values I got.

      Nearly the same is for writing. What values should I put into uint16_t to enable the DI?

      And how can I configure the AO?

      Thanks a lot for hints!

      /KNEBB

      1 Reply Last reply Reply Quote 0
      • ntd
        ntd last edited by

        @knebb said in How to Read M103 Modbus Doc?:

        *dest is supposed to be uint16_t.

        No, it is an uint8_t array. Every element of the array contains the value of a single digital channel, set to TRUE (that is, IIRC, 1) or FALSE (that is surely 0).

        Here is my untested snippet:

        // Reading one channel
        uint8_t my_digital;
        modbus_read_bits(bus, 108, 1, &my_digital);
        if (my_digital) {
            ...
        }
        // Reading multiple channels
        uint8_t my_digitals[2];
        modbus_read_bits(bus, 108, 2, my_digitals);
        if (my_digitals[0]) {
            ...
        }
        
        K 1 Reply Last reply Reply Quote 0
        • K
          knebb @ntd last edited by

          @ntd :Thanks a lot four your input, I really appreciate it.

          I am just wondering as there seems to be no difference between uint8_t, uint16_t and uint32_t.

          See my question here.
          When using uint8_t the result is exactly the same. All seem to be 32bit long...

          So how does modbus deal with this? It writes only the least significant 8 bits? Others are set to zero?

          /KNEBB

          ntd 1 Reply Last reply Reply Quote 0
          • ntd
            ntd @knebb last edited by ntd

            @knebb TL/DR: just use uint8_t. This is the right thing to do.

            There is no differences (because of integer promotion) only when you use that variable in an expression. Incidentally, and for different reasons [1], reading a single bit will work with whatever 0-initialized integer you throw at modbus_read_bits, although conceptually wrong. But I can assure you that, when reading multiple channels, using anything but uint8_t will give you wrong results.

            [1] Raspberry Pi is little-endian. Setting the first byte of any 0-initialized uint* variable to a specific value (e.g., TRUE), makes that very same variable initialized to that value, regardless of its type.

            Here is an example that hopefully will shed some light:

            #include <assert.h>
            #include <stdint.h>
            #include <string.h>
            
            #define TRUE 12
            
            int main()
            {
                // Using an union to be able to set the first byte of everything
                // with only one instruction (m.byte = ...)
                union {
                    uint8_t byte;
                    uint16_t word;
                    uint32_t dword;
                    uint64_t qword;
                } m;
            
                // Let's set the first byte of 0 initialized memory
                memset(&m, 0, sizeof m);
                m.byte = TRUE;
            
                assert(m.byte == TRUE);  // Ok
                assert(m.word == TRUE);  // Ok on little-endian machines
                assert(m.dword == TRUE); // Ok on little-endian machines
                assert(m.qword == TRUE); // Ok on little-endian machines
            
                // Now let's try with random initialized memory
                memset(&m, 3, sizeof m);
                m.byte = TRUE;
            
                assert(m.byte == TRUE);  // Ok
                assert(m.word == TRUE);  // Error!
                assert(m.dword == TRUE); // Error!
                assert(m.qword == TRUE); // Error!
            
                return 0;
            }
            
            1 Reply Last reply Reply Quote 1
            • K
              knebb last edited by

              Thanks for the explanation; I figured it already out Raspbian is littleEndian.

              Just to make sure:
              To read i.e. the counter for digital input 2.1 (address 103-104 with DWORD, 32bit in total) I would use:

              uint8_t counter[4];
              modbus_read_bits(bus, 103, 4, counter);
              

              Or this one?

              uint8_t counter1[2],counter2[2];
              modbus_read_bits(bus, 103, 2, counter1);
              modbus_read_bits(bus, 104, 2, counter2);
              

              Would both work?

              Sorry for my dumb question, I am really new to modbus...

              /KNEBB

              ntd 1 Reply Last reply Reply Quote 0
              • ntd
                ntd @knebb last edited by ntd

                @knebb said:

                Would both work?

                None of them will. You should really read a ModBUS introduction... I think the wikipedia page should suffice.

                The counter is an input register, not a bit, so:

                union {
                    uint16_t word[2];
                    uint32_t dword;
                } counter;
                
                if (modbus_read_registers(bus, 103, 2, counter.word) != 2) {
                    printf("Error\n");
                } else {
                    printf("DI2.1 counter is %u\n", counter.dword);
                }
                

                Not sure which is the less and the most significant word, so if the above gives wrong results just swap counter.word[0] and counter.word[1].

                K 1 Reply Last reply Reply Quote 1
                • K
                  knebb @ntd last edited by

                  @ntd
                  Perfect!

                  Got it finally. I mixed up modbus_read_bits and modbus_read_register. Now working like a charm!

                  Thanks a lot!

                  /KNEBB

                  K 1 Reply Last reply Reply Quote 0
                  • K
                    knebb @knebb last edited by

                    @all

                    As I was struggling again with this I figured it out now finally (at least I hope so).

                    My issue was the interpretation of whatever value I got.

                    So for alle future readings:

                    In the linked Calc-Sheet above you have notes about the registers (read only registers, not bits!).

                    So for the M103 you have the "Registers - group 1" and "Registers - group 2". Use these. The coils tables are somehow misleading (at least for me).

                    Additionally be aware modbus_read_registers() need to write the data into an uint16_t variable. With the help of @ntd and his suggestes "union" I was easy to "convert" them into the uint32_t. Unions access the same memory by different variables. See more here.

                    In the code sniplet above modbus_read_registers() writes the content into the counter.word memory location and the prinft reads them as uint32_t. Easy going now to re-calculate the counter.dword variable to get the values you need.

                    So to read the current settings and values of all relays and digital inputs I use the following code:

                    uint16_t state;
                    union {
                            uint16_t word[2];
                            uint32_t dword;
                    } counter;
                    
                    
                    /* Relais */
                    modbus_read_registers(modbusglob,101,1,&state);
                    printf("Lesen Relay Register: %u\n",state);
                    printf("\n");
                    
                    /* DigitalIN 1 */
                    modbus_read_registers(modbusglob,0,1,&state);
                    printf("Lesen DigIn1: %u\n",state);
                    
                    printf("\n");
                    /* Digital-In 1 Counter */
                    for (i=8;i<16;i=i+2) {
                            modbus_read_registers(modbusglob,i,2,counter.word);
                            printf("Counter %d DigIn1: %u\n",i,counter.dword);
                    }
                    printf("\n");
                    
                    /* DigitalIN 2 */
                    modbus_read_registers(modbusglob,100,1,&state);
                    printf("Lesen DigIn1: %d\n",state);
                    printf("\n");
                    
                    
                    /* DigitalIN 2 Counter */
                    for (i=103;i<118;i=i+2) {
                            modbus_read_registers(modbusglob,i,2,counter.word);
                            printf("Counter %d DigIn2: %u\n",i,counter.dword);
                    }
                    
                    
                    

                    This works now perfectly.

                    /KNEBB

                    K 1 Reply Last reply Reply Quote 0
                    • K
                      knebb @knebb last edited by

                      Just as an update.

                      I was struggling with multiple registers which are used i.e. for the AO1.1 port of my Neuron M103.

                      So for the AO registers 3000 and 3001 are given as "real".

                      I extended the above union as follows:

                      union {
                              uint16_t word[2];
                              uint32_t dword;
                              float fl;
                      } counter;
                      

                      And I am reading the register like this:

                      modbus_read_registers(modbusglob, 3000, 2, &counter.word[0]);
                      

                      It reads 2 registers starting at 3000 and writes the result in the word array where it is automatically converted to "float" by the union structure.

                      Works fine!

                      /KNEBB

                      1 Reply Last reply Reply Quote 0
                      • First post
                        Last post