Reputation: 5102
Here some structures are defined:
#pragma pack(push, 1)
typedef struct
{
uint8_t discrete_input0:1;
uint8_t discrete_input1:1;
uint8_t discrete_input2:1;
uint8_t discrete_input3:1;
uint8_t discrete_input4:1;
uint8_t discrete_input5:1;
uint8_t discrete_input6:1;
uint8_t discrete_input7:1;
uint8_t discrete_input_port1;
uint8_t discrete_input_port2;
} discrete_reg_params_t;
#pragma pack(pop)
#pragma pack(push, 1)
typedef struct
{
uint8_t coils_port0;
uint8_t coils_port1;
uint8_t coils_port2;
} coil_reg_params_t;
#pragma pack(pop)
#pragma pack(push, 1)
typedef struct
{
float input_data0; // 0
float input_data1; // 2
float input_data2; // 4
float input_data3; // 6
uint16_t data[150]; // 8 + 150 = 158
float input_data4; // 158
float input_data5;
float input_data6;
float input_data7;
uint16_t data_block1[150];
} input_reg_params_t;
#pragma pack(pop)
#pragma pack(push, 1)
typedef struct
{
float holding_data0;
float holding_data1;
float holding_data2;
float holding_data3;
uint16_t test_regs[150];
float holding_data4;
float holding_data5;
float holding_data6;
float holding_data7;
} holding_reg_params_t;
#pragma pack(pop)
and here are defined the offsets:
#define HOLD_OFFSET(field) ((uint16_t)(offsetof(holding_reg_params_t, field) >> 1))
#define INPUT_OFFSET(field) ((uint16_t)(offsetof(input_reg_params_t, field) >> 1))
#define MB_REG_DISCRETE_INPUT_START (0x0000)
#define MB_REG_COILS_START (0x0000)
#define MB_REG_INPUT_START_AREA0 (INPUT_OFFSET(input_data0)) // register offset input area 0
#define MB_REG_INPUT_START_AREA1 (INPUT_OFFSET(input_data4)) // register offset input area 1
#define MB_REG_HOLDING_START_AREA0 (HOLD_OFFSET(holding_data0))
#define MB_REG_HOLDING_START_AREA1 (HOLD_OFFSET(holding_data4))
to be used later in the same file:
reg_area.type = MB_PARAM_HOLDING; // Set type of register area
reg_area.start_offset = MB_REG_HOLDING_START_AREA0; // Offset of register area in Modbus protocol
reg_area.address = (void*)&holding_reg_params.holding_data0; // Set pointer to storage instance
reg_area.size = (MB_REG_HOLDING_START_AREA1 - MB_REG_HOLDING_START_AREA0) << 1; // Set the size of register storage instance
err = mbc_slave_set_descriptor(reg_area);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"mbc_slave_set_descriptor fail, returns(0x%x).",
(int)err);
reg_area.type = MB_PARAM_HOLDING; // Set type of register area
reg_area.start_offset = MB_REG_HOLDING_START_AREA1; // Offset of register area in Modbus protocol
reg_area.address = (void*)&holding_reg_params.holding_data4; // Set pointer to storage instance
reg_area.size = sizeof(float) << 2; // Set the size of register storage instance
err = mbc_slave_set_descriptor(reg_area);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"mbc_slave_set_descriptor fail, returns(0x%x).",
(int)err);
// Initialization of Input Registers area
reg_area.type = MB_PARAM_INPUT;
reg_area.start_offset = MB_REG_INPUT_START_AREA0;
reg_area.address = (void*)&input_reg_params.input_data0;
reg_area.size = sizeof(float) << 2;
err = mbc_slave_set_descriptor(reg_area);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"mbc_slave_set_descriptor fail, returns(0x%x).",
(int)err);
reg_area.type = MB_PARAM_INPUT;
reg_area.start_offset = MB_REG_INPUT_START_AREA1;
reg_area.address = (void*)&input_reg_params.input_data4;
reg_area.size = sizeof(float) << 2;
err = mbc_slave_set_descriptor(reg_area);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"mbc_slave_set_descriptor fail, returns(0x%x).",
(int)err);
// Initialization of Coils register area
reg_area.type = MB_PARAM_COIL;
reg_area.start_offset = MB_REG_COILS_START;
reg_area.address = (void*)&coil_reg_params;
reg_area.size = sizeof(coil_reg_params);
err = mbc_slave_set_descriptor(reg_area);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"mbc_slave_set_descriptor fail, returns(0x%x).",
(int)err);
// Initialization of Discrete Inputs register area
reg_area.type = MB_PARAM_DISCRETE;
reg_area.start_offset = MB_REG_DISCRETE_INPUT_START;
reg_area.address = (void*)&discrete_reg_params;
reg_area.size = sizeof(discrete_reg_params);
err = mbc_slave_set_descriptor(reg_area);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
"mbc_slave_set_descriptor fail, returns(0x%x).",
(int)err);
Why the offset for "discrete inputs" and "coils" is 0 while for the other structures requires the use of offsetof()
? To me the structures seem very similar.
Here I read it is the modbus register's offset: but still I don't understand why it is 0 for discrete inputs and coils (they should lie in 1x and 0x areas).
Bonus: I don't understand the uint16_t data[150];
and data_block1[150];
inside input_reg_params_t
. I see they divide the structure into two areas, but looking at the given reg_area.size
it seems they don't include at all those variables.
Upvotes: 1
Views: 112
Reputation: 16047
Why the offset for "discrete inputs" and "coils" is 0 while for the other structures requires the use of offsetof()? To me the structures seem very similar.
In Modbus input and holding registers are (usually) 16-bit units. HOLD_OFFSET
and INPUT_OFFSET
calculate these modbus addresses from using relative position of members in struct with offsetof
macro. You can even see the division by 2 with the right shift >> 1
at the end of macro, which essentially converts 8-bit unit offset to 16-bit unit offset.
Discrete inputs and and coils are 1-bit registers in Modbus specification. offsetof
macro won't work, because in C you can not have address with 1-bit precision. Smallest memory unit is byte, which is usually 8-bits.
It seems that since Modbus start address for discrete inputs and coils is always 0 anyway, might as well use that directly, and not bother with fancy macro code.
Bonus: I don't understand the uint16_t data[150]; and data_block1[150]; inside input_reg_params_t. I see they divide the structure into two areas, but looking at the given reg_area.size it seems they don't include at all those variables.
I am guessing that it is simply padding for registers they are not interested in. It makes offsetof
produce presumably correct address value.
I don't like that code at all.
It abuses packed structs for serialization purposes, relies on poorly defined struct bit-field positioning, uses poor member naming (holding_data4
), uses hacky offsetof
address calculation and bitshift for division for no reason.
I know it may simply be example code, but please keep above in mind.
Upvotes: 2