independent instance command handling using lists, fixes as per review and test

This commit is contained in:
aleks
2025-03-12 10:33:11 +01:00
parent ddb731578c
commit 20b3dbaa82
42 changed files with 845 additions and 542 deletions

View File

@ -1,6 +1,7 @@
# The following five lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
set(srcs
"mb_controller/common/esp_modbus_common.c"
"mb_controller/common/esp_modbus_master.c"
"mb_controller/common/esp_modbus_slave.c"
"mb_controller/common/esp_modbus_master_serial.c"
@ -13,6 +14,7 @@ set(srcs
"mb_controller/tcp/mbc_tcp_slave.c"
"mb_objects/mb_master.c"
"mb_objects/mb_slave.c"
"mb_objects/functions/mbfunc_handling.c"
"mb_objects/functions/mbfunccoils_master.c"
"mb_objects/functions/mbfunccoils.c"
"mb_objects/functions/mbfuncdiag.c"

11
Kconfig
View File

@ -224,18 +224,9 @@ menu "Modbus configuration"
otherwise the only legacy types are supported. The extended types include
integer, float, double types with different endianness and size.
config FMB_CONTROLLER_SLAVE_ID_MAX_SIZE
int "Modbus Slave ID maximum buffer size (bytes)"
range 4 255
default 32
depends on FMB_CONTROLLER_SLAVE_ID_SUPPORT
help
Modbus slave ID buffer size used to store vendor specific ID information
for the <Report Slave ID> command.
config FMB_FUNC_HANDLERS_MAX
int "Maximum number of Modbus function handlers"
range 16 64
range 16 255
default 16
help
This option defines the maximum number of Modbus command handlers for Modbus master and slave.

View File

@ -48,11 +48,11 @@ The examples below demonstrate the library port for serial, TCP slave and master
.. _example_mb_tcp_master:
- `Modbus TCP master example <https://github.com/espressif/esp-modbus/tree/main/examples/tcp/mb_tcp_slave>`__
- `Modbus TCP master example <https://github.com/espressif/esp-modbus/tree/main/examples/tcp/mb_tcp_master>`__
.. _example_mb_tcp_slave:
- `Modbus TCP slave example <https://github.com/espressif/esp-modbus/tree/main/examples/tcp/mb_tcp_master>`__
- `Modbus TCP slave example <https://github.com/espressif/esp-modbus/tree/main/examples/tcp/mb_tcp_slave>`__
Please refer to the specific example README.md for details.

View File

@ -313,38 +313,56 @@ Master Customize Function Handlers
The Master object contains the command handling tables to define the specific handling functionality for each supported Modbus command. The default handling functions in this table support most common Modbus commands. However, the list of commands can be extended by adding the new command into handling table with its custom handling behavior. It is also possible overriding the function handler for the specific command. The below described API functions allow using this behavior for master objects.
:cpp:func:`mbc_master_set_handler`
:cpp:func:`mbc_set_handler`
:cpp:func:`mbc_master_get_handler`
The function adds new handler for the function or overrides the existing handler for the function.
The example code to overide the handler routine for command `0x04 - Read Input Registers`:
:cpp:func:`mbc_get_handler`
The function returns the handler for the specified function code from handling table. Allows to keep and use the predefined handlers for standard functions.
:cpp:func:`mbc_delete_handler`
The function allows to delete the handler for specified command and free the handler table entry for this.
:cpp:func:`mbc_get_handler_count`
The function returns the actual number of command handlers registered for the object reffered by parameter.
The example code to override the handler routine for the command `<0x04 - Read Input Registers>` is below. This example allows to perform a custom action and then calls the standard handler, which maps the device data to the command buffer from the actual parameter. This is just recommended behavior for handling functions, but users can change the order of the calls if absolutely required. Please refer to the existing handler :cpp:func:`mbm_fn_read_inp_reg` for more information.
.. code:: c
static void *master_handle = NULL; // Pointer to allocated interface structure
uint8_t override_command = 0x04;
const uint8_t override_command = 0x04;
mb_fn_handler_fp pstandard_handler = NULL;
....
// Define the custom function handler for the command.
// The handler body must be short and don't use any unpredictable logic. The handler
// is executed in the context of modbus controller event task.
// This is the custom function handler for the command.
// The handler is executed from the context of modbus controller event task and should be as simple as possible.
// Parameters: frame_ptr - the pointer to the incoming ADU frame from slave starting from function code,
// plen - the pointer to length of the frame. After return from the handler the modbus object will
// handle the end of transaction according to the exception returned.
mb_exception_t my_custom_fc04_handler(void *pinst, uint8_t *frame_ptr, uint16_t *plen)
{
MB_RETURN_ON_FALSE(frame_ptr && plen, MB_EX_CRITICAL, TAG,
"incorrect frame buffer length");
// This error handler will be executed to check the request for the command 0x04
// See the default handler in the file `esp-modbus//modbus/mb_objects/functions/mbfuncinput_master.c` for more information.
// The pframe is pointer to command buffer, plen - is pointer to length of the buffer
return MB_EX_CRITICAL;
mb_exception_t exception = MB_EX_CRITICAL;
MB_RETURN_ON_FALSE(frame_ptr && plen, exception, TAG, "incorrect frame buffer length");
// It is the possible place for the custom behavior
if (pstandard_handler) {
exception = pstandard_handler(pinst, frame_ptr, plen); // invoke standard behavior with mapping
}
return exception;
}
....
err = mbc_master_set_handler(master_handle, override_command, NULL);
// Get the standard handler for the command to use it in the handler.
err = mbc_get_handler(master_handle, custom_command, &pstandard_handler);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"could not override handler, returned (0x%x).", (int)err);
err = mbc_master_set_handler(master_handle, override_command, my_custom_fc04_handler);
"could not get handler for command %d, returned (0x%x).", (int)custom_command, (int)err);
// This call overrides the handler for the standard command.
err = mbc_set_handler(master_handle, override_command, my_custom_fc04_handler);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"could not override handler, returned (0x%x).", (int)err);
.. note:: The custom handler set by the function :cpp:func:`mbc_master_set_handler` should be as short as possible and should contain simple and safe logic to not break the normal functionality of the stack. This is user application responsibility to handle the command appropriately.
.. note:: The custom handler set by the function :cpp:func:`mbc_set_handler` should be as short as possible and should contain simple and safe logic to not break the normal functionality of the stack. This is user application responsibility to handle the command appropriately.
The example code to handle custom vendor specific command is below. This example sends the 'Master' string to slave and gets the response from slave with the string being appended from slave. It is just a simple echo example to demonstrate the approach.
@ -355,33 +373,29 @@ The example code to handle custom vendor specific command is below. This example
static void *master_handle = NULL; // Pointer to allocated interface structure
// This is the custom function handler to process incoming slave response.
// Refer the function handler examples in `esp-modbus/modbus/mb_objects/functions/mbfuncinput_master.c` for more information.
// Parameters: pframe: is pointer to incoming frame buffer, plen: is pointer to length including the function code
// In spite of logging showed here, try to use just simple functionality here.
// Parameters: frame_ptr: is a pointer to incoming frame buffer, plen: is pointer to length including the function code
// In spite of logging showed here, try to use just simple functionality in the handler.
mb_exception_t my_custom_fc_handler(void *pinst, uint8_t *frame_ptr, uint16_t *plen)
{
MB_RETURN_ON_FALSE((frame_ptr && plen && *plen && *plen < (MB_CUST_DATA_LEN - 1)), MB_EX_CRITICAL, TAG,
MB_RETURN_ON_FALSE((frame_ptr && plen && *plen && *plen < (MB_CUST_DATA_LEN - 1)), MB_EX_ILLEGAL_DATA_VALUE, TAG,
"incorrect custom frame buffer");
ESP_LOGW(TAG, "Custom handler, Frame ptr: %p, len: %u", frame_ptr, *plen);
ESP_LOGI(TAG, "Custom handler, Frame ptr: %p, len: %u", frame_ptr, *plen);
strncpy((char *)&my_custom_data[0], (char *)&frame_ptr[1], MB_CUST_DATA_LEN);
ESP_LOG_BUFFER_HEXDUMP("CUSTOM_DATA", &my_custom_data[0], (*plen - 1), ESP_LOG_WARN);
ESP_LOG_BUFFER_HEXDUMP("CUSTOM_DATA", &my_custom_data[0], (*plen - 1), ESP_LOG_INFO);
return MB_EX_NONE;
}
....
// The setup of the master object is completed and the master_handle is already actual
// Add custom command handler
uint8_t custom_command = 0x41; // the function code for the request
// Reset the handler for the command if already set
err = mbc_master_set_handler(master_handle, custom_command, NULL);
MB_RETURN_ON_FALSE((err == ESP_OK || err == ESP_ERR_INVALID_STATE), ESP_ERR_INVALID_STATE, TAG,
"could not override handler, returned (0x%x).", (int)err);
err = mbc_master_set_handler(master_handle, custom_command, my_custom_fc_handler);
const uint8_t custom_command = 0x41; // the function code for the request
// Override or add new handler entry.
err = mbc_set_handler(master_handle, custom_command, my_custom_fc_handler);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"could not override handler, returned (0x%x).", (int)err);
mb_fn_handler_fp phandler = NULL;
// Make sure the handler is updated correctly
err = mbc_master_get_handler(master_handle, custom_command, &phandler);
err = mbc_get_handler(master_handle, custom_command, &phandler);
MB_RETURN_ON_FALSE((err == ESP_OK && phandler == my_custom_fc_handler), ESP_ERR_INVALID_STATE, TAG,
"could not get handler for command %d, returned (0x%x).", (int)custom_command, (int)err);
@ -394,14 +408,18 @@ The example code to handle custom vendor specific command is below. This example
};
// Send the request with custom command (vendor speciic)
// This function supports sending of even number of bytes
// as instructed by req.reg_size (Modbus register = 2 bytes)
err = mbc_master_send_request(master_handle, &req, pcustom_string);
if (err != ESP_OK) {
ESP_LOGE("CUSTOM_DATA", "Send custom request fail.");
} else {
// The request is processed correctly and the `my_custom_data[]` contains the sent string with appended slave string
...
// The request is processed correctly and the `my_custom_data[]` contains the sent string with appended slave string
...
}
Refer to :ref:`example Serial master <example_mb_master>` for more information.
.. _modbus_api_master_start_communication:
Master Communication

View File

@ -167,46 +167,66 @@ Example to get the actual slave identificator:
Slave Customize Function Handlers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The Slave object contains the command handling tables to define the specific handling functionality for each supported Modbus command. The default handling functions in this table support most useful Modbus commands. However, the list of commands can be extended by adding the new command into handling table with its custom handling behavior. It is also possible overriding the function handler for the specific command. The below described API functions allow using this behavior slave objects.
The Slave object contains the command handling tables to define the specific handling functionality for each supported Modbus command. The default handling functions in this table support most useful Modbus commands. However, the list of commands can be extended by adding the new command into handling table with its custom handling behavior. It is also possible overriding the function handler for the specific command. The below described API functions allow using this behavior for slave objects.
:cpp:func:`mbc_slave_set_handler`
:cpp:func:`mbc_set_handler`
:cpp:func:`mbc_slave_get_handler`
The function adds new handler for the function or overrides the existing handler for the function.
The following example allows to override the standard command to read input registers. Refer to examples for more information on how to handle custom commands.
:cpp:func:`mbc_get_handler`
The function returns the handler for the specified function code from handling table. Allows to keep and use the predefined handlers for standard functions.
:cpp:func:`mbc_delete_handler`
The function allows to delete the handler for specified command and free the handler table entry for this.
:cpp:func:`mbc_get_handler_count`
The function returns the actual number of command handlers registered for the object reffered by parameter.
The following example allows to override the standard command to read input registers. Refer to standard handler function :cpp:func:`mbs_fn_read_input_reg` for more information on how to handle custom commands.
.. code:: c
static void *slave_handle = NULL; // Pointer to allocated interface structure (must be actual)
mb_fn_handler_fp pstandard_handler = NULL;
....
// The custom function handler for the function returns exception code for now
// Please place your custom handling behavior here.
// This error handler will be executed to check the request for the command 0x04
// See the default handler in the file `esp-modbus//modbus/mb_objects/functions/mbfuncinput_slave.c` for more information.
// The pframe is pointer to command buffer, plen - is pointer to the variable with the length of the buffer
// This is the custom function handler for the command.
// The handler is executed from the context of modbus controller event task and should be as simple as possible.
// Parameters: frame_ptr - the pointer to the incoming ADU request frame from master starting from function code,
// plen - the pointer to length of the frame. The handler body can override the buffer and return the length of data.
// After return from the handler the modbus object will handle the end of transaction according to the exception returned,
// then builds the response frame and send it back to the master. If the whole transaction time including the response
// latency exceeds the configured slave response time set in the master configuration the master will ignore the transaction.
mb_exception_t my_custom_fc04_handler(void *pinst, uint8_t *frame_ptr, uint16_t *plen)
{
MB_RETURN_ON_FALSE(frame_ptr && plen, MB_EX_CRITICAL, TAG,
"incorrect frame buffer length");
return MB_EX_CRITICAL;
MB_RETURN_ON_FALSE(frame_ptr && plen, MB_EX_CRITICAL, TAG, "incorrect frame buffer length");
// Place the custom behavior to process the buffer here
if (pstandard_handler) {
exception = pstandard_handler(pinst, frame_ptr, plen); // invoke standard behavior with mapping
}
return exception;
}
...
uint8_t override_command = 0x04;
// Reset the existing handler for the command
esp_err_t err = mbc_slave_set_handler(slave_handle, override_command, NULL);
MB_RETURN_ON_FALSE((err == ESP_OK), ;, TAG,
"could not reset handler, returned (0x%x).", (int)err);
const uint8_t override_command = 0x04;
// Get the standard handler for the command to use it in the handler.
err = mbc_get_handler(master_handle, override_command, &pstandard_handler);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"could not get handler for command %d, returned (0x%x).", (int)override_command, (int)err);
// Set the custom handler function for the command
err = mbc_slave_set_handler(slave_handle, override_command, my_custom_fc04_handler);
err = mbc_set_handler(slave_handle, override_command, my_custom_fc04_handler);
MB_RETURN_ON_FALSE((err == ESP_OK), ;, TAG,
"could not override handler, returned (0x%x).", (int)err);
mb_fn_handler_fp phandler = NULL;
// Get the actual handler for the command
err = mbc_slave_get_handler(slave_handle, override_command, &phandler);
// Check the actual handler for the command
err = mbc_get_handler(slave_handle, override_command, &phandler);
MB_RETURN_ON_FALSE((err == ESP_OK && phandler == my_custom_fc04_handler), ;, TAG,
"could not get handler, returned (0x%x).", (int)err);
.. note:: The custom handlers set by the function :cpp:func:`mbc_slave_set_handler` should be as short as possible and should contain simple logic to not break the normal functionality of the stack. This is user application responsibility to handle the command appropriately.
Refer to :ref:`example Serial slave <example_mb_slave>` for more information.
.. note:: The custom handlers set by the function :cpp:func:`mbc_set_handler` should be as short as possible, contain simple and safe logic and avoid blocking calls to not break the normal functionality of the stack. The possible latency in this handler may prevent to respond properly to the master request which waits for response during the slave response time configured in the configuration structure. If the slave does not respond to the master during the slave response time the master will report timeout failure and ignores the late response. This is user application responsibility to handle the command appropriately.
.. _modbus_api_slave_communication:

