The following functions must be called when the Modbus controller slave port is already initialized. Refer to :ref:`modbus_api_port_initialization`.
The slave stack requires the user to define structures (memory storage areas) that store the Modbus parameters accessed by stack. These structures should be prepared by the user and be assigned to the Modbus controller interface using :cpp:func:`mbc_slave_set_descriptor` API call before the start of communication. The slave task can call the :cpp:func:`mbc_slave_check_event` function which will block until the Modbus master access the slave. The slave task can then get information about the data being accessed.
..note:: One slave can define several area descriptors per each type of Modbus register area with different start_offset.
Register area is defined by using the :cpp:type:`mb_register_area_descriptor_t` structure.
..list-table:: Table 3 Modbus register area descriptor
:widths:8 92
:header-rows:1
* - Field
- Description
* - ``start_offset``
- Zero based register relative offset for defined register area. Example: register address = 40002 ( 4x register area - Function 3 - holding register ), start_offset = 2
* - ``type``
- Type of the Modbus register area. Refer to :cpp:type:`mb_param_type_t` for more information.
* - ``address``
- A pointer to the memory area which is used to store the register data for this area descriptor.
* - ``size``
- The size of the memory area in bytes which is used to store register data.
:cpp:func:`mbc_slave_set_descriptor`
The function initializes Modbus communication descriptors for each type of Modbus register area (Holding Registers, Input Registers, Coils (single bit output), Discrete Inputs). Once areas are initialized and the :cpp:func:`mbc_slave_start()` API is called the Modbus stack can access the data in user data structures by request from master.
..code::c
#define MB_REG_INPUT_START_AREA0 (0)
#define MB_REG_HOLDING_START_AREA0 (0)
#define MB_REG_HOLD_CNT (100)
#define MB_REG_INPUT_CNT (100)
....
staticvoid*slave_handle=NULL;// Pointer to interface structure allocated by constructor
....
mb_register_area_descriptor_treg_area;// Modbus register area descriptor structure
unit16_tholding_reg_area[MB_REG_HOLD_CNT]={0};// storage area for holding registers
unit16_tinput_reg_area[MB_REG_INPUT_CNT]={0};// storage area for input registers
reg_area.type=MB_PARAM_HOLDING;// Set type of register area
reg_area.start_offset=MB_REG_HOLDING_START_AREA0;// Offset of register area in Modbus protocol
reg_area.address=(void*)&holding_reg_area[0];// Set pointer to storage instance
reg_area.size=(sizeof(holding_reg_area)<<1);// Set the size of register storage area in bytes!
At least one area descriptor per each Modbus register type must be set in order to provide register access to its area. If the master tries to access an undefined area, the stack will generate a Modbus exception.
The slave communication object supports initialization of special object identification structure which is vendor specific and can clarify some slave specific information for each slave object. The API functions below can be used to set and get this information from the slave object accordingly.
This information set in the slave can be retrieved by master object using the standard Modbus command `0x11 - <Report Slave ID>`.
:cpp:func:`mbc_set_slave_id`
Allows to set vendor specific slave ID for the concrete slave object.
..note:: Each slave object sets the short default identificator defined in ``CONFIG_FMB_CONTROLLER_SLAVE_ID`` Kconfig value on start. This can be overridden by this API function. The option ``CONFIG_FMB_CONTROLLER_SLAVE_ID_SUPPORT`` allows disabling this functionality and the option ``CONFIG_FMB_CONTROLLER_SLAVE_ID_MAX_SIZE`` defines the maximum size of the slave identification structure.
Example of initialization for slave ID:
..code::c
#include"mbcontroller.h" // for mbcontroller defines and api
...
staticvoid*mbc_slave_handle=NULL;
mb_communication_info_tcomm_config={
.ser_opts.port=MB_PORT_NUM,
.ser_opts.mode=MB_RTU,
.ser_opts.baudrate=MB_DEV_SPEED,
.ser_opts.parity=MB_PARITY_NONE,
.ser_opts.uid=MB_SLAVE_ADDR,
.ser_opts.data_bits=UART_DATA_8_BITS,
.ser_opts.stop_bits=UART_STOP_BITS_1
};
// Initialization of Modbus slave controller object
ESP_LOGE("SET_SLAVE_ID","Set slave ID fail, err=%d.",err);
}
...
:cpp:func:`mbc_get_slave_id`
Allows to get actual slave UID, running status of slave and vendor specific data. The default object identificator is defined by option ``CONFIG_FMB_CONTROLLER_SLAVE_ID`` as ``01 ff 33 22 11`` (slave UID, running state, extended vendor data structure) and can be overridden in user application.
Example to get the actual slave identificator:
..code::c
#include"mbcontroller.h"
#include"sdkconfig.h"
...
staticvoid*mbc_slave_handle=NULL;// the object is initialized and started
// the vendor specific part of structure for slave to be retrieved by master
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
staticvoid*slave_handle=NULL;// Pointer to allocated interface structure (must be actual)
mb_fn_handler_fppstandard_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.
"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.
ESP_ERROR_CHECK(mbc_slave_start(slave_handle));// The handle must be initialized prior to start call.
:cpp:func:`mbc_slave_check_event`
The blocking call to function waits for a event specified (represented as an event mask parameter). Once the master accesses the parameter and the event mask matches the parameter type, the application task will be unblocked and function will return the corresponding event :cpp:type:`mb_event_group_t` which describes the type of register access being done.
:cpp:func:`mbc_slave_get_param_info`
The function gets information about accessed parameters from the Modbus controller event queue. The KConfig ``CONFIG_FMB_CONTROLLER_NOTIFY_QUEUE_SIZE`` key can be used to configure the notification queue size. The timeout parameter allows a timeout to be specified when waiting for a notification. The :cpp:type:`mb_param_info_t` structure contains information about accessed parameter.
..list-table:: Table 4 Description of the register info structure: :cpp:type:`mb_param_info_t`
:widths:10 90
:header-rows:1
* - Field
- Description
* - ``time_stamp``
- the time stamp of the event when defined parameter is accessed
* - ``mb_offset``
- start Modbus register accessed by master
* - ``type``
- type of the Modbus register area being accessed (See the :cpp:type:`mb_event_group_t` for more information)
* - ``address``
- memory address that corresponds to accessed register in defined area descriptor
* - ``size``
- number of registers being accessed by master
Example to get event when holding or input registers accessed in the slave:
The direct access to slave register area from user application must be protected by critical section. The following functions can be used to protect access to the data from registered mapping area while the communication object is active.
..code::c
staticvoid*slave_handle=NULL;// communication object handle
...
(void)mbc_slave_lock(slave_handle);// ignore the returned error if the object is not actual