diff --git a/examples/serial/mb_serial_master/main/serial_master.c b/examples/serial/mb_serial_master/main/serial_master.c index 8526876..65ff998 100644 --- a/examples/serial/mb_serial_master/main/serial_master.c +++ b/examples/serial/mb_serial_master/main/serial_master.c @@ -73,6 +73,7 @@ enum { CID_HOLD_DATA_1, CID_INP_DATA_2, CID_HOLD_DATA_2, + CID_HOLD_CUSTOM1, CID_HOLD_TEST_REG, CID_RELAY_P1, CID_RELAY_P2, @@ -133,6 +134,10 @@ const mb_parameter_descriptor_t device_parameters[] = { TEST_HOLD_REG_START(holding_data2), TEST_HOLD_REG_SIZE(holding_data2), HOLD_OFFSET(holding_data2), PARAM_TYPE_FLOAT, 4, OPTS( TEST_HUMI_MIN, TEST_HUMI_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER }, + { CID_HOLD_CUSTOM1, STR("CustomHoldReg"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, + TEST_HOLD_REG_START(holding_area1_end), 1, + HOLD_OFFSET(holding_area1_end), PARAM_TYPE_U16, 2, + OPTS( 0x03, 0x06, 0x5555 ), PAR_PERMS_READ_WRITE_CUST_CMD }, { CID_HOLD_TEST_REG, STR("Test_regs"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, TEST_HOLD_REG_START(test_regs), TEST_ARR_REG_SZ, HOLD_OFFSET(test_regs), PARAM_TYPE_ASCII, (TEST_ARR_REG_SZ * 2), @@ -320,8 +325,9 @@ static void master_operation_func(void *arg) if ((err != ESP_ERR_NOT_FOUND) && (param_descriptor != NULL)) { void *temp_data_ptr = master_get_param_data(param_descriptor); assert(temp_data_ptr); - if ((param_descriptor->param_type == PARAM_TYPE_ASCII) && - (param_descriptor->cid == CID_HOLD_TEST_REG)) { + if ((param_descriptor->cid >= CID_HOLD_CUSTOM1) + && (param_descriptor->cid <= CID_HOLD_TEST_REG)) { + // Check test parameters if (TEST_VERIFY_VALUES(master_handle, param_descriptor, (uint32_t *)temp_data_ptr) == ESP_OK) { ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = (0x%" PRIx32 ") read successful.", (int)param_descriptor->cid, diff --git a/examples/tcp/mb_tcp_master/main/tcp_master.c b/examples/tcp/mb_tcp_master/main/tcp_master.c index 71125a1..02e015e 100644 --- a/examples/tcp/mb_tcp_master/main/tcp_master.c +++ b/examples/tcp/mb_tcp_master/main/tcp_master.c @@ -102,6 +102,7 @@ enum { CID_HOLD_DATA_1, CID_INP_DATA_2, CID_HOLD_DATA_2, + CID_HOLD_CUSTOM1, CID_HOLD_TEST_REG, CID_RELAY_P1, CID_RELAY_P2, @@ -162,6 +163,10 @@ const mb_parameter_descriptor_t device_parameters[] = { TEST_HOLD_REG_START(holding_data2), TEST_HOLD_REG_SIZE(holding_data2), HOLD_OFFSET(holding_data2), PARAM_TYPE_FLOAT, 4, OPTS( TEST_HUMI_MIN, TEST_HUMI_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER }, + { CID_HOLD_CUSTOM1, STR("CustomHoldReg"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, + TEST_HOLD_REG_START(holding_area1_end), 1, + HOLD_OFFSET(holding_area1_end), PARAM_TYPE_U16, 2, + OPTS( 0x03, 0x06, 0x5555 ), PAR_PERMS_READ_WRITE_CUST_CMD }, { CID_HOLD_TEST_REG, STR("Test_regs"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, TEST_HOLD_REG_START(test_regs), TEST_ARR_REG_SZ, HOLD_OFFSET(test_regs), PARAM_TYPE_ASCII, (TEST_ARR_REG_SZ * 2), @@ -460,8 +465,9 @@ static void master_operation_func(void *arg) if ((err != ESP_ERR_NOT_FOUND) && (param_descriptor != NULL)) { void *temp_data_ptr = master_get_param_data(param_descriptor); assert(temp_data_ptr); - if ((param_descriptor->param_type == PARAM_TYPE_ASCII) && - (param_descriptor->cid == CID_HOLD_TEST_REG)) { + if ((param_descriptor->cid >= CID_HOLD_CUSTOM1) + && (param_descriptor->cid <= CID_HOLD_TEST_REG)) { + // Check test parameters if (TEST_VERIFY_VALUES(master_handle, param_descriptor, (uint32_t *)temp_data_ptr) == ESP_OK) { ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = (0x%" PRIx32 ") read successful.", (int)param_descriptor->cid, diff --git a/modbus/mb_controller/common/esp_modbus_master.c b/modbus/mb_controller/common/esp_modbus_master.c index 99bece9..6862f97 100644 --- a/modbus/mb_controller/common/esp_modbus_master.c +++ b/modbus/mb_controller/common/esp_modbus_master.c @@ -8,6 +8,9 @@ #include "mbc_master.h" // for master interface define #include "esp_modbus_master.h" // for public interface defines +// Helper macro to set custom command +#define SET_CMD(mode, rd_cmd, wr_cmd) ((mode == MB_PARAM_WRITE) ? wr_cmd : (mode == MB_PARAM_READ) ? rd_cmd : 0) + static const char TAG[] __attribute__((unused)) = "MB_CONTROLLER_MASTER"; // This file implements public API for Modbus master controller. @@ -714,3 +717,46 @@ esp_err_t mbc_master_set_param_data(void* dest, void* src, mb_descr_type_t param } 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. +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: + if (pdescr->access & PAR_PERMS_CUST_CMD) { + command = SET_CMD(mode, (uint8_t)pdescr->param_opts.cust_cmd_read, (uint8_t)pdescr->param_opts.cust_cmd_write); + } else { + command = SET_CMD(mode, MB_FUNC_READ_HOLDING_REGISTER, MB_FUNC_WRITE_MULTIPLE_REGISTERS); + } + break; + case MB_PARAM_INPUT: + if (pdescr->access & PAR_PERMS_CUST_CMD) { + command = SET_CMD(mode, (uint8_t)pdescr->param_opts.cust_cmd_read, 0); + } else { + command = SET_CMD(mode, MB_FUNC_READ_INPUT_REGISTER, 0); + } + break; + case MB_PARAM_COIL: + if (pdescr->access & PAR_PERMS_CUST_CMD) { + command = SET_CMD(mode, (uint8_t)pdescr->param_opts.cust_cmd_read, (uint8_t)pdescr->param_opts.cust_cmd_write); + } else { + command = SET_CMD(mode, MB_FUNC_READ_COILS, MB_FUNC_WRITE_MULTIPLE_COILS); + } + break; + case MB_PARAM_DISCRETE: + if (pdescr->access & PAR_PERMS_CUST_CMD) { + command = SET_CMD(mode, (uint8_t)pdescr->param_opts.cust_cmd_read, 0); + } else { + command = SET_CMD(mode, MB_FUNC_READ_DISCRETE_INPUTS, 0); + } + break; + default: + ESP_LOGE(TAG, "%s: Incorrect param type (%u)", __FUNCTION__, (unsigned)pdescr->mb_param_type); + break; + } + return command; +} diff --git a/modbus/mb_controller/common/include/esp_modbus_master.h b/modbus/mb_controller/common/include/esp_modbus_master.h index cfe86f0..56d6839 100644 --- a/modbus/mb_controller/common/include/esp_modbus_master.h +++ b/modbus/mb_controller/common/include/esp_modbus_master.h @@ -45,6 +45,37 @@ extern "C" { #define MB_EACH_ELEM(psrc, pdest, arr_size, elem_size) \ (int i = 0; (i < (arr_size / elem_size)); i++, pdest += elem_size, psrc += elem_size) +/** + * @brief Request mode for parameter to use in data dictionary + */ +typedef enum { + MB_PARAM_READ, /*!< Read parameter values. */ + MB_PARAM_WRITE /*!< Write parameter values. */ +} mb_param_mode_t; + + +/*! + * \brief Modbus parameter options for description table + */ +typedef union { + struct { + int opt1; /*!< Parameter option1 */ + int opt2; /*!< Parameter option2 */ + int opt3; /*!< Parameter option3 */ + }; + struct { + int min; /*!< Parameter minimum value */ + int max; /*!< Parameter maximum value */ + int step; /*!< Step of parameter change tracking */ + }; + struct { + int cust_cmd_read; /*!< Parameter custom read request command */ + int cust_cmd_write; /*!< Parameter custom write request command */ + int cust_cmd_not_used; /*!< Not used option for custom request */ + }; +} mb_parameter_opt_t; + + /*! * \brief Modbus descriptor table parameter type defines. */ @@ -109,23 +140,6 @@ typedef enum { PARAM_MAX_SIZE } mb_descr_size_t; -/*! - * \brief Modbus parameter options for description table (associated with the characteristic). - * and can be used in user application to process data. - */ -typedef union { - struct { - int opt1; /*!< Parameter option1 */ - int opt2; /*!< Parameter option2 */ - int opt3; /*!< Parameter option3 */ - }; /*!< Parameter options version 1 */ - struct { - int min; /*!< Parameter minimum value */ - int max; /*!< Parameter maximum value */ - int step; /*!< Step of parameter change tracking */ - }; /*!< Parameter options version 2 */ -} mb_parameter_opt_t; - /** * @brief Permissions for the characteristics */ @@ -133,12 +147,15 @@ typedef enum { PAR_PERMS_READ = 1 << BIT0, /**< the characteristic of the device are readable */ PAR_PERMS_WRITE = 1 << BIT1, /**< the characteristic of the device are writable*/ PAR_PERMS_TRIGGER = 1 << BIT2, /**< the characteristic of the device are triggerable */ + PAR_PERMS_CUST_CMD = 1 << BIT3, /**< the characteristic uses custom commands */ PAR_PERMS_READ_WRITE = PAR_PERMS_READ | PAR_PERMS_WRITE, /**< the characteristic of the device are readable & writable */ PAR_PERMS_READ_TRIGGER = PAR_PERMS_READ | PAR_PERMS_TRIGGER, /**< the characteristic of the device are readable & triggerable */ PAR_PERMS_WRITE_TRIGGER = PAR_PERMS_WRITE | PAR_PERMS_TRIGGER, /**< the characteristic of the device are writable & triggerable */ PAR_PERMS_READ_WRITE_TRIGGER = PAR_PERMS_READ_WRITE | PAR_PERMS_TRIGGER, /**< the characteristic of the device are readable & writable & triggerable */ + PAR_PERMS_READ_WRITE_CUST_CMD = PAR_PERMS_READ_WRITE | PAR_PERMS_CUST_CMD /**< the characteristic supports custom read/write commands */ } mb_param_perms_t; + /** * @brief Characteristics descriptor type is used to describe characteristic and * link it with Modbus parameters that reflect its data. @@ -466,6 +483,18 @@ mb_err_enum_t mbc_reg_coils_master_cb(mb_base_t *inst, uint8_t *reg_buffer, uint */ esp_err_t mbc_master_set_param_data(void* dest, void* src, mb_descr_type_t param_type, size_t param_size); +/** + * @brief The helper function to get supported modbus function code (command) according to parameter type + * + * @param[in] pdescr the pointer to the characteristic descriptor in data dictionary + * @param[in] mode access mode for characteristic + * + * @return + * - modbus function code, if the command is correctly configured + * - 0 - invalid argument or command not found +*/ +uint8_t mbc_master_get_command(const mb_parameter_descriptor_t *pdescr, mb_param_mode_t mode); + #ifdef __cplusplus } #endif diff --git a/modbus/mb_controller/common/mbc_master.h b/modbus/mb_controller/common/mbc_master.h index c0e1900..413ce71 100644 --- a/modbus/mb_controller/common/mbc_master.h +++ b/modbus/mb_controller/common/mbc_master.h @@ -31,14 +31,6 @@ extern "C" { // will be dependent on response time set by timer + convertion time if the command is received #define MB_MAX_RESP_DELAY_MS (3000) -/** - * @brief Request mode for parameter to use in data dictionary - */ -typedef enum { - MB_PARAM_READ, /*!< Read parameter values. */ - MB_PARAM_WRITE /*!< Write parameter values. */ -} mb_param_mode_t; - /** * @brief Modbus controller handler structure */ diff --git a/modbus/mb_controller/serial/mbc_serial_master.c b/modbus/mb_controller/serial/mbc_serial_master.c index bee0afa..9642c0b 100644 --- a/modbus/mb_controller/serial/mbc_serial_master.c +++ b/modbus/mb_controller/serial/mbc_serial_master.c @@ -312,40 +312,6 @@ static esp_err_t mbc_serial_master_get_cid_info(void *ctx, uint16_t cid, const m return ESP_OK; } -// Helper function to get modbus command for each type of Modbus register area -static uint8_t mbc_serial_master_get_command(mb_param_type_t param_type, mb_param_mode_t mode) -{ - uint8_t command = 0; - switch (param_type) - { - case MB_PARAM_HOLDING: - command = (mode == MB_PARAM_WRITE) ? MB_FUNC_WRITE_MULTIPLE_REGISTERS : MB_FUNC_READ_HOLDING_REGISTER; - break; - case MB_PARAM_INPUT: - command = MB_FUNC_READ_INPUT_REGISTER; - break; - case MB_PARAM_COIL: - command = (mode == MB_PARAM_WRITE) ? MB_FUNC_WRITE_MULTIPLE_COILS : MB_FUNC_READ_COILS; - break; - case MB_PARAM_DISCRETE: - if (mode != MB_PARAM_WRITE) - { - command = MB_FUNC_READ_DISCRETE_INPUTS; - } - else - { - ESP_LOGE(TAG, "%s: Incorrect mode (%u)", - __FUNCTION__, (unsigned)mode); - } - break; - default: - ESP_LOGE(TAG, "%s: Incorrect param type (%u)", - __FUNCTION__, (unsigned)param_type); - break; - } - return command; -} - // Helper to search parameter by name in the parameter description table // and fills Modbus request fields accordingly static esp_err_t mbc_serial_master_set_request(void *ctx, uint16_t cid, mb_param_mode_t mode, @@ -365,7 +331,7 @@ static esp_err_t mbc_serial_master_set_request(void *ctx, uint16_t cid, mb_param request->slave_addr = reg_ptr->mb_slave_addr; request->reg_start = reg_ptr->mb_reg_start; request->reg_size = reg_ptr->mb_size; - request->command = mbc_serial_master_get_command(reg_ptr->mb_param_type, mode); + request->command = mbc_master_get_command(reg_ptr, mode); MB_RETURN_ON_FALSE((request->command > 0), ESP_ERR_INVALID_ARG, TAG, "mb incorrect command or parameter type."); if (reg_data) { diff --git a/modbus/mb_controller/tcp/mbc_tcp_master.c b/modbus/mb_controller/tcp/mbc_tcp_master.c index e966a6e..a9e786e 100644 --- a/modbus/mb_controller/tcp/mbc_tcp_master.c +++ b/modbus/mb_controller/tcp/mbc_tcp_master.c @@ -317,41 +317,6 @@ static esp_err_t mbc_tcp_master_get_cid_info(void *ctx, uint16_t cid, const mb_p return ESP_OK; } -// Helper function to get modbus command for each type of Modbus register area -static uint8_t mbc_tcp_master_get_command(mb_param_type_t param_type, mb_param_mode_t mode) -{ - uint8_t command = 0; - switch(param_type){ // Check commands - case MB_PARAM_HOLDING: - command = (mode == MB_PARAM_WRITE) ? - MB_FUNC_WRITE_MULTIPLE_REGISTERS : - MB_FUNC_READ_HOLDING_REGISTER; - break; - case MB_PARAM_INPUT: - command = MB_FUNC_READ_INPUT_REGISTER; - break; - case MB_PARAM_COIL: - command = (mode == MB_PARAM_WRITE) ? - MB_FUNC_WRITE_MULTIPLE_COILS : - MB_FUNC_READ_COILS; - break; - case MB_PARAM_DISCRETE: - if (mode != MB_PARAM_WRITE) { - command = MB_FUNC_READ_DISCRETE_INPUTS; - } else { - ESP_LOGE(TAG, "%s: Incorrect mode (%u)", - __FUNCTION__, (unsigned)mode); - } - break; - default: - ESP_LOGE(TAG, "%s: Incorrect param type (%u)", - __FUNCTION__, (unsigned)param_type); - break; - } - return command; -} - - // Helper to search parameter in the parameter description table and fills Modbus request fields accordingly static esp_err_t mbc_tcp_master_set_request(void *ctx, uint16_t cid, mb_param_mode_t mode, mb_param_request_t *request, mb_parameter_descriptor_t *reg_data) @@ -368,7 +333,7 @@ static esp_err_t mbc_tcp_master_set_request(void *ctx, uint16_t cid, mb_param_mo request->slave_addr = reg_ptr->mb_slave_addr; request->reg_start = reg_ptr->mb_reg_start; request->reg_size = reg_ptr->mb_size; - request->command = mbc_tcp_master_get_command(reg_ptr->mb_param_type, mode); + request->command = mbc_master_get_command(reg_ptr, mode); MB_RETURN_ON_FALSE((request->command > 0), ESP_ERR_INVALID_ARG, TAG, "mb incorrect command or parameter type."); if (reg_data) { *reg_data = *reg_ptr; // Set the cid registered parameter data