View File

@ -20,7 +20,7 @@
#define MASTER_MAX_CIDS num_device_parameters
// Number of reading of parameters from slave
#define MASTER_MAX_RETRY 30
#define MASTER_MAX_RETRY (10)
// Timeout to update cid over Modbus
#define UPDATE_CIDS_TIMEOUT_MS (500)
@ -56,7 +56,7 @@
#define OPTS(min_val, max_val, step_val) { .opt1 = min_val, .opt2 = max_val, .opt3 = step_val }
#define EACH_ITEM(array, length) \
(typeof(*(array)) *pitem = (array); (pitem < &((array)[length])); pitem++)
(typeof(*(array)) *pitem = (array); (pitem < &((array)[length])); pitem++)
#define MB_CUST_DATA_LEN 100 // The length of custom command buffer
@ -305,19 +305,6 @@ static void *master_get_param_data(const mb_parameter_descriptor_t *param_descri
} \
))
mb_exception_t my_custom_handler(void *, uint8_t *frame_ptr, uint16_t *plen)
{
MB_RETURN_ON_FALSE((frame_ptr && plen && *plen && *plen < (MB_CUST_DATA_LEN - 1)), MB_EX_CRITICAL, TAG,
"incorrect custom frame buffer");
ESP_LOGW(TAG, "Custom handler, Frame ptr: %p, len: %u", frame_ptr, *plen);
// This error handler will be executed to handle the request for the registered custom command
// Refer the handler functions in `esp-modbus/modbus/mb_objects/functions/mbfuncinput_master.c` for more information.
// Parameters: pframe: is pointer to incoming frame buffer, plen: is pointer to length including the function code
strncpy((char *)&my_custom_data[0], (char *)&frame_ptr[1], MB_CUST_DATA_LEN);
ESP_LOG_BUFFER_HEXDUMP("CUSTOM_DATA", &my_custom_data[0], (*plen - 1), ESP_LOG_WARN);
return MB_EX_NONE;
}
// User operation function to read slave values and check alarm
static void master_operation_func(void *arg)
{
@ -336,12 +323,14 @@ static void master_operation_func(void *arg)
};
// Send the request with custom command (vendor speciic)
// This function supports sending of only even number of bytes
// as instructed by req.reg_size (Modbus register = 2 bytes)
err = mbc_master_send_request(master_handle, &req, pcustom_string);
if (err != ESP_OK) {
ESP_LOGE("CUSTOM_DATA", "Send custom request fail.");
}
#if MB_FUNC_OTHER_REP_SLAVEID_ENABLED
#if CONFIG_FMB_CONTROLLER_SLAVE_ID_SUPPORT
// Command - 17 (0x11) Report Slave ID
// The command contains vendor specific data and should be interpreted accordingly.
// This version of command handler needs to define the maximum number
@ -349,12 +338,10 @@ static void master_operation_func(void *arg)
// The returned slave info data will be stored into the `info_buf`.
// Request fields: slave_addr - the UID of slave, reg_start - not used,
// reg_size = max size of buffer (registers).
req = {
.slave_addr = MB_DEVICE_ADDR1, // slave UID to retrieve ID
.command = 0x11, // the <Report Slave ID> command,
.reg_start = 0, // must be zero,
.reg_size = (CONFIG_FMB_CONTROLLER_SLAVE_ID_MAX_SIZE >> 1) // the expected length of buffer in registers
};
req.slave_addr = MB_DEVICE_ADDR1; // slave UID to retrieve ID
req.command = 0x11; // the <Report Slave ID> command,
req.reg_start = 0; // must be zero,
req.reg_size = (CONFIG_FMB_CONTROLLER_SLAVE_ID_MAX_SIZE >> 1); // the expected length of buffer in registers
uint8_t info_buf[CONFIG_FMB_CONTROLLER_SLAVE_ID_MAX_SIZE] = {0}; // The buffer to save slave ID
@ -498,6 +485,21 @@ static void master_operation_func(void *arg)
ESP_ERROR_CHECK(mbc_master_delete(master_handle));
}
// This is the custom function handler for the command.
// The handler is executed from the context of modbus controller event task and should be as simple as possible.
// Parameters: frame_ptr - the pointer to the incoming ADU frame from slave starting from function code,
// plen - the pointer to length of the frame. After return from the handler the modbus object will
// handle the end of transaction according to the exception returned.
mb_exception_t my_custom_handler(void *inst, uint8_t *frame_ptr, uint16_t *plen)
{
MB_RETURN_ON_FALSE((frame_ptr && plen && *plen && *plen < (MB_CUST_DATA_LEN - 1)), MB_EX_ILLEGAL_DATA_VALUE, TAG,
"incorrect custom frame buffer");
ESP_LOGD(TAG, "Custom handler, Frame ptr: %p, len: %u", frame_ptr, *plen);
strncpy((char *)&my_custom_data[0], (char *)&frame_ptr[1], MB_CUST_DATA_LEN);
ESP_LOG_BUFFER_HEXDUMP("CUSTOM_DATA", &my_custom_data[0], (*plen - 1), ESP_LOG_WARN);
return MB_EX_NONE;
}
// Modbus master initialization
static esp_err_t master_init(void)
{
@ -523,15 +525,16 @@ static esp_err_t master_init(void)
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb controller initialization fail, returns(0x%x).", (int)err);
uint8_t override_command = 0x41;
err = mbc_master_set_handler(master_handle, override_command, NULL);
const uint8_t override_command = 0x41;
// Delete the handler for specified command, if available
err = mbc_delete_handler(master_handle, override_command);
MB_RETURN_ON_FALSE((err == ESP_OK || err == ESP_ERR_INVALID_STATE), ESP_ERR_INVALID_STATE, TAG,
"could not override handler, returned (0x%x).", (int)err);
err = mbc_master_set_handler(master_handle, override_command, my_custom_handler);
err = mbc_set_handler(master_handle, override_command, my_custom_handler);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"could not override handler, returned (0x%x).", (int)err);
mb_fn_handler_fp phandler = NULL;
err = mbc_master_get_handler(master_handle, override_command, &phandler);
err = mbc_get_handler(master_handle, override_command, &phandler);
MB_RETURN_ON_FALSE((err == ESP_OK && phandler == my_custom_handler), ESP_ERR_INVALID_STATE, TAG,
"could not get handler for command %d, returned (0x%x).", (int)override_command, (int)err);

View File

@ -145,20 +145,22 @@ static void setup_reg_data(void)
input_reg_params.input_data7 = 4.78;
}
// This is a simple custom function handler for the command.
// The handler is executed from the context of modbus controller event task and should be as simple as possible.
// Parameters: frame_ptr - the pointer to the incoming ADU request frame from master starting from function code,
// plen - the pointer to length of the frame. The handler body can override the buffer and return the length of data.
// After return from the handler the modbus object will handle the end of transaction according to the exception returned,
// then builds the response frame and send it back to the master. If the whole transaction time including the response
// latency exceeds the configured slave response time set in the master configuration the master will ignore the transaction.
mb_exception_t my_custom_fc_handler(void *pinst, uint8_t *frame_ptr, uint16_t *plen)
{
char *str_append = ":Slave";
MB_RETURN_ON_FALSE((frame_ptr && plen && *plen < (MB_CUST_DATA_MAX_LEN - strlen(str_append))), MB_EX_ILLEGAL_DATA_VALUE, TAG,
"incorrect custom frame");
//ESP_LOGW("CUSTOM_DATA", "Custom handler, frame ptr: %p, len: %u", frame_ptr, *plen);
//ESP_LOG_BUFFER_HEXDUMP("CUSTOM_DATA", frame_ptr, *plen, ESP_LOG_WARN);
// This command handler will be executed to check the request for the custom command
// See the `esp-modbus/modbus/mb_objects/functions/functions/mbfuncinput.c` for more information
// pframe is pointer to the buffer starting from function code, plen - is pointer to length of the data
frame_ptr[*plen] = '\0';
strcat((char *)&frame_ptr[1], str_append);
*plen = (strlen(str_append) + *plen); // the length of (response + command)
return MB_EX_NONE; // Set the exception code for slave appropriately
return MB_EX_NONE; // Set the exception code for modbus object appropriately
}
// An example application of Modbus slave. It is based on esp-modbus stack.
@ -190,15 +192,16 @@ void app_main(void)
ESP_ERROR_CHECK(mbc_slave_create_serial(&comm_config, &mbc_slave_handle)); // Initialization of Modbus controller
uint8_t custom_command = 0x41; // The custom command to be sent to slave
esp_err_t err = mbc_slave_set_handler(mbc_slave_handle, custom_command, NULL);
const uint8_t custom_command = 0x41; // The custom command to be sent to slave
// Try to delete the handler for specified command.
esp_err_t err = mbc_delete_handler(mbc_slave_handle, custom_command);
MB_RETURN_ON_FALSE((err == ESP_OK || err == ESP_ERR_INVALID_STATE), ;, TAG,
"could not reset handler, returned (0x%x).", (int)err);
err = mbc_slave_set_handler(mbc_slave_handle, custom_command, my_custom_fc_handler);
"could not delete handler, returned (0x%x).", (int)err);
err = mbc_set_handler(mbc_slave_handle, custom_command, my_custom_fc_handler);
MB_RETURN_ON_FALSE((err == ESP_OK), ;, TAG,
"could not set or override handler, returned (0x%x).", (int)err);
mb_fn_handler_fp phandler = NULL;
err = mbc_slave_get_handler(mbc_slave_handle, custom_command, &phandler);
err = mbc_get_handler(mbc_slave_handle, custom_command, &phandler);
MB_RETURN_ON_FALSE((err == ESP_OK && phandler == my_custom_fc_handler), ;, TAG,
"could not get handler for command %d, returned (0x%x).", (int)custom_command, (int)err);

View File

@ -65,6 +65,9 @@
// Options can be used as bit masks or parameter limits
#define OPTS(min_val, max_val, step_val) { .opt1 = min_val, .opt2 = max_val, .opt3 = step_val }
#define EACH_ITEM(array, length) \
(typeof(*(array)) *pitem = (array); (pitem < &((array)[length])); pitem++)
#define MB_ID_BYTE0(id) ((uint8_t)(id))
#define MB_ID_BYTE1(id) ((uint8_t)(((uint16_t)(id) >> 8) & 0xFF))
#define MB_ID_BYTE2(id) ((uint8_t)(((uint32_t)(id) >> 16) & 0xFF))
@ -80,9 +83,6 @@
#define MB_MDNS_INSTANCE(pref) pref"mb_master_tcp"
#define EACH_ITEM(array, length) \
(typeof(*(array)) *pitem = (array); (pitem < &((array)[length])); pitem++)
#define MB_CUST_DATA_LEN 100 // The length of custom command buffer
static const char *TAG = "MASTER_TEST";
@ -675,16 +675,18 @@ static esp_err_t destroy_services(void)
return err;
}
mb_exception_t my_custom_fc_handler(void *pinst, uint8_t *frame_ptr, uint16_t *plen)
// This is the custom function handler for the command.
// The handler is executed from the context of modbus controller event task and should be as simple as possible.
// Parameters: frame_ptr - the pointer to the incoming ADU frame from slave starting from function code,
// plen - the pointer to length of the frame. After return from the handler the modbus object will
// handle the end of transaction according to the exception returned.
mb_exception_t my_custom_fc_handler(void *inst, uint8_t *frame_ptr, uint16_t *plen)
{
MB_RETURN_ON_FALSE((frame_ptr && plen && *plen && *plen < (MB_CUST_DATA_LEN - 1)), MB_EX_CRITICAL, TAG,
MB_RETURN_ON_FALSE((frame_ptr && plen && *plen && *plen < (MB_CUST_DATA_LEN - 1)), MB_EX_ILLEGAL_DATA_VALUE, TAG,
"incorrect custom frame buffer");
ESP_LOGW(TAG, "Custom handler, Frame ptr: %p, len: %u", frame_ptr, *plen);
// This error handler will be executed to handle the request for the registered custom command
// Refer the handler functions in `esp-modbus/modbus/mb_objects/functions/mbfuncinput_master.c` for more information.
// Parameters: pframe: is pointer to incoming frame buffer, plen: is pointer to length including the function code
ESP_LOGI(TAG, "Custom handler, Frame ptr: %p, len: %u", frame_ptr, *plen);
strncpy((char *)&my_custom_data[0], (char *)&frame_ptr[1], MB_CUST_DATA_LEN);
ESP_LOG_BUFFER_HEXDUMP("CUSTOM_DATA", &my_custom_data[0], (*plen - 1), ESP_LOG_WARN);
ESP_LOG_BUFFER_HEXDUMP("CUSTOM_DATA", &my_custom_data[0], (*plen - 1), ESP_LOG_INFO);
return MB_EX_NONE;
}
@ -702,15 +704,15 @@ static esp_err_t master_init(mb_communication_info_t *pcomm_info)
// Add custom command handler
uint8_t custom_command = 0x41;
// Make sure the handler is undefined for the command
err = mbc_master_set_handler(master_handle, custom_command, NULL);
// Delete the handler for specified command, if available
err = mbc_delete_handler(master_handle, custom_command);
MB_RETURN_ON_FALSE((err == ESP_OK || err == ESP_ERR_INVALID_STATE), ESP_ERR_INVALID_STATE, TAG,
"could not override handler, returned (0x%x).", (int)err);
err = mbc_master_set_handler(master_handle, custom_command, my_custom_fc_handler);
err = mbc_set_handler(master_handle, custom_command, my_custom_fc_handler);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"could not override handler, returned (0x%x).", (int)err);
mb_fn_handler_fp phandler = NULL;
err = mbc_master_get_handler(master_handle, custom_command, &phandler);
err = mbc_get_handler(master_handle, custom_command, &phandler);
MB_RETURN_ON_FALSE((err == ESP_OK && phandler == my_custom_fc_handler), ESP_ERR_INVALID_STATE, TAG,
"could not get handler for command %d, returned (0x%x).", (int)custom_command, (int)err);

View File

