ESP32 MODBus Communication Failing

dhruvpareek_
Posts: 4
Joined: Fri Jul 19, 2024 11:27 pm

ESP32 MODBus Communication Failing

Postby dhruvpareek_ » Fri Jul 19, 2024 11:37 pm

Hi, I am using an ESP32 c6 that I am programming with a modified version of the sample Espressif Code to send read/write requests over MODBus. This is a link to the framework of the code I am using - https://github.com/espressif/esp-idf/tr ... /mb_master

I am using my ESP32 as a master that communicates to other devices that include SunGold/Mean Well solar inverters and a MeanWell DRS - https://www.meanwell.com/Upload/PDF/DRS-240,480.pdf.

With all of these devices I am able to successfuy program the ESP32 to be a master that sends an individual read/write with one parameter such as:
  1. const mb_parameter_descriptor_t device_parameters[] = {
  2.     // { CID, Param Name, Units, Modbus Slave Addr, Modbus Reg Type, Reg Start, Reg Size, Instance Offset, Data Type, Data Size, Parameter Options, Access Mode}
  3.     { CID_INP_DATA_0, STR("Data_channel_0"), STR("..."), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0x00, 1,
  4.         HOLD_OFFSET(holding_data0), PARAM_TYPE_U16, 2, OPTS( 0, 0xFFFF, 1 ), PAR_PERMS_READ_WRITE_TRIGGER }
  5. };
.

