How to Read M103 Modbus Doc?

  • 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 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]) {

  • @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 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;

  • 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 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) {
    } 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].

  • @ntd

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

    Thanks a lot!


Log in to reply