The following overview describes how to setup Modbus master communication. The overview reflects a typical programming workflow and is broken down into the sections provided below:
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.
The architectural approach of ESP_Modbus includes one level above standard Modbus IO driver. The additional layer is called Modbus controller and its goal is to add an abstraction such as CID - characteristic identifier. The CID is linked to a corresponding Modbus registers through the table called Data Dictionary and represents device physical parameter (such as temperature, humidity, etc.) in specific Modbus slave device. This approach allows the upper layer (e.g., MESH or MQTT) to be isolated from Modbus specifics thus simplify Modbus integration with other protocols/networks.
The Data Dictionary is the list in the Modbus master which shall be defined by user to link each CID to its corresponding Modbus registers representation using Register Mapping table of the Modbus slave being used.
Each element in this data dictionary is of type :cpp:type:`mb_parameter_descriptor_t` and represents the description of one physical characteristic:
..list-table:: Table 1 Modbus master Data Dictionary description
:widths:8 10 82
:header-rows:1
* - Field
- Description
- Detailed information
* - ``cid``
- Characteristic ID
- The identifier of characteristic (must be unique).
* - ``param_key``
- Characteristic Name
- String description of the characteristic.
* - ``param_units``
- Characteristic Units
- Physical Units of the characteristic.
* - ``mb_slave_addr``
- Modbus Slave Address
- The short address of the device with correspond parameter UID.
* - ``mb_param_type``
- Modbus Register Type
- Type of Modbus register area.
:cpp:enumerator:`MB_PARAM_INPUT`, :cpp:enumerator:`MB_PARAM_HOLDING`, :cpp:enumerator:`MB_PARAM_COIL`, :cpp:enumerator:`MB_PARAM_DISCRETE` - represents Input , Holding, Coil and Discrete input register area accordingly;
* - ``mb_reg_start``
- Modbus Register Start
- Relative register address of the characteristic in the register area.
- The storage size of the characteristic (in bytes) describes the size of data to keep into data instance during mapping. For the :ref:`modbus_mapping_complex_data_types` this allows to define the data container of the corresponded type.
..note:: The ``cid`` and ``param_key`` have to be unique. Please use the prefix to the parameter key if you have several similar parameters in your register map table.
..list-table:: Table 2 Example Register mapping table of Modbus slave
:widths:5 5 2 10 5 5 68
:header-rows:1
* - CID
- Register
- Length
- Range
- Type
- Units
- Description
* - 0
- 30000
- 4
- MAX_UINT
- U32
- Not defined
- Serial number of device (4 bytes) read-only
* - 1
- 30002
- 2
- MAX_UINT
- U16
- Not defined
- Software version (4 bytes) read-only
* - 2
- 40000
- 4
- -20..40
- FLOAT
- DegC
- Room temperature in DegC. Writing a temperature value to this register for single point calibration.
* - 3
- 40002
- 16
- 1..100 bytes
- ASCII or binary array
- Not defined
- Device name (16 bytes) ASCII string. The type of `PARAM_TYPE_ASCII` allows to read/write complex parameter (string or binary data) that corresponds to one CID.
The example above describes the definition of just several extended types. The types described in the :ref:`modbus_mapping_complex_data_types` allow to address the most useful value formats from devices of known third-party vendors.
Once the type of characteristic is defined in data dictionary the stack is responsible for conversion of values to/from the corresponding type option into the format recognizable by compiler.
..note:: Please refer to your vendor device manual and its mapping table to select the types suitable for your device.
The Modbus stack contains also the :ref:`modbus_api_endianness_conversion` - endianness conversion API functions that allow to convert values from/to each extended type into compiler representation.
During initialization of the Modbus stack, a pointer to the Data Dictionary (called descriptor) must be provided as the parameter of the function below.
:cpp:func:`mbc_master_set_descriptor`:
Initialization of master descriptor. The descriptor represents an array of type :cpp:type:`mb_parameter_descriptor_t` and describes all the characteristics accessed by master.
..code::c
staticvoid*master_handle=NULL;// Must exist in the module and be initialized prior to call
....
// Set master data dictionary for initialized master instance - master_handle
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.
The list of functions below are used by the Modbus master stack from a user's application:
:cpp:func:`mbc_master_send_request`:
This function executes a blocking Modbus request. The master sends a data request (as defined in parameter request structure :cpp:type:`mb_param_request_t`) and then blocks until a response from corresponding slave and returns the status of command execution. This function provides a standard way for read/write access to Modbus devices in the network.
..note:: The function can be used to form the custom request with non-standard commands to resolve compatibility issues with the custom slaves. If it is not the case the regular API should be used: :cpp:func:`mbc_master_set_parameter`, :cpp:func:`mbc_master_get_parameter`.
:cpp:func:`mbc_master_get_cid_info`:
The function gets information about each characteristic supported in the data dictionary and returns the characteristic's description in the form of the :cpp:type:`mb_parameter_descriptor_t` structure. Each characteristic is accessed using its CID.
:cpp:func:`mbc_master_get_parameter`
The function reads the data of a characteristic defined in the parameters of a Modbus slave device. The additional data for request is taken from parameter description table.
:cpp:func:`mbc_master_get_parameter_with`
The function allows to read the data of a characteristic from any slave device addressed by `uid` parameter of the function instead of slave address defined in the data dictionary. In this case the ``mb_slave_addr`` field of the parameter descriptor :cpp:type:`mb_parameter_descriptor_t` shall be equal to ``MB_SLAVE_ADDR_PLACEHOLDER``. In case of TCP type of communication the connection phase should be completed prior call of this function.
ESP_LOGE(TAG,"Could not get information for characteristic %d.",cid);
}
:cpp:func:`mbc_master_set_parameter`
The function writes characteristic's value defined as `cid` parameter in corresponded slave device. The additional data for parameter request is taken from master parameter description table.
:cpp:func:`mbc_master_set_parameter_with`
The function is similar to previous function but allows to set the data of a characteristic in any slave device addressed by `uid` parameter of the function instead of the slave address ``mb_slave_addr`` field defined in the data dictionary. The corresponded ``mb_slave_addr`` field for the characteristic in the object disctionary shall be defined as ``MB_SLAVE_ADDR_PLACEHOLDER``.
..note:: When the TCP mode of communication is used the functions above additionally check the connection state of the slave being accessed and return error if the slave connection is not actual.
ESP_LOGE(TAG,"Set data fail, err = 0x%x (%s).",(int)err,(char*)esp_err_to_name(err));
}
The master supports the <0x11 - Report Slave ID> Modbus command to read vendor specific information from the slave. It uses the :cpp:func:`mbc_master_send_request` function to send request.
The example to retrieve the slave identificator from slave:
..code::c
#define MB_DEVICE_ADDR1 1 // the slave UID to retrieve information
...
staticvoid*master_handle=NULL;// the master handler is initialized previously
...
// Set the request stucture for the master to send the <Report Slave ID> command
mb_param_request_treq={
.slave_addr=MB_DEVICE_ADDR1,// the UID of the device to get the information,
.command=0x11,// the <Report Slave ID> command,
.reg_start=0,// is obsolete, need to be zero for this request,
.reg_size=(CONFIG_FMB_CONTROLLER_SLAVE_ID_MAX_SIZE>>1)// size of the buffer in registers to save ID