@ -13,7 +13,7 @@ CONFIG_FMB_MASTER_DELAY_MS_CONVERT=300
CONFIG_FMB_TCP_UID_ENABLED=n
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y
CONFIG_MB_SLAVE_IP_FROM_STDIN=y
CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y
# CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y # intentionally keep it here to change when needed
CONFIG_FMB_EXT_TYPE_SUPPORT=y
CONFIG_EXAMPLE_CONNECT_IPV6=n

View File

@ -7,7 +7,7 @@ CONFIG_FMB_COMM_MODE_RTU_EN=n
CONFIG_FMB_COMM_MODE_ASCII_EN=n
CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=3000
CONFIG_FMB_MASTER_DELAY_MS_CONVERT=300
CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y
# CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y # intentionally keep it here to change when needed
CONFIG_FMB_TCP_UID_ENABLED=n
CONFIG_MB_SLAVE_IP_FROM_STDIN=y
CONFIG_EXAMPLE_CONNECT_IPV6=n

View File

@ -4,8 +4,6 @@
* SPDX-License-Identifier: Apache-2.0
*/
// FreeModbus Slave Example ESP32
#include <stdio.h>
#include "esp_err.h"
#include "sdkconfig.h"
@ -43,7 +41,7 @@
#define MB_REG_HOLDING_START_AREA2_SIZE ((size_t)((HOLD_OFFSET(holding_area2_end) - HOLD_OFFSET(holding_u8_a)) << 1))
#define MB_PAR_INFO_GET_TOUT (10) // Timeout for get parameter info
#define MB_CHAN_DATA_MAX_VAL (50)
#define MB_CHAN_DATA_MAX_VAL (60)
#define MB_CHAN_DATA_OFFSET (10.1f)
#define MB_READ_MASK (MB_EVENT_INPUT_REG_RD \
@ -267,27 +265,28 @@ static esp_err_t destroy_services(void)
return err;
}
// This custom command handler will be executed to check the request for the custom command
// See the `esp-modbus/modbus/mb_objects/functions/mbfuncinput.c` for more information
// pframe is pointer to the buffer starting from function code, plen - is pointer to length of the data
// This is a simple custom function handler for the command.
// The handler is executed from the context of modbus controller event task and should be as simple as possible.
// Parameters: frame_ptr - the pointer to the incoming ADU request frame from master starting from function code,
// plen - the pointer to length of the frame. The handler body can override the buffer and return the length of data.
// After return from the handler the modbus object will handle the end of transaction according to the exception returned,
// then builds the response frame and send it back to the master. If the whole transaction time including the response
// latency exceeds the configured slave response time set in the master configuration the master will ignore the transaction.
mb_exception_t my_custom_fc_handler(void *pinst, uint8_t *frame_ptr, uint16_t *plen)
{
char *str_append = ":Slave";
MB_RETURN_ON_FALSE((frame_ptr && plen && *plen < (MB_CUST_DATA_MAX_LEN - strlen(str_append))),
MB_EX_ILLEGAL_DATA_VALUE, TAG,
MB_RETURN_ON_FALSE((frame_ptr && plen && *plen < (MB_CUST_DATA_MAX_LEN - strlen(str_append))), MB_EX_ILLEGAL_DATA_VALUE, TAG,
"incorrect custom frame");
ESP_LOGW("CUSTOM_DATA", "Custom handler, frame ptr: %p, len: %u", frame_ptr, *plen);
ESP_LOG_BUFFER_HEXDUMP("CUSTOM_DATA", frame_ptr, *plen, ESP_LOG_WARN);
frame_ptr[*plen] = '\0';
strcat((char *)&frame_ptr[1], str_append);
*plen = (strlen(str_append) + *plen); // the length of (response + command)
return MB_EX_NONE; // Set the exception code for slave appropriately
return MB_EX_NONE; // Set the exception code for modbus object appropriately
}
// Modbus slave initialization
static esp_err_t slave_init(mb_communication_info_t *pcomm_info)
{
mb_register_area_descriptor_t reg_area; // Modbus register area descriptor structure
mb_register_area_descriptor_t reg_area = {0}; // Modbus register area descriptor structure
// Initialization of Modbus controller
esp_err_t err = mbc_slave_create_tcp(pcomm_info, &slave_handle);
@ -296,14 +295,15 @@ static esp_err_t slave_init(mb_communication_info_t *pcomm_info)
"mb controller create fail.");
uint8_t custom_command = 0x41;
err = mbc_slave_set_handler(slave_handle, custom_command, NULL);
// Delete the handler for specified command, if available.
err = mbc_delete_handler(slave_handle, custom_command);
MB_RETURN_ON_FALSE((err == ESP_OK || err == ESP_ERR_INVALID_STATE), ESP_ERR_INVALID_STATE, TAG,
"could not reset handler, returned (0x%x).", (int)err);
err = mbc_slave_set_handler(slave_handle, custom_command, my_custom_fc_handler);
err = mbc_set_handler(slave_handle, custom_command, my_custom_fc_handler);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"could not set or override handler, returned (0x%x).", (int)err);
mb_fn_handler_fp phandler = NULL;
err = mbc_slave_get_handler(slave_handle, custom_command, &phandler);
err = mbc_get_handler(slave_handle, custom_command, &phandler);
MB_RETURN_ON_FALSE((err == ESP_OK && phandler == my_custom_fc_handler), ESP_ERR_INVALID_STATE, TAG,
"could not get handler for command %d, returned (0x%x).", (int)custom_command, (int)err);
@ -317,6 +317,7 @@ static esp_err_t slave_init(mb_communication_info_t *pcomm_info)
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
reg_area.access = MB_ACCESS_RW;
err = mbc_slave_set_descriptor(slave_handle, reg_area);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
@ -327,6 +328,7 @@ static esp_err_t slave_init(mb_communication_info_t *pcomm_info)
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
reg_area.access = MB_ACCESS_RW;
err = mbc_slave_set_descriptor(slave_handle, reg_area);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
@ -339,6 +341,7 @@ static esp_err_t slave_init(mb_communication_info_t *pcomm_info)
reg_area.start_offset = MB_REG_HOLDING_START_AREA2;
reg_area.address = (void*)&holding_reg_params.holding_u8_a;
reg_area.size = MB_REG_HOLDING_START_AREA2_SIZE;
reg_area.access = MB_ACCESS_RW;
err = mbc_slave_set_descriptor(slave_handle, reg_area);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
@ -351,6 +354,7 @@ static esp_err_t slave_init(mb_communication_info_t *pcomm_info)
reg_area.start_offset = MB_REG_INPUT_START_AREA0;
reg_area.address = (void*)&input_reg_params.input_data0;
reg_area.size = sizeof(float) << 2;
reg_area.access = MB_ACCESS_RW;
err = mbc_slave_set_descriptor(slave_handle, reg_area);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
@ -360,6 +364,7 @@ static esp_err_t slave_init(mb_communication_info_t *pcomm_info)
reg_area.start_offset = MB_REG_INPUT_START_AREA1;
reg_area.address = (void*)&input_reg_params.input_data4;
reg_area.size = sizeof(float) << 2;
reg_area.access = MB_ACCESS_RW;
err = mbc_slave_set_descriptor(slave_handle, reg_area);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
@ -371,6 +376,7 @@ static esp_err_t slave_init(mb_communication_info_t *pcomm_info)
reg_area.start_offset = MB_REG_COILS_START;
reg_area.address = (void*)&coil_reg_params;
reg_area.size = sizeof(coil_reg_params);
reg_area.access = MB_ACCESS_RW;
err = mbc_slave_set_descriptor(slave_handle, reg_area);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,
@ -382,6 +388,7 @@ static esp_err_t slave_init(mb_communication_info_t *pcomm_info)
reg_area.start_offset = MB_REG_DISCRETE_INPUT_START;
reg_area.address = (void*)&discrete_reg_params;
reg_area.size = sizeof(discrete_reg_params);
reg_area.access = MB_ACCESS_RW;
err = mbc_slave_set_descriptor(slave_handle, reg_area);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE,
TAG,

View File

@ -14,7 +14,7 @@ CONFIG_FMB_EXT_TYPE_SUPPORT=y
CONFIG_FMB_TCP_UID_ENABLED=n
CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD=y
CONFIG_MB_SLAVE_ADDR=1
CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y
# CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y # intentionally keep it here to change when needed!
CONFIG_EXAMPLE_CONNECT_IPV6=n
CONFIG_EXAMPLE_CONNECT_WIFI=n
CONFIG_EXAMPLE_CONNECT_ETHERNET=y

View File

@ -8,7 +8,7 @@ CONFIG_FMB_COMM_MODE_ASCII_EN=n
CONFIG_FMB_EXT_TYPE_SUPPORT=y
CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=3000
CONFIG_FMB_MASTER_DELAY_MS_CONVERT=300
CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y
# CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y # intentionally keep it here to change when needed!
CONFIG_FMB_TCP_UID_ENABLED=n
CONFIG_MB_SLAVE_ADDR=1
CONFIG_EXAMPLE_CONNECT_IPV6=n

View File

@ -39,8 +39,7 @@ LOGGER_NAME = 'modbus_test'
logger = logging.getLogger(LOGGER_NAME)
test_configs = [
'wifi',
# 'ethernet'
'ethernet'
]
@pytest.mark.esp32

View File

@ -0,0 +1,91 @@
/*
* SPDX-FileCopyrightText: 2016-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_err.h"
#include "mbc_master.h" // for master interface define
#include "mbc_slave.h" // for slave interface define
#include "esp_modbus_common.h" // for public interface defines
static const char TAG[] __attribute__((unused)) = "MB_CONTROLLER_COMMON";
/**
* Register or override command handler for the command in object command handler table
*/
esp_err_t mbc_set_handler(void *ctx, uint8_t func_code, mb_fn_handler_fp phandler)
{
MB_RETURN_ON_FALSE((ctx && phandler && func_code), ESP_ERR_INVALID_STATE, TAG,
"Incorrect arguments for the function.");
mb_err_enum_t ret = MB_EINVAL;
mb_controller_common_t *mb_controller = (mb_controller_common_t *)(ctx);
mb_base_t *pmb_obj = (mb_base_t *)mb_controller->mb_base;
MB_RETURN_ON_FALSE(pmb_obj, ESP_ERR_INVALID_STATE, TAG,
"Controller interface is not correctly initialized.");
if (pmb_obj->descr.is_master) {
ret = mbm_set_handler(mb_controller->mb_base, func_code, phandler);
} else {
ret = mbs_set_handler(mb_controller->mb_base, func_code, phandler);
}
return MB_ERR_TO_ESP_ERR(ret);
}
/**
* Get command handler from the command handler table of the object
*/
esp_err_t mbc_get_handler(void *ctx, uint8_t func_code, mb_fn_handler_fp *phandler)
{
MB_RETURN_ON_FALSE((ctx && func_code && phandler), ESP_ERR_INVALID_STATE, TAG,
"Incorrect arguments for the function.");
mb_err_enum_t ret = MB_EINVAL;
mb_controller_common_t *mb_controller = (mb_controller_common_t *)(ctx);
mb_base_t *pmb_obj = (mb_base_t *)mb_controller->mb_base;
MB_RETURN_ON_FALSE(pmb_obj, ESP_ERR_INVALID_STATE, TAG,
"Controller interface is not correctly initialized.");
if (pmb_obj->descr.is_master) {
ret = mbm_get_handler(mb_controller->mb_base, func_code, phandler);
} else {
ret = mbs_get_handler(mb_controller->mb_base, func_code, phandler);
}
return MB_ERR_TO_ESP_ERR(ret);
}
/**
* Delete command handler from the command handler table of the object
*/
esp_err_t mbc_delete_handler(void *ctx, uint8_t func_code)
{
MB_RETURN_ON_FALSE((ctx && func_code), ESP_ERR_INVALID_STATE, TAG,
"Incorrect arguments for the function.");
mb_err_enum_t ret = MB_EINVAL;
mb_controller_common_t *mb_controller = (mb_controller_common_t *)(ctx);
mb_base_t *pmb_obj = (mb_base_t *)mb_controller->mb_base;
MB_RETURN_ON_FALSE(pmb_obj, ESP_ERR_INVALID_STATE, TAG,
"Controller interface is not correctly initialized.");
if (pmb_obj->descr.is_master) {
ret = mbm_delete_handler(mb_controller->mb_base, func_code);
} else {
ret = mbs_delete_handler(mb_controller->mb_base, func_code);
}
return MB_ERR_TO_ESP_ERR(ret);
}
/**
* Get number of registered command handlers for the object
*/
esp_err_t mbc_get_handler_count(void *ctx, uint16_t *pcount)
{
MB_RETURN_ON_FALSE((ctx && pcount), ESP_ERR_INVALID_STATE, TAG,
"Controller interface is not correctly initialized.");
mb_err_enum_t ret = MB_EINVAL;
mb_controller_common_t *mb_controller = (mb_controller_common_t *)(ctx);
mb_base_t *pmb_obj = (mb_base_t *)mb_controller->mb_base;
MB_RETURN_ON_FALSE(pmb_obj, ESP_ERR_INVALID_STATE, TAG,
"Controller interface is not correctly initialized.");
if (pmb_obj->descr.is_master) {
ret = mbm_get_handler_count(mb_controller->mb_base, pcount);
} else {
ret = mbs_get_handler_count(mb_controller->mb_base, pcount);
}
return MB_ERR_TO_ESP_ERR(ret);
}

View File

@ -733,32 +733,3 @@ esp_err_t mbc_master_set_param_data(void* dest, void* src, mb_descr_type_t param
return err;
}
/**
* Register or override command handler for the command in master command handler table
*/
esp_err_t mbc_master_set_handler(void *ctx, uint8_t func_code, mb_fn_handler_fp phandler)
{
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->descr.is_master), ESP_ERR_INVALID_STATE, TAG,
"Master interface is not correctly initialized.");
mb_err_enum_t ret = mbm_set_handler(func_code, phandler);
return MB_ERR_TO_ESP_ERR(ret);
}
/**
* Get command master command handler from the slave command handler table
*/
esp_err_t mbc_master_get_handler(void *ctx, uint8_t func_code, mb_fn_handler_fp *phandler)
{
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->descr.is_master), ESP_ERR_INVALID_STATE, TAG,
"Master interface is not correctly initialized.");
mb_err_enum_t ret = mbm_get_handler(func_code, phandler);
return MB_ERR_TO_ESP_ERR(ret);
}

View File

