Unable to get parameter response using MODBUS RTU

Kirti_Lalwani10
Posts: 1
Joined: Fri Nov 17, 2023 11:58 am

Unable to get parameter response using MODBUS RTU

Postby Kirti_Lalwani10 » Fri Nov 17, 2023 12:07 pm

/*
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#include "string.h"
#include "esp_log.h"
#include "modbus_params.h" // for modbus parameters structures
#include "mbcontroller.h"
#include "sdkconfig.h"

#define MB_PORT_NUM (2) // Number of UART port used for Modbus connection
#define MB_DEV_SPEED (9600) // The communication speed of the UART

// Note: Some pins on target chip cannot be assigned for UART communication.
// See UART documentation for selected board and target to configure pins using Kconfig.

// The number of parameters that intended to be used in the particular control process
#define MASTER_MAX_CIDS num_device_parameters

// Number of reading of parameters from slave
#define MASTER_MAX_RETRY 30

// Timeout to update cid over Modbus
#define UPDATE_CIDS_TIMEOUT_MS (2000)
#define UPDATE_CIDS_TIMEOUT_TICS (UPDATE_CIDS_TIMEOUT_MS / portTICK_PERIOD_MS)

// Timeout between polls
#define POLL_TIMEOUT_MS (100)
#define POLL_TIMEOUT_TICS (POLL_TIMEOUT_MS / portTICK_PERIOD_MS)

// 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 = 5 // 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_INP_DATA_0 = 0,
// CID_HOLD_DATA_0,
// CID_INP_DATA_1,
// CID_HOLD_DATA_1,
// CID_INP_DATA_2,
// CID_HOLD_DATA_2,
// CID_HOLD_TEST_REG,
// CID_RELAY_P1,
// CID_RELAY_P2,
CID_SERIAL_NO_1 = 0, // , Address :- 40001
CID_SERIAL_NO_2, // , Address :- 40002
CID_SERIAL_NO_3, // , Address :- 40003
CID_POWER_INFO, // In kVA , Address :- 40004
CID_SOLAR_POWER_INFO, // In KW , Address :- 40005
CID_GRID_OR_RECTIFIER_POWER_INFO, // In KW , Address :- 40006
CID_BATTERY_TYPE_INFO, // , Address :- 40007
CID_BATTERY_VOLTAGE_INFO, // In Volt , Address :- 40008
CID_PRODUCT_TYPE_INFO, // , Address :- 40009
CID_CONFIG_INFO, // , Address :- 40010
CID_COUNT
};

// 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_INP_DATA_0, STR("Data_channel_0"), STR("Volts"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0, 2,
// INPUT_OFFSET(input_data0), PARAM_TYPE_FLOAT, 4, OPTS( -10, 10, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
// { CID_HOLD_DATA_0, STR("Humidity_1"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0, 2,
// HOLD_OFFSET(holding_data7), PARAM_TYPE_FLOAT, 4, OPTS( 0, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
// { CID_INP_DATA_1, STR("Temperature_1"), STR("C"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 2, 2,
// INPUT_OFFSET(input_data1), PARAM_TYPE_FLOAT, 4, OPTS( -40, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
// { CID_HOLD_DATA_1, STR("Humidity_2"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 4, 2, //address from 2 to & holding 1 to holding 4
// HOLD_OFFSET(holding_data4), PARAM_TYPE_FLOAT, 4, OPTS( 0, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
// { CID_INP_DATA_2, STR("Temperature_2"), STR("C"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 4, 2,
// INPUT_OFFSET(input_data2), PARAM_TYPE_FLOAT, 4, OPTS( -40, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
// { CID_HOLD_DATA_2, STR("Humidity_3"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 6, 2, //address from 4 to 6 & holding data2 to holding data 5
// HOLD_OFFSET(holding_data5), PARAM_TYPE_FLOAT, 4, OPTS( 0, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
// { CID_HOLD_TEST_REG, STR("Test_regs"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 70, 58,
// HOLD_OFFSET(test_regs), PARAM_TYPE_ASCII, 116, OPTS( 0, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
// { CID_RELAY_P1, STR("RelayP1"), STR("on/off"), MB_DEVICE_ADDR1, MB_PARAM_COIL, 0, 8,
// COIL_OFFSET(coils_port0), PARAM_TYPE_U16, 2, OPTS( BIT1, 0, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
// { CID_RELAY_P2, STR("RelayP2"), STR("on/off"), MB_DEVICE_ADDR1, MB_PARAM_COIL, 8, 8,
// COIL_OFFSET(coils_port1), PARAM_TYPE_U16, 2, OPTS( BIT0, 0, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
// Changed Param type from PARAM_TYPE_U32 to PARM_TYPE_U16 and size from 4 to 2
{CID_SERIAL_NO_1, STR("SERIAL_NO_1"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0, 2,
HOLD_OFFSET(SERIAL_NO_1), PARAM_TYPE_U16, 4 , OPTS(0, 65535, 1), PAR_PERMS_READ_WRITE_TRIGGER},
{CID_SERIAL_NO_2, STR("SERIAL_NO_2"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 1, 2,
HOLD_OFFSET(SERIAL_NO_2), PARAM_TYPE_U16, 4 , OPTS(0, 65535, 1), PAR_PERMS_READ_WRITE_TRIGGER},
{CID_SERIAL_NO_3, STR("SERIAL_NO_3"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 2, 2,
HOLD_OFFSET(SERIAL_NO_3), PARAM_TYPE_U16, 4 , OPTS(0, 65535, 1), PAR_PERMS_READ_WRITE_TRIGGER},
{CID_POWER_INFO, STR("POWER_INFO"), STR("KVA"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 3, 2,
HOLD_OFFSET(POWER_INFO), PARAM_TYPE_U16, 4 , OPTS(0, 65535, 1), PAR_PERMS_READ_WRITE_TRIGGER},
{CID_SOLAR_POWER_INFO, STR("SOLAR_POWER_INFO"), STR("KW"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 4, 2,
HOLD_OFFSET(SOLAR_POWER_INFO), PARAM_TYPE_U16, 4 , OPTS(0, 65535, 1), PAR_PERMS_READ_WRITE_TRIGGER},
{CID_GRID_OR_RECTIFIER_POWER_INFO, STR("GRID_OR_RECTIFIER_POWER_INFO"), STR("KW"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 5, 2,
HOLD_OFFSET(GRID_OR_RECTIFIER_POWER_INFO), PARAM_TYPE_U16, 4 , OPTS(0, 65535, 1), PAR_PERMS_READ_WRITE_TRIGGER},
{CID_BATTERY_TYPE_INFO, STR("BATTERY_TYPE_INFO"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 6, 2,
HOLD_OFFSET(BATTERY_TYPE_INFO), PARAM_TYPE_U16, 4 , OPTS(0, 65535, 1), PAR_PERMS_READ_WRITE_TRIGGER},
{CID_BATTERY_VOLTAGE_INFO, STR("BATTERY_VOLTAGE_INFO"), STR("VOLTS"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 7, 2,
HOLD_OFFSET(BATTERY_VOLTAGE_INFO), PARAM_TYPE_U16, 4 , OPTS(0, 65535, 1), PAR_PERMS_READ_WRITE_TRIGGER},
{CID_PRODUCT_TYPE_INFO, STR("PRODUCT_TYPE_INFO"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 8, 2,
HOLD_OFFSET(PRODUCT_TYPE_INFO), PARAM_TYPE_U16, 4 , OPTS(0, 65535, 1), PAR_PERMS_READ_WRITE_TRIGGER},
{CID_CONFIG_INFO, STR("CONFIG_INFO"), STR("__"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 9, 2,
HOLD_OFFSET(CONFIG_INFO), PARAM_TYPE_U16, 4 , OPTS(0, 65535, 1), PAR_PERMS_READ_WRITE_TRIGGER},
};



// Calculate number of parameters in the table
const uint16_t num_device_parameters = (sizeof(device_parameters)/sizeof(device_parameters[0]));

// The function to get pointer to parameter storage (instance) according to parameter description table
static void* master_get_param_data(const mb_parameter_descriptor_t* param_descriptor)
{
assert(param_descriptor != NULL);
void* instance_ptr = NULL;
if (param_descriptor->param_offset != 0) {
switch(param_descriptor->mb_param_type)
{
case MB_PARAM_HOLDING:
instance_ptr = ((void*)&holding_reg_params + param_descriptor->param_offset - 1);
break;
case MB_PARAM_INPUT:
instance_ptr = ((void*)&input_reg_params + param_descriptor->param_offset - 1);
break;
case MB_PARAM_COIL:
instance_ptr = ((void*)&coil_reg_params + param_descriptor->param_offset - 1);
break;
case MB_PARAM_DISCRETE:
instance_ptr = ((void*)&discrete_reg_params + param_descriptor->param_offset - 1);
break;
default:
instance_ptr = NULL;
break;
}
} else {
ESP_LOGE(TAG, "Wrong parameter offset for CID #%d", param_descriptor->cid);
assert(instance_ptr != NULL);
}
return instance_ptr;
}

// User operation function to read slave values and check alarm
static void master_operation_func(void *arg)
{
esp_err_t err = ESP_OK;
uint16_t value = 0; //changing float to uint16_t
bool alarm_state = false;
const mb_parameter_descriptor_t* param_descriptor = NULL;

ESP_LOGI(TAG, "Start modbus test...");

for(uint16_t retry = 0; retry <= MASTER_MAX_RETRY && (!alarm_state); retry++) {
// Read all found characteristics from slave(s)
for (uint16_t cid = 0; (err != ESP_ERR_NOT_FOUND) && cid < MASTER_MAX_CIDS; cid++) //changed CID from 0 to 1
{
// 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);
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_ASCII) /*&&
(param_descriptor->cid == CID_HOLD_TEST_REG)*/) {
// Check for long array of registers of type PARAM_TYPE_ASCII
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);
// Initialize data of test array and write to slave
// if (*(uint32_t*)temp_data_ptr != 0xAAAAAAAA) {
// memset((void*)temp_data_ptr, 0xAA, param_descriptor->param_size);
// *(uint32_t*)temp_data_ptr = 0xAAAAAAAA;
// err = mbc_master_set_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), write 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) write fail, err = 0x%x (%s).",
// param_descriptor->cid,
// (char*)param_descriptor->param_key,
// (int)err,
// (char*)esp_err_to_name(err));
// }
// }
} else {
ESP_LOGE(TAG, "Characteristic #%d (%s) read fail, err = 0x%x (%s).",
param_descriptor->cid,
(char*)param_descriptor->param_key,
(int)err,
(char*)esp_err_to_name(err));
}
} else {
err = mbc_master_get_parameter(cid, (char*)param_descriptor->param_key,
(uint8_t*)&value, &type);
if (err == ESP_OK) {
*(uint16_t*)temp_data_ptr = value; // changed (float*) to (uint16_t*)
if ((param_descriptor->mb_param_type == MB_PARAM_HOLDING) /*||
(param_descriptor->mb_param_type == MB_PARAM_INPUT)*/) {
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = %u (0x%x) read successful.", //changed %f to %u
param_descriptor->cid,
(char*)param_descriptor->param_key,
(char*)param_descriptor->param_units,
value,
*(uint16_t*)temp_data_ptr); //changed (uint32_t*) to (uint16_t*)
printf("value :- %u\n",value);
printf("temp_data_ptr :- %u\n",(*(uint16_t*)temp_data_ptr));
value = 0;
(*(uint16_t*)temp_data_ptr) = 0;
if (((value > param_descriptor->param_opts.max) ||
(value < param_descriptor->param_opts.min))) {
alarm_state = true;
break;
}
} else {
uint16_t state = *(uint16_t*)temp_data_ptr;
const char* rw_str = (state & param_descriptor->param_opts.opt1) ? "ON" : "OFF";
ESP_LOGI(TAG, "Characteristic #%d %s (%s) value = %s (0x%x) read successful.",
((param_descriptor->cid)+1), // changed cid to cid + 1
(char*)param_descriptor->param_key,
(char*)param_descriptor->param_units,
(const char*)rw_str,
*(uint16_t*)temp_data_ptr);
if (state & param_descriptor->param_opts.opt1) {
alarm_state = true;
break;
}
}
} else {
ESP_LOGE(TAG, "Characteristic #%d (%s) read fail, err = 0x%x (%s).",
param_descriptor->cid,
(char*)param_descriptor->param_key,
(int)err,
(char*)esp_err_to_name(err));
printf("value :- %u\n",value);
printf("temp_data_ptr :- %u\n",(*(uint16_t*)temp_data_ptr));
value = 0;
(*(uint16_t*)temp_data_ptr) = 0;
}
}
vTaskDelay(POLL_TIMEOUT_TICS); // timeout between polls
}
}
vTaskDelay(UPDATE_CIDS_TIMEOUT_TICS); //
}

