forked from espressif/esp-modbus
feature add custom fc handlers for master and slave v2
This commit is contained in:
1 parent
439c767aed
commit
8215e3d0d2
51 files changed
+1345
-300
No files matched your search
@@ -40,19 +40,19 @@ The examples below demonstrate the library port for serial, TCP slave and master
|
||||
|
||||
.. _example_mb_slave:
|
||||
|
||||
- `Modbus serial slave example <https://github.com/espressif/esp-modbus/tree/release/v2.0/examples/serial/mb_serial_slave>`__
|
||||
- `Modbus serial slave example <https://github.com/espressif/esp-modbus/tree/main/examples/serial/mb_serial_slave>`__
|
||||
|
||||
.. _example_mb_master:
|
||||
|
||||
- `Modbus serial master example <https://github.com/espressif/esp-modbus/tree/release/v2.0/examples/serial/mb_serial_master>`__
|
||||
- `Modbus serial master example <https://github.com/espressif/esp-modbus/tree/main/examples/serial/mb_serial_master>`__
|
||||
|
||||
.. _example_mb_tcp_master:
|
||||
|
||||
- `Modbus TCP master example <https://github.com/espressif/esp-modbus/tree/release/v2.0/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/release/v2.0/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.
|
||||
|
||||
|
||||
@@ -7,9 +7,10 @@ The following overview describes how to setup Modbus master communication. The o
|
||||
|
||||
1. :ref:`modbus_api_port_initialization` - Initialization of Modbus controller interface for the selected port.
|
||||
2. :ref:`modbus_api_master_configure_descriptor` - Configure data descriptors to access slave parameters.
|
||||
3. :ref:`modbus_api_master_setup_communication_options` - Allows to setup communication options for selected port.
|
||||
4. :ref:`modbus_api_master_start_communication` - Start stack and sending / receiving data.
|
||||
5. :ref:`modbus_api_master_destroy` - Destroy Modbus controller and its resources.
|
||||
3. :ref:`modbus_api_master_handler_customization` - Customization of Modbus function handling.
|
||||
4. :ref:`modbus_api_master_setup_communication_options` - Allows to setup communication options for selected port.
|
||||
5. :ref:`modbus_api_master_start_communication` - Start stack and sending / receiving data.
|
||||
6. :ref:`modbus_api_master_destroy` - Destroy Modbus controller and its resources.
|
||||
|
||||
.. _modbus_api_master_configure_descriptor:
|
||||
|
||||
@@ -305,6 +306,120 @@ Initialization of master descriptor. The descriptor represents an array of type
|
||||
The Data Dictionary can be initialized from SD card, MQTT or other source before start of stack. Once the initialization and setup is done, the Modbus controller allows the reading of complex parameters from any slave included in descriptor table using its CID.
|
||||
Refer to :ref:`example TCP master <example_mb_tcp_master>`, :ref:`example Serial master <example_mb_master>` for more information.
|
||||
|
||||
.. _modbus_api_master_handler_customization:
|
||||
|
||||
Master Customize Function Handlers
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The Master object contains the command handling table to define specific handling functionality for each supported Modbus command. The default handling functions in this table support the most common Modbus commands. However, the list of commands can be extended by adding a new command into the handling table with its custom handling behavior. It is also possible to override the function handler for a specific command. The below described API functions allow using this behavior for Master objects.
|
||||
|
||||
:cpp:func:`mbc_set_handler`
|
||||
|
||||
The function adds new handler for the function or overrides the existing handler for the function.
|
||||
|
||||
: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
|
||||
const uint8_t override_command = 0x04;
|
||||
mb_fn_handler_fp pstandard_handler = NULL;
|
||||
....
|
||||
// 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_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;
|
||||
}
|
||||
....
|
||||
// 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 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_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.
|
||||
|
||||
.. code:: c
|
||||
|
||||
#define MB_CUST_DATA_LEN 100
|
||||
static char my_custom_data[MB_CUST_DATA_LEN] = {0}; // custom data buffer for the request
|
||||
static void *master_handle = NULL; // Pointer to allocated interface structure
|
||||
|
||||
// This is the custom function handler to process incoming slave response.
|
||||
// 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_ILLEGAL_DATA_VALUE, TAG,
|
||||
"incorrect custom frame buffer");
|
||||
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_INFO);
|
||||
return MB_EX_NONE;
|
||||
}
|
||||
....
|
||||
// The setup of the master object is completed and the master_handle is already actual
|
||||
|
||||
// Add custom command 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_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);
|
||||
|
||||
char *pcustom_string = "Master"; // The custom request string that will be sent to the slave
|
||||
mb_param_request_t req = {
|
||||
.slave_addr = MB_DEVICE_ADDR1, // the slave UID to send the request
|
||||
.command = 0x41, // the custom function code,
|
||||
.reg_start = 0, // unused,
|
||||
.reg_size = (strlen(pcustom_string) >> 1) // length of the data to send (registers)
|
||||
};
|
||||
|
||||
// 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
|
||||
...
|
||||
}
|
||||
|
||||
Refer to :ref:`example Serial master <example_mb_master>` for more information.
|
||||
|
||||
.. _modbus_api_master_start_communication:
|
||||
|
||||
Master Communication
|
||||
|
||||
@@ -276,7 +276,7 @@ The below diagrams show how the extended data types appear on network layer.
|
||||
|
||||
The approach showed above can be used to pack the data into MBAP frames used by Modbus TCP as well as for other types with similar size.
|
||||
|
||||
The following sections give an overview of how to use the ESP_Modbus component found under `components/freemodbus`. The sections cover initialization of a Modbus port, and the setup a master or slave device accordingly:
|
||||
The following sections give an overview of how to use the ESP_Modbus component found under `components/esp-modbus`. The sections cover initialization of a Modbus port, and the setup a master or slave device accordingly:
|
||||
|
||||
- :ref:`modbus_api_port_initialization`
|
||||
- :ref:`modbus_api_slave_overview`
|
||||
|
||||
@@ -7,9 +7,10 @@ The sections below represent typical programming workflow for the slave API whic
|
||||
|
||||
1. :ref:`modbus_api_port_initialization` - Initialization of Modbus controller interface using communication options.
|
||||
2. :ref:`modbus_api_slave_configure_descriptor` - Configure data descriptors to access slave parameters.
|
||||
3. :ref:`modbus_api_slave_setup_communication_options` - Allows to setup communication options for selected port.
|
||||
4. :ref:`modbus_api_slave_communication` - Start stack and sending / receiving data. Filter events when master accesses the register areas.
|
||||
5. :ref:`modbus_api_slave_destroy` - Destroy Modbus controller and its resources.
|
||||
3. :ref:`modbus_api_slave_handler_customization` - Customization of Modbus function handling in slave object.
|
||||
4. :ref:`modbus_api_slave_setup_communication_options` - Allows to setup communication options for selected port.
|
||||
5. :ref:`modbus_api_slave_communication` - Start stack and sending / receiving data. Filter events when master accesses the register areas.
|
||||
6. :ref:`modbus_api_slave_destroy` - Destroy Modbus controller and its resources.
|
||||
|
||||
.. _modbus_api_slave_configure_descriptor:
|
||||
|
||||
@@ -161,6 +162,72 @@ Example to get the actual slave identificator:
|
||||
}
|
||||
...
|
||||
|
||||
.. _modbus_api_slave_handler_customization:
|
||||
|
||||
Slave Customize Function Handlers
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The Slave object contains the command handling table to define specific handling functionality for each supported Modbus command. The default handling functions in this table support the most common Modbus commands. However, the list of commands can be extended by adding a new command into the handling table with its custom handling behavior. It is also possible to override the function handler for a specific command. The below described API functions allow using this behavior for Slave objects.
|
||||
|
||||
:cpp:func:`mbc_set_handler`
|
||||
|
||||
The function adds new handler for the function or overrides the existing handler for the function.
|
||||
|
||||
: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;
|
||||
....
|
||||
// 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");
|
||||
// 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;
|
||||
}
|
||||
...
|
||||
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_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;
|
||||
// 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);
|
||||
|
||||
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:
|
||||
|
||||
Slave Communication
|
||||
|
||||
Reference in new issue
Block a user