RS 485 request error (Absence of LRC bit)

Vaibhav Singh
Posts: 8
Joined: Wed Jan 18, 2023 5:25 am

RS 485 request error (Absence of LRC bit)

Postby Vaibhav Singh » Wed Jan 18, 2023 6:44 am

Hello,

I am trying to communicate with a Selec MFM384-R-C energy meter via RS 485 communication, where my master is an esp 32 microcontroller and the meter is the slave. As of now, I am only reading one value(Voltage). According to the Modbus poll master software to read this specific value the request from my master should be something like this -> :01 04 00 00 00 01 31 CA, But upon examining through the Hercules software I found that my Master(ESP 32) is giving an output like -> :01 04 00 00 00 01 FA.

Not only I am missing 2 bits of my LRC in my request, I am also getting a slave response time-out error although I have set it to 100ms via menuconfig.

I am trying to figure out where exactly I have to integrate my LRC function code, I am using a modified code which was a Modbus RS 485 master example in esp idf.

https://drive.google.com/drive/folders/ ... sp=sharing

Please help me out in identifying what problem am I exactly facing and what changes and modifications are needed to be done. I am also attaching my code for your reference and better understanding.

Thank you

ESP_alisitsyn
Posts: 211
Joined: Fri Feb 01, 2019 4:02 pm
Contact:

Re: RS 485 request error (Absence of LRC bit)

Postby ESP_alisitsyn » Wed Jan 18, 2023 10:07 am

Hello,

The modbus RTU uses the CRC16 control sum at the end of the command to check consistency. After the fast review of your device manual it supports the Modbus RTU frame format. So, the device should follow the RTU standard and CRC16.

The frame

Code: Select all

 :01 04 00 00 00 01 FA
