RS 485 request error (Absence of LRC bit)
-
- Posts: 8
- Joined: Wed Jan 18, 2023 5:25 am
RS 485 request error (Absence of LRC bit)
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
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
-
- Posts: 211
- Joined: Fri Feb 01, 2019 4:02 pm
- Contact:
Re: RS 485 request error (Absence of LRC bit)
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 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
The communication mode is defined in your code below according to kconfig settings:
https://docs.espressif.com/projects/esp ... on-options
Let me know if you need further help.
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
Please use the
and change the communication mode to RTU in the menu (https://github.com/espressif/esp-modbus ... jbuild#L69).idf.py menuconfig
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
};
.................................
Let me know if you need further help.
-
- Posts: 8
- Joined: Wed Jan 18, 2023 5:25 am
Re: RS 485 request error (Absence of LRC bit)
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
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
-
- Posts: 211
- Joined: Fri Feb 01, 2019 4:02 pm
- Contact:
Re: RS 485 request error (Absence of LRC bit)
Please see the response here: https://github.com/espressif/esp-idf/issues/7527
-
- Posts: 8
- Joined: Wed Jan 18, 2023 5:25 am
Re: RS 485 request error (Absence of LRC bit)
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 in the input structure, it is still giving me an invalid argument error.
Can you explain why is this happing ?? here is my code
The changes I have made are in the of the line
Code: Select all
float input_data8;
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 },
};
Code: Select all
input_reg_params_t
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.
-
- Posts: 211
- Joined: Fri Feb 01, 2019 4:02 pm
- Contact:
-
- Posts: 8
- Joined: Wed Jan 18, 2023 5:25 am
Re: RS 485 request error (Absence of LRC bit)
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:
It's quite obvious that the 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
Here the master is able to send a request after
is executed. The
and
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
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, ¶m_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));
Code: Select all
mbc_master_get_parameter
we get:esp_modbus_master.c
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;
}
Code: Select all
**error = master_interface_ptr->get_parameter(cid, name, value, type);**
function is defined asget_parameter
Code: Select all
iface_get_parameter get_parameter; /*!< Interface get_parameter method */
Code: Select all
typedef esp_err_t (*iface_get_parameter)(uint16_t, char*, uint8_t*, uint8_t*); /*!< Interface get_parameter method */
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
-
- Posts: 211
- Joined: Fri Feb 01, 2019 4:02 pm
- Contact:
Re: RS 485 request error (Absence of LRC bit)
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
https://github.com/espressif/esp-modbus ... ter.c#L460
https://github.com/alisitsyn/modbus_sup ... ster.c#L75
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
A Callbacks to store data: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.
https://github.com/espressif/esp-modbus ... ter.c#L460
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.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.
https://github.com/alisitsyn/modbus_sup ... ster.c#L75
Who is online
Users browsing this forum: Bing [Bot] and 57 guests