Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions docs/en/port_initialization.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ The ESP_Modbus supports Modbus SERIAL and TCP communication objects and an objec

- :cpp:func:`mbc_slave_create_serial`
- :cpp:func:`mbc_master_create_serial`
- :cpp:func:`mbc_slave_create_serial_with_transport`
- :cpp:func:`mbc_master_create_serial_with_transport`
- :cpp:func:`mbc_master_create_tcp`
- :cpp:func:`mbc_slave_create_tcp`

Expand Down Expand Up @@ -40,6 +42,49 @@ Calling the constructor function allows to create communication object with the

Refer to :ref:`modbus_api_master_setup_communication_options` and :ref:`modbus_api_slave_setup_communication_options` for more information on how to configure communication options for the master and slave object accordingly.

Custom RTU Transport
^^^^^^^^^^^^^^^^^^^^

The serial master and slave constructors normally create the standard RTU
transport and serial port. Applications that need to provide their own RTU
framing, serial I/O, or routing can instead use
:cpp:func:`mbc_master_create_serial_with_transport` or
:cpp:func:`mbc_slave_create_serial_with_transport`. These constructors accept
a factory which creates an initialized ``mb_trans_base_t`` transport.

The custom factory path applies only when ``config.ser_opts.mode`` is
``MB_RTU``. ASCII mode continues to use the standard transport. A factory
returns the initialized transport through its output argument and must set the
transport's ``port_obj``. It must also implement the transport callbacks used
by the controller. The controller takes ownership of a successfully returned
transport and calls its ``frm_delete`` callback during cleanup.

The following slave example shows the factory call. The master equivalent uses
``mbc_master_create_serial_with_transport()`` and
``mbc_master_transport_factory_t``.

.. code:: c

static mb_err_enum_t custom_slave_transport_factory(
const mbc_slave_transport_factory_args_t *args,
mb_trans_base_t **transport)
{
// Create an initialized custom RTU transport using args->comm_info
// and, when needed, args->user_ctx.
return my_rtu_transport_create(args, transport);
}

static void *slave_handle = NULL;
ESP_ERROR_CHECK(mbc_slave_create_serial_with_transport(
&config, custom_slave_transport_factory, my_transport_context,
&slave_handle));

The factory receives the requested communication configuration, the owning
controller object, and the caller-defined context. The caller retains ownership
of the context itself unless the custom transport documents otherwise. Pass
``NULL`` as the factory to use the standard RTU transport; the ordinary
``mbc_*_create_serial()`` constructors already do this.

.. _modbus_api_master_setup_communication_options:

Master Communication Options
Expand Down
10 changes: 10 additions & 0 deletions examples/serial/mb_serial_slave/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,16 @@ Set ```Modbus slave address``` for the example application (by default for examp
The communication parameters of esp-modbus stack (Component config->Modbus configuration) allow to configure it appropriately but usually it is enough to use default settings.
See the help strings of parameters for more information.

### Custom RTU transport factory

Enable `Create the RTU slave with a transport factory` in the `Modbus Example
Configuration` menu to exercise `mbc_slave_create_serial_with_transport()`.
The example factory delegates to the stock RTU transport, so its on-wire behavior
is unchanged. It shows the construction boundary where an application can supply
its own initialized `mb_trans_base_t` implementation instead, for example to own
serial I/O or route complete RTU frames. A custom transport must set its
`port_obj` and implement the transport callbacks required by the controller.

### Setup external Modbus master software
Option 1:
Configure the external Modbus master software according to port configuration parameters used in application.
Expand Down
16 changes: 15 additions & 1 deletion examples/serial/mb_serial_slave/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
set(priv_include_dirs)

if(CONFIG_MB_USE_TRANSPORT_FACTORY)
# The factory example delegates to the stock RTU transport, whose constructor
# and transitive headers are intentionally internal to the esp-modbus component.
list(APPEND priv_include_dirs
"../../../../modbus/mb_transports/rtu"
"../../../../modbus/mb_transports"
"../../../../modbus/mb_objects/include"
"../../../../modbus/mb_objects/common"
"../../../../modbus/mb_ports/common")
endif()

idf_component_register(SRCS "serial_slave.c"
INCLUDE_DIRS ".")
INCLUDE_DIRS "."
PRIV_INCLUDE_DIRS "${priv_include_dirs}")

set(PROJECT_NAME "modbus_serial_slave")

Expand Down
10 changes: 10 additions & 0 deletions examples/serial/mb_serial_slave/main/Kconfig.projbuild
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,16 @@ menu "Modbus Example Configuration"

endchoice

config MB_USE_TRANSPORT_FACTORY
bool "Create the RTU slave with a transport factory"
default n
depends on MB_COMM_MODE_RTU
help
Demonstrate mbc_slave_create_serial_with_transport(). The example
factory delegates to the stock RTU transport. Replace that factory
with an application transport implementation to customize framing,
serial I/O, or routing.