@ -547,33 +547,3 @@ mb_err_enum_t mbc_reg_discrete_slave_cb(mb_base_t *inst, uint8_t *reg_buffer, ui
}
return status;
}
/**
* Register or override command handler for the command in slave command handler table
*/
esp_err_t mbc_slave_set_handler(void *ctx, uint8_t func_code, mb_fn_handler_fp phandler)
{
MB_RETURN_ON_FALSE(ctx, ESP_ERR_INVALID_STATE, TAG,
"Slave interface is not correctly initialized.");
mbs_controller_iface_t *mbs_controller = MB_SLAVE_GET_IFACE(ctx);
mb_base_t *pmb_obj = (mb_base_t *)mbs_controller->mb_base;
MB_RETURN_ON_FALSE((pmb_obj && !pmb_obj->descr.is_master), ESP_ERR_INVALID_STATE, TAG,
"Slave interface is not correctly initialized.");
mb_err_enum_t ret = mbs_set_handler(func_code, phandler);
return MB_ERR_TO_ESP_ERR(ret);
}
/**
* Get slave command handler from the slave command handler table
*/
esp_err_t mbc_slave_get_handler(void *ctx, uint8_t func_code, mb_fn_handler_fp *phandler)
{
MB_RETURN_ON_FALSE(ctx, ESP_ERR_INVALID_STATE, TAG,
"Slave interface is not correctly initialized.");
mbs_controller_iface_t *mbs_controller = MB_SLAVE_GET_IFACE(ctx);
mb_base_t *pmb_obj = (mb_base_t *)mbs_controller->mb_base;
MB_RETURN_ON_FALSE((pmb_obj && !pmb_obj->descr.is_master), ESP_ERR_INVALID_STATE, TAG,
"Slave interface is not correctly initialized.");
mb_err_enum_t ret = mbs_get_handler(func_code, phandler);
return MB_ERR_TO_ESP_ERR(ret);
}

View File

@ -76,6 +76,47 @@ extern "C" {
*(uint8_t *)(dst + 0) = *(uint8_t *)(src)++; \
}
#define mb_err_var esp_err##__func__##__line__
#define esp_err_var mb_error##__func__##__line__
#define MB_ERR_TO_ESP_ERR(error_code) (__extension__( \
{ \
mb_err_enum_t mb_err_var = (mb_err_enum_t)error_code; \
esp_err_t esp_err_var = ESP_FAIL; \
switch(mb_err_var) { \
case MB_ENOERR: \
esp_err_var = ESP_OK; \
break; \
case MB_ENOREG: \
esp_err_var = ESP_ERR_NOT_SUPPORTED; \
break; \
case MB_ETIMEDOUT: \
esp_err_var = ESP_ERR_TIMEOUT; \
break; \
case MB_EINVAL: \
esp_err_var = ESP_ERR_INVALID_ARG; \
break; \
case MB_EILLFUNC: \
esp_err_var = ESP_ERR_INVALID_RESPONSE; \
break; \
case MB_ERECVDATA: \
esp_err_var = ESP_ERR_INVALID_RESPONSE; \
break; \
case MB_EBUSY: \
case MB_EILLSTATE: \
case MB_EPORTERR: \
case MB_ENORES: \
case MB_ENOCONN: \
esp_err_var = ESP_ERR_INVALID_STATE; \
break; \
default: \
ESP_LOGE(TAG, "%s: Incorrect return code (%x) ", __FUNCTION__, (int)mb_err_var); \
esp_err_var = ESP_FAIL; \
break; \
} \
(esp_err_var); \
} \
))
/**
* @brief Types of actual Modbus implementation
*/
@ -117,45 +158,6 @@ typedef enum {
MB_PARAM_UNKNOWN = 0xFF
} mb_param_type_t;
#define mb_err_var esp_err##__func__##__line__
#define esp_err_var mb_error##__func__##__line__
#define MB_ERR_TO_ESP_ERR(error_code) (__extension__( \
{ \
mb_err_enum_t mb_err_var = (mb_err_enum_t)error_code; \
esp_err_t esp_err_var = ESP_FAIL; \
switch(mb_err_var) { \
case MB_ENOERR: \
esp_err_var = ESP_OK; \
break; \
case MB_ENOREG: \
esp_err_var = ESP_ERR_NOT_SUPPORTED; \
break; \
case MB_ETIMEDOUT: \
esp_err_var = ESP_ERR_TIMEOUT; \
break; \
case MB_EILLFUNC: \
case MB_EINVAL: \
esp_err_var = ESP_ERR_INVALID_RESPONSE; \
break; \
case MB_ERECVDATA: \
esp_err_var = ESP_ERR_INVALID_RESPONSE; \
break; \
case MB_EBUSY: \
case MB_EILLSTATE: \
case MB_EPORTERR: \
case MB_ENORES: \
case MB_ENOCONN: \
esp_err_var = ESP_ERR_INVALID_STATE; \
break; \
default: \
ESP_LOGE(TAG, "%s: Incorrect return code (%x) ", __FUNCTION__, (int)mb_err_var); \
esp_err_var = ESP_FAIL; \
break; \
} \
(esp_err_var); \
} \
))
typedef enum _mb_comm_mode mb_mode_type_t;
typedef struct mb_base_t mb_base_t;
@ -168,17 +170,17 @@ typedef enum _addr_type_enum mb_tcp_addr_type_t;
/*!
* \brief Modbus TCP communication options structure.
*/
typedef struct _port_tcp_opts mb_tcp_opts_t;
typedef struct port_tcp_opts_s mb_tcp_opts_t;
/*!
* \brief Modbus serial communication options structure.
*/
typedef struct _port_serial_opts mb_serial_opts_t;
typedef struct port_serial_opts_s mb_serial_opts_t;
/*!
* \brief Modbus common communication options structure.
*/
typedef struct _port_common_opts mb_common_opts_t;
typedef struct port_common_opts_s mb_common_opts_t;
/**
* @brief Device communication structure to setup Modbus controller
@ -199,28 +201,70 @@ typedef union
* common interface method types
*/
typedef esp_err_t (*iface_create_fp)(mb_communication_info_t*, void **); /*!< Interface method create */
typedef esp_err_t (*iface_method_default_fp)(void *ctx); /*!< Interface method default prototype */
typedef esp_err_t (*iface_method_default_fp)(void *ctx); /*!< Interface method default prototype */
/**
* @brief The function registers the new function handler for specified command and
* allows to override the existing one for the communication object.
* If the phandler == 0, the function allows to clear the actual function handler for the `func_code` parameter.
* @brief Modbus controller common interface structure
*/
typedef struct {
mb_base_t *mb_base; /*!< base object pointer */
} mb_controller_common_t;
/**
* @brief The function registers the new function handler for specified command
* and allows to override the existing handler for the the controller object.
*
* @param[in] ctx context pointer to the communication object
* @param[in] ctx context pointer to the controller object (master or slave)
* @param[in] func_code the function code for the handler
* @param[in] phandler the pointer to function handler being used for command
*
* @return
* - esp_err_t ESP_OK - the function handler is correctly set
* - esp_err_t ESP_OK - the function handler is correctly set the handler
* - esp_err_t ESP_ERR_INVALID_ARG - invalid argument of function or parameter descriptor
* - esp_err_t ESP_ERR_INVALID_RESPONSE - an invalid response from slave during processing of parameter
* - esp_err_t ESP_ERR_INVALID_STATE - invalid state during data processing or allocation failure
* - esp_err_t ESP_ERR_INVALID_STATE - can not register non-existent handler or can not
* - esp_err_t ESP_ERR_NOT_FOUND - the requested slave is not found (not connected or not configured)
* - esp_err_t ESP_ERR_TIMEOUT - operation timed out and no response from slave
* - esp_err_t ESP_ERR_NOT_SUPPORTED - the request command is not supported by slave
* - esp_err_t ESP_FAIL - slave returned an exception or other failure
*/
esp_err_t mbc_register_cb(void *ctx, uint8_t func_code, mb_fn_handler_fp phandler);
esp_err_t mbc_set_handler(void *ctx, uint8_t func_code, mb_fn_handler_fp phandler);
/**
* @brief The function gets function handler for specified command from the controller object handler table.
*
* @param[in] ctx context pointer to the controller object (master or slave)
* @param[in] func_code the function code for the handler
* @param[out] phandler the pointer to function handler being returned
*
* @return
* - esp_err_t ESP_OK - the function handler is returned
* - esp_err_t ESP_ERR_INVALID_ARG - invalid argument of function or parameter descriptor
* esp_err_t ESP_ERR_INVALID_STATE - can not register non-existent handler or incorrect configuration
*/
esp_err_t mbc_get_handler(void *ctx, uint8_t func_code, mb_fn_handler_fp *phandler);
/**
* @brief The function deletes function handler for specified command from the controller object command handler table.
*
* @param[in] ctx context pointer to the controller object (master or slave)
* @param[in] func_code the function code for the handler
*
* @return
* - esp_err_t ESP_OK - the function handler is deleted
* - esp_err_t ESP_ERR_INVALID_ARG - invalid argument of function or parameter descriptor
* esp_err_t ESP_ERR_INVALID_STATE - can not register non-existent handler or incorrect configuration
*/
esp_err_t mbc_delete_handler(void *ctx, uint8_t func_code);
/**
* @brief The function gets the number of registered function handlers for the controller object.
*
* @param[in] ctx context pointer to the controller object (master or slave)
* @param[out] pcount the pointer to returned counter
*
* @return
* - esp_err_t ESP_OK - the function handler is returned in the
* - esp_err_t ESP_ERR_INVALID_ARG - invalid argument of function or parameter descriptor
* esp_err_t ESP_ERR_INVALID_STATE - can not register non-existent handler or incorrect configuration
*/
esp_err_t mbc_get_handler_count(void *ctx, uint16_t *pcount);
#ifdef __cplusplus
}

View File

@ -466,38 +466,6 @@ 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 function registers the new function handler for specified command
* and allows to override the existing one for the master object.
* If the phandler == NULL, the function allows to reset the actual function handler
* for the `func_code` parameter.
*
* @param[in] ctx context pointer to the master controller object
* @param[in] func_code the function code for the handler
* @param[in] phandler the pointer to function handler being used for command
*
* @return
* - esp_err_t ESP_OK - the function handler is correctly set
* - esp_err_t ESP_ERR_INVALID_ARG - invalid argument of function or parameter descriptor
* - esp_err_t ESP_ERR_INVALID_RESPONSE - an invalid response from slave during processing of parameter
* - esp_err_t ESP_ERR_INVALID_STATE - invalid state during data processing or allocation failure
* - esp_err_t ESP_ERR_NOT_FOUND - the requested slave is not found (not connected or not configured)
*/
esp_err_t mbc_master_set_handler(void *ctx, uint8_t func_code, mb_fn_handler_fp phandler);
/**
* @brief The function gets function handler for specified command from master command handler table.
*
* @param[in] ctx context pointer to the master controller object
* @param[in] func_code the function code for the handler
* @param[out] phandler the pointer to function handler being returned
*
* @return
* - esp_err_t ESP_OK - the function handler is returned in the
* - esp_err_t ESP_ERR_INVALID_ARG - invalid argument of function or parameter descriptor
*/
esp_err_t mbc_master_get_handler(void *ctx, uint8_t func_code, mb_fn_handler_fp *phandler);
#ifdef __cplusplus
}
#endif

View File

@ -292,37 +292,6 @@ mb_err_enum_t mbc_reg_discrete_slave_cb(mb_base_t *inst, uint8_t *reg_buffer, ui
*/
mb_err_enum_t mbc_reg_coils_slave_cb(mb_base_t *inst, uint8_t *reg_buffer, uint16_t address, uint16_t n_coils, mb_reg_mode_enum_t mode) __attribute__ ((weak));
/**
* @brief The function registers the new function handler for specified command
* and allows to override the existing one for the slave object.
* If the phandler == NULL, the function allows to reset the actual function handler
* for the `func_code` parameter.
*
* @param[in] ctx context pointer to the slave controller object
* @param[in] func_code the function code for the handler
* @param[in] phandler the pointer to function handler being used for command
*
* @return
* - esp_err_t ESP_OK - the function handler is correctly set
* - esp_err_t ESP_ERR_INVALID_ARG - invalid argument of function or parameter descriptor
* - esp_err_t ESP_ERR_INVALID_RESPONSE - an invalid response from slave during processing of parameter
* - esp_err_t ESP_ERR_INVALID_STATE - invalid state during data processing or allocation failure
*/
esp_err_t mbc_slave_set_handler(void *ctx, uint8_t func_code, mb_fn_handler_fp phandler);
/**
* @brief The function gets function handler for specified command from slave command handler table.
*
* @param[in] ctx context pointer to the slave controller object
* @param[in] func_code the function code for the handler
* @param[out] phandler the pointer to function handler being returned
*
* @return
* - esp_err_t ESP_OK - the function handler is returned in the
* - esp_err_t ESP_ERR_INVALID_ARG - invalid argument of function or parameter descriptor
*/
esp_err_t mbc_slave_get_handler(void *ctx, uint8_t func_code, mb_fn_handler_fp *phandler);
#ifdef __cplusplus
}
#endif

View File

@ -14,7 +14,10 @@
#include "driver/uart.h" // for uart port number defines
#include "sdkconfig.h" // for KConfig options
#include "esp_modbus_common.h"
#include "esp_modbus_master.h"
#include "esp_modbus_slave.h"

View File

