espressif / esp-idf

Espressif IoT Development Framework. Official development framework for Espressif SoCs.
Apache License 2.0
12.57k stars 7.02k forks source link

ESP32 Modbus Communication Issue: Temperature Data Consistently Reads Zero Despite Successful Transactions (IDFGH-12721) #13705

Open herryliq opened 2 weeks ago

herryliq commented 2 weeks ago

Answers checklist.

IDF version.

5.1.1

Espressif SoC revision.

ESP WROOM-32

Operating System used.

Windows

How did you build your project?

VS Code IDE

If you are using Windows, please specify command line type.

PowerShell

Development Kit.

espressif32

Power Supply used.

USB

What is the expected behavior?

Issue Summary:

I am experiencing an issue with reading temperature data via Modbus using an ESP32 microcontroller. The temperature values are consistently reported as zero, despite successful communications without any error codes.

Detailed Description:

  1. Communication Status:
  1. Temperature Reading:
  1. Log Insights:

What is the actual behavior?

I can receive the buffer from device, but can not map to the real value though by function mbc_master_get_parameter.

Steps to reproduce.

The follows is my device_parameters define. {CID_INP_DATA_0,STR("Temperature"),STR("Celsius"),1,MB_PARAM_INPUT,0x0000,1,INPUT_OFFSET(input_data0),PARAM_TYPE_FLOAT,PARAM_SIZE_FLOAT,OPTS(-40,80,0.1),PAR_PERMS_READ},

The source code is use modbus master sample as follows: https://github.com/espressif/esp-idf/blob/b3f7e2c8a4d354df8ef8558ea7caddc07283a57b/examples/protocols/modbus/serial/mb_master/main/master.c#L313

Debug Logs.