However, as soon as I program the ESP32 to read/write multiple parameters, such as:
  1. const mb_parameter_descriptor_t device_parameters[] = {
  2.     // { CID, Param Name, Units, Modbus Slave Addr, Modbus Reg Type, Reg Start, Reg Size, Instance Offset, Data Type, Data Size, Parameter Options, Access Mode}
  3.     { CID_INP_DATA_0, STR("Data_channel_0"), STR("..."), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0x00, 1,
  4.         HOLD_OFFSET(holding_data0), PARAM_TYPE_U16, 2, OPTS( 0, 0xFFFF, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
  5.     { CID_HOLD_DATA_0, STR("Data_channel_0"), STR("..."), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0x20, 1,
  6.         HOLD_OFFSET(holding_data0), PARAM_TYPE_U16, 2, OPTS( 0, 0xFFFF, 1 ), PAR_PERMS_READ_WRITE_TRIGGER }
  7. };
I get 107 or 102 errors back that look the following text (DRS returns error code 102 that is pasted below, solar inverters by SunGold returns 107s in a similar format). The first parameter is always read, but the second parameter always fails:
  1. W (252) spi_flash: Detected size(8192k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
  2. I (265) sleep: Configure to isolate all GPIO pins in sleep state
  3. I (272) sleep: Enable automatic switching of GPIO sleep configuration
  4. I (279) coexist: coex firmware version: 66616e60c
  5. I (284) coexist: coexist rom version 5b8dcfa
  6. I (290) main_task: Started on CPU0
  7. I (290) main_task: Calling app_main()
  8. I (290) uart: queue free spaces: 20
  9. I (350) MASTER_TEST: Modbus master stack initialized...
  10. I (450) MASTER_TEST: Start modbus test...
  11. I (450) MASTER_TEST: Characteristic #0 Data_channel_0 (...) value = 0.000000 (0x1) read successful.
  12. E (450) MB_CONTROLLER_MASTER: mbc_serial_master_get_parameter: The requested cid(1) is not configured correctly. Check data dictionary for correctness.
  13. E (460) MB_CONTROLLER_MASTER: mbc_master_get_parameter(74): Master get parameter failure, error=(0x102) (ESP_ERR_INVALID_ARG).
  14. E (480) MASTER_TEST: Characteristic #1 (Data_channel_0) read fail, err = 0x102 (ESP_ERR_INVALID_ARG).
  15. I (990) MASTER_TEST: Characteristic #0 Data_channel_0 (...) value = 0.000000 (0x1) read successful.
  16. E (990) MB_CONTROLLER_MASTER: mbc_serial_master_get_parameter: The requested cid(1) is not configured correctly. Check data dictionary for correctness.
  17. E (1000) MB_CONTROLLER_MASTER: mbc_master_get_parameter(74): Master get parameter failure, error=(0x102) (ESP_ERR_INVALID_ARG).
  18. E (1010) MASTER_TEST: Characteristic #1 (Data_channel_0) read fail, err = 0x102 (ESP_ERR_INVALID_ARG).
  19. I (1530) MASTER_TEST: Characteristic #0 Data_channel_0 (...) value = 0.000000 (0x1) read successful.
  20. E (1530) MB_CONTROLLER_MASTER: mbc_serial_master_get_parameter: The requested cid(1) is not configured correctly. Check data dictionary for correctness.
  21. E (1540) MB_CONTROLLER_MASTER: mbc_master_get_parameter(74): Master get parameter failure, error=(0x102) (ESP_ERR_INVALID_ARG).
  22. E (1560) MASTER_TEST: Characteristic #1 (Data_channel_0) read fail, err = 0x102 (ESP_ERR_INVALID_ARG).
  23. I (2070) MASTER_TEST: Characteristic #0 Data_channel_0 (...) value = 0.000000 (0x1) read successful.
  24. E (2070) MB_CONTROLLER_MASTER: mbc_serial_master_get_parameter: The requested cid(1) is not configured correctly. Check data dictionary for correctness.
  25. E (2080) MB_CONTROLLER_MASTER: mbc_master_get_parameter(74): Master get parameter failure, error=(0x102) (ESP_ERR_INVALID_ARG).
  26. E (2100) MASTER_TEST: Characteristic #1 (Data_channel_0) read fail, err = 0x102 (ESP_ERR_INVALID_ARG).
This is the entire code I program the ESP32 with:
  1. /*
  2.  * SPDX-FileCopyrightText: 2016-2023 Espressif Systems (Shanghai) CO LTD
  3.  *
  4.  * SPDX-License-Identifier: Apache-2.0
  5.  */
  6.  
  7. #include "string.h"
  8. #include "esp_log.h"
  9. #include "modbus_params.h"  // for modbus parameters structures
  10. #include "mbcontroller.h"
  11. #include "sdkconfig.h"
  12.  
  13. #define MB_PORT_NUM     (CONFIG_MB_UART_PORT_NUM)   // Number of UART port used for Modbus connection
  14. #define MB_DEV_SPEED    (CONFIG_MB_UART_BAUD_RATE)  // The communication speed of the UART
  15.  
  16. // Note: Some pins on target chip cannot be assigned for UART communication.
  17. // See UART documentation for selected board and target to configure pins using Kconfig.
  18.  
  19. // The number of parameters that intended to be used in the particular control process
  20. #define MASTER_MAX_CIDS num_device_parameters
  21.  
  22. // Number of reading of parameters from slave
  23. #define MASTER_MAX_RETRY 30
  24.  
  25. // Timeout to update cid over Modbus
  26. #define UPDATE_CIDS_TIMEOUT_MS          (500)
  27. #define UPDATE_CIDS_TIMEOUT_TICS        (UPDATE_CIDS_TIMEOUT_MS / portTICK_PERIOD_MS)
  28.  
  29. // Timeout between polls
  30. #define POLL_TIMEOUT_MS                 (1)
  31. #define POLL_TIMEOUT_TICS               (POLL_TIMEOUT_MS / portTICK_PERIOD_MS)
  32.  
  33. // The macro to get offset for parameter in the appropriate structure
  34. #define HOLD_OFFSET(field) ((uint16_t)(offsetof(holding_reg_params_t, field) + 1))
  35. #define INPUT_OFFSET(field) ((uint16_t)(offsetof(input_reg_params_t, field) + 1))
  36. #define COIL_OFFSET(field) ((uint16_t)(offsetof(coil_reg_params_t, field) + 1))
  37. // Discrete offset macro
  38. #define DISCR_OFFSET(field) ((uint16_t)(offsetof(discrete_reg_params_t, field) + 1))
  39.  
  40. #define STR(fieldname) ((const char*)( fieldname ))
  41. // Options can be used as bit masks or parameter limits
  42. #define OPTS(min_val, max_val, step_val) { .opt1 = min_val, .opt2 = max_val, .opt3 = step_val }
  43.  
  44. static const char *TAG = "MASTER_TEST";
  45.  
  46. // Enumeration of modbus device addresses accessed by master device
  47. enum {
  48.     MB_DEVICE_ADDR1 = 131 // Only one slave device used for the test (add other slave addresses here)
  49. };
  50.  
  51. // Enumeration of all supported CIDs for device (used in parameter definition table)
  52. enum {
  53.     CID_INP_DATA_0 = 0,
  54.     CID_HOLD_DATA_0,
  55.     CID_INP_DATA_1,
  56.     CID_HOLD_DATA_1,
  57.     CID_INP_DATA_2,
  58.     CID_HOLD_DATA_2,
  59.     CID_HOLD_TEST_REG,
  60.     CID_RELAY_P1,
  61.     CID_RELAY_P2,
  62.     CID_DISCR_P1,
  63.     CID_COUNT
  64. };
  65.  
  66. // Example Data (Object) Dictionary for Modbus parameters:
  67. // The CID field in the table must be unique.
  68. // Modbus Slave Addr field defines slave address of the device with correspond parameter.
  69. // Modbus Reg Type - Type of Modbus register area (Holding register, Input Register and such).
  70. // Reg Start field defines the start Modbus register number and Reg Size defines the number of registers for the characteristic accordingly.
  71. // The Instance Offset defines offset in the appropriate parameter structure that will be used as instance to save parameter value.
  72. // Data Type, Data Size specify type of the characteristic and its data size.
  73. // Parameter Options field specifies the options that can be used to process parameter value (limits or masks).
  74. // Access Mode - can be used to implement custom options for processing of characteristic (Read/Write restrictions, factory mode values and etc).
  75. const mb_parameter_descriptor_t device_parameters[] = {
  76.     // { CID, Param Name, Units, Modbus Slave Addr, Modbus Reg Type, Reg Start, Reg Size, Instance Offset, Data Type, Data Size, Parameter Options, Access Mode}
  77.     { CID_INP_DATA_0, STR("Data_channel_0"), STR("..."), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0x00, 1,
  78.         HOLD_OFFSET(holding_data0), PARAM_TYPE_U16, 2, OPTS( 0, 0xFFFF, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
  79.     { CID_HOLD_DATA_0, STR("Data_channel_0"), STR("..."), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0x20, 1,
  80.         HOLD_OFFSET(holding_data0), PARAM_TYPE_U16, 2, OPTS( 0, 0xFFFF, 1 ), PAR_PERMS_READ_WRITE_TRIGGER }
  81. };
  82.  
  83. //// { CID, Param Name, Units, Modbus Slave Addr, Modbus Reg Type, Reg Start, Reg Size, Instance Offset, Data Type, Data Size, Parameter Options, Access Mode}
  84. //{ CID_INP_DATA_0, STR("Data_channel_0"), STR("..."), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0x00, 1,
  85. //    HOLD_OFFSET(holding_data0), PARAM_TYPE_U16, 2, OPTS( 0, 0xFFFF, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
  86. //{ CID_HOLD_DATA_0, STR("Data_channel_0"), STR("..."), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0x20, 1,
  87. //    HOLD_OFFSET(holding_data0), PARAM_TYPE_U16, 2, OPTS( 0, 0xFFFF, 1 ), PAR_PERMS_READ_WRITE_TRIGGER }
  88.  
  89. // Calculate number of parameters in the table
  90. const uint16_t num_device_parameters = (sizeof(device_parameters)/sizeof(device_parameters[0]));
  91.  
  92. // The function to get pointer to parameter storage (instance) according to parameter description table
  93. static void* master_get_param_data(const mb_parameter_descriptor_t* param_descriptor)
  94. {
  95.     assert(param_descriptor != NULL);
  96.     void* instance_ptr = NULL;
  97.     if (param_descriptor->param_offset != 0) {
  98.        switch(param_descriptor->mb_param_type)
  99.        {
  100.            case MB_PARAM_HOLDING:
  101.                instance_ptr = ((void*)&holding_reg_params + param_descriptor->param_offset - 1);
  102.                break;
  103.            case MB_PARAM_INPUT:
  104.                instance_ptr = ((void*)&input_reg_params + param_descriptor->param_offset - 1);
  105.                break;
  106.            case MB_PARAM_COIL:
  107.                instance_ptr = ((void*)&coil_reg_params + param_descriptor->param_offset - 1);
  108.                break;
  109.            case MB_PARAM_DISCRETE:
  110.                instance_ptr = ((void*)&discrete_reg_params + param_descriptor->param_offset - 1);
  111.                break;
  112.            default:
  113.                instance_ptr = NULL;
  114.                break;
  115.        }
  116.     } else {
  117.         ESP_LOGE(TAG, "Wrong parameter offset for CID #%u", (unsigned)param_descriptor->cid);
  118.         assert(instance_ptr != NULL);
  119.     }
  120.     return instance_ptr;
  121. }
  122.  
  123. // User operation function to read slave values and check alarm
  124. static void master_operation_func(void *arg)
  125. {
  126.     esp_err_t err = ESP_OK;
  127.     float value = 0;
  128.     bool alarm_state = false;
  129.     const mb_parameter_descriptor_t* param_descriptor = NULL;
  130.  
  131.     ESP_LOGI(TAG, "Start modbus test...");
  132.  
  133.     for(uint16_t retry = 0; retry <= MASTER_MAX_RETRY && (!alarm_state); retry++) {
  134.         // Read all found characteristics from slave(s)
  135.         for (uint16_t cid = 0; (err != ESP_ERR_NOT_FOUND) && cid < MASTER_MAX_CIDS; cid++)
  136.         {
  137.             // Get data from parameters description table
  138.             // and use this information to fill the characteristics description table
  139.             // and having all required fields in just one table
  140.             err = mbc_master_get_cid_info(cid, &param_descriptor);
  141.             if ((err != ESP_ERR_NOT_FOUND) && (param_descriptor != NULL)) {
  142.                 void* temp_data_ptr = master_get_param_data(param_descriptor);
  143.                 assert(temp_data_ptr);
  144.                 uint8_t type = 0;
  145.                 if ((param_descriptor->param_type == PARAM_TYPE_ASCII) &&
  146.                         (param_descriptor->cid == CID_HOLD_TEST_REG)) {
  147.                    // Check for long array of registers of type PARAM_TYPE_ASCII
  148.                     err = mbc_master_get_parameter(cid, (char*)param_descriptor->param_key,
  149.                                                     (uint8_t*)temp_data_ptr, &type);
  150.                     if (err == ESP_OK) {
  151.                         ESP_LOGI(TAG, "Characteristic #%u %s (%s) value = (0x%" PRIx32 ") read successful.",
  152.                                         param_descriptor->cid,
  153.                                         param_descriptor->param_key,
  154.                                         param_descriptor->param_units,
  155.                                         *(uint32_t*)temp_data_ptr);
  156.                         // Initialize data of test array and write to slave
  157.                         if (*(uint32_t*)temp_data_ptr != 0xAAAAAAAA) {
  158.                             memset((void*)temp_data_ptr, 0xAA, param_descriptor->param_size);
  159.                             *(uint32_t*)temp_data_ptr = 0xAAAAAAAA;
  160.                             err = mbc_master_set_parameter(cid, (char*)param_descriptor->param_key,
  161.                                                               (uint8_t*)temp_data_ptr, &type);
  162.                             if (err == ESP_OK) {
  163.                                 ESP_LOGI(TAG, "Characteristic #%u %s (%s) value = (0x%" PRIx32 "), write successful.",
  164.                                                 param_descriptor->cid,
  165.                                                 param_descriptor->param_key,
  166.                                                 param_descriptor->param_units,
  167.                                                 *(uint32_t*)temp_data_ptr);
  168.                             } else {
  169.                                 ESP_LOGE(TAG, "Characteristic #%u (%s) write fail, err = 0x%x (%s).",
  170.                                                 param_descriptor->cid,
  171.                                                 param_descriptor->param_key,
  172.                                                 (int)err,
  173.                                                 (char*)esp_err_to_name(err));
  174.                             }
  175.                         }
  176.                     } else {
  177.                         ESP_LOGE(TAG, "Characteristic #%u (%s) read fail, err = 0x%x (%s).",
  178.                                         param_descriptor->cid,
  179.                                         param_descriptor->param_key,
  180.                                         (int)err,
  181.                                         (char*)esp_err_to_name(err));
  182.                     }
  183.                 } else {
  184.                     err = mbc_master_get_parameter(cid, (char*)param_descriptor->param_key,
  185.                                                         (uint8_t*)temp_data_ptr, &type);
  186.                     if (err == ESP_OK) {
  187.                         if ((param_descriptor->mb_param_type == MB_PARAM_HOLDING) ||
  188.                             (param_descriptor->mb_param_type == MB_PARAM_INPUT)) {
  189.                             value = *(float*)temp_data_ptr;
  190.                             ESP_LOGI(TAG, "Characteristic #%u %s (%s) value = %f (0x%" PRIx32 ") read successful.",
  191.                                             param_descriptor->cid,
  192.                                             param_descriptor->param_key,
  193.                                             param_descriptor->param_units,
  194.                                             value,
  195.                                             *(uint32_t*)temp_data_ptr);
  196.                             if (((value > param_descriptor->param_opts.max) ||
  197.                                 (value < param_descriptor->param_opts.min))) {
  198.                                     alarm_state = true;
  199.                                     break;
  200.                             }
  201.                         } else {
  202.                             uint8_t state = *(uint8_t*)temp_data_ptr;
  203.                             const char* rw_str = (state & param_descriptor->param_opts.opt1) ? "ON" : "OFF";
  204.                             if ((state & param_descriptor->param_opts.opt2) == param_descriptor->param_opts.opt2) {
  205.                                 ESP_LOGI(TAG, "Characteristic #%u %s (%s) value = %s (0x%" PRIx8 ") read successful.",
  206.                                                 param_descriptor->cid,
  207.                                                 param_descriptor->param_key,
  208.                                                 param_descriptor->param_units,
  209.                                                 (const char*)rw_str,
  210.                                                 *(uint8_t*)temp_data_ptr);
  211.                             } else {
  212.                                 ESP_LOGE(TAG, "Characteristic #%u %s (%s) value = %s (0x%" PRIx8 "), unexpected value.",
  213.                                                 param_descriptor->cid,
  214.                                                 param_descriptor->param_key,
  215.                                                 param_descriptor->param_units,
  216.                                                 (const char*)rw_str,
  217.                                                 *(uint8_t*)temp_data_ptr);
  218.                                 alarm_state = true;
  219.                                 break;
  220.                             }
  221.                             if (state & param_descriptor->param_opts.opt1) {
  222.                                 alarm_state = true;
  223.                                 break;
  224.                             }
  225.                         }
  226.                     } else {
  227.                         ESP_LOGE(TAG, "Characteristic #%u (%s) read fail, err = 0x%x (%s).",
  228.                                         param_descriptor->cid,
  229.                                         param_descriptor->param_key,
  230.                                         (int)err,
  231.                                         (char*)esp_err_to_name(err));
  232.                     }
  233.                 }
  234.                 vTaskDelay(POLL_TIMEOUT_TICS); // timeout between polls
  235.             }
  236.         }
  237.         vTaskDelay(UPDATE_CIDS_TIMEOUT_TICS);
  238.     }
  239.  
  240.     if (alarm_state) {
  241.         ESP_LOGI(TAG, "Alarm triggered by cid #%u.", param_descriptor->cid);
  242.     } else {
  243.         ESP_LOGE(TAG, "Alarm is not triggered after %u retries.", MASTER_MAX_RETRY);
  244.     }
  245.     ESP_LOGI(TAG, "Destroy master...");
  246.     ESP_ERROR_CHECK(mbc_master_destroy());
  247. }
  248.  
  249. // Modbus master initialization
  250. static esp_err_t master_init(void)
  251. {
  252.     // Initialize and start Modbus controller
  253.     mb_communication_info_t comm = {
  254.             .port = MB_PORT_NUM,
  255. #if CONFIG_MB_COMM_MODE_ASCII
  256.             .mode = MB_MODE_ASCII,
  257. #elif CONFIG_MB_COMM_MODE_RTU
  258.             .mode = MB_MODE_RTU,
  259. #endif
  260.             .baudrate = MB_DEV_SPEED,
  261.             .parity = MB_PARITY_NONE
  262.     };
  263.     void* master_handler = NULL;
  264.  
  265.     esp_err_t err = mbc_master_init(MB_PORT_SERIAL_MASTER, &master_handler);
  266.     MB_RETURN_ON_FALSE((master_handler != NULL), ESP_ERR_INVALID_STATE, TAG,
  267.                                 "mb controller initialization fail.");
  268.     MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
  269.                             "mb controller initialization fail, returns(0x%x).", (int)err);
  270.     err = mbc_master_setup((void*)&comm);
  271.     MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
  272.                             "mb controller setup fail, returns(0x%x).", (int)err);
  273.  
  274.     // Set UART pin numbers
  275.     err = uart_set_pin(MB_PORT_NUM, CONFIG_MB_UART_TXD, CONFIG_MB_UART_RXD,
  276.                               CONFIG_MB_UART_RTS, UART_PIN_NO_CHANGE);
  277.     MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
  278.         "mb serial set pin failure, uart_set_pin() returned (0x%x).", (int)err);
  279.  
  280.     err = mbc_master_start();
  281.     MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
  282.                             "mb controller start fail, returned (0x%x).", (int)err);
  283.  
  284.     // Set driver mode to Half Duplex
  285.     err = uart_set_mode(MB_PORT_NUM, UART_MODE_RS485_HALF_DUPLEX);
  286.     MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
  287.             "mb serial set mode failure, uart_set_mode() returned (0x%x).", (int)err);
  288.  
  289.     vTaskDelay(5);
  290.     err = mbc_master_set_descriptor(&device_parameters[0], num_device_parameters);
  291.     MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
  292.                                 "mb controller set descriptor fail, returns(0x%x).", (int)err);
  293.     ESP_LOGI(TAG, "Modbus master stack initialized...");
  294.     return err;
  295. }
  296.  
  297. void app_main(void)
  298. {
  299.     // Initialization of device peripheral and objects
  300.     ESP_ERROR_CHECK(master_init());
  301.     vTaskDelay(10);
  302.  
  303.     master_operation_func(NULL);
  304. }

Who is online

Users browsing this forum: No registered users and 349 guests