### Modbus Serial Master Example Source: https://github.com/espressif/esp-modbus/blob/main/docs/en/applications_and_references.rst Link to the example demonstrating the Modbus serial master implementation. ```APIDOC ## Modbus Serial Master Example This example demonstrates the Modbus serial master implementation. ### Endpoint N/A (Example Link) ### Usage Refer to the README.md file in the example directory for detailed instructions. ### Link - `Modbus serial master example `__ ``` -------------------------------- ### Initialize Modbus TCP Slave Source: https://github.com/espressif/esp-modbus/blob/main/docs/en/port_initialization.rst Configure and create a Modbus TCP slave instance. This example binds to any available IP address. Ensure the `esp_netif` component is initialized for network interface setup. ```c #define MB_SLAVE_ADDR 1 #define MB_TCP_PORT_NUMBER 1502 static void* slave_handle = NULL; .... mb_communication_info_t tcp_slave_config = { .tcp_opts.port = MB_TCP_PORT_NUMBER, // communication port number for Modbus slave .tcp_opts.mode = MB_TCP, // mode of communication for slave .tcp_opts.addr_type = MB_IPV4, // type of addressing being used .tcp_opts.ip_addr_table = NULL, // Bind to any address .tcp_opts.ip_netif_ptr = (void*)get_example_netif(),// the pointer to netif interface .tcp_opts.uid = MB_SLAVE_ADDR // Modbus slave Unit Identifier }; esp_err_t err = mbc_slave_create_tcp(&tcp_slave_config, &slave_handle); if (slave_handle == NULL || err != ESP_OK) { ESP_LOGE(TAG, "mb controller initialization fail."); } ``` -------------------------------- ### Modbus TCP Master Example Source: https://github.com/espressif/esp-modbus/blob/main/docs/en/applications_and_references.rst Link to the example demonstrating the Modbus TCP master implementation. ```APIDOC ## Modbus TCP Master Example This example demonstrates the Modbus TCP master implementation. ### Endpoint N/A (Example Link) ### Usage Refer to the README.md file in the example directory for detailed instructions. ### Link - `Modbus TCP master example `__ ``` -------------------------------- ### Include ESP-IDF Project Setup Source: https://github.com/espressif/esp-modbus/blob/main/test_apps/unit_tests/mb_ext_types/CMakeLists.txt Includes the main project setup script provided by the ESP-IDF build system. This is essential for ESP-IDF projects. ```cmake include($ENV{IDF_PATH}/tools/cmake/project.cmake) ``` -------------------------------- ### Modbus TCP Slave Example Source: https://github.com/espressif/esp-modbus/blob/main/docs/en/applications_and_references.rst Link to the example demonstrating the Modbus TCP slave implementation. ```APIDOC ## Modbus TCP Slave Example This example demonstrates the Modbus TCP slave implementation. ### Endpoint N/A (Example Link) ### Usage Refer to the README.md file in the example directory for detailed instructions. ### Link - `Modbus TCP slave example `__ ``` -------------------------------- ### Modbus Serial Slave Example Source: https://github.com/espressif/esp-modbus/blob/main/docs/en/applications_and_references.rst Link to the example demonstrating the Modbus serial slave implementation. ```APIDOC ## Modbus Serial Slave Example This example demonstrates the Modbus serial slave implementation. ### Endpoint N/A (Example Link) ### Usage Refer to the README.md file in the example directory for detailed instructions. ### Link - `Modbus serial slave example `__ ``` -------------------------------- ### Build and Flash ESP-IDF Project Source: https://github.com/espressif/esp-modbus/blob/main/examples/tcp/mb_tcp_master/README.md Build the project, flash it to the development board, and start the serial monitor to view output. Use Ctrl+] to exit the monitor. ```bash idf.py -p PORT flash monitor ``` -------------------------------- ### Modbus Slave Output Example Source: https://github.com/espressif/esp-modbus/blob/main/examples/serial/README.md Example output from the Modbus slave device, showing initialization and continuous holding register reads. ```text I (343) SLAVE_TEST: Modbus slave stack initialized. I (343) SLAVE_TEST: Start modbus test... I (81463) SLAVE_TEST: HOLDING READ (81150420 us), ADDR:1, TYPE:2, INST_ADDR:0x3ffb2868, SIZE:6 I (82463) SLAVE_TEST: HOLDING READ (82150720 us), ADDR:1, TYPE:2, INST_ADDR:0x3ffb2868, SIZE:6 I (83573) SLAVE_TEST: HOLDING READ (83260630 us), ADDR:1, TYPE:2, INST_ADDR:0x3ffb2868, SIZE:6 I (84603) SLAVE_TEST: HOLDING READ (84290530 us), ADDR:1, TYPE:2, INST_ADDR:0x3ffb2868, SIZE:6 I (85703) SLAVE_TEST: HOLDING READ (85396692 us), ADDR:1, TYPE:2, INST_ADDR:0x3ffb2868, SIZE:6 ``` -------------------------------- ### Modbus Master Output Example Source: https://github.com/espressif/esp-modbus/blob/main/examples/serial/README.md Example output from the Modbus master device, showing successful reads and subsequent read failures. ```text I (399) MASTER_TEST: Modbus master stack initialized... I (499) MASTER_TEST: Start modbus test... I (549) MASTER_TEST: Characteristic #0 Data_channel_0 (Volts) value = 1.230000 (0x3f9d70a4) read successful. I (629) MASTER_TEST: Characteristic #1 Humidity_1 (%rH) value = 12.100000 (0x4141999a) read successful. I (709) MASTER_TEST: Characteristic #2 Temperature_1 (C) value = 3.560000 (0x4063d70a) read successful. I (769) MASTER_TEST: Characteristic #3 Humidity_2 (%rH) value = 23.400000 (0x41bb3333) read successful. I (829) MASTER_TEST: Characteristic #4 Temperature_2 (C) value = 5.890000 (0x40bc7ae1) read successful. I (889) MASTER_TEST: Characteristic #5 Humidity_3 (%rH) value = 34.500000 (0x420a0000) read successful. E (949) MB_CONTROLLER_MASTER: mbc_master_get_parameter(111): SERIAL master get parameter failure error=(0x108) (ESP_ERR_INVALID_RESPONSE). E (949) MASTER_TEST: Characteristic #6 (RelayP1) read fail, err = 264 (ESP_ERR_INVALID_RESPONSE). E (1029) MB_CONTROLLER_MASTER: mbc_master_get_parameter(111): SERIAL master get parameter failure error=(0x108) (ESP_ERR_INVALID_RESPONSE). E (1029) MASTER_TEST: Characteristic #7 (RelayP2) read fail, err = 264 (ESP_ERR_INVALID_RESPONSE). ``` -------------------------------- ### Master Communication Options Setup Source: https://github.com/espressif/esp-modbus/blob/main/docs/en/port_initialization.rst Configuration details for setting up Modbus master communication, including serial and TCP options. ```APIDOC ## Master Communication Options Setup ### Description This section details the configuration structures and parameters required to initialize Modbus master controllers for both serial and TCP communication. ### Serial Master Configuration Example This example demonstrates the configuration for a Modbus serial master in RTU mode. ```c #define MB_PORT_NUM 2 #define MB_DEV_SPEED 115200 static void *master_handle = NULL; .... // Initialize Modbus controller mb_communication_info_t config = { .ser_opts.port = MB_PORT_NUM, // master communication port number .ser_opts.mode = MB_RTU, // mode of Modbus communication (MB_RTU, MB_ASCII) .ser_opts.baudrate = MB_DEV_SPEED, // baud rate of the port .ser_opts.parity = MB_PARITY_NONE, // parity option for the port .ser_opts.uid = 0, // unused for master .ser_opts.response_tout_ms = 1000, // slave response time for master (if = 0, taken from default config) .ser_opts.data_bits = UART_DATA_8_BITS, // number of data bits for communication port .ser_opts.stop_bits = UART_STOP_BITS_1 // number of stop bits for the communication port }; esp_err_t err = mbc_master_create_serial(&config, &master_handle); if (master_handler == NULL || err != ESP_OK) { ESP_LOGE(TAG, "mb controller initialization fail."); } ``` ### TCP Master Configuration Example This example shows the configuration for a Modbus TCP master, including the slave IP address table. ```c // This is public pointer for the module and used by master // to resolve slave addresses and reconnect when connection is broken static char *slave_ip_address_table[] = { "01;mb_slave_tcp_01;502", // Define the slave using mdns host name ("mb_slave_tcp_01") with UID = 01 and communication port 502 // ... other slave definitions }; // Further TCP master configuration would follow here, including passing the slave_ip_address_table to the creation function. ``` ### Notes - RS485 communication requires additional UART specific API calls for mode and pin setup. - MDNS integration is enabled by default (`CONFIG_FMB_MDNS_INTEGRATION_ENABLE`) for resolving node names. It can be disabled to use IP addresses or handle resolution manually. ``` -------------------------------- ### Define Extra Component Directories Source: https://github.com/espressif/esp-modbus/blob/main/test_apps/unit_tests/mb_ext_types/CMakeLists.txt Sets up additional directories where CMake should look for project components. This example conditionally includes test utility components based on the ESP-IDF version. ```cmake set(EXTRA_COMPONENT_DIRS) # The workaround for the test_utils under ESP-IDF v6.0 if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER "5.5") list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/test_apps/components") else() list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components") endif() ``` -------------------------------- ### Start Modbus Master Communication Source: https://github.com/espressif/esp-modbus/blob/main/docs/en/master_api_overview.rst This snippet shows how to start the Modbus master controller to enable communication. It calls the `mbc_master_start` function and includes basic error handling. ```c static void *master_handle = NULL; // Pointer to allocated interface structure .... esp_err_t err = mbc_master_start(); if (err != ESP_OK) { ESP_LOGE(TAG, "mb controller start fail, err = 0x%x.", (int)err); } ``` -------------------------------- ### Project CMakeLists.txt Configuration Source: https://github.com/espressif/esp-modbus/blob/main/test_apps/physical_tests/CMakeLists.txt Sets up the CMake build environment for the ESP-MODBUS test subproject. Includes ESP-IDF project setup and defines additional component directories based on the ESP-IDF version. ```cmake cmake_minimum_required(VERSION 3.22) include($ENV{IDF_PATH}/tools/cmake/project.cmake) set(EXTRA_COMPONENT_DIRS "../test_common") # The workaround for the test_utils under ESP-IDF v6.0 if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER "5.5") list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/test_apps/components") else() list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components") endif() project(test_modbus_comm_multi_dev) ``` -------------------------------- ### CMake Project Setup for ESP-MODBUS Source: https://github.com/espressif/esp-modbus/blob/main/test_apps/console_helper_test/CMakeLists.txt Essential CMake boilerplate required at the beginning of your project's CMakeLists file for the ESP-MODBUS framework. This setup must be in the exact order shown. ```cmake cmake_minimum_required(VERSION 3.22) set(EXCLUDE_COMPONENTS freemodbus) list(APPEND EXTRA_COMPONENT_DIRS "../../components/mb_console_helper") include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(modbus_console_helper_test) ``` -------------------------------- ### Configure Modbus Application Source: https://github.com/espressif/esp-modbus/blob/main/examples/tcp/mb_tcp_slave/README.md Use this command to access the configuration menu for the Modbus application. Adjust Wi-Fi or Ethernet settings within the 'Example Connection Configuration' menu. ```bash idf.py menuconfig ``` -------------------------------- ### Initialize Modbus TCP Master Source: https://github.com/espressif/esp-modbus/blob/main/docs/en/port_initialization.rst Configure and create a Modbus TCP master instance. Ensure the `esp_netif` component is initialized for network interface setup. The `slave_ip_address_table` must be defined. ```c #define MB_TCP_PORT 502 static void *master_handle = NULL; .... mb_communication_info_t tcp_master_config = { .tcp_opts.port = MB_TCP_PORT, // Default TCP Port number .tcp_opts.mode = MB_TCP, // TCP mode of communication .tcp_opts.addr_type = MB_IPV4, // type of IP address (MB_IPV4, MB_IPV6) .tcp_opts.ip_addr_table = (void *)slave_ip_address_table, // list of slaves for master (must be defined) .tcp_opts.uid = 0, // the UID unused for master .tcp_opts.start_disconnected = false, // false - manage connections to all slaves before start .tcp_opts.response_tout_ms = 2000, // slave response time in milliseconds for master, 0 - use default konfig .tcp_opts.ip_netif_ptr = (void*)get_example_netif(), // the pointer to netif interface }; esp_err_t err = mbc_master_create_tcp(pcomm_info, &master_handle); if (master_handler == NULL || err != ESP_OK) { ESP_LOGE(TAG, "mb controller initialization fail."); } ``` -------------------------------- ### ESP-IDF Modbus Project CMake Setup Source: https://github.com/espressif/esp-modbus/blob/main/examples/serial/mb_serial_slave/CMakeLists.txt Essential boilerplate for ESP-Modbus projects. Ensure these lines are in your project's CMakeLists.txt in the exact order shown. ```cmake cmake_minimum_required(VERSION 3.22) # Exclude old component feemodbus which exists in old versions set(EXCLUDE_COMPONENTS freemodbus) include($ENV{IDF_PATH}/tools/cmake/project.cmake) # Minimal build to avoid TCP disable build errors set(COMPONENTS main) project(modbus_serial_slave) ``` -------------------------------- ### Initialize Modbus Serial Slave (ASCII) Source: https://github.com/espressif/esp-modbus/blob/main/docs/en/port_initialization.rst Configure and create a Modbus serial slave instance using ASCII communication mode. Requires UART specific APIs for RS485 setup. Ensure correct port, baud rate, and slave address are set. ```c #define MB_PORT_NUM 2 #define MB_DEV_SPEED 115200 #define MB_SLAVE_ADDR 1 static void* slave_handle = NULL; .... mb_communication_info_t config = { .ser_opts.port = MB_PORT_NUM, .ser_opts.mode = MB_ASCII, // ASCII communication mode .ser_opts.baudrate = MB_DEV_SPEED, .ser_opts.parity = MB_PARITY_NONE, .ser_opts.uid = MB_SLAVE_ADDR, // Modbus slave UID - Unit Identifier (short address) .ser_opts.data_bits = UART_DATA_8_BITS, .ser_opts.stop_bits = UART_STOP_BITS_1 }; // Initialization and setup of Modbus serial slave in ASCII communication mode esp_err_t err = mbc_slave_create_serial(&config, &slave_handle); if (slave_handle == NULL || err != ESP_OK) { ESP_LOGE(TAG, "mb controller initialization fail."); } ``` -------------------------------- ### Start Modbus Slave Communication Source: https://github.com/espressif/esp-modbus/blob/main/docs/en/slave_api_overview.rst This snippet shows how to start the Modbus controller interface for slave communication. The `slave_handle` must be initialized before calling this function. Error checking is performed using `ESP_ERROR_CHECK`. ```c static void* slave_handle = NULL; .... ESP_ERROR_CHECK(mbc_slave_start(slave_handle)); // The handle must be initialized prior to start call. ``` -------------------------------- ### RS485 Example Circuit Schematic Source: https://github.com/espressif/esp-modbus/blob/main/examples/serial/mb_serial_slave/README.md This schematic illustrates the hardware connections for an RS485 interface using a MAX485 line driver between an ESP32 board and a Modbus master. ```text VCC ---------------+ +--------------- VCC | | +-------x-------+ +-------x-------+ RXD <------| RO | DIFFERENTIAL | RO|-----> RXD | B|---------------|B | TXD ------>| DI MAX485 | \ / | MAX485 DI|<----- TXD ESP32 board | | RS-485 side | | Modbus master RTS --+--->| DE | / \ | DE|---+ | | A|---------------|A | | +----| /RE | PAIR | /RE|---+-- RTS +-------x--------+ +-------x-------+ | --- ``` -------------------------------- ### Modbus Master Timeout Error Example Source: https://github.com/espressif/esp-modbus/blob/main/docs/en/applications_and_references.rst This example shows the output format for a Modbus master timeout error (ESP_ERR_TIMEOUT). It indicates that the slave did not respond within the configured timeout period. ```none E (1692332) MB_CONTROLLER_MASTER: mbc_master_get_parameter(111): SERIAL master get parameter failure error=(0x107) (ESP_ERR_TIMEOUT). ``` -------------------------------- ### Modbus TCP Master Example Schematics Source: https://github.com/espressif/esp-modbus/blob/main/examples/tcp/mb_tcp_master/README.md Illustrates the network connection for a single Modbus TCP slave device and a multi-slave segment connected to the ESP32 master. ```text MB_DEVICE_ADDR1 ------------- | | | Slave 1 |---<>--+---<>---| Master | | | | | ------------- ``` ```text MB_DEVICE_ADDR1 ------------- | | | Slave 1 |---<>--+ | | | ------------- MB_DEVICE_ADDR2 ------------- | | | Slave 2 |---<>--+---<>---| Master | | | | | | ------------- MB_DEVICE_ADDR3 ------------- | | | Slave 3 |---<>--+ | | ------------- Network (Ethernet or WiFi connection) ``` -------------------------------- ### Custom Vendor-Specific Command Handler Example Source: https://github.com/espressif/esp-modbus/blob/main/docs/en/master_api_overview.rst This snippet demonstrates how to set up and use a custom handler for vendor-specific Modbus commands. It includes defining a handler function, registering it with mbc_set_handler, and sending a custom request. ```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).\n", (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).\n", (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 specific) // 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 ... } ``` -------------------------------- ### Modbus Slave API Overview Source: https://github.com/espressif/esp-modbus/blob/main/docs/en/slave_api_overview.rst The typical programming workflow for the Modbus slave API involves several steps in a specific order: port initialization, configuring data descriptors, customizing function handling, setting communication options, starting communication, and finally destroying the controller. ```APIDOC ## Modbus Slave API Workflow ### Description This outlines the sequential steps required for utilizing the Modbus Slave API. ### Steps 1. **Port Initialization**: Initialize the Modbus controller interface. 2. **Configure Descriptor**: Configure data descriptors for accessing slave parameters. 3. **Customize Handler**: Customize Modbus function handling. 4. **Setup Communication Options**: Set communication options for the selected port. 5. **Start Communication**: Start the stack and manage data sending/receiving, filtering events. 6. **Destroy Controller**: Destroy the Modbus controller and release resources. ``` -------------------------------- ### Example Modbus Master Application Output Source: https://github.com/espressif/esp-modbus/blob/main/examples/serial/mb_serial_master/README.md This output shows the application reading characteristics from slave devices, including their IDs, names, values, and hexadecimal representations. It also indicates when an alarm is triggered. ```text I (9035) MASTER_TEST: Characteristic #0 Data_channel_0 (Volts) value = 1.120000 (0x3f8f5c29) read successful. I (9045) MASTER_TEST: Characteristic #1 Humidity_1 (%rH) value = 5.539999 (0x40b147ac) read successful. I (9045) MASTER_TEST: Characteristic #2 Temperature_1 (C) value = 2.340000 (0x4015c28f) read successful. I (9055) MASTER_TEST: Characteristic #3 Humidity_2 (%rH) value = 2.560000 (0x4023d70a) read successful. I (9065) MASTER_TEST: Characteristic #4 Temperature_2 (C) value = 3.560000 (0x4063d70a) read successful. I (9075) MASTER_TEST: Characteristic #5 Humidity_3 (%rH) value = 3.780000 (0x4071eb85) read successful. I (9085) MASTER_TEST: Characteristic #6 RelayP1 (on/off) value = OFF (0x55) read successful. I (9095) MASTER_TEST: Characteristic #7 RelayP2 (on/off) value = OFF (0xaa) read successful. I (9605) MASTER_TEST: Characteristic #0 Data_channel_0 (Volts) value = 1.120000 (0x3f8f5c29) read successful. I (9615) MASTER_TEST: Characteristic #1 Humidity_1 (%rH) value = 5.739999 (0x40b7ae12) read successful. I (9615) MASTER_TEST: Characteristic #2 Temperature_1 (C) value = 2.340000 (0x4015c28f) read successful. I (9625) MASTER_TEST: Characteristic #3 Humidity_2 (%rH) value = 2.560000 (0x4023d70a) read successful. I (9635) MASTER_TEST: Characteristic #4 Temperature_2 (C) value = 3.560000 (0x4063d70a) read successful. I (9645) MASTER_TEST: Characteristic #5 Humidity_3 (%rH) value = 3.780000 (0x4071eb85) read successful. I (9655) MASTER_TEST: Characteristic #6 RelayP1 (on/off) value = OFF (0x55) read successful. I (9665) MASTER_TEST: Characteristic #7 RelayP2 (on/off) value = ON (0xff) read successful. I (10175) MASTER_TEST: Alarm triggered by cid #7. I (10175) MASTER_TEST: Destroy master... ``` -------------------------------- ### Read/Write Multiple Holding Registers Source: https://github.com/espressif/esp-modbus/blob/main/docs/en/master_api_overview.rst This example demonstrates using command 0x17 (Read/Write Multiple Registers) to simultaneously write float holding values and read a float holding value in a single transaction. Note that `reg_size` and `wr_rd_multi_reg_func.wr_reg_size` must be in registers (2 bytes per register), and the `HOLD_REG_START()` macro helps calculate register offsets. ```c #define MB_DEVICE_ADDR1 1 // Slave UID to send the request ... static void *master_handle = NULL; // The master handler should be initialized before send request function ... float read_write_holding_buffer[3] = {23.4f, 45.6f, 56.7f}; mb_param_request_t read_write_holding_request = { .slave_addr = MB_DEVICE_ADDR1, .command = 0x17, // 0x17 (23) -- Read/Write Multiple Registers .reg_start = HOLD_REG_START(holding_data2), // Read start address: field holding_data2 in holding_reg_params_t .reg_size = 2, // Length of the data to read (registers) .wr_rd_multi_reg_func.wr_reg_start = HOLD_REG_START(holding_data0), // Write start address: field holding_data0 in holding_reg_params_t .wr_rd_multi_reg_func.wr_reg_size = 6 // Length of the data to write (registers) }; err = mbc_master_send_request(master_handle, &read_write_holding_request, &read_write_holding_buffer); if (err != ESP_OK) { ESP_LOGE("MASTER_COMMAND_0x17", "Read and write of multiple holding registers fail."); } else { ESP_LOGI("MASTER_COMMAND_0x17", "Read and write of multiple holding registers success. Value read: %f", read_write_holding_buffer[0]); } ``` -------------------------------- ### Configure Modbus Serial Master (RTU) Source: https://github.com/espressif/esp-modbus/blob/main/docs/en/port_initialization.rst Example configuration for a Modbus serial master using RTU mode. Ensure `MB_PORT_NUM` and `MB_DEV_SPEED` are defined appropriately. The `config` structure specifies serial port options. ```c #define MB_PORT_NUM 2 #define MB_DEV_SPEED 115200 static void *master_handle = NULL; .... // Initialize Modbus controller mb_communication_info_t config = { .ser_opts.port = MB_PORT_NUM, // master communication port number .ser_opts.mode = MB_RTU, // mode of Modbus communication (MB_RTU, MB_ASCII) .ser_opts.baudrate = MB_DEV_SPEED, // baud rate of the port .ser_opts.parity = MB_PARITY_NONE, // parity option for the port .ser_opts.uid = 0, // unused for master .ser_opts.response_tout_ms = 1000, // slave response time for master (if = 0, taken from default config) .ser_opts.data_bits = UART_DATA_8_BITS, // number of data bits for communication port .ser_opts.stop_bits = UART_STOP_BITS_1 // number of stop bits for the communication port }; esp_err_t err = mbc_master_create_serial(&config, &master_handle); if (master_handler == NULL || err != ESP_OK) { ESP_LOGE(TAG, "mb controller initialization fail."); } ``` -------------------------------- ### RS485 Example Circuit Schematic Source: https://github.com/espressif/esp-modbus/blob/main/examples/serial/mb_serial_master/README.md This schematic illustrates the connection of a Modbus master and slave device using an RS485 line driver. Ensure correct wiring of RXD, TXD, RTS, DE, and /RE pins. ```text VCC ---------------+ +--------------- VCC | | +-------x-------+ +-------x-------+ RXD <------| RO | DIFFERENTIAL | RO|-----> RXD | B|---------------|B | TXD ------>| DI MAX485 | \ / | MAX485 DI|<----- TXD ESP32 BOARD | | RS-485 side | | External PC (emulator) with USB to serial or RTS --+--->| DE | / \ | DE|---+ ESP32 BOARD (slave) | | A|---------------|A | | +----| /RE | PAIR | /RE|---+-- RTS +-------x-------+ +-------x-------+ | | --- Modbus Master device Modbus Slave device ``` -------------------------------- ### Override Modbus Command Handler Source: https://github.com/espressif/esp-modbus/blob/main/docs/en/slave_api_overview.rst This example demonstrates how to override the standard handler for a specific Modbus command (e.g., FC04) with a custom function. It shows retrieving the standard handler, defining a custom handler, and then setting the custom handler for the command. Ensure custom handlers are simple and avoid blocking calls to prevent timeouts. ```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); ``` -------------------------------- ### Define Modbus Master Data Dictionary Source: https://github.com/espressif/esp-modbus/blob/main/docs/en/master_api_overview.rst Example of defining an array of mb_parameter_descriptor_t to describe device characteristics, including various data types and their Modbus register mappings. This setup is crucial for the Modbus master to correctly interpret and access data from slave devices. ```c typedef struct { uint8_t u8_a; uint16_t u16_ab; uint32_t uint32_abcd; float float_cdab; uint64_t uint64_abcdefgh; double double_hgfedcba; } holding_reg_params_t; #pragma pack(pop) // Enumeration of modbus slave addresses accessed by master device enum { MB_DEVICE_ADDR1 = 1, // Short address of Modbus slave device MB_SLAVE_COUNT }; // Enumeration of all supported CIDs for device (used in parameter definition table) enum { CID_HOLD_U8_A = 0, CID_HOLD_U16_AB, CID_HOLD_UINT32_ABCD, CID_HOLD_FLOAT_CDAB, CID_HOLD_UINT64_ABCDEFGH, CID_HOLD_DOUBLE_HGFEDCBA, CID_COUNT }; // Example Data Dictionary for to address parameters from slaves with different options of endianness mb_parameter_descriptor_t device_parameters[] = { // CID, Name, Units, Modbus addr, register type, Modbus Reg Start Addr, Modbus Reg read length, // Instance offset (NA), Instance type, Instance length (bytes), Options (NA), Permissions { CID_HOLD_U8_A, STR("U8_A"), STR("--"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, HOLD_REG_START(holding_u8_a), HOLD_REG_SIZE(holding_u8_a), HOLD_OFFSET(holding_u8_a), PARAM_TYPE_U8_A, (HOLD_REG_SIZE(holding_u8_a) << 1), OPTS( 0, UCHAR_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER }, { CID_HOLD_U16_AB, STR("U16_AB"), STR("--"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, HOLD_REG_START(holding_u16_ab), HOLD_REG_SIZE(holding_u16_ab), HOLD_OFFSET(holding_u16_ab), PARAM_TYPE_U16_AB, (HOLD_REG_SIZE(holding_u16_ab) << 1), OPTS( 0, USHRT_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER }, { CID_HOLD_UINT32_ABCD, STR("UINT32_ABCD"), STR("--"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, HOLD_REG_START(holding_uint32_abcd), HOLD_REG_SIZE(holding_uint32_abcd), HOLD_OFFSET(holding_uint32_abcd), PARAM_TYPE_U32_ABCD, (HOLD_REG_SIZE(holding_uint32_abcd) << 1), OPTS( 0, ULONG_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER }, { CID_HOLD_FLOAT_CDAB, STR("FLOAT_CDAB"), STR("--"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, HOLD_REG_START(holding_float_cdab), HOLD_REG_SIZE(holding_float_cdab), HOLD_OFFSET(holding_float_cdab), PARAM_TYPE_FLOAT_CDAB, (HOLD_REG_SIZE(holding_float_cdab) << 1), OPTS( 0, ULONG_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER }, { CID_HOLD_UINT64_ABCDEFGH, STR("UINT64_ABCDEFGH"), STR("--"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, HOLD_REG_START(holding_uint64_abcdefgh), HOLD_REG_SIZE(holding_uint64_abcdefgh), HOLD_OFFSET(holding_uint64_abcdefgh), PARAM_TYPE_UINT64_ABCDEFGH, (HOLD_REG_SIZE(holding_uint64_abcdefgh) << 1), OPTS( 0, ULLONG_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER }, { CID_HOLD_DOUBLE_HGFEDCBA, STR("DOUBLE_HGFEDCBA"), STR("--"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, HOLD_REG_START(holding_double_hgfedcba), HOLD_REG_SIZE(holding_double_hgfedcba), HOLD_OFFSET(holding_double_hgfedcba), PARAM_TYPE_DOUBLE_HGFEDCBA, (HOLD_REG_SIZE(holding_double_hgfedcba) << 1), OPTS( 0, ULLONG_MAX, 0 ), PAR_PERMS_READ_WRITE_TRIGGER } }; uint16_t num_device_parameters = (sizeof(device_parameters) / sizeof(device_parameters[0])); ``` -------------------------------- ### Modbus Device Parameters Definition Source: https://github.com/espressif/esp-modbus/blob/main/docs/en/master_api_overview.rst Defines Modbus slave addresses and CIDs (Common Information Domain) for devices. This snippet shows an example data dictionary for Modbus parameters in two slaves, specifying details like CID, name, units, Modbus address, register type, register start address, register read length, instance offset, instance type, instance length, options, and permissions. ```c // Enumeration of modbus slave addresses accessed by master device enum { MB_DEVICE_ADDR1 = 1, MB_DEVICE_ADDR2, MB_SLAVE_COUNT }; // Enumeration of all supported CIDs for device enum { CID_SER_NUM1 = 0, CID_SW_VER1, CID_DEV_NAME1, CID_TEMP_DATA_1, CID_SER_NUM2, CID_SW_VER2, CID_DEV_NAME2, CID_TEMP_DATA_2 }; // Example Data Dictionary for Modbus parameters in 2 slaves in the segment mb_parameter_descriptor_t device_parameters[] = { // CID, Name, Units, Modbus addr, register type, Modbus Reg Start Addr, Modbus Reg read length, // Instance offset (NA), Instance type, Instance length (bytes), Options (NA), Permissions { CID_SER_NUM1, STR("Serial_number_1"), STR("--"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0, 2, 0, PARAM_TYPE_U32, 4, OPTS( 0,0,0 ), PAR_PERMS_READ_WRITE_TRIGGER }, { CID_SW_VER1, STR("Software_version_1"), STR("--"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 2, 1, 0, PARAM_TYPE_U16, 2, OPTS( 0,0,0 ), PAR_PERMS_READ_WRITE_TRIGGER }, { CID_DEV_NAME1, STR("Device name"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 2, 8, 0, PARAM_TYPE_ASCII, 16, OPTS( 0, 0, 0 ), PAR_PERMS_READ_WRITE_TRIGGER }, { CID_TEMP_DATA_1, STR("Temperature_1"), STR("C"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0, 2, 0, PARAM_TYPE_FLOAT, 4, OPTS( 16, 30, 1 ), PAR_PERMS_READ_WRITE_TRIGGER }, { CID_SER_NUM2, STR("Serial_number_2"), STR("--"), MB_DEVICE_ADDR2, MB_PARAM_INPUT, 0, 2, 0, PARAM_TYPE_U32, 4, OPTS( 0,0,0 ), PAR_PERMS_READ_WRITE_TRIGGER }, { CID_SW_VER2, STR("Software_version_2"), STR("--"), MB_DEVICE_ADDR2, MB_PARAM_INPUT, 2, 1, 0, PARAM_TYPE_U16, 2, OPTS( 0,0,0 ), PAR_PERMS_READ_WRITE_TRIGGER }, { CID_DEV_NAME2, STR("Device name"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 2, 8, 0, PARAM_TYPE_ASCII, 16, OPTS( 0, 0, 0 ), PAR_PERMS_READ_WRITE_TRIGGER }, { CID_TEMP_DATA_2, STR("Temperature_2"), STR("C"), MB_DEVICE_ADDR2, MB_PARAM_HOLDING, 0, 2, 0, PARAM_TYPE_FLOAT, 4, OPTS( 20, 30, 1 ), PAR_PERMS_READ_WRITE_TRIGGER }, }; // Calculate number of parameters in the table uint16_t num_device_parameters = (sizeof(device_parameters) / sizeof(device_parameters[0])); ``` -------------------------------- ### Run `idf.py menuconfig` Source: https://github.com/espressif/esp-modbus/blob/main/examples/tcp/mb_tcp_master/README.md Execute this command to access the configuration menu for the ESP-IDF project. ```bash idf.py menuconfig ``` -------------------------------- ### Build and Flash Modbus Application Source: https://github.com/espressif/esp-modbus/blob/main/examples/tcp/mb_tcp_slave/README.md Build the project, flash it to the ESP32 board, and monitor serial output using this command. Exit the serial monitor by pressing Ctrl+]. ```bash idf.py -p PORT flash monitor ``` -------------------------------- ### Modbus Master Timeout Error Example Source: https://github.com/espressif/esp-modbus/blob/main/examples/serial/README.md Example of a Modbus master timeout error, indicating no response from the slave device within the configured time. ```text E (1692332) MB_CONTROLLER_MASTER: mbc_master_get_parameter(111): SERIAL master get parameter failure error=(0x107) (ESP_ERR_TIMEOUT). ``` -------------------------------- ### Set Project Components Source: https://github.com/espressif/esp-modbus/blob/main/test_apps/unit_tests/mb_ext_types/CMakeLists.txt Defines the main components to be included in the project build. 'main' typically refers to the application's source code directory. ```cmake set(COMPONENTS main) ``` -------------------------------- ### Slave Communication API Source: https://github.com/espressif/esp-modbus/blob/main/docs/en/slave_api_overview.rst Functions to start and manage Modbus slave communication. ```APIDOC ## mbc_slave_start ### Description Initializes and starts the Modbus controller interface for slave communication. ### Method N/A (C function) ### Endpoint N/A ### Parameters #### Path Parameters N/A #### Query Parameters N/A #### Request Body N/A ### Request Example ```c // Example of starting the slave communication void* slave_handle = NULL; // Must be initialized prior to this call mbc_slave_start(slave_handle); ``` ### Response #### Success Response (0) N/A (C function return value indicates success or error) #### Response Example N/A ``` ```APIDOC ## mbc_slave_check_event ### Description Checks for incoming Modbus events on the slave interface. ### Method N/A (C function) ### Endpoint N/A ### Parameters #### Path Parameters N/A #### Query Parameters N/A #### Request Body N/A ### Request Example ```c // Example of checking for events mbc_slave_check_event(slave_handle); ``` ### Response #### Success Response (0) N/A (C function return value indicates success or error) #### Response Example N/A ``` -------------------------------- ### Set Minimum CMake Version Source: https://github.com/espressif/esp-modbus/blob/main/test_apps/unit_tests/mb_ext_types/CMakeLists.txt Specifies the minimum required CMake version for the project. Ensure your CMake installation meets this requirement. ```cmake cmake_minimum_required(VERSION 3.22) ``` -------------------------------- ### Example Modbus Master Output Source: https://github.com/espressif/esp-modbus/blob/main/examples/serial/mb_serial_slave/README.md This output indicates various Modbus operations performed by the master, including read types, addresses, and timestamps. ```text I (13941) SLAVE_TEST: INPUT READ (13651163 us), ADDR:1, TYPE:8, INST_ADDR:0x3ffb2fd0, SIZE:2 I (13951) SLAVE_TEST: HOLDING READ (13656431 us), ADDR:1, TYPE:2, INST_ADDR:0x3ffb2fe0, SIZE:2 I (13961) SLAVE_TEST: INPUT READ (13665877 us), ADDR:3, TYPE:8, INST_ADDR:0x3ffb2fd4, SIZE:2 I (13971) SLAVE_TEST: HOLDING READ (13676010 us), ADDR:3, TYPE:2, INST_ADDR:0x3ffb2fe4, SIZE:2 I (13981) SLAVE_TEST: INPUT READ (13686130 us), ADDR:5, TYPE:8, INST_ADDR:0x3ffb2fd8, SIZE:2 I (13991) SLAVE_TEST: HOLDING READ (13696267 us), ADDR:5, TYPE:2, INST_ADDR:0x3ffb2fe8, SIZE:2 I (14001) SLAVE_TEST: COILS READ (13706331 us), ADDR:0, TYPE:32, INST_ADDR:0x3ffb2fcc, SIZE:8 I (14001) SLAVE_TEST: Modbus controller destroyed. ``` -------------------------------- ### Set Modbus Float and Double Parameters Source: https://github.com/espressif/esp-modbus/blob/main/docs/en/slave_api_overview.rst Demonstrates setting float and double values for Modbus parameters using specific formatting functions. Critical sections are required if the stack is active. ```c #include "mbcontroller.h" // for mbcontroller defines and api val_32_arr holding_float_abcd[2] = {0}; val_64_arr holding_double_ghefcdab[2] = {0}; ... // set the Modbus parameter to specific format portENTER_CRITICAL(¶m_lock); // critical section is required if the stack is active mb_set_float_abcd(&holding_float_abcd[0], (float)12345.0); mb_set_float_abcd(&holding_float_abcd[1], (float)12345.0); mb_set_double_ghefcdab(&holding_double_ghefcdab[0], (double)12345.0); portEXIT_CRITICAL(¶m_lock); ... ``` ```c // The actual abcd formatted value can be converted to actual float representation as below ESP_LOGI("TEST", "Test value abcd: %f", mb_get_float_abcd(&holding_float_abcd[0])); ESP_LOGI("TEST", "Test value abcd: %f", mb_get_float_abcd(&holding_float_abcd[1])); ESP_LOGI("TEST", "Test value ghefcdab: %lf", mb_get_double_ghefcdab(&holding_double_ghefcdab[0])); ... ``` -------------------------------- ### Configure Project Name and Component Registration Source: https://github.com/espressif/esp-modbus/blob/main/test_apps/tcp_instances_tests/mb_tcp_slave_instances/main/CMakeLists.txt Sets the project name and registers the component's source files, include directories, and required dependencies for the ESP-MODBUS project. ```cmake set(PROJECT_NAME "modbus_tcp_slave_instances") idf_component_register(SRCS "tcp_slave_instances.c" INCLUDE_DIRS "." PRIV_REQUIRES test_utils test_common unity nvs_flash esp_event esp_eth ) ``` -------------------------------- ### Project CMakeLists.txt Configuration Source: https://github.com/espressif/esp-modbus/blob/main/test_apps/unit_tests/mb_controller_mapping/CMakeLists.txt This is the main CMakeLists.txt file for the test subproject. It sets up the build environment for ESP-IDF projects, including specifying components and handling version-specific configurations. ```cmake cmake_minimum_required(VERSION 3.22) include($ENV{IDF_PATH}/tools/cmake/project.cmake) set(EXTRA_COMPONENT_DIRS "../../test_common" "../test_stubs") # The workaround for the test_utils under ESP-IDF v6.0 if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER "5.5") list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/test_apps/components") else() list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components") endif() set(COMPONENTS main) project(test_mb_controller_mapping_unit) ```