D (14182) MB_MASTER_SERIAL: MB_TX_buffer sent: (9) bytes.␛[0m
D (14192) MB_PORT_COMMON: xMBPortSerialWaitEvent, UART event: 1 ␛[0m
D (14202) MB_MASTER_SERIAL: MB_uart[2] event:␛[0m
D (14202) MB_MASTER_SERIAL: uart rx break.␛[0m
D (14202) MB_PORT_COMMON: xMBPortSerialWaitEvent, UART event: 0 ␛[0m
D (14212) MB_MASTER_SERIAL: MB_uart[2] event:␛[0m
D (14212) MB_MASTER_SERIAL: Data event, len: 7.␛[0m
D (14222) MB_MASTER_SERIAL: Received data: 8(bytes in buffer)␛[0m
D (14222) MB_MASTER_SERIAL: Timeout occured, processed: 8 bytes␛[0m
D (14232) MB_PORT_COMMON: 13496898:EV_MASTER_FRAME_RECEIVED␛[0m
D (14242) MB_PORT_COMMON:  xMBMasterPortSerialGetResponse default␛[0m
D (14242) MB_PORT_COMMON: eMBMasterRTUReceive: Port enter critical.␛[0m
D (14252) MB_PORT_COMMON: eMBMasterRTUReceive: Port exit critical␛[0m
D (14252) MB_PORT_COMMON: 13496898: Packet data received successfully (0).␛[0m
D (14262) POLL receive buffer: 04 02 01 3c ␛[0m
D (14272) MB_PORT_COMMON: 13496898:EV_MASTER_FRAME_SENT␛[0m
D (14272) POLL sent buffer: 04 00 00 00 01 ␛[0m
D (14282) MB_PORT_COMMON: 13496898:EV_MASTER_EXECUTE␛[0m
D (14282) MB_PORT_COMMON: 13496898:set event EV_ERROR_OK␛[0m
D (14292) MB_PORT_COMMON: 13496898:EV_MASTER_ERROR_PROCESS␛[0m
D (14292) MB_PORT_COMMON: vMBMasterCBRequestSuccess: Callback request success.␛[0m
D (14302) MB_PORT_COMMON: Transaction (13496898), processing time(us) = 139053␛[0m
D (14312) MB_PORT_COMMON: eMBMasterWaitRequestFinish: returned event = 0x80␛[0m
D (14312) MB_CONTROLLER_MASTER: mbc_serial_master_get_parameter: Good response for get cid(0) = ESP_OK␛[0m
␛[0;32mI (14322) MASTER_TEST: Characteristic #0 Temperature (Celsius) value = (0x0) read successful.␛[0m
␛[0;32mI (14832) MASTER_TEST: mbc_master_get_cid_info: (param_key:1061160820), (mb_slave_addr:1)␛[0m
D (14832) MB_PORT_COMMON: xMBMasterRunResTake:Take MB resource (500 ticks).␛[0m
D (14832) MB_PORT_COMMON: xMBMasterRunResTake:Take MB resource (500 ticks).␛[0m
D (14842) MB_PORT_COMMON: 14186894:EV_MASTER_FRAME_TRANSMIT␛[0m
D (14842) POLL transmit buffer: 04 00 00 00 01 ␛[0m

More Information.

No response

alisitsyn commented 2 weeks ago

Hi @herryliq ,

Thanks for the issue. As per your information above I can guess about the misconfiguration of the example you are using. The code tries to display the float value (length = 4 bytes) of characteristic CID_INP_DATA_0 but your data dictionary is configured incorrectly to read just one register (2 byes) from slave:

{CID_INP_DATA_0,STR("Temperature"),STR("Celsius"),1, // << read data from slave address 1
MB_PARAM_INPUT, // << read input registers
0x0000,1, // <<  this means read one register from address 0x0000 from slave (2 bytes)
INPUT_OFFSET(input_data0), // << means pack the read data into parameter `input_data0`
PARAM_TYPE_FLOAT, PARAM_SIZE_FLOAT, // <<< the packet value will be mapped into device memory as of type float 4 bytes
OPTS(-40,80,0.1),PAR_PERMS_READ},

The example uses the float values but if you need to read the 2 bytes values you need to modify the type of input_data0 here to PARAM_TYPE_U16 and modify the code here to display the correct unsigned 16 bit value instead of float and uint32_t. When the code tries to print the incorrectly set float value it usually prints it as 0.00.

Please read the readme.md file for example carefully and take a look to mapping examples in the documentation here. Also, please investigate the mapping table of your slave or check if the mapping areas in your slave configured correctly.

Please verify the above and let me know if you still have issues.

thanks. UPDATED: See more information about this fail here

herryliq commented 2 weeks ago

Thank for your response. In the file managed_components\espressif__esp-modbus\freemodbus\common\esp_modbus_master.c, the function mbc_master_get_parameter is implemented as follows:

esp_err_t mbc_master_get_parameter(uint16_t cid, char* name, uint8_t* value, uint8_t* type)
{
    esp_err_t error = ESP_OK;
    MB_MASTER_CHECK((master_interface_ptr != NULL),
                    ESP_ERR_INVALID_STATE,
                    "Master interface is not correctly initialized.");
    MB_MASTER_CHECK((master_interface_ptr->get_parameter != NULL),
                    ESP_ERR_INVALID_STATE,
                    "Master interface is not correctly initialized.");
    error = master_interface_ptr->get_parameter(cid, name, value, type);
    MB_MASTER_CHECK((error == ESP_OK),
                    error,
                    "Master get parameter failure, error=(0x%x) (%s).",
                    (int)error, esp_err_to_name(error));
    return error;
}

In this code snippet, the mbc_master_get_parameter function is responsible for calling another function to actually perform the parameter retrieval. The master_interface_ptr->get_parameter function is called with the provided parameters cid, name, value, and type.

However, the conversion or mapping of the parameter value from hexadecimal to decimal does not seem to be explicitly handled in this function. It's likely that the conversion or mapping is performed inside the master_interface_ptr->get_parameter function or its related functions.

The master_interface_ptr->get_parameter function ultimately calls the mbc_serial_master_get_parameter function located in managed_components\espressif__esp-modbus\freemodbus\serial_master\modbus_controller\mbc_serial_master.c. In this function, the actual Modbus communication and data handling take place, but the specific conversion or mapping of the parameter value is not immediately apparent from the provided code snippets.

static esp_err_t mbc_serial_master_get_parameter(uint16_t cid, char* name,
                                                    uint8_t* value_ptr, uint8_t *type)
{
    MB_MASTER_CHECK((name != NULL),
                        ESP_ERR_INVALID_ARG, "mb incorrect descriptor.");
    MB_MASTER_CHECK((type != NULL),
                        ESP_ERR_INVALID_ARG, "type pointer is incorrect.");
    esp_err_t error = ESP_ERR_INVALID_RESPONSE;
    mb_param_request_t request ;
    mb_parameter_descriptor_t reg_info = { 0 };

    error = mbc_serial_master_set_request(name, MB_PARAM_READ, &request, &reg_info);
    if ((error == ESP_OK) && (cid == reg_info.cid)) {
        // Send request to read characteristic data
        error = mbc_serial_master_send_request(&request, value_ptr);
        if (error == ESP_OK) {
            ESP_LOG_BUFFER_HEX_LEVEL("POLL receive buffer", (void*)value_ptr, 2, ESP_LOG_INFO);
            ESP_LOGD(TAG, "%s: Good response for get cid(%u) = %s. reg size=%d, reg start = %d",
                                    __FUNCTION__, (unsigned)reg_info.cid, (char*)esp_err_to_name(error), request.reg_size, request.reg_start);
        } else {
            ESP_LOGD(TAG, "%s: Bad response to get cid(%u) = %s",
                                            __FUNCTION__, (unsigned)reg_info.cid, (char*)esp_err_to_name(error));
        }
        // Set the type of parameter found in the table
        *type = reg_info.param_type;
    } else {
        ESP_LOGE(TAG, "%s: The cid(%u) not found in the data dictionary.",
                                                    __FUNCTION__, (unsigned)reg_info.cid);
        error = ESP_ERR_INVALID_ARG;
    }
    return error;
}

Therefore, could you please provide a brief explanation of whether automatic value mapping processing will be performed and its related implementation logic?

alisitsyn commented 2 weeks ago

automatic value mapping into the parameter set as INPUT_OFFSET(input_data0) is not performed. The mapping is performed as below:

herryliq commented 2 weeks ago

Thanks for your feedback. I checked the version of the esp-modbus component I'm using and found it to be v1.0.13. However, there are some differences between v1.0.13 and the master version.

image

Is the 'Map Value With Response Value' feature a new addition in version 1.0.15

alisitsyn commented 1 week ago

@herryliq ,

No, the latest update includes Modbus extended types for mapping but the mapping was implemented in v1.0.13 as well. The functions esp_err_t mbc_master_set_parameter(uint16_t cid, char* name, uint8_t* value, uint8_t* type), esp_err_t mbc_master_get_parameter(uint16_t cid, char* name, uint8_t* value, uint8_t* type) work the same as it was before for the basic types.

[!NOTE] The basic mapping of the response values is a part of function handler functions.

alisitsyn commented 1 week ago

@herryliq , Could you give me some feedback? As per discussion above is the issue resolved on your side?