if (alarm_state) {
ESP_LOGI(TAG, "Alarm triggered by cid #%d.",
param_descriptor->cid);
} else {
ESP_LOGE(TAG, "Alarm is not triggered after %d retries.",
MASTER_MAX_RETRY);
}
ESP_LOGI(TAG, "Destroy master...");
ESP_ERROR_CHECK(mbc_master_destroy());
}

// 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
};
void* master_handler = NULL;

esp_err_t err = mbc_master_init(MB_PORT_SERIAL_MASTER, &master_handler);
MB_RETURN_ON_FALSE((master_handler != NULL), ESP_ERR_INVALID_STATE, TAG,
"mb controller initialization fail.");
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb controller initialization fail, returns(0x%x).",
(uint32_t)err);
err = mbc_master_setup((void*)&comm);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb controller setup fail, returns(0x%x).",
(uint32_t)err);

// Set UART pin numbers
err = uart_set_pin(MB_PORT_NUM, CONFIG_MB_UART_TXD, CONFIG_MB_UART_RXD,
CONFIG_MB_UART_RTS, UART_PIN_NO_CHANGE);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb serial set pin failure, uart_set_pin() returned (0x%x).", (uint32_t)err);

err = mbc_master_start();
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb controller start fail, returns(0x%x).",
(uint32_t)err);