config MB_SLAVE_ADDR
int "Modbus slave address"
range 1 247
Expand Down
42 changes: 40 additions & 2 deletions examples/serial/mb_serial_slave/main/serial_slave.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2016-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2016-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand All @@ -16,6 +16,37 @@
#define MB_SLAVE_ADDR (CONFIG_MB_SLAVE_ADDR) // The address of device in Modbus network
#define MB_DEV_SPEED (CONFIG_MB_UART_BAUD_RATE) // The communication speed of the UART

#if CONFIG_MB_USE_TRANSPORT_FACTORY
#include "rtu_transport.h" // for stock RTU transport factory delegate
/**
* @brief Create the standard RTU transport through the custom factory interface.
*
* @param[in] args Controller-provided communication settings.
* @param[out] transport Initialized RTU transport.
*
* @return `MB_ENOERR` on success; another `mb_err_enum_t` value on failure.
*/
static mb_err_enum_t serial_slave_transport_factory(const mbc_slave_transport_factory_args_t *args,
mb_trans_base_t **transport)
{
mb_serial_opts_t serial_opts = args->comm_info->ser_opts;
mb_port_base_t port_parent = {
.descr = {
.parent_name = "factory_example",
.obj_name = "factory_example",
.parent = args->parent,
.is_master = false,
},
};
void *transport_instance = &port_parent;
mb_err_enum_t err = mbs_rtu_transp_create(&serial_opts, &transport_instance);
if (err == MB_ENOERR) {
*transport = transport_instance;
}
return err;
}
#endif

// Note: Some pins on target chip cannot be assigned for UART communication.
// Please refer to documentation for selected board and target to configure pins using Kconfig.

Expand Down Expand Up @@ -198,7 +229,14 @@ void app_main(void)
.ser_opts.stop_bits = UART_STOP_BITS_1
};

ESP_ERROR_CHECK(mbc_slave_create_serial(&comm_config, &mbc_slave_handle)); // Initialization of Modbus controller
#if CONFIG_MB_USE_TRANSPORT_FACTORY
ESP_ERROR_CHECK(mbc_slave_create_serial_with_transport(&comm_config,
serial_slave_transport_factory,
NULL,
&mbc_slave_handle));
#else
ESP_ERROR_CHECK(mbc_slave_create_serial(&comm_config, &mbc_slave_handle));
#endif