@ -258,15 +258,16 @@ static esp_err_t mbc_serial_master_send_request(void *ctx, mb_param_request_t *r
default:
mb_fn_handler_fp phandler = NULL;
// check registered function handler
mb_error = mbm_get_handler(mb_command, &phandler);
mb_error = mbm_get_handler(mbm_controller_iface->mb_base, mb_command, &phandler);
if (mb_error == MB_ENOERR) {
// send the request for custom command
mb_error = mbm_rq_custom(mbm_controller_iface->mb_base, mb_slave_addr, mb_command,
data_ptr, (uint16_t)(mb_size << 1),
pdMS_TO_TICKS(MB_MAX_RESP_DELAY_MS));
ESP_LOGD(TAG, "%s: Send custom request (%u), error = (0x%d) ", __FUNCTION__, mb_command, (int)mb_error);
ESP_LOGD(TAG, "%s: Send custom request (%u)", __FUNCTION__, mb_command);
} else {
ESP_LOGE(TAG, "%s: Incorrect or unsupported function in request (%u) ", __FUNCTION__, mb_command);
ESP_LOGE(TAG, "%s: Incorrect or unsupported function in request (%u), error = (0x%x)",
__FUNCTION__, mb_command, (int)mb_error);
mb_error = MB_ENOREG;
}
break;

View File

@ -257,15 +257,15 @@ static esp_err_t mbc_tcp_master_send_request(void *ctx, mb_param_request_t *requ
default:
mb_fn_handler_fp phandler = NULL;
// check registered function handler
mb_error = mbm_get_handler(mb_command, &phandler);
mb_error = mbm_get_handler(mbm_controller_iface->mb_base, mb_command, &phandler);
if (mb_error == MB_ENOERR) {
// send the request for custom command
mb_error = mbm_rq_custom(mbm_controller_iface->mb_base, mb_slave_addr, mb_command,
data_ptr, (uint16_t)(mb_size << 1),
pdMS_TO_TICKS(MB_MAX_RESP_DELAY_MS));
ESP_LOGD(TAG, "%s: Send custom request (%u), error = (0x%d) ", __FUNCTION__, mb_command, (int)mb_error);
ESP_LOGD(TAG, "%s: Send custom request (%u)", __FUNCTION__, mb_command);
} else {
ESP_LOGE(TAG, "%s: Incorrect or unsupported function in request (%u) ", __FUNCTION__, mb_command);
ESP_LOGE(TAG, "%s: Incorrect or unsupported function in request (%u), error = (0x%x) ", __FUNCTION__, mb_command, (int)mb_error);
mb_error = MB_ENOREG;
}
break;
@ -598,7 +598,6 @@ static esp_err_t mbc_tcp_master_delete(void *ctx)
MB_RETURN_ON_FALSE((mbc_tcp_master_stop(ctx) == ESP_OK),
ESP_ERR_INVALID_STATE, TAG, "mb stack stop failure.");
}
mbm_iface->is_active = false;
vTaskDelete(mbm_opts->task_handle);
mbm_opts->task_handle = NULL;

View File

@ -39,7 +39,7 @@ typedef enum _mb_comm_mode mb_mode_type_t;
#include "driver/uart.h"
struct _port_serial_opts {
struct port_serial_opts_s {
mb_mode_type_t mode; /*!< Modbus communication mode */
uart_port_t port; /*!< Modbus communication port (UART) number */
uint8_t uid; /*!< Modbus slave address field (dummy for master) */
@ -51,7 +51,7 @@ struct _port_serial_opts {
uart_parity_t parity; /*!< Modbus UART parity settings */
} __attribute__((__packed__));
typedef struct _port_serial_opts mb_serial_opts_t;
typedef struct port_serial_opts_s mb_serial_opts_t;
#endif
@ -62,7 +62,7 @@ typedef enum _addr_type_enum {
} mb_addr_type_t;
struct _port_common_opts {
struct port_common_opts_s {
mb_mode_type_t mode; /*!< Modbus communication mode */
uint16_t port; /*!< Modbus communication port (UART) number */
uint8_t uid; /*!< Modbus slave address field (dummy for master) */
@ -70,7 +70,7 @@ struct _port_common_opts {
uint64_t test_tout_us; /*!< Modbus test timeout (reserved) */
} __attribute__((__packed__));
struct _port_tcp_opts {
struct port_tcp_opts_s {
mb_mode_type_t mode; /*!< Modbus communication mode */
uint16_t port; /*!< Modbus communication port (UART) number */
uint8_t uid; /*!< Modbus slave address field (dummy for master) */
@ -83,7 +83,7 @@ struct _port_tcp_opts {
bool start_disconnected; /*!< (Master only option) do not wait for connection to all nodes before polling */
} __attribute__((__packed__));
typedef struct _port_tcp_opts mb_tcp_opts_t;
typedef struct port_tcp_opts_s mb_tcp_opts_t;
// The common object descriptor struture (common for mb, transport, port objects)
struct _obj_descr {

View File

@ -0,0 +1,108 @@
/*
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "mb_common.h"
#include "mb_proto.h"
/* ----------------------- Defines ------------------------------------------*/
#define MB_IS_VALID_FUNC_CODE(fc) ((fc) >= MB_FUNC_CODE_MIN && (fc) <= MB_FUNC_CODE_MAX)
static const char TAG[] __attribute__((unused)) = "MB_FUNC_HANDLING";
mb_err_enum_t mb_set_handler(handler_descriptor_t *pdescriptor, uint8_t func_code, mb_fn_handler_fp phandler)
{
MB_RETURN_ON_FALSE((pdescriptor && phandler && pdescriptor->instance), MB_EINVAL, TAG, "invalid arguments.");
MB_RETURN_ON_FALSE(MB_IS_VALID_FUNC_CODE(func_code), MB_EINVAL, TAG,
"invalid function code (0x%x)", (int)func_code);
mb_command_entry_t *pitem = NULL;
LIST_FOREACH(pitem, &pdescriptor->head, entries) {
if (pitem && pitem->func_code == func_code) {
// The handler for the function already exists, rewrite it.
pitem->handler = phandler;
ESP_LOGD(TAG, "Inst: %p, set handler: 0x%x, %p", pdescriptor->instance, pitem->func_code, pitem->handler);
return MB_ENOERR;
}
}
// Insert new handler entry into list
pitem = (mb_command_entry_t *) heap_caps_malloc(sizeof(mb_command_entry_t), MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT);
MB_RETURN_ON_FALSE(pitem, MB_ENORES, TAG, "mb can not allocate memory for command handler 0x%x.", func_code);
pitem->func_code = func_code;
pitem->handler = phandler;
LIST_INSERT_HEAD(&pdescriptor->head, pitem, entries);
ESP_LOGD(TAG, "Inst: %p, add handler: 0x%x, %p", pdescriptor->instance, pitem->func_code, pitem->handler);
if (pdescriptor->count < MB_FUNC_HANDLERS_MAX) {
pdescriptor->count += 1;
}
return MB_ENOERR;
}
mb_err_enum_t mb_get_handler(handler_descriptor_t *pdescriptor, uint8_t func_code, mb_fn_handler_fp *phandler)
{
MB_RETURN_ON_FALSE((pdescriptor && phandler && pdescriptor->instance), MB_EINVAL, TAG, "invalid arguments.");
MB_RETURN_ON_FALSE(MB_IS_VALID_FUNC_CODE(func_code), MB_EINVAL, TAG,
"invalid function code (0x%x)", (int)func_code);
mb_command_entry_t *pitem = NULL;
LIST_FOREACH(pitem, &pdescriptor->head, entries) {
if (pitem && pitem->func_code == func_code) {
*phandler = pitem->handler;
ESP_LOGD(TAG, "Inst: %p, get handler: 0x%x, %p", pdescriptor->instance, pitem->func_code, pitem->handler);
return MB_ENOERR;
}
}
return MB_ENORES;
}
// Helper function to get handler
mb_err_enum_t mb_delete_handler(handler_descriptor_t *pdescriptor, uint8_t func_code)
{
MB_RETURN_ON_FALSE((pdescriptor && pdescriptor->instance), MB_EINVAL, TAG, "invalid arguments.");
MB_RETURN_ON_FALSE(MB_IS_VALID_FUNC_CODE(func_code), MB_EINVAL, TAG,
"invalid function code (0x%x)", (int)func_code);
if (LIST_EMPTY(&pdescriptor->head)) {
return MB_EINVAL;
}
mb_command_entry_t *pitem = NULL;
mb_command_entry_t *ptemp = NULL;
LIST_FOREACH_SAFE(pitem, &pdescriptor->head, entries, ptemp) {
if (pitem && pitem->func_code == func_code) {
ESP_LOGD(TAG, "Inst: %p, remove handler: 0x%x, %p", pdescriptor->instance, pitem->func_code, pitem->handler);
LIST_REMOVE(pitem, entries);
free(pitem);
if (pdescriptor->count) {
pdescriptor->count--;
}
return MB_ENOERR;
}
}
return MB_ENORES;
}
// Helper function to close all registered handlers in the list
mb_err_enum_t mb_delete_command_handlers(handler_descriptor_t *pdescriptor)
{
MB_RETURN_ON_FALSE((pdescriptor), MB_EINVAL, TAG, "invalid arguments.");
if (LIST_EMPTY(&pdescriptor->head)) {
return MB_EINVAL;
}
mb_command_entry_t *pitem = NULL;
while ((pitem = LIST_FIRST(&pdescriptor->head))) {
ESP_LOGD(TAG, "Inst: %p, close handler: 0x%x, %p", pdescriptor->instance, pitem->func_code, pitem->handler);
LIST_REMOVE(pitem, entries);
free(pitem);
if (pdescriptor->count) {
pdescriptor->count--;
}
}
return MB_ENOERR;
}

View File

@ -28,10 +28,11 @@
*
* File: $Id: mbutils.c, v 1.6 2007/02/18 23:49:07 wolti Exp $
*/
#include <mb_common.h>
#include <mb_proto.h>
#include "mb_common.h"
#include "mb_proto.h"
/* ----------------------- Defines ------------------------------------------*/
#define BITS_uint8_t 8U
#define BITS_uint8_t (8U)
static const char TAG[] __attribute__((unused)) = "MB_UTILS";
/* ----------------------- Start implementation -----------------------------*/
void mb_util_set_bits(uint8_t *byte_buf, uint16_t bit_offset, uint8_t but_num, uint8_t value)
@ -125,53 +126,3 @@ mb_exception_t mb_error_to_exception(mb_err_enum_t error_code)
return status;
}
mb_err_enum_t mb_set_handler(mb_fn_handler_t *pfh_table, uint8_t func_code, mb_fn_handler_fp phandler)
{
int index;
mb_err_enum_t status = MB_EILLSTATE;
if ((0 < func_code) && (func_code <= MB_FUNC_CODE_MAX)) {
if (phandler != NULL) {
for(index = 0; index < MB_FUNC_HANDLERS_MAX; index++) {
if ((pfh_table[index].handler == NULL) ||
(pfh_table[index].handler == phandler)) {
pfh_table[index].func_code = func_code;
pfh_table[index].handler = phandler;
break;
}
}
status = (index != MB_FUNC_HANDLERS_MAX) ? MB_ENOERR : MB_ENORES;
} else {
for (index = 0; index < MB_FUNC_HANDLERS_MAX; index++) {
if (pfh_table[index].func_code == func_code) {
pfh_table[index].func_code = 0;
pfh_table[index].handler = NULL;
break;
}
}
status = (index < MB_FUNC_HANDLERS_MAX) ? MB_ENOERR : MB_ENORES;
}
} else {
status = MB_EINVAL;
}
return status;
}
mb_err_enum_t mb_get_handler(mb_fn_handler_t *pfh_table, uint8_t func_code, mb_fn_handler_fp *phandler)
{
int index;
mb_err_enum_t status = MB_EILLSTATE;
if (pfh_table && phandler && (0 < func_code) && (func_code <= MB_FUNC_CODE_MAX)) {
for(index = 0; index < MB_FUNC_HANDLERS_MAX; index++) {
if (func_code == pfh_table[index].func_code) {
*phandler = pfh_table[index].handler;
break;
}
}
status = (index < MB_FUNC_HANDLERS_MAX) ? MB_ENOERR : MB_ENORES;
} else {
status = MB_EINVAL;
}
return status;
}

View File

@ -6,6 +6,7 @@
#pragma once
#include <stdint.h>
#include <sys/queue.h>
#include "mb_config.h"
#include "mb_frame.h"
@ -63,6 +64,9 @@ extern "C" {
#endif
#define MB_CAT_BUF_SIZE (100)
#define MB_HANDLER_UNLOCK_TICKS (pdMS_TO_TICKS(5000))
#define SEMA_SECTION(sema, tout) for (int st = (int)xSemaphoreTake(sema, tout); (st > 0); xSemaphoreGive(sema), st = -1)
#define MB_STR_CAT(pref, message) (__extension__( \
{ \
@ -104,6 +108,24 @@ extern "C" {
} \
))
/**
* @brief Modbus function handlers entry
*/
typedef struct mb_command_entry_s {
uint8_t func_code; /*!< function code */
mb_fn_handler_fp handler; /*!< handler function pointer */
LIST_ENTRY(mb_command_entry_s) entries; /*!< command handler entry */
} mb_command_entry_t;
typedef LIST_HEAD(handler_head, mb_command_entry_s) handler_head_t;
typedef struct mb_handler_descriptor_s {
SemaphoreHandle_t sema;
void* instance;
handler_head_t head;
uint16_t count;
} handler_descriptor_t;
typedef struct mb_base_t mb_base_t;
typedef struct mb_trans_base_t mb_trans_base_t;
typedef struct mb_port_base_t mb_port_base_t;
@ -152,12 +174,15 @@ struct mb_base_t
mb_rw_callbacks_t rw_cbs;
};
mb_err_enum_t mb_set_handler(mb_fn_handler_t *pfh_table, uint8_t func_code, mb_fn_handler_fp phandler);
mb_err_enum_t mb_get_handler(mb_fn_handler_t *pfh_table, uint8_t func_code, mb_fn_handler_fp *phandler);
// Helper functions for command handlers registration
mb_err_enum_t mb_set_handler(handler_descriptor_t *pdescriptor, uint8_t func_code, mb_fn_handler_fp phandler);
mb_err_enum_t mb_get_handler(handler_descriptor_t *pdescriptor, uint8_t func_code, mb_fn_handler_fp *phandler);
mb_err_enum_t mb_delete_handler(handler_descriptor_t *pdescriptor, uint8_t func_code);
mb_err_enum_t mb_delete_command_handlers(handler_descriptor_t *pdescriptor);
#if (CONFIG_FMB_COMM_MODE_ASCII_EN || CONFIG_FMB_COMM_MODE_RTU_EN)
typedef struct _port_serial_opts mb_serial_opts_t;
typedef struct port_serial_opts_s mb_serial_opts_t;
mb_err_enum_t mbs_rtu_create(mb_serial_opts_t *ser_opts, void **in_out_obj);
mb_err_enum_t mbs_ascii_create(mb_serial_opts_t *ser_opts, void **in_out_obj);
@ -166,7 +191,7 @@ mb_err_enum_t mbs_ascii_create(mb_serial_opts_t *ser_opts, void **in_out_obj);
#if (CONFIG_FMB_COMM_MODE_TCP_EN)
typedef struct _port_tcp_opts mb_tcp_opts_t;
typedef struct port_tcp_opts_s mb_tcp_opts_t;
mb_err_enum_t mbs_tcp_create(mb_tcp_opts_t *tcp_opts, void **in_out_obj);
#endif

View File

@ -12,7 +12,8 @@
extern "C" {
#endif
#define MB_FUNC_CODE_MAX 127
#define MB_FUNC_CODE_MIN (0x01)
#define MB_FUNC_CODE_MAX (0x7F)
typedef struct mb_base_t mb_base_t;

View File

@ -46,10 +46,16 @@ mb_err_enum_t mbc_reg_common_cb(mb_base_t *inst, uint8_t *pdata, uint16_t addres
#endif
// The function to register custom function handler for master
mb_err_enum_t mbm_set_handler(uint8_t func_code, mb_fn_handler_fp phandler);
mb_err_enum_t mbm_set_handler(mb_base_t *inst, uint8_t func_code, mb_fn_handler_fp phandler);
// The helper function to get custom function handler for master
mb_err_enum_t mbm_get_handler(uint8_t fc, mb_fn_handler_fp *phandler);
mb_err_enum_t mbm_get_handler(mb_base_t *inst, uint8_t func_code, mb_fn_handler_fp *phandler);
// The helper function to delete custom function handler for master
mb_err_enum_t mbm_delete_handler(mb_base_t *inst, uint8_t func_code);
// The helper function to get count of handlers for master
mb_err_enum_t mbm_get_handler_count(mb_base_t *inst, uint16_t *pcount);
#ifdef __cplusplus
}

View File

@ -6,6 +6,7 @@
#pragma once
#include <stdint.h>
#include "mb_func.h"
#ifdef __cplusplus
extern "C" {
@ -33,7 +34,7 @@ typedef enum _mb_commands_enum
MB_FUNC_DIAG_GET_COM_EVENT_CNT = ( 11 ),
MB_FUNC_DIAG_GET_COM_EVENT_LOG = ( 12 ),
MB_FUNC_OTHER_REPORT_SLAVEID = ( 17 ),
MB_FUNC_ERROR = ( 128U ),
MB_FUNC_ERROR = ( 0x80 )
} mb_commands_t;
/* ----------------------- Type definitions ---------------------------------*/

View File

@ -16,8 +16,16 @@ mb_exception_t mbs_fn_report_slave_id(mb_base_t *inst, uint8_t *frame_ptr, uint1
#endif
// The helper function to register custom function handler for slave
mb_err_enum_t mbs_set_handler(uint8_t func_code, mb_fn_handler_fp phandler);
mb_err_enum_t mbs_get_handler(uint8_t func_code, mb_fn_handler_fp *phandler);
mb_err_enum_t mbs_set_handler(mb_base_t *inst, uint8_t func_code, mb_fn_handler_fp phandler);
// The helper function to get custom function handler for slave
mb_err_enum_t mbs_get_handler(mb_base_t *inst, uint8_t func_code, mb_fn_handler_fp *phandler);
// The helper function to delete custom function handler for slave
mb_err_enum_t mbs_delete_handler(mb_base_t *inst, uint8_t func_code);
// The helper function to get count of handlers for slave
mb_err_enum_t mbs_get_handler_count(mb_base_t *inst, uint16_t *pcount);
#ifdef __cplusplus
}

View File

@ -18,49 +18,16 @@ static const char *TAG = "mb_object.master";
#if (MB_MASTER_ASCII_ENABLED || MB_MASTER_RTU_ENABLED)
typedef struct _port_serial_opts mb_serial_opts_t;
typedef struct port_serial_opts_s mb_serial_opts_t;
#endif
#if (MB_MASTER_ASCII_ENABLED || MB_MASTER_RTU_ENABLED || MB_MASTER_TCP_ENABLED)
static mb_fn_handler_t master_handlers[MB_FUNC_HANDLERS_MAX] =
{
#if MB_FUNC_OTHER_REP_SLAVEID_ENABLED
{MB_FUNC_OTHER_REPORT_SLAVEID, (void *)mbm_fn_report_slave_id},
#endif
#if MB_FUNC_READ_INPUT_ENABLED
{MB_FUNC_READ_INPUT_REGISTER, (void *)mbm_fn_read_inp_reg},
#endif
#if MB_FUNC_READ_HOLDING_ENABLED
{MB_FUNC_READ_HOLDING_REGISTER, (void *)mbm_fn_read_holding_reg},
#endif
#if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED
{MB_FUNC_WRITE_MULTIPLE_REGISTERS, (void *)mbm_fn_write_multi_holding_reg},
#endif
#if MB_FUNC_WRITE_HOLDING_ENABLED
{MB_FUNC_WRITE_REGISTER, (void *)mbm_fn_write_holding_reg},
#endif
#if MB_FUNC_READWRITE_HOLDING_ENABLED
{MB_FUNC_READWRITE_MULTIPLE_REGISTERS, (void *)mbm_fn_rw_multi_holding_regs},
#endif
#if MB_FUNC_READ_COILS_ENABLED
{MB_FUNC_READ_COILS, (void *)mbm_fn_read_coils},
#endif
#if MB_FUNC_WRITE_COIL_ENABLED
{MB_FUNC_WRITE_SINGLE_COIL, (void *)mbm_fn_write_coil},
#endif
#if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED
{MB_FUNC_WRITE_MULTIPLE_COILS, (void *)mbm_fn_write_multi_coils},
#endif
#if MB_FUNC_READ_DISCRETE_INPUTS_ENABLED
{MB_FUNC_READ_DISCRETE_INPUTS, (void *)mbm_fn_read_discrete_inputs},
#endif
};
typedef struct
{
mb_base_t base;
mb_comm_mode_t cur_mode;
mb_state_enum_t cur_state;
uint8_t *rcv_frame;
@ -72,6 +39,7 @@ typedef struct
mb_exception_t exception;
uint8_t master_dst_addr;
uint64_t curr_trans_id;
handler_descriptor_t handler_descriptor;
} mbm_object_t;
mb_err_enum_t mbm_tcp_create(mb_tcp_opts_t *tcp_opts, void **in_out_obj);
@ -87,56 +55,133 @@ static void mbm_set_dest_addr(mb_base_t *inst, uint8_t dest_addr);
static uint8_t mbm_get_dest_addr(mb_base_t *inst);
static void mbm_get_pdu_send_buf(mb_base_t *inst, uint8_t **pbuf);
static _lock_t s_mbm_lock; // lock section for command handlers
mb_err_enum_t mbm_set_handler(uint8_t func_code, mb_fn_handler_fp phandler)
mb_err_enum_t mbm_set_handler(mb_base_t *inst, uint8_t func_code, mb_fn_handler_fp phandler)
{
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);
mb_err_enum_t status = MB_EILLSTATE;
CRITICAL_SECTION(s_mbm_lock) {
status = mb_set_handler(&master_handlers[0], func_code, phandler);
SEMA_SECTION(mbm_obj->handler_descriptor.sema, MB_HANDLER_UNLOCK_TICKS) {
status = mb_set_handler(&mbm_obj->handler_descriptor, func_code, phandler);
}
return status;
}
// The helper function to register custom function handler for master
mb_err_enum_t mbm_get_handler(uint8_t fc, mb_fn_handler_fp *phandler)
mb_err_enum_t mbm_get_handler(mb_base_t *inst, uint8_t func_code, mb_fn_handler_fp *phandler)
{
mb_err_enum_t status = MB_EINVAL;
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);
mb_err_enum_t status = MB_EILLSTATE;
if (phandler) {
CRITICAL_SECTION(s_mbm_lock) {
status = mb_get_handler(&master_handlers[0], fc, phandler);
SEMA_SECTION(mbm_obj->handler_descriptor.sema, MB_HANDLER_UNLOCK_TICKS) {
status = mb_get_handler(&mbm_obj->handler_descriptor, func_code, phandler);
}
}
return status;
}
static mb_err_enum_t mbm_check_invoke_handler(mb_base_t *inst, uint8_t fc, uint8_t *pbuf, uint16_t *plen)
mb_err_enum_t mbm_delete_handler(mb_base_t *inst, uint8_t func_code)
{
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);
mb_err_enum_t status = MB_EILLSTATE;
SEMA_SECTION(mbm_obj->handler_descriptor.sema, MB_HANDLER_UNLOCK_TICKS) {
status = mb_delete_handler(&mbm_obj->handler_descriptor, func_code);
}
return status;
}
mb_err_enum_t mbm_get_handler_count(mb_base_t *inst, uint16_t *pcount)
{
MB_RETURN_ON_FALSE((pcount && inst), MB_EINVAL, TAG, "get handler count wrong arguments.");
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);
SEMA_SECTION(mbm_obj->handler_descriptor.sema, MB_HANDLER_UNLOCK_TICKS) {
*pcount = mbm_obj->handler_descriptor.count;
}
return MB_ENOERR;
}
static mb_err_enum_t mbm_check_invoke_handler(mb_base_t *inst, uint8_t func_code, uint8_t *pbuf, uint16_t *plen)
{
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);
mb_exception_t exception = MB_EX_ILLEGAL_FUNCTION;
if (!fc) {
if (!func_code || !pbuf) {
return MB_EX_ILLEGAL_FUNCTION;
}
if (fc & MB_FUNC_ERROR) {
if (func_code & MB_FUNC_ERROR) {
exception = (mb_exception_t)pbuf[MB_PDU_DATA_OFF];
return exception;
}
CRITICAL_SECTION(s_mbm_lock) {
for (int i = 0; i < MB_FUNC_HANDLERS_MAX; i++) {
/* No more function handlers registered. Abort. */
if (master_handlers[i].func_code == 0) {
ESP_LOGE(TAG, MB_OBJ_FMT": function (0x%x), handler is missing.", MB_OBJ_PARENT(inst), (int)fc);
break;
}
if (master_handlers[i].func_code == fc) {
exception = master_handlers[i].handler(inst, pbuf, plen);
ESP_LOGD(__func__, MB_OBJ_FMT": function (0x%x), invoke handler.", MB_OBJ_PARENT(inst), (int)fc);
break;
}
SEMA_SECTION(mbm_obj->handler_descriptor.sema, MB_HANDLER_UNLOCK_TICKS) {
mb_fn_handler_fp phandler = NULL;
mb_err_enum_t status = mb_get_handler(&mbm_obj->handler_descriptor, func_code, &phandler);
if ((status == MB_ENOERR) && phandler) {
exception = phandler(inst, pbuf, plen);
}
}
return exception;
}
static mb_err_enum_t mbm_register_default_handlers(mb_base_t *inst)
{
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);
mb_err_enum_t err = MB_EILLSTATE;
LIST_INIT(&mbm_obj->handler_descriptor.head);
mbm_obj->handler_descriptor.sema = xSemaphoreCreateBinary();
(void)xSemaphoreGive(mbm_obj->handler_descriptor.sema);
mbm_obj->handler_descriptor.instance = (void *)inst->descr.parent;
#if MB_FUNC_OTHER_REP_SLAVEID_ENABLED
err = mbm_set_handler(inst, MB_FUNC_OTHER_REPORT_SLAVEID, (void *)mbm_fn_report_slave_id);
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
#endif
#if MB_FUNC_READ_INPUT_ENABLED
err = mbm_set_handler(inst, MB_FUNC_READ_INPUT_REGISTER, (void *)mbm_fn_read_inp_reg);
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
#endif
#if MB_FUNC_READ_HOLDING_ENABLED
err = mbm_set_handler(inst, MB_FUNC_READ_HOLDING_REGISTER, (void *)mbm_fn_read_holding_reg);
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
#endif
#if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED
err = mbm_set_handler(inst, MB_FUNC_WRITE_MULTIPLE_REGISTERS, (void *)mbm_fn_write_multi_holding_reg);
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
#endif
#if MB_FUNC_WRITE_HOLDING_ENABLED
err = mbm_set_handler(inst, MB_FUNC_WRITE_REGISTER, (void *)mbm_fn_write_holding_reg);
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
#endif
#if MB_FUNC_READWRITE_HOLDING_ENABLED
err = mbm_set_handler(inst, MB_FUNC_READWRITE_MULTIPLE_REGISTERS, (void *)mbm_fn_rw_multi_holding_regs);
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
#endif
#if MB_FUNC_READ_COILS_ENABLED
err = mbm_set_handler(inst, MB_FUNC_READ_COILS, (void *)mbm_fn_read_coils);
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
#endif
#if MB_FUNC_WRITE_COIL_ENABLED
err = mbm_set_handler(inst, MB_FUNC_WRITE_SINGLE_COIL, (void *)mbm_fn_write_coil);
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
#endif
#if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED
err = mbm_set_handler(inst, MB_FUNC_WRITE_MULTIPLE_COILS, (void *)mbm_fn_write_multi_coils);
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
#endif
#if MB_FUNC_READ_DISCRETE_INPUTS_ENABLED
err = mbm_set_handler(inst, MB_FUNC_READ_DISCRETE_INPUTS, (void *)mbm_fn_read_discrete_inputs);
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
#endif
return MB_ENOERR;
}
static mb_err_enum_t mbm_unregister_handlers(mb_base_t *inst)
{
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);
(void)xSemaphoreTake(mbm_obj->handler_descriptor.sema, MB_HANDLER_UNLOCK_TICKS);
ESP_LOGD(TAG, "Close %s command handlers.", mbm_obj->base.descr.parent_name);
(void)mb_delete_command_handlers(&mbm_obj->handler_descriptor);
mbm_obj->handler_descriptor.instance = NULL;
(void)xSemaphoreGive(mbm_obj->handler_descriptor.sema);
vSemaphoreDelete(mbm_obj->handler_descriptor.sema);
return MB_ENOERR;
}
#if (MB_MASTER_RTU_ENABLED)
mb_err_enum_t mbm_rtu_create(mb_serial_opts_t *ser_opts, void **in_out_obj)
@ -181,6 +226,9 @@ mb_err_enum_t mbm_rtu_create(mb_serial_opts_t *ser_opts, void **in_out_obj)
mbm_obj->curr_trans_id = 0;
mbm_obj->base.port_obj = transp_obj->port_obj;
mbm_obj->base.transp_obj = transp_obj;
ret = mbm_register_default_handlers(&mbm_obj->base);
MB_GOTO_ON_FALSE((ret == MB_ENOERR), MB_EILLSTATE, error,
TAG, "default handlers registration fail, err: %d", (int)ret);
*in_out_obj = (void *)&(mbm_obj->base);
ESP_LOGD(TAG, "created object %s", mbm_obj->base.descr.parent_name);
return MB_ENOERR;
@ -189,6 +237,7 @@ error:
if (transp_obj) {
mbm_rtu_transp_delete(transp_obj);
}
(void)mbm_unregister_handlers(&mbm_obj->base);
free(mbm_obj->base.descr.parent_name);
CRITICAL_SECTION_CLOSE(mbm_obj->base.lock);
free(mbm_obj);
@ -235,6 +284,9 @@ mb_err_enum_t mbm_ascii_create(mb_serial_opts_t *ser_opts, void **in_out_obj)
ret = mbm_ascii_transp_create(ser_opts, (void **)&transp_obj);
MB_GOTO_ON_FALSE((transp_obj && (ret == MB_ENOERR)), MB_EILLSTATE, error,
TAG, "transport creation, err: %d", (int)ret);
ret = mbm_register_default_handlers(&mbm_obj->base);
MB_GOTO_ON_FALSE((ret == MB_ENOERR), MB_EILLSTATE, error,
TAG, "default handlers registration fail, err: %d", (int)ret);
mbm_obj->cur_mode = ser_opts->mode;
mbm_obj->cur_state = STATE_DISABLED;
transp_obj->get_tx_frm(transp_obj, (uint8_t **)&mbm_obj->snd_frame);
@ -250,6 +302,7 @@ error:
{
mbm_ascii_transp_delete(transp_obj);
}
(void)mbm_unregister_handlers(&mbm_obj->base);
free(mbm_obj->base.descr.parent_name);
CRITICAL_SECTION_CLOSE(mbm_obj->base.lock);
free(mbm_obj);
@ -296,6 +349,9 @@ mb_err_enum_t mbm_tcp_create(mb_tcp_opts_t *tcp_opts, void **in_out_obj)
ret = mbm_tcp_transp_create(tcp_opts, (void **)&transp_obj);
MB_GOTO_ON_FALSE((transp_obj && (ret == MB_ENOERR)), MB_EILLSTATE, error,
TAG, "transport creation, err: %d", (int)ret);
ret = mbm_register_default_handlers(&mbm_obj->base);
MB_GOTO_ON_FALSE((ret == MB_ENOERR), MB_EILLSTATE, error,
TAG, "default handlers registration fail, err: %d", (int)ret);
mbm_obj->cur_mode = tcp_opts->mode;
mbm_obj->cur_state = STATE_DISABLED;
transp_obj->get_tx_frm(transp_obj, (uint8_t **)&mbm_obj->snd_frame);
@ -310,6 +366,7 @@ error:
if (transp_obj) {
mbm_tcp_transp_delete(transp_obj);
}
(void)mbm_unregister_handlers(&mbm_obj->base);
free(mbm_obj->base.descr.parent_name);
CRITICAL_SECTION_CLOSE(mbm_obj->base.lock);
free(mbm_obj);
@ -338,6 +395,7 @@ mb_err_enum_t mbm_delete(mb_base_t *inst)
ESP_LOGW(TAG, "%p, Master object ID is not supported!", mbm_obj);
}
#endif
(void)mbm_unregister_handlers(&mbm_obj->base);
// delete the modbus instance
free(mbm_obj->base.descr.parent_name);
CRITICAL_SECTION_CLOSE(inst->lock);
@ -373,6 +431,9 @@ mb_err_enum_t mbm_disable(mb_base_t *inst)
{
mb_err_enum_t status = MB_ENOERR;
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);
// Wait for function handler to be unlocked before disable the object
(void)xSemaphoreTake(mbm_obj->handler_descriptor.sema, MB_HANDLER_UNLOCK_TICKS);
(void)xSemaphoreGive(mbm_obj->handler_descriptor.sema);
CRITICAL_SECTION(inst->lock)
{
if (mbm_obj->cur_state == STATE_ENABLED) {
@ -547,7 +608,7 @@ mb_err_enum_t mbm_poll(mb_base_t *inst)
mbm_obj->func_code = mbm_obj->rcv_frame[MB_PDU_FUNC_OFF];
exception = MB_EX_ILLEGAL_FUNCTION;
/* If master request is broadcast,
* the master need to execute function for all slaves.
* the master needs to execute function for all slaves.
*/
if (MB_OBJ(inst->transp_obj)->frm_is_bcast(inst->transp_obj)) {
length = mbm_obj->pdu_snd_len;
@ -556,7 +617,7 @@ mb_err_enum_t mbm_poll(mb_base_t *inst)
exception = mbm_check_invoke_handler(inst, mbm_obj->func_code, mbm_obj->rcv_frame, &length);
}
} else {
ESP_LOGD(__func__, MB_OBJ_FMT": function (0x%x), invoke handler.", MB_OBJ_PARENT(inst), (int)mbm_obj->func_code);
ESP_LOGD(TAG, MB_OBJ_FMT": function (0x%x), invoke handler.", MB_OBJ_PARENT(inst), (int)mbm_obj->func_code);
exception = mbm_check_invoke_handler(inst, mbm_obj->func_code, mbm_obj->rcv_frame, &mbm_obj->pdu_rcv_len);
}
/* If master has exception, will send error process event. Otherwise the master is idle.*/

View File

@ -20,7 +20,7 @@ static const char *TAG = "mb_object.slave";
#if (MB_SLAVE_ASCII_ENABLED || MB_SLAVE_RTU_ENABLED)
typedef struct _port_serial_opts mb_serial_opts_t;
typedef struct port_serial_opts_s mb_serial_opts_t;
#endif
@ -37,94 +37,139 @@ typedef struct
uint8_t rcv_addr;
uint64_t curr_trans_id;
volatile uint16_t *pdu_snd_len;
handler_descriptor_t handler_descriptor;
} mbs_object_t;
static mb_fn_handler_t slave_handlers[MB_FUNC_HANDLERS_MAX] =
{
#if MB_FUNC_OTHER_REP_SLAVEID_ENABLED
{MB_FUNC_OTHER_REPORT_SLAVEID, (void *)mbs_fn_report_slave_id},
#endif
#if MB_FUNC_READ_INPUT_ENABLED
{MB_FUNC_READ_INPUT_REGISTER, (void *)mbs_fn_read_input_reg},
#endif
#if MB_FUNC_READ_HOLDING_ENABLED
{MB_FUNC_READ_HOLDING_REGISTER, (void *)mbs_fn_read_holding_reg},
#endif
#if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED
{MB_FUNC_WRITE_MULTIPLE_REGISTERS, (void *)mbs_fn_write_multi_holding_reg},
#endif
#if MB_FUNC_WRITE_HOLDING_ENABLED
{MB_FUNC_WRITE_REGISTER, (void *)mbs_fn_write_holding_reg},
#endif
#if MB_FUNC_READWRITE_HOLDING_ENABLED
{MB_FUNC_READWRITE_MULTIPLE_REGISTERS, (void *)mbs_fn_rw_multi_holding_reg},
#endif
#if MB_FUNC_READ_COILS_ENABLED
{MB_FUNC_READ_COILS, (void *)mbs_fn_read_coils},
#endif
#if MB_FUNC_WRITE_COIL_ENABLED
{MB_FUNC_WRITE_SINGLE_COIL, (void *)mbs_fn_write_coil},
#endif
#if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED
{MB_FUNC_WRITE_MULTIPLE_COILS, (void *)mbs_fn_write_multi_coils},
#endif
#if MB_FUNC_READ_DISCRETE_INPUTS_ENABLED
{MB_FUNC_READ_DISCRETE_INPUTS, (void *)mbs_fn_read_discrete_inp},
#endif
};
static _lock_t s_mbs_lock;
mb_err_enum_t mbs_delete(mb_base_t *inst);
mb_err_enum_t mbs_enable(mb_base_t *inst);
mb_err_enum_t mbs_disable(mb_base_t *inst);
mb_err_enum_t mbs_poll(mb_base_t *inst);
// The helper function to register custom function handler for slave
mb_err_enum_t mbs_set_handler(uint8_t fc, mb_fn_handler_fp phandler)
mb_err_enum_t mbs_set_handler(mb_base_t *inst, uint8_t func_code, mb_fn_handler_fp phandler)
{
mbs_object_t *mbs_obj = MB_GET_OBJ_CTX(inst, mbs_object_t, base);
mb_err_enum_t status = MB_EINVAL;
CRITICAL_SECTION(s_mbs_lock) {
status = mb_set_handler(&slave_handlers[0], fc, phandler);
SEMA_SECTION(mbs_obj->handler_descriptor.sema, MB_HANDLER_UNLOCK_TICKS) {
status = mb_set_handler(&mbs_obj->handler_descriptor, func_code, phandler);
}
return status;
}
// The helper function to register custom function handler for slave
mb_err_enum_t mbs_get_handler(uint8_t fc, mb_fn_handler_fp *phandler)
mb_err_enum_t mbs_get_handler(mb_base_t *inst, uint8_t func_code, mb_fn_handler_fp *phandler)
{
mbs_object_t *mbs_obj = MB_GET_OBJ_CTX(inst, mbs_object_t, base);
mb_err_enum_t status = MB_EINVAL;
if (phandler) {
CRITICAL_SECTION(s_mbs_lock) {
status = mb_get_handler(&slave_handlers[0], fc, phandler);
SEMA_SECTION(mbs_obj->handler_descriptor.sema, MB_HANDLER_UNLOCK_TICKS) {
status = mb_get_handler(&mbs_obj->handler_descriptor, func_code, phandler);
}
}
return status;
}
static mb_err_enum_t mbs_check_invoke_handler(mb_base_t *inst, uint8_t fc, uint8_t *pbuf, uint16_t *plen)
mb_err_enum_t mbs_delete_handler(mb_base_t *inst, uint8_t func_code)
{
mbs_object_t *mbs_obj = MB_GET_OBJ_CTX(inst, mbs_object_t, base);
mb_err_enum_t status = MB_EILLSTATE;
SEMA_SECTION(mbs_obj->handler_descriptor.sema, MB_HANDLER_UNLOCK_TICKS) {
status = mb_delete_handler(&mbs_obj->handler_descriptor, func_code);
}
return status;
}
mb_err_enum_t mbs_get_handler_count(mb_base_t *inst, uint16_t *pcount)
{
MB_RETURN_ON_FALSE((pcount && inst), MB_EINVAL, TAG, "get handler count wrong arguments");
mbs_object_t *mbs_obj = MB_GET_OBJ_CTX(inst, mbs_object_t, base);
SEMA_SECTION(mbs_obj->handler_descriptor.sema, MB_HANDLER_UNLOCK_TICKS) {
*pcount = mbs_obj->handler_descriptor.count;
}
return MB_ENOERR;
}
static mb_exception_t mbs_check_invoke_handler(mb_base_t *inst, uint8_t func_code, uint8_t *pbuf, uint16_t *plen)
{
mbs_object_t *mbs_obj = MB_GET_OBJ_CTX(inst, mbs_object_t, base);
mb_exception_t exception = MB_EX_ILLEGAL_FUNCTION;
if (!fc || (fc & MB_FUNC_ERROR)) {
if (!func_code || (func_code & MB_FUNC_ERROR)) {
return MB_EX_ILLEGAL_FUNCTION;
}
CRITICAL_SECTION(s_mbs_lock) {
for (int i = 0; i < MB_FUNC_HANDLERS_MAX; i++) {
/* No more function handlers registered. Abort. */
if (slave_handlers[i].func_code == 0) {
ESP_LOGD(TAG, MB_OBJ_FMT": function (0x%x), handler is missing.", MB_OBJ_PARENT(inst), (int)fc);
break;
}
if (slave_handlers[i].func_code == fc) {
exception = slave_handlers[i].handler(inst, pbuf, plen);
ESP_LOGD(TAG, MB_OBJ_FMT": function (0x%x), invoke handler.", MB_OBJ_PARENT(inst), (int)fc);
break;
}
SEMA_SECTION(mbs_obj->handler_descriptor.sema, MB_HANDLER_UNLOCK_TICKS) {
mb_fn_handler_fp phandler = NULL;
mb_err_enum_t status = mb_get_handler(&mbs_obj->handler_descriptor, func_code, &phandler);
if ((status == MB_ENOERR) && phandler) {
exception = phandler(inst, pbuf, plen);
ESP_LOGD(TAG, MB_OBJ_FMT": function (0x%x), invoke handler %p.", MB_OBJ_PARENT(inst), (int)func_code, phandler);
}
}
return exception;
}
static mb_err_enum_t mbs_register_default_handlers(mb_base_t *inst)
{
mbs_object_t *mbs_obj = MB_GET_OBJ_CTX(inst, mbs_object_t, base);
mb_err_enum_t err = MB_EILLSTATE;
LIST_INIT(&mbs_obj->handler_descriptor.head);
mbs_obj->handler_descriptor.sema = xSemaphoreCreateBinary();
(void)xSemaphoreGive(mbs_obj->handler_descriptor.sema);
mbs_obj->handler_descriptor.instance = (void *)inst->descr.parent;
#if MB_FUNC_OTHER_REP_SLAVEID_ENABLED
err = mbs_set_handler(inst, MB_FUNC_OTHER_REPORT_SLAVEID, (void *)mbs_fn_report_slave_id);
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
#endif
#if MB_FUNC_READ_INPUT_ENABLED
err = mbs_set_handler(inst, MB_FUNC_READ_INPUT_REGISTER, (void *)mbs_fn_read_input_reg);
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
#endif
#if MB_FUNC_READ_HOLDING_ENABLED
err = mbs_set_handler(inst, MB_FUNC_READ_HOLDING_REGISTER, (void *)mbs_fn_read_holding_reg);
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
#endif
#if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED
err = mbs_set_handler(inst, MB_FUNC_WRITE_MULTIPLE_REGISTERS, (void *)mbs_fn_write_multi_holding_reg);
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
#endif
#if MB_FUNC_WRITE_HOLDING_ENABLED
err = mbs_set_handler(inst, MB_FUNC_WRITE_REGISTER, (void *)mbs_fn_write_holding_reg);
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
#endif
#if MB_FUNC_READWRITE_HOLDING_ENABLED
err = mbs_set_handler(inst, MB_FUNC_READWRITE_MULTIPLE_REGISTERS, (void *)mbs_fn_rw_multi_holding_reg);
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
#endif
#if MB_FUNC_READ_COILS_ENABLED
err = mbs_set_handler(inst, MB_FUNC_READ_COILS, (void *)mbs_fn_read_coils);
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
#endif
#if MB_FUNC_WRITE_COIL_ENABLED
err = mbs_set_handler(inst, MB_FUNC_WRITE_SINGLE_COIL, (void *)mbs_fn_write_coil);
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
#endif
#if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED
err = mbs_set_handler(inst, MB_FUNC_WRITE_MULTIPLE_COILS, (void *)mbs_fn_write_multi_coils);
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
#endif
#if MB_FUNC_READ_DISCRETE_INPUTS_ENABLED
err = mbs_set_handler(inst, MB_FUNC_READ_DISCRETE_INPUTS, (void *)mbs_fn_read_discrete_inp);
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
#endif
return MB_ENOERR;
}
static mb_err_enum_t mbs_unregister_handlers(mb_base_t *inst)
{
mbs_object_t *mbs_obj = MB_GET_OBJ_CTX(inst, mbs_object_t, base);
(void)xSemaphoreTake(mbs_obj->handler_descriptor.sema, MB_HANDLER_UNLOCK_TICKS);
ESP_LOGD(TAG, "Close %s command handlers.", mbs_obj->base.descr.parent_name);
(void)mb_delete_command_handlers(&mbs_obj->handler_descriptor);
mbs_obj->handler_descriptor.instance = NULL;
(void)xSemaphoreGive(mbs_obj->handler_descriptor.sema);
vSemaphoreDelete(mbs_obj->handler_descriptor.sema);
return MB_ENOERR;
}
#if (MB_SLAVE_RTU_ENABLED)
mb_err_enum_t mbs_rtu_create(mb_serial_opts_t *ser_opts, void **in_out_obj)
@ -157,6 +202,9 @@ mb_err_enum_t mbs_rtu_create(mb_serial_opts_t *ser_opts, void **in_out_obj)
ret = mbs_rtu_transp_create(ser_opts, (void **)&transp_obj);
MB_GOTO_ON_FALSE((transp_obj && (ret == MB_ENOERR)), MB_EILLSTATE, error,
TAG, "transport creation, err: %d", (int)ret);
ret = mbs_register_default_handlers(&mbs_obj->base);
MB_GOTO_ON_FALSE((ret == MB_ENOERR), MB_EILLSTATE, error,
TAG, "default handlers registration fail, err: %d", (int)ret);
mbs_obj->cur_mode = ser_opts->mode;
mbs_obj->mb_address = ser_opts->uid;
mbs_obj->cur_state = STATE_DISABLED;
@ -171,6 +219,7 @@ error:
if (transp_obj) {
mbs_rtu_transp_delete(transp_obj);
}
(void)mbs_unregister_handlers(&mbs_obj->base);
free(mbs_obj->base.descr.parent_name);
CRITICAL_SECTION_CLOSE(mbs_obj->base.lock);
free(mbs_obj);
@ -211,6 +260,9 @@ mb_err_enum_t mbs_ascii_create(mb_serial_opts_t *ser_opts, void **in_out_obj)
ret = mbs_ascii_transp_create(ser_opts, (void **)&transp_obj);
MB_GOTO_ON_FALSE((transp_obj && (ret == MB_ENOERR)), MB_EILLSTATE, error,
TAG, "transport creation, err: %d", (int)ret);
ret = mbs_register_default_handlers(&mbs_obj->base);
MB_GOTO_ON_FALSE((ret == MB_ENOERR), MB_EILLSTATE, error,
TAG, "default handlers registration fail, err: %d", (int)ret);
mbs_obj->cur_mode = ser_opts->mode;
mbs_obj->mb_address = ser_opts->uid;
mbs_obj->cur_state = STATE_DISABLED;
@ -225,6 +277,7 @@ error:
if (transp_obj) {
mbs_ascii_transp_delete(transp_obj);
}
(void)mbs_unregister_handlers(&mbs_obj->base);
free(mbs_obj->base.descr.parent_name);
CRITICAL_SECTION_CLOSE(mbs_obj->base.lock);
free(mbs_obj);
@ -266,6 +319,9 @@ mb_err_enum_t mbs_tcp_create(mb_tcp_opts_t *tcp_opts, void **in_out_obj)
ret = mbs_tcp_transp_create(tcp_opts, (void **)&transp_obj);
MB_GOTO_ON_FALSE((transp_obj && (ret == MB_ENOERR)), MB_EILLSTATE, error,
TAG, "transport creation, err: %d", (int)ret);
ret = mbs_register_default_handlers(&mbs_obj->base);
MB_GOTO_ON_FALSE((ret == MB_ENOERR), MB_EILLSTATE, error,
TAG, "default handlers registration fail, err: %d", (int)ret);
mbs_obj->cur_mode = tcp_opts->mode;
mbs_obj->mb_address = tcp_opts->uid;
mbs_obj->cur_state = STATE_DISABLED;
@ -280,6 +336,7 @@ error:
if (transp_obj) {
mbs_tcp_transp_delete(transp_obj);
}
(void)mbs_unregister_handlers(&mbs_obj->base);
free(mbs_obj->base.descr.parent_name);
CRITICAL_SECTION_CLOSE(mbs_obj->base.lock);
free(mbs_obj);
@ -307,6 +364,7 @@ mb_err_enum_t mbs_delete(mb_base_t *inst)
mbs_obj->base.obj_id_chunks = 0;
}
#endif
(void)mbs_unregister_handlers(&mbs_obj->base);
// delete the modbus instance
free(mbs_obj->base.descr.parent_name);
CRITICAL_SECTION_CLOSE(inst->lock);
@ -344,7 +402,10 @@ mb_err_enum_t mbs_enable(mb_base_t *inst)
mb_err_enum_t mbs_disable(mb_base_t *inst)
{
mb_err_enum_t status = MB_ENOERR;
mbs_object_t *mbs_obj = MB_GET_OBJ_CTX(inst, mbs_object_t, base);;
mbs_object_t *mbs_obj = MB_GET_OBJ_CTX(inst, mbs_object_t, base);
// Wait for handler to be unlocked before disable the object
(void)xSemaphoreTake(mbs_obj->handler_descriptor.sema, MB_HANDLER_UNLOCK_TICKS);
(void)xSemaphoreGive(mbs_obj->handler_descriptor.sema);
CRITICAL_SECTION(inst->lock) {
if (mbs_obj->cur_state == STATE_ENABLED) {
MB_OBJ(mbs_obj->base.transp_obj)->frm_stop(mbs_obj->base.transp_obj);

View File

@ -19,8 +19,7 @@
#include "mb_port_types.h"
#ifdef __cplusplus
extern "C"
{
extern "C" {
#endif
#define MB_SER_PDU_SIZE_MIN (3)
@ -29,7 +28,7 @@ extern "C"
#define MB_EVENT_QUEUE_TIMEOUT_MAX_MS (3000)
#define MB_EVENT_QUEUE_TIMEOUT (pdMS_TO_TICKS(CONFIG_FMB_EVENT_QUEUE_TIMEOUT))
#define MB_EVENT_QUEUE_TIMEOUT_MAX (pdMS_TO_TICKS(MB_EVENT_QUEUE_TIMEOUT_MAX_MS))
#define MB_MS_TO_TICKS(time_ms) ((TickType_t)(((time_ms)/portTICK_PERIOD_MS)))
#define MB_MS_TO_TICKS(time_ms) (pdMS_TO_TICKS(time_ms))
int lock_obj(_lock_t *plock);
void unlock_obj(_lock_t *plock);
@ -66,29 +65,6 @@ void unlock_obj(_lock_t *plock);
spinlock_initialize(&lock); \
} while (0)
#define CRITICAL_STORE(LOCK, PTR, VAL) \
__extension__ \
({ \
__auto_type __atomic_ptr = (PTR); \
__typeof__ ((void)0, *__atomic_ptr) __atomic_tmp = (VAL); \
_lock_acquire((_lock_t *)&LOCK); \
*__atomic_ptr = __atomic_tmp; \
_lock_release((_lock_t *)&LOCK); \
(__atomic_tmp); \
})
#define CRITICAL_LOAD(LOCK, PTR) \
__extension__ \
({ \
__auto_type __atomic_ptr = (PTR); \
__typeof__ ((void)0, *__atomic_ptr) __atomic_tmp; \
_lock_acquire((_lock_t *)&LOCK); \
__atomic_tmp = (*__atomic_ptr); \
_lock_release((_lock_t *)&LOCK); \
(__atomic_tmp); \
})
#define SPIN_LOCK_ENTER(lock) \
do \
{ \

View File

@ -28,7 +28,7 @@ extern "C" {
#define MB_ASCII_SER_PDU_ADDR_OFF 0 /*!< Offset of slave address in Ser-PDU. */
#define MB_ASCII_SER_PDU_PDU_OFF 1 /*!< Offset of Modbus-PDU in Ser-PDU. */
typedef struct _port_serial_opts mb_serial_opts_t;
typedef struct port_serial_opts_s mb_serial_opts_t;
typedef struct mb_trans_base_t mb_trans_base_t;
mb_err_enum_t mbm_ascii_transp_create(mb_serial_opts_t *ser_opts, void **in_out_inst);

View File

@ -53,7 +53,7 @@ typedef enum
MB_RTU_STATE_ERROR /*!< If the frame is invalid. */
} mb_rtu_state_enum_t;
typedef struct _port_serial_opts mb_serial_opts_t;
typedef struct port_serial_opts_s mb_serial_opts_t;
typedef struct mb_trans_base_t mb_trans_base_t;
mb_err_enum_t mbm_rtu_transp_create(mb_serial_opts_t *ser_opts, void **in_out_inst);

View File

@ -105,6 +105,44 @@ static esp_err_t slave_serial_init(void **pinst)
return err;
}
mb_exception_t test_handler(void *pinst, uint8_t *frame_ptr, uint16_t *plen)
{
return MB_EX_CRITICAL; // Set the exception code for slave appropriately
}
static int check_custom_handlers(void *pinst)
{
mb_fn_handler_fp phandler = NULL;
int entry;
uint16_t count = 0;
esp_err_t err = ESP_FAIL;
err = mbc_get_handler_count(pinst, &count);
MB_RETURN_ON_FALSE((err == ESP_OK), 0, TAG,
"mbc slave get handler count, returns(0x%x).", (int)err);
ESP_LOGI(TAG,"Object %p, custom handler test, (registered:max) handlers: %d:%d.", pinst, count, CONFIG_FMB_FUNC_HANDLERS_MAX);
for (entry = 0x01; entry < CONFIG_FMB_FUNC_HANDLERS_MAX; entry++) {
// Try to remove the handler
err = mbc_delete_handler(pinst, (uint8_t)entry);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Could not remove handler for command: (0x%x), returned (0x%x), already empty?", entry, (int)err);
}
err = mbc_set_handler(pinst, (uint8_t)entry, test_handler);
if (err != ESP_OK) {
ESP_LOGE(TAG,"Could not set handler for command 0x%x, returned (0x%x).", entry, (int)err);
break;
} else {
ESP_LOGI(TAG,"Set handler for command 0x%x, returned (0x%x).", entry, (int)err);
}
err = mbc_get_handler(pinst, (uint8_t)entry, &phandler);
if (err != ESP_OK || phandler != test_handler) {
ESP_LOGE(TAG, "Could not get handler for command (0x%x) = (%p), returned (0x%x).", entry, phandler, (int)err);
break;
}
}
ESP_LOGI(TAG, "Last entry processed: %d.", entry);
return entry;
}
// Intentionally verify that atomic values are layout compatible with original types
static_assert(
sizeof(std::atomic<int>) == sizeof(int),
@ -117,12 +155,18 @@ extern "C" void app_main(void)
ESP_LOGI(TAG, "Setup master cpp....");
ESP_ERROR_CHECK(master_serial_init(&pmaster_handle));
ESP_ERROR_CHECK(mbc_master_stop(pmaster_handle));
int last_entry = check_custom_handlers(pmaster_handle);
MB_RETURN_ON_FALSE((last_entry >= CONFIG_FMB_FUNC_HANDLERS_MAX), ;, TAG,
"Incorrect number of command entries for master: %d.", (int)last_entry);
ESP_ERROR_CHECK(mbc_master_delete(pmaster_handle));
ESP_LOGI(TAG, "Master test passed successfully.");
ESP_LOGI(TAG, "Setup slave cpp....");
ESP_ERROR_CHECK(slave_serial_init(&pslave_handle));
last_entry = check_custom_handlers(pslave_handle);
// explicitly check stop method before delete
ESP_ERROR_CHECK(mbc_slave_stop(pslave_handle));
ESP_ERROR_CHECK(mbc_slave_delete(pslave_handle));
MB_RETURN_ON_FALSE((last_entry >= CONFIG_FMB_FUNC_HANDLERS_MAX), ;, TAG,
"Incorrect number of command entries for slave: %d.", (int)last_entry);
ESP_LOGI(TAG, "Slave test passed successfully.");
}

View File

@ -15,7 +15,7 @@ def test_modbus_comm_multi_dev_serial(case_tester) -> None: # typ
@pytest.mark.esp32
@pytest.mark.multi_dut_modbus_tcp
@pytest.mark.parametrize('count, config', [(2, 'wifi'), (2, 'ethernet')], indirect=True)
@pytest.mark.parametrize('count, config', [(2, 'ethernet')], indirect=True)
def test_modbus_comm_multi_dev_tcp(case_tester) -> None: # type: ignore
for case in case_tester.test_menu:
if case.attributes.get('test_env', 'multi_dut_modbus_tcp') == 'multi_dut_modbus_tcp':

View File

@ -30,7 +30,7 @@ mb_err_enum_t mb_port_event_wait_req_finish(mb_port_base_t *inst);
#if (CONFIG_FMB_COMM_MODE_ASCII_EN || CONFIG_FMB_COMM_MODE_RTU_EN)
typedef struct _port_serial_opts mb_serial_opts_t;
typedef struct port_serial_opts_s mb_serial_opts_t;
mb_err_enum_t mbs_rtu_create(mb_serial_opts_t *ser_opts, void **in_out_obj);
mb_err_enum_t mbs_ascii_create(mb_serial_opts_t *ser_opts, void **in_out_obj);
@ -93,7 +93,7 @@ mb_exception_t mbs_fn_rw_multi_holding_reg(mb_base_t *inst, uint8_t *frame_ptr,u
#if (CONFIG_FMB_COMM_MODE_TCP_EN)
typedef struct _port_tcp_opts mb_tcp_opts_t;
typedef struct port_tcp_opts_s mb_tcp_opts_t;
mb_err_enum_t mbs_tcp_create(mb_tcp_opts_t *tcp_opts, void **in_out_obj);

View File

@ -81,7 +81,7 @@ mb_err_enum_t mb_port_event_wait_req_finish(mb_port_base_t *inst);
#if (CONFIG_FMB_COMM_MODE_ASCII_EN || CONFIG_FMB_COMM_MODE_RTU_EN)
typedef struct _port_serial_opts mb_serial_opts_t;
typedef struct port_serial_opts_s mb_serial_opts_t;
mb_err_enum_t mbs_rtu_create(mb_serial_opts_t *ser_opts, void **in_out_obj);
mb_err_enum_t mbs_ascii_create(mb_serial_opts_t *ser_opts, void **in_out_obj);
@ -102,7 +102,7 @@ mb_err_enum_t mbs_set_slv_id(mb_base_t *inst, uint8_t slv_id, bool is_running, u
#if (CONFIG_FMB_COMM_MODE_TCP_EN)
typedef struct _port_tcp_opts mb_tcp_opts_t;
typedef struct port_tcp_opts_s mb_tcp_opts_t;
mb_err_enum_t mbm_tcp_create(mb_tcp_opts_t *tcp_opts, void **in_out_obj);

View File

@ -79,7 +79,7 @@ error:
#if (MB_MASTER_ASCII_ENABLED || MB_MASTER_RTU_ENABLED)
typedef struct _port_serial_opts mb_serial_opts_t;
typedef struct port_serial_opts_s mb_serial_opts_t;
mb_err_enum_t mb_stub_serial_create(mb_serial_opts_t *ser_opts, void **in_out_obj)
{

View File

@ -14,7 +14,7 @@ ${suiteConnection} None
Test Cusom Command Request
[Documentation] Test reading slave UID, running status, identificator structure (use custom frame template)
[Template] Custom Command
0x01 [0x41] 0 ${None} # Try o send shortest request for custom command, do not check the buffer
0x01 [0x41] 0 ${None} # Try to send shortest request for custom command, do not check the buffer
0x01 [0x41, 0x11, 0x22, 0x33, 0x44] 0 ${None} # Send custom data, do not compare the response buffer
0x01 [0x41, 0x11, 0x22, 0x33] 0 [17, 34, 51, 0x3A, 83, 108, 97, 118, 101] # Send the custom command and compare expected response (can use hex or dec values)