/* * SPDX-FileCopyrightText: 2016-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include "esp_err.h" // for esp_err_t #include "mbc_master.h" // for master interface define #include "esp_modbus_master.h" // for public interface defines // Helper macro to set custom command #define GET_CMD(mode, access, rd_cmd, wr_cmd) (((mode == MB_PARAM_WRITE) && (access & PAR_PERMS_WRITE)) ? wr_cmd : \ ((mode == MB_PARAM_READ) && (access & PAR_PERMS_READ)) ? rd_cmd : 0) static const char TAG[] __attribute__((unused)) = "MB_CONTROLLER_MASTER"; // This file implements public API for Modbus master controller. /** * Modbus controller delete function */ esp_err_t mbc_master_delete(void *ctx) { esp_err_t error = ESP_OK; MB_RETURN_ON_FALSE(ctx, ESP_ERR_INVALID_STATE, TAG, "Master interface is not correctly initialized."); mbm_controller_iface_t *mbm_controller = MB_MASTER_GET_IFACE(ctx); MB_RETURN_ON_FALSE(mbm_controller->delete, ESP_ERR_INVALID_STATE, TAG, "Master interface is not correctly initialized."); error = mbm_controller->delete (ctx); MB_RETURN_ON_FALSE((error == ESP_OK), error, TAG, "Master delete failure, error=(0x%x).", (uint16_t)error); return error; } /** * Critical section lock function */ esp_err_t mbc_master_lock(void *ctx) { MB_RETURN_ON_FALSE(ctx, ESP_ERR_INVALID_STATE, TAG, "Master interface is not correctly initialized."); mbm_controller_iface_t *mbm_controller = MB_MASTER_GET_IFACE(ctx); mb_base_t *pmb_obj = (mb_base_t *)mbm_controller->mb_base; MB_RETURN_ON_FALSE((pmb_obj && pmb_obj->lock), ESP_ERR_INVALID_STATE, TAG, "Master interface is not correctly initialized."); CRITICAL_SECTION_LOCK(pmb_obj->lock); return ESP_OK; } /** * Critical section unlock function */ esp_err_t mbc_master_unlock(void *ctx) { MB_RETURN_ON_FALSE(ctx, ESP_ERR_INVALID_STATE, TAG, "Master interface is not correctly initialized."); mbm_controller_iface_t *mbm_controller = MB_MASTER_GET_IFACE(ctx); mb_base_t *pmb_obj = (mb_base_t *)mbm_controller->mb_base; MB_RETURN_ON_FALSE((pmb_obj && pmb_obj->lock), ESP_ERR_INVALID_STATE, TAG, "Master interface is not correctly initialized."); CRITICAL_SECTION_UNLOCK(pmb_obj->lock); return ESP_OK; } esp_err_t mbc_master_get_cid_info(void *ctx, uint16_t cid, const mb_parameter_descriptor_t **param_info) { esp_err_t error = ESP_OK; MB_RETURN_ON_FALSE(ctx, ESP_ERR_INVALID_STATE, TAG, "Master interface is not correctly initialized."); mbm_controller_iface_t *mbm_controller = MB_MASTER_GET_IFACE(ctx); MB_RETURN_ON_FALSE((mbm_controller->get_cid_info && mbm_controller->is_active), ESP_ERR_INVALID_STATE, TAG, "Master interface is not correctly configured."); error = mbm_controller->get_cid_info(ctx, cid, param_info); MB_RETURN_ON_FALSE((error == ESP_OK), error, TAG, "Master get cid info failure, error=(0x%x).", (uint16_t)error); return error; } /** * Set parameter value for characteristic selected by name and cid */ esp_err_t mbc_master_set_parameter(void *ctx, uint16_t cid, uint8_t *value, uint8_t *type) { esp_err_t error = ESP_OK; MB_RETURN_ON_FALSE(ctx, ESP_ERR_INVALID_STATE, TAG, "Master interface is not correctly initialized."); mbm_controller_iface_t *mbm_controller = MB_MASTER_GET_IFACE(ctx); MB_RETURN_ON_FALSE((mbm_controller->set_parameter && mbm_controller->is_active), ESP_ERR_INVALID_STATE, TAG, "Master interface is not correctly initialized."); error = mbm_controller->set_parameter(ctx, cid, value, type); MB_RETURN_ON_FALSE((error == ESP_OK), error, TAG, "Master set parameter failure, error=(0x%x) (%s).", (uint16_t)error, esp_err_to_name(error)); return ESP_OK; } /** * Set parameter value for characteristic selected by name and cid */ esp_err_t mbc_master_set_parameter_with(void *ctx, uint16_t cid, uint8_t uid, uint8_t *value, uint8_t *type) { esp_err_t error = ESP_OK; MB_RETURN_ON_FALSE(ctx, ESP_ERR_INVALID_STATE, TAG, "Master interface is not correctly initialized."); mbm_controller_iface_t *mbm_controller = MB_MASTER_GET_IFACE(ctx); MB_RETURN_ON_FALSE((mbm_controller->set_parameter_with && mbm_controller->is_active), ESP_ERR_INVALID_STATE, TAG, "Master interface is not correctly initialized."); error = mbm_controller->set_parameter_with(ctx, cid, uid, value, type); MB_RETURN_ON_FALSE((error == ESP_OK), error, TAG, "Master set parameter failure, error=(0x%x) (%s).", (uint16_t)error, esp_err_to_name(error)); return ESP_OK; } /** * Get parameter data for corresponding characteristic */ esp_err_t mbc_master_get_parameter(void *ctx, uint16_t cid, uint8_t *value, uint8_t *type) { esp_err_t error = ESP_OK; MB_RETURN_ON_FALSE(ctx, ESP_ERR_INVALID_STATE, TAG, "Master interface is not correctly initialized."); mbm_controller_iface_t *mbm_controller = MB_MASTER_GET_IFACE(ctx); MB_RETURN_ON_FALSE((mbm_controller->get_parameter && mbm_controller->is_active), ESP_ERR_INVALID_STATE, TAG, "Master interface is not correctly configured."); error = mbm_controller->get_parameter(ctx, cid, value, type); MB_RETURN_ON_FALSE((error == ESP_OK), error, TAG, "Master get parameter failure, error=(0x%x) (%s).", (uint16_t)error, esp_err_to_name(error)); return error; } /** * Get parameter data for corresponding characteristic */ esp_err_t mbc_master_get_parameter_with(void *ctx, uint16_t cid, uint8_t uid, uint8_t *value, uint8_t *type) { esp_err_t error = ESP_OK; MB_RETURN_ON_FALSE(ctx, ESP_ERR_INVALID_STATE, TAG, "Master interface is not correctly initialized."); mbm_controller_iface_t *mbm_controller = MB_MASTER_GET_IFACE(ctx); MB_RETURN_ON_FALSE((mbm_controller->get_parameter_with && mbm_controller->is_active), ESP_ERR_INVALID_STATE, TAG, "Master interface is not correctly configured."); error = mbm_controller->get_parameter_with(ctx, cid, uid, value, type); MB_RETURN_ON_FALSE((error == ESP_OK), error, TAG, "Master get parameter failure, error=(0x%x) (%s).", (uint16_t)error, esp_err_to_name(error)); return error; } /** * Send custom Modbus request defined as mb_param_request_t structure */ esp_err_t mbc_master_send_request(void *ctx, mb_param_request_t *request, void *data_ptr) { esp_err_t error = ESP_OK; MB_RETURN_ON_FALSE(ctx, ESP_ERR_INVALID_STATE, TAG, "Master interface is not correctly initialized."); mbm_controller_iface_t *mbm_controller = MB_MASTER_GET_IFACE(ctx); MB_RETURN_ON_FALSE((mbm_controller->send_request && mbm_controller->is_active), ESP_ERR_INVALID_STATE, TAG, "Master interface is not correctly configured."); error = mbm_controller->send_request(ctx, request, data_ptr); MB_RETURN_ON_FALSE((error == ESP_OK), error, TAG, "Master send request failure error=(0x%x) (%s).", (uint16_t)error, esp_err_to_name(error)); return ESP_OK; } /** * Set Modbus parameter description table */ esp_err_t mbc_master_set_descriptor(void *ctx, const mb_parameter_descriptor_t *descriptor, const uint16_t num_elements) { esp_err_t error = ESP_OK; MB_RETURN_ON_FALSE(ctx, ESP_ERR_INVALID_STATE, TAG, "Master interface is not correctly initialized."); mbm_controller_iface_t *mbm_controller = MB_MASTER_GET_IFACE(ctx); MB_RETURN_ON_FALSE(mbm_controller->set_descriptor, ESP_ERR_INVALID_STATE, TAG, "Master interface is not correctly configured."); error = mbm_controller->set_descriptor(ctx, descriptor, num_elements); MB_RETURN_ON_FALSE((error == ESP_OK), error, TAG, "Master set descriptor failure, error=(0x%x) (%s).", (uint16_t)error, esp_err_to_name(error)); return ESP_OK; } /** * Modbus controller stack start function */ esp_err_t mbc_master_start(void *ctx) { esp_err_t error = ESP_OK; MB_RETURN_ON_FALSE(ctx, ESP_ERR_INVALID_STATE, TAG, "Master interface is not correctly initialized."); mbm_controller_iface_t *mbm_controller = MB_MASTER_GET_IFACE(ctx); MB_RETURN_ON_FALSE(mbm_controller->start, ESP_ERR_INVALID_STATE, TAG, "Master interface is not correctly initialized."); error = mbm_controller->start(ctx); MB_RETURN_ON_FALSE((error == ESP_OK), error, TAG, "Master start failure, error=(0x%x) (%s).", (uint16_t)error, esp_err_to_name(error)); return ESP_OK; } /** * Modbus controller stack stop function */ esp_err_t mbc_master_stop(void *ctx) { esp_err_t error = ESP_OK; MB_RETURN_ON_FALSE(ctx, ESP_ERR_INVALID_STATE, TAG, "Master interface is not correctly initialized."); mbm_controller_iface_t *mbm_controller = MB_MASTER_GET_IFACE(ctx); MB_RETURN_ON_FALSE(mbm_controller->stop, ESP_ERR_INVALID_STATE, TAG, "Master interface is not correctly initialized."); error = mbm_controller->stop(ctx); MB_RETURN_ON_FALSE((error == ESP_OK), error, TAG, "Master stop failure, error=(0x%x) (%s).", (uint16_t)error, esp_err_to_name(error)); return ESP_OK; } /* ----------------------- Callback functions for Modbus stack ---------------------------------*/ // These are executed by modbus stack to read appropriate type of registers. mb_err_enum_t mbc_reg_common_cb(mb_base_t *inst, uint8_t *pdata, uint16_t address, uint16_t bytes) { MB_RETURN_ON_FALSE((pdata), MB_EINVAL, TAG, "incorrect parameters provided."); mb_master_options_t *popts = MB_MASTER_GET_OPTS(MB_MASTER_GET_IFACE_FROM_BASE(inst)); uint16_t reg_len = popts->reg_buffer_size; uint8_t *ppar_buffer = (uint8_t *)popts->reg_buffer_ptr; // Get instance address mb_err_enum_t status = MB_ENOERR; if (ppar_buffer && !address && (bytes >= 2) && (((reg_len << 1) >= bytes))){ CRITICAL_SECTION(inst->lock) { memmove(ppar_buffer, pdata, bytes); } } else { status = MB_ENORES; } return status; } /** * Modbus master input register callback function. * * @param inst interface context pointer * @param reg_buffer input register buffer * @param reg_addr input register address * @param num_regs input register number * * @return result */ // Callback function for reading of MB Input Registers // mbm_reg_input_cb_serial mb_err_enum_t mbc_reg_input_master_cb(mb_base_t *inst, uint8_t *reg_buffer, uint16_t reg_addr, uint16_t num_regs) { MB_RETURN_ON_FALSE((reg_buffer), MB_EINVAL, TAG, "Master stack processing error."); mb_master_options_t *mbm_opts = MB_MASTER_GET_OPTS(MB_MASTER_GET_IFACE_FROM_BASE(inst)); // Number of input registers to be transferred uint16_t num_input_regs = (uint16_t)mbm_opts->reg_buffer_size; uint8_t *input_reg_buf = (uint8_t *)mbm_opts->reg_buffer_ptr; // Get instance address uint16_t regs_cnt = num_regs; mb_err_enum_t status = MB_ENOERR; // If input or configuration parameters are incorrect then return an error to stack layer if ((input_reg_buf) && (num_regs >= 1) && (num_input_regs == regs_cnt)) { CRITICAL_SECTION(inst->lock) { while (regs_cnt > 0) { _XFER_2_RD(input_reg_buf, reg_buffer); regs_cnt -= 1; } } } else { status = MB_ENOREG; } return status; } /** * Modbus master holding register callback function. * * @param inst interface context pointer * @param reg_buffer holding register buffer * @param reg_addr holding register address * @param num_regs holding register number * @param mode read or write * * @return result */ // Callback function for reading of MB Holding Registers // Executed by stack when request to read/write holding registers is received // mbm_reg_holding_cb_serial mb_err_enum_t mbc_reg_holding_master_cb(mb_base_t *inst, uint8_t *reg_buffer, uint16_t reg_addr, uint16_t num_regs, mb_reg_mode_enum_t mode) { MB_RETURN_ON_FALSE((reg_buffer), MB_EINVAL, TAG, "Master stack processing error."); mb_master_options_t *mbm_opts = MB_MASTER_GET_OPTS(MB_MASTER_GET_IFACE_FROM_BASE(inst)); uint16_t num_hold_regs = (uint16_t)mbm_opts->reg_buffer_size; uint8_t *holding_buf = (uint8_t *)mbm_opts->reg_buffer_ptr; mb_err_enum_t status = MB_ENOERR; uint16_t regs_cnt = num_regs; // Check input and configuration parameters for correctness if ((holding_buf) && (num_hold_regs == num_regs) && (num_regs >= 1)) { switch (mode) { case MB_REG_WRITE: CRITICAL_SECTION(inst->lock) { while (regs_cnt > 0) { _XFER_2_RD(reg_buffer, holding_buf); regs_cnt -= 1; } } break; case MB_REG_READ: CRITICAL_SECTION(inst->lock) { while (regs_cnt > 0) { _XFER_2_WR(holding_buf, reg_buffer); holding_buf += 2; regs_cnt -= 1; } } break; } } else { status = MB_ENOREG; } return status; } /** * Modbus master coils callback function. * * @param inst interface context pointer * @param reg_buffer coils buffer * @param reg_addr coils address * @param ncoils coils number * @param mode read or write * * @return result */ // Callback function for reading of MB Coils Registers // mbm_reg_coils_cb_serial mb_err_enum_t mbc_reg_coils_master_cb(mb_base_t *inst, uint8_t *reg_buffer, uint16_t reg_addr, uint16_t ncoils, mb_reg_mode_enum_t mode) { MB_RETURN_ON_FALSE((reg_buffer), MB_EINVAL, TAG, "Master stack processing error."); mb_master_options_t *mbm_opts = MB_MASTER_GET_OPTS(MB_MASTER_GET_IFACE_FROM_BASE(inst)); uint16_t num_coil_regs = (uint16_t)mbm_opts->reg_buffer_size; uint8_t *coils_buf = (uint8_t *)mbm_opts->reg_buffer_ptr; mb_err_enum_t status = MB_ENOERR; uint16_t reg_index; uint16_t coils_cnt = ncoils; reg_addr--; // The address is already + 1 if ((num_coil_regs >= 1) && (coils_buf) && (ncoils == num_coil_regs)) { reg_index = (reg_addr % 8); switch (mode) { case MB_REG_WRITE: CRITICAL_SECTION(inst->lock) { while (coils_cnt > 0) { uint8_t result = mb_util_get_bits((uint8_t *)coils_buf, reg_index - (reg_addr % 8), 1); mb_util_set_bits(reg_buffer, reg_index - (reg_addr % 8), 1, result); reg_index++; coils_cnt--; } } break; case MB_REG_READ: CRITICAL_SECTION(inst->lock) { while (coils_cnt > 0) { uint8_t result = mb_util_get_bits(reg_buffer, reg_index - (reg_addr % 8), 1); mb_util_set_bits((uint8_t *)coils_buf, reg_index - (reg_addr % 8), 1, result); reg_index++; coils_cnt--; } } break; } // switch ( mode ) } else { // If the configuration or input parameters are incorrect then return error to stack status = MB_ENOREG; } return status; } /** * Modbus master discrete callback function. * * @param inst - pointer to interface structure * @param reg_buffer discrete buffer * @param reg_addr discrete address * @param n_discrete discrete number * * @return result */ // Callback function for reading of MB Discrete Input Registers // mbm_reg_discrete_cb_serial mb_err_enum_t mbc_reg_discrete_master_cb(mb_base_t *inst, uint8_t *reg_buffer, uint16_t reg_addr, uint16_t n_discrete) { MB_RETURN_ON_FALSE((reg_buffer), MB_EINVAL, TAG, "Master stack processing error."); mb_master_options_t *mbm_opts = MB_MASTER_GET_OPTS(MB_MASTER_GET_IFACE_FROM_BASE(inst)); uint16_t num_discr_regs = (uint16_t)mbm_opts->reg_buffer_size; uint8_t *discr_buf = (uint8_t *)mbm_opts->reg_buffer_ptr; mb_err_enum_t status = MB_ENOERR; uint16_t bit_index, num_reg; uint8_t *temp_discr_buf; num_reg = n_discrete; temp_discr_buf = (uint8_t *)discr_buf; // It is already plus one in Modbus function method. reg_addr--; if ((num_discr_regs >= 1) && (discr_buf) && (n_discrete >= 1) && (n_discrete == num_discr_regs)) { bit_index = (uint16_t)(reg_addr) % 8; // Get bit index CRITICAL_SECTION(inst->lock) { while (num_reg > 0) { uint8_t result = mb_util_get_bits(reg_buffer, bit_index - (reg_addr % 8), 1); mb_util_set_bits(temp_discr_buf, bit_index - (reg_addr % 8), 1, result); bit_index++; num_reg--; } } } else { status = MB_ENOREG; } return status; } // Helper function to set parameter buffer according to its type esp_err_t mbc_master_set_param_data(void* dest, void* src, mb_descr_type_t param_type, size_t param_size) { esp_err_t err = ESP_OK; MB_RETURN_ON_FALSE((src), ESP_ERR_INVALID_STATE, TAG,"incorrect data pointer."); MB_RETURN_ON_FALSE((dest), ESP_ERR_INVALID_STATE, TAG,"incorrect data pointer."); void *pdest = dest; void *psrc = src; // Transfer parameter data into value of characteristic switch(param_type) { case PARAM_TYPE_U8: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U8) { *((uint8_t *)pdest) = *((uint8_t*)psrc); } break; case PARAM_TYPE_U16: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U16) { *((uint16_t *)pdest) = *((uint16_t*)psrc); } break; case PARAM_TYPE_U32: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U32) { *((uint32_t *)pdest) = *((uint32_t*)psrc); } break; case PARAM_TYPE_FLOAT: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_FLOAT) { *((float *)pdest) = *(float*)psrc; } break; case PARAM_TYPE_ASCII: case PARAM_TYPE_BIN: memcpy((void *)dest, (void*)src, (size_t)param_size); break; #if CONFIG_FMB_EXT_TYPE_SUPPORT case PARAM_TYPE_I8_A: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U8_REG) { mb_set_int8_a((val_16_arr *)pdest, (*(int8_t*)psrc)); ESP_LOGV(TAG, "Convert uint8 B[%d] 0x%04" PRIx16 " = 0x%04" PRIx16, i, *(uint16_t *)psrc, *(uint16_t *)pdest); } break; case PARAM_TYPE_I8_B: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U8_REG) { mb_set_int8_b((val_16_arr *)pdest, (int8_t)((*(uint16_t*)psrc) >> 8)); ESP_LOGV(TAG, "Convert int8 A[%d] 0x%02" PRIx16 " = 0x%02" PRIx16, i, *(uint16_t *)psrc, *(uint16_t *)pdest); } break; case PARAM_TYPE_U8_A: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U8_REG) { mb_set_uint8_a((val_16_arr *)pdest, (*(uint8_t*)psrc)); ESP_LOGV(TAG, "Convert uint8 A[%d] 0x%02" PRIx16 " = %02" PRIx16, i, *(uint16_t *)psrc, *(uint16_t *)pdest); } break; case PARAM_TYPE_U8_B: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U8_REG) { uint8_t data = (uint8_t)((*(uint16_t*)psrc) >> 8); mb_set_uint8_b((val_16_arr *)pdest, data); ESP_LOGV(TAG, "Convert uint8 B[%d] 0x%02" PRIx16 " = 0x%02" PRIx16, i, *(uint16_t *)psrc, *(uint16_t *)pdest); } break; case PARAM_TYPE_I16_AB: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_I16) { mb_set_int16_ab((val_16_arr *)pdest, *(int16_t*)psrc); ESP_LOGV(TAG, "Convert int16 AB[%d] 0x%04" PRIx16 " = 0x%04" PRIx16, i, *(uint16_t *)psrc, *(uint16_t *)pdest); } break; case PARAM_TYPE_I16_BA: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_I16) { mb_set_int16_ba((val_16_arr *)pdest, *(int16_t*)psrc); ESP_LOGV(TAG, "Convert int16 BA[%d] 0x%04" PRIx16 " = 0x%04" PRIx16, i, *(uint16_t *)psrc, *(uint16_t *)pdest); } break; case PARAM_TYPE_U16_AB: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U16) { mb_set_uint16_ab((val_16_arr *)pdest, *(uint16_t*)psrc); ESP_LOGV(TAG, "Convert uint16 AB[%d] 0x%02" PRIx16 " = 0x%02" PRIx16, i, *(uint16_t *)psrc, *(uint16_t *)pdest); } break; case PARAM_TYPE_U16_BA: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U16) { mb_set_uint16_ba((val_16_arr *)pdest, *(uint16_t*)psrc); ESP_LOGV(TAG, "Convert uint16 BA[%d] 0x%02" PRIx16 " = 0x%02" PRIx16, i, *(uint16_t *)psrc, *(uint16_t *)pdest); } break; case PARAM_TYPE_I32_ABCD: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_I32) { mb_set_int32_abcd((val_32_arr *)pdest, *(int32_t *)psrc); ESP_LOGV(TAG, "Convert int32 ABCD[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest); } break; case PARAM_TYPE_U32_ABCD: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U32) { mb_set_uint32_abcd((val_32_arr *)pdest, *(uint32_t *)psrc); ESP_LOGV(TAG, "Convert uint32 ABCD[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest); } break; case PARAM_TYPE_FLOAT_ABCD: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_FLOAT) { mb_set_float_abcd((val_32_arr *)pdest, *(float *)psrc); ESP_LOGV(TAG, "Convert float ABCD[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest); } break; case PARAM_TYPE_I32_CDAB: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_I32) { mb_set_int32_cdab((val_32_arr *)pdest, *(int32_t *)psrc); ESP_LOGV(TAG, "Convert int32 CDAB[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest); } break; case PARAM_TYPE_U32_CDAB: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U32) { mb_set_uint32_cdab((val_32_arr *)pdest, *(uint32_t *)psrc); ESP_LOGV(TAG, "Convert uint32 CDAB[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest); } break; case PARAM_TYPE_FLOAT_CDAB: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_FLOAT) { mb_set_float_cdab((val_32_arr *)pdest, *(float *)psrc); ESP_LOGV(TAG, "Convert float CDAB[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest); } break; case PARAM_TYPE_I32_BADC: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_I32) { mb_set_int32_badc((val_32_arr *)pdest, *(int32_t *)psrc); ESP_LOGV(TAG, "Convert int32 BADC[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest); } break; case PARAM_TYPE_U32_BADC: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U32) { mb_set_uint32_badc((val_32_arr *)pdest, *(uint32_t *)psrc); ESP_LOGV(TAG, "Convert uint32 BADC[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest); } break; case PARAM_TYPE_FLOAT_BADC: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_FLOAT) { mb_set_float_badc((val_32_arr *)pdest, *(float *)psrc); ESP_LOGV(TAG, "Convert float BADC[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest); } break; case PARAM_TYPE_I32_DCBA: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_I32) { mb_set_int32_dcba((val_32_arr *)pdest, *(int32_t *)psrc); ESP_LOGV(TAG, "Convert int32 DCBA[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest); } break; case PARAM_TYPE_U32_DCBA: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U32) { mb_set_uint32_dcba((val_32_arr *)pdest, *(uint32_t *)psrc); ESP_LOGV(TAG, "Convert uint32 DCBA[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest); } break; case PARAM_TYPE_FLOAT_DCBA: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_FLOAT) { mb_set_float_dcba((val_32_arr *)pdest, *(float *)psrc); ESP_LOGV(TAG, "Convert float DCBA[%d] 0x%04" PRIx32 " = 0x%04" PRIx32, i, *(uint32_t *)psrc, *(uint32_t *)pdest); } break; case PARAM_TYPE_I64_ABCDEFGH: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_I64) { mb_set_int64_abcdefgh((val_64_arr *)pdest, *(int64_t *)psrc); ESP_LOGV(TAG, "Convert int64 ABCDEFGH[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest); } break; case PARAM_TYPE_U64_ABCDEFGH: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U64) { mb_set_uint64_abcdefgh((val_64_arr *)pdest, *(uint64_t *)psrc); ESP_LOGV(TAG, "Convert double ABCDEFGH[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest); } break; case PARAM_TYPE_DOUBLE_ABCDEFGH: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_DOUBLE) { mb_set_double_abcdefgh((val_64_arr *)pdest, *(double *)psrc); ESP_LOGV(TAG, "Convert double ABCDEFGH[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest); } break; case PARAM_TYPE_I64_HGFEDCBA: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_I64) { mb_set_int64_hgfedcba((val_64_arr *)pdest, *(int64_t *)psrc); ESP_LOGV(TAG, "Convert int64 HGFEDCBA[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest); } break; case PARAM_TYPE_U64_HGFEDCBA: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U64) { mb_set_uint64_hgfedcba((val_64_arr *)pdest, *(uint64_t *)psrc); ESP_LOGV(TAG, "Convert double HGFEDCBA[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest); } break; case PARAM_TYPE_DOUBLE_HGFEDCBA: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_DOUBLE) { mb_set_double_hgfedcba((val_64_arr *)pdest, *(double *)psrc); ESP_LOGV(TAG, "Convert double HGFEDCBA[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest); } break; case PARAM_TYPE_I64_GHEFCDAB: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_I64) { mb_set_int64_ghefcdab((val_64_arr *)pdest, *(int64_t *)psrc); ESP_LOGV(TAG, "Convert int64 GHEFCDAB[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest); } break; case PARAM_TYPE_U64_GHEFCDAB: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U64) { mb_set_uint64_ghefcdab((val_64_arr *)pdest, *(uint64_t *)psrc); ESP_LOGV(TAG, "Convert uint64 GHEFCDAB[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest); } break; case PARAM_TYPE_DOUBLE_GHEFCDAB: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_DOUBLE) { mb_set_double_ghefcdab((val_64_arr *)pdest, *(double *)psrc); ESP_LOGV(TAG, "Convert double GHEFCDAB[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest); } break; case PARAM_TYPE_I64_BADCFEHG: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_I64) { mb_set_int64_badcfehg((val_64_arr *)pdest, *(int64_t *)psrc); ESP_LOGV(TAG, "Convert int64 BADCFEHG[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest); } break; case PARAM_TYPE_U64_BADCFEHG: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_U64) { mb_set_uint64_badcfehg((val_64_arr *)pdest, *(uint64_t *)psrc); ESP_LOGV(TAG, "Convert uint64 BADCFEHG[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest); } break; case PARAM_TYPE_DOUBLE_BADCFEHG: for MB_EACH_ELEM(psrc, pdest, param_size, PARAM_SIZE_DOUBLE) { mb_set_double_badcfehg((val_64_arr *)pdest, *(double *)psrc); ESP_LOGV(TAG, "Convert double BADCFEHG[%d] 0x%" PRIx64 " = 0x%" PRIx64, i, *(uint64_t *)psrc, *(uint64_t *)pdest); } break; #endif default: ESP_LOGE(TAG, "%s: Incorrect param type (%u).", __FUNCTION__, (unsigned)param_type); err = ESP_ERR_NOT_SUPPORTED; break; } return err; } // Helper function to get configured Modbus command for each type of Modbus register area. // Supports custom command options using the PAR_PERMS_CUST_CMD permission. // The MB_PARAM_CUSTOM register type mimics the custom commands specificly handled with // custom command handlers which have to be defined in command handling table. uint8_t mbc_master_get_command(const mb_parameter_descriptor_t *pdescr, mb_param_mode_t mode) { MB_RETURN_ON_FALSE((pdescr), 0, TAG, "incorrect data pointer."); uint8_t command = 0; switch(pdescr->mb_param_type) { case MB_PARAM_HOLDING: command = GET_CMD(mode, pdescr->access, MB_FUNC_READ_HOLDING_REGISTER, MB_FUNC_WRITE_MULTIPLE_REGISTERS); break; case MB_PARAM_INPUT: command = GET_CMD(mode, pdescr->access, MB_FUNC_READ_INPUT_REGISTER, 0); break; case MB_PARAM_COIL: command = GET_CMD(mode, pdescr->access, MB_FUNC_READ_COILS, MB_FUNC_WRITE_MULTIPLE_COILS); break; case MB_PARAM_DISCRETE: command = GET_CMD(mode, pdescr->access, MB_FUNC_READ_DISCRETE_INPUTS, 0); break; case MB_PARAM_CUSTOM: if (pdescr->access & PAR_PERMS_CUST_CMD) { // Use custom command in the request for read or write command = GET_CMD(mode, pdescr->access, (uint8_t)pdescr->param_opts.cust_cmd_read, (uint8_t)pdescr->param_opts.cust_cmd_write); } else { command = 0; } break; default: ESP_LOGE(TAG, "%s: Incorrect param type (%u)", __FUNCTION__, (unsigned)pdescr->mb_param_type); break; } return command; }