const uint8_t custom_command = 0x41; // The custom command to be sent to slave
// Try to delete the handler for specified command.
Expand Down
14 changes: 11 additions & 3 deletions modbus/mb_controller/common/esp_modbus_master_serial.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2016-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand All @@ -16,14 +16,17 @@
/**
* Initialization of Modbus master serial
*/
esp_err_t mbc_master_create_serial(mb_communication_info_t *config, void **ctx)
esp_err_t mbc_master_create_serial_with_transport(mb_communication_info_t *config,
mbc_master_transport_factory_t factory,
void *user_ctx,
void **ctx)
{
void *obj = NULL;
esp_err_t error = ESP_ERR_NOT_SUPPORTED;
switch (config->mode) {
case MB_RTU:
case MB_ASCII:
error = mbc_serial_master_create(config, &obj);
error = mbc_serial_master_create_with_transport(config, factory, user_ctx, &obj);
break;
default:
return ESP_ERR_NOT_SUPPORTED;
Expand All @@ -34,4 +37,9 @@ esp_err_t mbc_master_create_serial(mb_communication_info_t *config, void **ctx)
return error;
}

esp_err_t mbc_master_create_serial(mb_communication_info_t *config, void **ctx)
{
return mbc_master_create_serial_with_transport(config, NULL, NULL, ctx);
}

#endif
14 changes: 11 additions & 3 deletions modbus/mb_controller/common/esp_modbus_slave_serial.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2016-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand All @@ -17,15 +17,18 @@
/**
* Initialization of Modbus Serial slave controller
*/
esp_err_t mbc_slave_create_serial(mb_communication_info_t *config, void **ctx)
esp_err_t mbc_slave_create_serial_with_transport(mb_communication_info_t *config,
mbc_slave_transport_factory_t factory,
void *user_ctx,
void **ctx)
{
void *obj = NULL;
esp_err_t error = ESP_ERR_NOT_SUPPORTED;
switch (config->mode) {
case MB_RTU:
case MB_ASCII:
// Call constructor function of actual port implementation
error = mbc_serial_slave_create(config, &obj);
error = mbc_serial_slave_create_with_transport(config, factory, user_ctx, &obj);
break;
default:
return ESP_ERR_NOT_SUPPORTED;
Expand All @@ -37,4 +40,9 @@ esp_err_t mbc_slave_create_serial(mb_communication_info_t *config, void **ctx)
return error;
}

esp_err_t mbc_slave_create_serial(mb_communication_info_t *config, void **ctx)
{
return mbc_slave_create_serial_with_transport(config, NULL, NULL, ctx);
}

#endif
45 changes: 45 additions & 0 deletions modbus/mb_controller/common/include/esp_modbus_master.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#include "soc/soc.h" // for BITN definitions
#include "esp_modbus_common.h" // for common types

typedef struct mb_trans_base_t mb_trans_base_t;

#ifdef __cplusplus
extern "C" {
#endif
Expand Down Expand Up @@ -223,6 +225,49 @@ esp_err_t mbc_master_create_tcp(mb_communication_info_t *config, void **ctx);
*/
esp_err_t mbc_master_create_serial(mb_communication_info_t *config, void **ctx);

/**
* @brief Arguments supplied when constructing a custom serial master transport.
*
* The factory must return an initialized RTU transport whose `port_obj` is set.
* The transport implementation owns `user_ctx`; the controller does not retain
* or delete it directly.
*/
typedef struct {
const mb_communication_info_t *comm_info; /*!< Requested serial configuration. */
void *parent; /*!< Owning controller object. */
void *user_ctx; /*!< Caller-defined factory context. */
} mbc_master_transport_factory_args_t;

/**
* @brief Factory used to construct a custom RTU master transport.
*
* @param[in] args Factory arguments supplied by the controller.
* @param[out] transport Initialized transport instance on success.
*
* @return `MB_ENOERR` on success; another `mb_err_enum_t` value on failure.
*/
typedef mb_err_enum_t (*mbc_master_transport_factory_t)(
const mbc_master_transport_factory_args_t *args, mb_trans_base_t **transport);

/**
* @brief Initialize a serial Modbus master controller with a caller-provided RTU transport.
*
* Passing a factory bypasses the stock serial port and RTU transport creation.
* The supplied factory is used only for `MB_RTU`; ASCII uses the stock transport.
*
* @param[in] config Pointer to the master communication configuration.
* @param[in] factory Custom RTU transport factory, or `NULL` for the stock transport.
* @param[in] user_ctx Caller-defined context passed to `factory`.
* @param[out] ctx Initialized controller context.
*
* @return ESP_OK on success; an ESP-IDF error code otherwise.
*/
esp_err_t mbc_master_create_serial_with_transport(
mb_communication_info_t *config,
mbc_master_transport_factory_t factory,
void *user_ctx,
void **ctx);

/**
* @brief Deletes Modbus controller and stack engine
*
Expand Down
47 changes: 46 additions & 1 deletion modbus/mb_controller/common/include/esp_modbus_slave.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2016-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2016-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand All @@ -14,6 +14,8 @@
#include "freertos/event_groups.h" // for event groups
#include "esp_modbus_common.h" // for common types

typedef struct mb_trans_base_t mb_trans_base_t;

#ifdef __cplusplus
extern "C" {
#endif
Expand Down Expand Up @@ -103,6 +105,49 @@ esp_err_t mbc_slave_create_tcp(mb_communication_info_t *config, void **ctx);
*/
esp_err_t mbc_slave_create_serial(mb_communication_info_t *config, void **ctx);

/**
* @brief Arguments supplied when constructing a custom serial slave transport.
*
* The factory must return an initialized RTU transport whose `port_obj` is set.
* The transport implementation owns `user_ctx`; the controller does not retain
* or delete it directly.
*/
typedef struct {
const mb_communication_info_t *comm_info; /*!< Requested serial configuration. */
void *parent; /*!< Owning controller object. */
void *user_ctx; /*!< Caller-defined factory context. */
} mbc_slave_transport_factory_args_t;

/**
* @brief Factory used to construct a custom RTU slave transport.
*
* @param[in] args Factory arguments supplied by the controller.
* @param[out] transport Initialized transport instance on success.
*
* @return `MB_ENOERR` on success; another `mb_err_enum_t` value on failure.
*/
typedef mb_err_enum_t (*mbc_slave_transport_factory_t)(
const mbc_slave_transport_factory_args_t *args, mb_trans_base_t **transport);

/**
* @brief Initialize a serial Modbus slave controller with a caller-provided RTU transport.
*
* Passing a factory bypasses the stock serial port and RTU transport creation.
* The supplied factory is used only for `MB_RTU`; ASCII uses the stock transport.
*
* @param[in] config Pointer to the slave communication configuration.
* @param[in] factory Custom RTU transport factory, or `NULL` for the stock transport.
* @param[in] user_ctx Caller-defined context passed to `factory`.
* @param[out] ctx Initialized controller context.
*
* @return ESP_OK on success; an ESP-IDF error code otherwise.
*/
esp_err_t mbc_slave_create_serial_with_transport(
mb_communication_info_t *config,
mbc_slave_transport_factory_t factory,
void *user_ctx,
void **ctx);

/**
* @brief Initialize Modbus Slave controller interface handle
*
Expand Down
Loading