corresponds to the Modbus ASCII frame format with LRC check sum and it is an option of Modbus protocol. The ESP-Modbus supports both the RTU and ASCII frame format. You need just to select the RTU mode in the kconfig options of your project.
Please use the
idf.py menuconfig
and change the communication mode to RTU in the menu (https://github.com/espressif/esp-modbus ... jbuild#L69).

The communication mode is defined in your code below according to kconfig settings:

Code: Select all

// Modbus master initialization
static esp_err_t master_init(void)
{
    // Initialize and start Modbus controller
    mb_communication_info_t comm = {
            .port = MB_PORT_NUM,
#if CONFIG_MB_COMM_MODE_ASCII
            .mode = MB_MODE_ASCII,
#elif CONFIG_MB_COMM_MODE_RTU
            .mode = MB_MODE_RTU,
#endif
            .baudrate = MB_DEV_SPEED,
            .parity = MB_PARITY_NONE
    };
    
    .................................
    
https://docs.espressif.com/projects/esp ... on-options


Let me know if you need further help.

Vaibhav Singh
Posts: 8
Joined: Wed Jan 18, 2023 5:25 am

Re: RS 485 request error (Absence of LRC bit)

Postby Vaibhav Singh » Wed Jan 18, 2023 11:44 am

I am grateful for your replay ESP_alisitsyn. Since now I have switched to RTU mode I am now getting a gibberish output for my serial request at the Hercules terminal. Since I am getting a timeout error for my set parameter I needed that output to understand what exactly am I doing wrong.

E (45960) MB_CONTROLLER_MASTER: mbc_master_set_parameter(134): Master set parameter failure, error=(0x107) (ESP_ERR_TIMEOUT).

I am getting this error although now I am able to establish communication with my energy meter. I am thankful to you for that. Can you also help me understand the above error, how can I resolve it and How can I get a readable output at the Hercules terminal for my serial request from my Master(Esp 32)?


Thank you

ESP_alisitsyn
Posts: 211
Joined: Fri Feb 01, 2019 4:02 pm
Contact:

Re: RS 485 request error (Absence of LRC bit)

Postby ESP_alisitsyn » Thu Jan 19, 2023 8:21 am


Vaibhav Singh
Posts: 8
Joined: Wed Jan 18, 2023 5:25 am

Re: RS 485 request error (Absence of LRC bit)

Postby Vaibhav Singh » Mon Jan 23, 2023 12:13 pm

Hey @alisitsyn I need one more help from you. I am only able to read around 8 parameters from the input type register although I am defining more

Code: Select all

float input_data8;
in the input structure, it is still giving me an invalid argument error.

Can you explain why is this happing ?? here is my code

Code: Select all

// The macro to get offset for parameter in the appropriate structure
#define HOLD_OFFSET(field) ((uint16_t)(offsetof(holding_reg_params_t, field) + 1))
#define INPUT_OFFSET(field) ((uint16_t)(offsetof(input_reg_params_t, field) + 1))
#define COIL_OFFSET(field) ((uint16_t)(offsetof(coil_reg_params_t, field) + 1))
// Discrete offset macro
#define DISCR_OFFSET(field) ((uint16_t)(offsetof(discrete_reg_params_t, field) + 1))

#define STR(fieldname) ((const char*)( fieldname ))
// Options can be used as bit masks or parameter limits
#define OPTS(min_val, max_val, step_val) { .opt1 = min_val, .opt2 = max_val, .opt3 = step_val }

static const char *TAG = "MASTER_TEST";

// Enumeration of modbus device addresses accessed by master device
enum {
    MB_DEVICE_ADDR1 = 1 // Only one slave device used for the test (add other slave addresses here)
};

// Enumeration of all supported CIDs for device (used in parameter definition table)
enum {
    CID_HOLDING_1=0,
    CID_INP_DATA_0,
    CID_INP_DATA_1,
    CID_INP_DATA_2,
    CID_INP_DATA_3,
    CID_INP_DATA_4,
    CID_INP_DATA_5,
    CID_INP_DATA_6,
    CID_INP_DATA_7,
    CID_INP_DATA_8,
};

// Example Data (Object) Dictionary for Modbus parameters:
// The CID field in the table must be unique.
// Modbus Slave Addr field defines slave address of the device with correspond parameter.
// Modbus Reg Type - Type of Modbus register area (Holding register, Input Register and such).
// Reg Start field defines the start Modbus register number and Reg Size defines the number of registers for the characteristic accordingly.
// The Instance Offset defines offset in the appropriate parameter structure that will be used as instance to save parameter value.
// Data Type, Data Size specify type of the characteristic and its data size.
// Parameter Options field specifies the options that can be used to process parameter value (limits or masks).
// Access Mode - can be used to implement custom options for processing of characteristic (Read/Write restrictions, factory mode values and etc).
const mb_parameter_descriptor_t device_parameters[] = {
    // { CID, Param Name, Units, Modbus Slave Addr, Modbus Reg Type, Reg Start, Reg Size, Instance Offset, Data Type, Data Size, Parameter Options, Access Mode}
    { CID_HOLDING_1, STR("Bauderate"), STR("bps"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 8, 1,
                    HOLD_OFFSET(holding_data0), PARAM_TYPE_FLOAT,  PARAM_SIZE_U8, OPTS( 0, 10, 1 ), PAR_PERMS_READ_TRIGGER },

    { CID_INP_DATA_0, STR("Voltage 1st Phase"), STR("Volts"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0, 2,
                    INPUT_OFFSET(input_data0), PARAM_TYPE_FLOAT,  PARAM_SIZE_U8, OPTS( 0, 300, 1 ), PAR_PERMS_READ_TRIGGER },
    
    { CID_INP_DATA_1, STR("Voltage 2nd Phase"), STR("Volts"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 2, 2,
                    INPUT_OFFSET(input_data1), PARAM_TYPE_FLOAT,  PARAM_SIZE_U8, OPTS( 0, 300, 1 ), PAR_PERMS_READ_TRIGGER },
   
    { CID_INP_DATA_2, STR("Voltage 3rd Phase"), STR("Volts"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 4, 2,
                    INPUT_OFFSET(input_data2), PARAM_TYPE_FLOAT,  PARAM_SIZE_U8, OPTS( 0, 300, 1 ), PAR_PERMS_READ_TRIGGER },

    { CID_INP_DATA_3, STR("Average Voltage LN"), STR("Volts"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 6, 2,
                    INPUT_OFFSET(input_data3), PARAM_TYPE_FLOAT,  PARAM_SIZE_U8, OPTS( 0, 300, 1 ), PAR_PERMS_READ_TRIGGER },

    { CID_INP_DATA_4, STR("Voltage 12"), STR("Volts"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 8, 2,
                    INPUT_OFFSET(input_data4), PARAM_TYPE_FLOAT,  PARAM_SIZE_U8, OPTS( 0, 300, 1 ), PAR_PERMS_READ_TRIGGER },

    { CID_INP_DATA_5, STR("Voltage 23"), STR("Volts"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 10, 2,
                    INPUT_OFFSET(input_data5), PARAM_TYPE_FLOAT,  PARAM_SIZE_U8, OPTS( 0, 300, 1 ), PAR_PERMS_READ_TRIGGER },

    { CID_INP_DATA_6, STR("Voltage 31"), STR("Volts"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 12, 2,
                    INPUT_OFFSET(input_data6), PARAM_TYPE_FLOAT,  PARAM_SIZE_U8, OPTS( 0, 300, 1 ), PAR_PERMS_READ_TRIGGER },

    { CID_INP_DATA_7, STR("Avg. Voltage LL "), STR("Volts"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 14, 2,
                    INPUT_OFFSET(input_data7), PARAM_TYPE_FLOAT,  PARAM_SIZE_U8, OPTS( 0, 300, 1 ), PAR_PERMS_READ_TRIGGER },

    { CID_INP_DATA_8, STR("Average Current"), STR("A"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 22, 2,
                    INPUT_OFFSET(input_data8), PARAM_TYPE_FLOAT,  PARAM_SIZE_U8, OPTS( 0, 300, 1 ), PAR_PERMS_READ_TRIGGER },                  
};
The changes I have made are in the

Code: Select all

input_reg_params_t
of the line

Code: Select all

#define INPUT_OFFSET(field) ((uint16_t)(offsetof(input_reg_params_t, field) + 1))

Code: Select all

This is the structure of input type register

Code: Select all

#pragma pack(push, 1)
typedef struct
{
    float input_data0; // 0
    float input_data1; // 2
    float input_data2; // 4
    float input_data3; // 6
    uint16_t data[150]; // 8 + 150 = 158
    float input_data4; // 158
    float input_data5;
    float input_data6;
    float input_data7;
    float input_data8;
    uint16_t data_block1[150];
} input_reg_params_t;
#pragma pack(pop)

Code: Select all

I am not able to build code and I am getting a error of something like this

C:/Users/Admin/Master_Comm/main/master.c:104:34: error: 'input_reg_params_t' has no member named 'input_data8'; did you mean 'input_data0'?
  104 |                     INPUT_OFFSET(input_data8), PARAM_TYPE_FLOAT,  PARAM_SIZE_U8, OPTS( 0, 300, 1 ), PAR_PERMS_READ_TRIGGER },
      |                                  ^~~~~~~~~~~
C:/Users/Admin/Master_Comm/main/master.c:104:21: note: in expansion of macro 'INPUT_OFFSET'
  104 |                     INPUT_OFFSET(input_data8), PARAM_TYPE_FLOAT,  PARAM_SIZE_U8, OPTS( 0, 300, 1 ), PAR_PERMS_READ_TRIGGER },
      |                     ^~~~~~~~~~~~
ninja: build stopped: subcommand failed.

 *  The terminal process "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -Command ninja " terminated with exit code: 1.

ESP_alisitsyn
Posts: 211
Joined: Fri Feb 01, 2019 4:02 pm
Contact:

Re: RS 485 request error (Absence of LRC bit)

Postby ESP_alisitsyn » Fri Jan 27, 2023 3:31 pm


Vaibhav Singh
Posts: 8
Joined: Wed Jan 18, 2023 5:25 am

Re: RS 485 request error (Absence of LRC bit)

Postby Vaibhav Singh » Fri Feb 03, 2023 4:14 am

Hello @alisitsyn, Thank you for your response earlier. I got a response for all 27 parameters described in the character dictionary. However, I am still finding an issue with completely understanding the working of this example. like if we look in main .c or operational function that sends the request and gets the data:

Code: Select all

for (uint16_t cid = 0; (err != ESP_ERR_NOT_FOUND) && cid < MASTER_MAX_CIDS; cid++)
        {
            // Get data from parameters description table
            // and use this information to fill the characteristics description table
            // and having all required fields in just one table
            err = mbc_master_get_cid_info(cid, &param_descriptor);
            /// Print error status
            if ((err != ESP_ERR_NOT_FOUND) && (param_descriptor != NULL))
            {
                void* temp_data_ptr = master_get_param_data(param_descriptor);
                assert(temp_data_ptr);
                uint8_t type = 0;
                if ((param_descriptor->param_type == PARAM_TYPE_FLOAT) &&
                    (param_descriptor->cid == CID_HOLDING_1))
                {
                    // Check for long array of registers of type PARAM_TYPE_FLOAT
                    err = mbc_master_get_parameter(cid, (char*)param_descriptor->param_key,
                                                   (uint8_t*)temp_data_ptr, &type);

                    if (err == ESP_OK)
                    {
                        ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = (0x%08x) read successful.",
                                 param_descriptor->cid,
                                 (char*)param_descriptor->param_key,
                                 (char*)param_descriptor->param_units,
                                 *(uint32_t*)temp_data_ptr);

                    }
                    else
                    {
                        ESP_LOGE(TAG, "Characteristic #%d (%s) read fail_2, err = 0x%x (%s).",
                                 param_descriptor->cid,
                                 (char*)param_descriptor->param_key,
                                 (int)err,
                                 (char*)esp_err_to_name(err));
It's quite obvious that the

Code: Select all

mbc_master_get_parameter
function sends the request on RS 485 and gets the readings from the slave and prints it in logI below. I guess this whole function is responsible for sending requests and receiving the data over RS 485 communication. So if we look closer into this function in
esp_modbus_master.c
we get:

Code: Select all

/**
 * Get parameter data for corresponding characteristic
 */
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).",
                    error, esp_err_to_name(error));
    return error;
}
Here the master is able to send a request after

Code: Select all

 **error = master_interface_ptr->get_parameter(cid, name, value, type);**
is executed. The
get_parameter
function is defined as

Code: Select all

iface_get_parameter get_parameter;      /*!< Interface get_parameter method */ 
and

Code: Select all

typedef esp_err_t (*iface_get_parameter)(uint16_t, char*, uint8_t*, uint8_t*);        /*!< Interface get_parameter method */
What I am unable to understand is how and when exactly the master sends a request and when is it receiving data and storing it in a buffer. As my ultimate goal is to send one request to the slave and receive multiple responses
from the slave.

For all my 27 Parameters my master is sending 27 requests to the slave and every time it is rewritten the variable where the data from the slave is stored. I want to modify it in such a way that I am able to send a range of registered addresses in a single request and get data from my slave to all those register in a single buffer.

Can you please help me understand how the above code sends requests and how is it receiving data from the slave? It would be really helpful to me.

Thankyou

ESP_alisitsyn
Posts: 211
Joined: Fri Feb 01, 2019 4:02 pm
Contact:

Re: RS 485 request error (Absence of LRC bit)

Postby ESP_alisitsyn » Mon Feb 13, 2023 8:57 am

Hello @Vaibhav Singh ,

You can see the details in the code:
The r/w place in the initialized modbus controller interface for serial:
https://github.com/espressif/esp-modbus ... ter.c#L380

Actual call to send Modbus request:
https://github.com/espressif/esp-modbus ... ter.c#L166
What I am unable to understand is how and when exactly the master sends a request and when is it receiving data and storing it in a buffer.
A Callbacks to store data:
https://github.com/espressif/esp-modbus ... ter.c#L460
I want to modify it in such a way that I am able to send a range of registered addresses in a single request and get data from my slave to all those register in a single buffer.
If I understood you correctly you need to read several registers from your slave in one request and store response data to specific location. If it is correct please take a look to an example which shows how to update the structure from slave.

https://github.com/alisitsyn/modbus_sup ... ster.c#L75

Who is online

Users browsing this forum: Bing [Bot], Google [Bot], michcfr, walkabout and 92 guests