// Set driver mode to Half Duplex
err = uart_set_mode(MB_PORT_NUM, UART_MODE_RS485_HALF_DUPLEX);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb serial set mode failure, uart_set_mode() returned (0x%x).", (uint32_t)err);

vTaskDelay(5);
err = mbc_master_set_descriptor(&device_parameters[0], num_device_parameters);
MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb controller set descriptor fail, returns(0x%x).",
(uint32_t)err);
ESP_LOGI(TAG, "Modbus master stack initialized...");
return err;
}

void app_main(void)
{
// Initialization of device peripheral and objects
ESP_ERROR_CHECK(master_init());
vTaskDelay(10);

master_operation_func(NULL);
}
  1. [Codebox=c file=Untitled.c]
[/Codebox]
Attachments
Error_received_in_code.txt
(2.54 KiB) Downloaded 197 times

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

Re: Unable to get parameter response using MODBUS RTU

Postby ESP_alisitsyn » Thu Nov 23, 2023 9:20 am

@ Kirti_Lalwani10,

What kind of slave device you are using with master? The error messages says that your device reports failure or sends incorrect response to request from master. Also, please send the communication log that will allow to recognize what is the reason for the errors.
As an alternative way you can change the log verbosity to DEBUG in kconfig menu and the input packets will be printed out.

Who is online

Users browsing this forum: MichaelS and 92 guests