Adicionar primeiro
This commit is contained in:
@@ -0,0 +1,735 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// mbc_serial_master.c
|
||||
// Serial master implementation of the Modbus controller
|
||||
|
||||
#include <sys/time.h> // for calculation of time stamp in milliseconds
|
||||
#include "esp_log.h" // for log_write
|
||||
#include <string.h> // for memcpy
|
||||
#include "freertos/FreeRTOS.h" // for task creation and queue access
|
||||
#include "freertos/task.h" // for task api access
|
||||
#include "freertos/event_groups.h" // for event groups
|
||||
#include "freertos/queue.h" // for queue api access
|
||||
#include "mb_m.h" // for modbus stack master types definition
|
||||
#include "port.h" // for port callback functions
|
||||
#include "mbutils.h" // for mbutils functions definition for stack callback
|
||||
#include "sdkconfig.h" // for KConfig values
|
||||
#include "esp_modbus_common.h" // for common types
|
||||
#include "esp_modbus_master.h" // for public master types
|
||||
#include "mbc_master.h" // for private master types
|
||||
#include "mbc_serial_master.h" // for serial master create function and types
|
||||
|
||||
// The Modbus Transmit Poll function defined in port
|
||||
extern BOOL xMBMasterPortSerialTxPoll(void);
|
||||
|
||||
/*-----------------------Master mode use these variables----------------------*/
|
||||
// Actual wait time depends on the response timer
|
||||
#define MB_SERIAL_API_RESP_TICS (pdMS_TO_TICKS(MB_MAX_RESPONSE_TIME_MS))
|
||||
|
||||
static mb_master_interface_t* mbm_interface_ptr = NULL;
|
||||
static const char *TAG = "MB_CONTROLLER_MASTER";
|
||||
|
||||
// Modbus event processing task
|
||||
static void modbus_master_task(void *pvParameters)
|
||||
{
|
||||
// The interface must be initialized before start of state machine
|
||||
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
// Main Modbus stack processing cycle
|
||||
for (;;) {
|
||||
// Wait for poll events
|
||||
BaseType_t status = xEventGroupWaitBits(mbm_opts->mbm_event_group,
|
||||
(BaseType_t)(MB_EVENT_STACK_STARTED),
|
||||
pdFALSE, // do not clear bits
|
||||
pdFALSE,
|
||||
portMAX_DELAY);
|
||||
// Check if stack started then poll for data
|
||||
if (status & MB_EVENT_STACK_STARTED) {
|
||||
(void)eMBMasterPoll(); // Allow stack to process data
|
||||
// Send response buffer if ready to be sent
|
||||
BOOL xSentState = xMBMasterPortSerialTxPoll();
|
||||
if (xSentState) {
|
||||
// Let state machine know that request frame was transmitted out
|
||||
(void)xMBMasterPortEventPost(EV_MASTER_FRAME_SENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setup Modbus controller parameters
|
||||
static esp_err_t mbc_serial_master_setup(void* comm_info)
|
||||
{
|
||||
MB_MASTER_CHECK((mbm_interface_ptr != NULL),
|
||||
ESP_ERR_INVALID_STATE,
|
||||
"Master interface uninitialized.");
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
|
||||
const mb_master_comm_info_t* comm_info_ptr = (mb_master_comm_info_t*)comm_info;
|
||||
// Check communication options
|
||||
MB_MASTER_CHECK(((comm_info_ptr->mode == MB_MODE_RTU) || (comm_info_ptr->mode == MB_MODE_ASCII)),
|
||||
ESP_ERR_INVALID_ARG, "mb incorrect mode = (%u).", (unsigned)comm_info_ptr->mode);
|
||||
MB_MASTER_CHECK((comm_info_ptr->port <= UART_NUM_MAX), ESP_ERR_INVALID_ARG,
|
||||
"mb wrong port to set = (%u).", (unsigned)comm_info_ptr->port);
|
||||
MB_MASTER_CHECK((comm_info_ptr->parity <= UART_PARITY_ODD), ESP_ERR_INVALID_ARG,
|
||||
"mb wrong parity option = (%u).", (unsigned)comm_info_ptr->parity);
|
||||
// Save the communication options
|
||||
mbm_opts->mbm_comm = *(mb_communication_info_t*)comm_info_ptr;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Modbus controller stack start function
|
||||
static esp_err_t mbc_serial_master_start(void)
|
||||
{
|
||||
MB_MASTER_CHECK((mbm_interface_ptr != NULL),
|
||||
ESP_ERR_INVALID_STATE,
|
||||
"Master interface uninitialized.");
|
||||
eMBErrorCode status = MB_EIO;
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
const mb_communication_info_t* comm_info = (mb_communication_info_t*)&mbm_opts->mbm_comm;
|
||||
|
||||
// Initialize Modbus stack using mbcontroller parameters
|
||||
status = eMBMasterSerialInit((eMBMode)comm_info->mode, (UCHAR)comm_info->port,
|
||||
(ULONG)comm_info->baudrate,
|
||||
MB_PORT_PARITY_GET(comm_info->parity));
|
||||
|
||||
MB_MASTER_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
|
||||
"mb stack initialization failure, eMBInit() returns (0x%x).", (int)status);
|
||||
status = eMBMasterEnable();
|
||||
MB_MASTER_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
|
||||
"mb stack set slave ID failure, eMBMasterEnable() returned (0x%x).", (int)status);
|
||||
// Set the mbcontroller start flag
|
||||
EventBits_t flag = xEventGroupSetBits(mbm_opts->mbm_event_group,
|
||||
(EventBits_t)MB_EVENT_STACK_STARTED);
|
||||
MB_MASTER_CHECK((flag & MB_EVENT_STACK_STARTED),
|
||||
ESP_ERR_INVALID_STATE, "mb stack start event set error.");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Modbus controller destroy function
|
||||
static esp_err_t mbc_serial_master_destroy(void)
|
||||
{
|
||||
MB_MASTER_CHECK((mbm_interface_ptr != NULL),
|
||||
ESP_ERR_INVALID_STATE,
|
||||
"Master interface uninitialized.");
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
eMBErrorCode mb_error = MB_ENOERR;
|
||||
// Stop polling by clearing correspondent bit in the event group
|
||||
EventBits_t flag = xEventGroupClearBits(mbm_opts->mbm_event_group,
|
||||
(EventBits_t)MB_EVENT_STACK_STARTED);
|
||||
MB_MASTER_CHECK((flag & MB_EVENT_STACK_STARTED),
|
||||
ESP_ERR_INVALID_STATE, "mb stack stop event failure.");
|
||||
// Desable and then destroy the Modbus stack
|
||||
mb_error = eMBMasterDisable();
|
||||
MB_MASTER_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE, "mb stack disable failure.");
|
||||
(void)vTaskDelete(mbm_opts->mbm_task_handle);
|
||||
(void)vEventGroupDelete(mbm_opts->mbm_event_group);
|
||||
mb_error = eMBMasterClose();
|
||||
MB_MASTER_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE,
|
||||
"mb stack close failure returned (0x%x).", (int)mb_error);
|
||||
free(mbm_interface_ptr); // free the memory allocated for options
|
||||
vMBPortSetMode((UCHAR)MB_PORT_INACTIVE);
|
||||
mbm_interface_ptr = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Set Modbus parameter description table
|
||||
static esp_err_t mbc_serial_master_set_descriptor(const mb_parameter_descriptor_t* descriptor, const uint16_t num_elements)
|
||||
{
|
||||
MB_MASTER_CHECK((descriptor != NULL),
|
||||
ESP_ERR_INVALID_ARG, "mb incorrect descriptor.");
|
||||
MB_MASTER_CHECK((num_elements >= 1),
|
||||
ESP_ERR_INVALID_ARG, "mb table size is incorrect.");
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
const mb_parameter_descriptor_t *reg_ptr = descriptor;
|
||||
// Go through all items in the table to check all Modbus registers
|
||||
for (uint16_t counter = 0; counter < (num_elements); counter++, reg_ptr++)
|
||||
{
|
||||
// Below is the code to check consistency of the table format and required fields.
|
||||
MB_MASTER_CHECK((reg_ptr->cid == counter),
|
||||
ESP_ERR_INVALID_ARG, "mb descriptor cid field is incorrect.");
|
||||
MB_MASTER_CHECK((reg_ptr->param_key != NULL),
|
||||
ESP_ERR_INVALID_ARG, "mb descriptor param key is incorrect.");
|
||||
MB_MASTER_CHECK((reg_ptr->mb_size > 0),
|
||||
ESP_ERR_INVALID_ARG, "mb descriptor param size is incorrect.");
|
||||
}
|
||||
mbm_opts->mbm_param_descriptor_table = descriptor;
|
||||
mbm_opts->mbm_param_descriptor_size = num_elements;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Send custom Modbus request defined as mb_param_request_t structure
|
||||
static esp_err_t mbc_serial_master_send_request(mb_param_request_t* request, void* data_ptr)
|
||||
{
|
||||
MB_MASTER_CHECK((mbm_interface_ptr != NULL),
|
||||
ESP_ERR_INVALID_STATE,
|
||||
"Master interface uninitialized.");
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
MB_MASTER_CHECK((request != NULL),
|
||||
ESP_ERR_INVALID_ARG, "mb request structure.");
|
||||
MB_MASTER_CHECK((data_ptr != NULL),
|
||||
ESP_ERR_INVALID_ARG, "mb incorrect data pointer.");
|
||||
|
||||
eMBMasterReqErrCode mb_error = MB_MRE_MASTER_BUSY;
|
||||
esp_err_t error = ESP_FAIL;
|
||||
|
||||
if (xMBMasterRunResTake(MB_SERIAL_API_RESP_TICS)) {
|
||||
|
||||
uint8_t mb_slave_addr = request->slave_addr;
|
||||
uint8_t mb_command = request->command;
|
||||
uint16_t mb_offset = request->reg_start;
|
||||
uint16_t mb_size = request->reg_size;
|
||||
|
||||
// Set the buffer for callback function processing of received data
|
||||
mbm_opts->mbm_reg_buffer_ptr = (uint8_t*)data_ptr;
|
||||
mbm_opts->mbm_reg_buffer_size = mb_size;
|
||||
|
||||
vMBMasterRunResRelease();
|
||||
|
||||
// Calls appropriate request function to send request and waits response
|
||||
switch(mb_command)
|
||||
{
|
||||
case MB_FUNC_READ_COILS:
|
||||
mb_error = eMBMasterReqReadCoils((UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
||||
(USHORT)mb_size , (LONG)MB_SERIAL_API_RESP_TICS );
|
||||
break;
|
||||
case MB_FUNC_WRITE_SINGLE_COIL:
|
||||
mb_error = eMBMasterReqWriteCoil((UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
||||
*(USHORT*)data_ptr, (LONG)MB_SERIAL_API_RESP_TICS );
|
||||
break;
|
||||
case MB_FUNC_WRITE_MULTIPLE_COILS:
|
||||
mb_error = eMBMasterReqWriteMultipleCoils((UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
||||
(USHORT)mb_size, (UCHAR*)data_ptr, (LONG)MB_SERIAL_API_RESP_TICS);
|
||||
break;
|
||||
case MB_FUNC_READ_DISCRETE_INPUTS:
|
||||
mb_error = eMBMasterReqReadDiscreteInputs((UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
||||
(USHORT)mb_size, (LONG)MB_SERIAL_API_RESP_TICS );
|
||||
break;
|
||||
case MB_FUNC_READ_HOLDING_REGISTER:
|
||||
mb_error = eMBMasterReqReadHoldingRegister((UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
||||
(USHORT)mb_size, (LONG)MB_SERIAL_API_RESP_TICS );
|
||||
break;
|
||||
case MB_FUNC_WRITE_REGISTER:
|
||||
mb_error = eMBMasterReqWriteHoldingRegister( (UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
||||
*(USHORT*)data_ptr, (LONG)MB_SERIAL_API_RESP_TICS );
|
||||
break;
|
||||
|
||||
case MB_FUNC_WRITE_MULTIPLE_REGISTERS:
|
||||
mb_error = eMBMasterReqWriteMultipleHoldingRegister( (UCHAR)mb_slave_addr,
|
||||
(USHORT)mb_offset, (USHORT)mb_size,
|
||||
(USHORT*)data_ptr, (LONG)MB_SERIAL_API_RESP_TICS );
|
||||
break;
|
||||
case MB_FUNC_READWRITE_MULTIPLE_REGISTERS:
|
||||
mb_error = eMBMasterReqReadWriteMultipleHoldingRegister( (UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
||||
(USHORT)mb_size, (USHORT*)data_ptr,
|
||||
(USHORT)mb_offset, (USHORT)mb_size,
|
||||
(LONG)MB_SERIAL_API_RESP_TICS );
|
||||
break;
|
||||
case MB_FUNC_READ_INPUT_REGISTER:
|
||||
mb_error = eMBMasterReqReadInputRegister( (UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
||||
(USHORT)mb_size, (LONG) MB_SERIAL_API_RESP_TICS );
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "%s: Incorrect function in request (%u) ", __FUNCTION__, (unsigned)mb_command);
|
||||
mb_error = MB_MRE_NO_REG;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Propagate the Modbus errors to higher level
|
||||
switch(mb_error)
|
||||
{
|
||||
case MB_MRE_NO_ERR:
|
||||
error = ESP_OK;
|
||||
break;
|
||||
|
||||
case MB_MRE_NO_REG:
|
||||
error = ESP_ERR_NOT_SUPPORTED; // Invalid register request
|
||||
break;
|
||||
|
||||
case MB_MRE_TIMEDOUT:
|
||||
error = ESP_ERR_TIMEOUT; // Slave did not send response
|
||||
break;
|
||||
|
||||
case MB_MRE_EXE_FUN:
|
||||
case MB_MRE_REV_DATA:
|
||||
error = ESP_ERR_INVALID_RESPONSE; // Invalid response from slave
|
||||
break;
|
||||
|
||||
case MB_MRE_MASTER_BUSY:
|
||||
error = ESP_ERR_INVALID_STATE; // Master is busy (previous request is pending)
|
||||
break;
|
||||
|
||||
default:
|
||||
ESP_LOGE(TAG, "%s: Incorrect return code (%x) ", __FUNCTION__, (int)mb_error);
|
||||
error = ESP_FAIL;
|
||||
break;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static esp_err_t mbc_serial_master_get_cid_info(uint16_t cid, const mb_parameter_descriptor_t** param_buffer)
|
||||
{
|
||||
MB_MASTER_CHECK((mbm_interface_ptr != NULL),
|
||||
ESP_ERR_INVALID_STATE,
|
||||
"Master interface uninitialized.");
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
|
||||
MB_MASTER_CHECK((param_buffer != NULL),
|
||||
ESP_ERR_INVALID_ARG, "mb incorrect data buffer pointer.");
|
||||
MB_MASTER_CHECK((mbm_opts->mbm_param_descriptor_table != NULL),
|
||||
ESP_ERR_INVALID_ARG, "mb incorrect descriptor table or not set.");
|
||||
MB_MASTER_CHECK((cid < mbm_opts->mbm_param_descriptor_size),
|
||||
ESP_ERR_NOT_FOUND, "mb incorrect cid of characteristic.");
|
||||
|
||||
// It is assumed that characteristics cid increased in the table
|
||||
const mb_parameter_descriptor_t* reg_info = &mbm_opts->mbm_param_descriptor_table[cid];
|
||||
|
||||
MB_MASTER_CHECK((reg_info->param_key != NULL),
|
||||
ESP_ERR_INVALID_ARG, "mb incorrect characteristic key.");
|
||||
*param_buffer = reg_info;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Helper function to get modbus command for each type of Modbus register area
|
||||
static uint8_t mbc_serial_master_get_command(mb_param_type_t param_type, mb_param_mode_t mode)
|
||||
{
|
||||
uint8_t command = 0;
|
||||
switch(param_type)
|
||||
{ //
|
||||
case MB_PARAM_HOLDING:
|
||||
command = (mode == MB_PARAM_WRITE) ?
|
||||
MB_FUNC_WRITE_MULTIPLE_REGISTERS :
|
||||
MB_FUNC_READ_HOLDING_REGISTER;
|
||||
break;
|
||||
case MB_PARAM_INPUT:
|
||||
command = MB_FUNC_READ_INPUT_REGISTER;
|
||||
break;
|
||||
case MB_PARAM_COIL:
|
||||
command = (mode == MB_PARAM_WRITE) ?
|
||||
MB_FUNC_WRITE_MULTIPLE_COILS :
|
||||
MB_FUNC_READ_COILS;
|
||||
break;
|
||||
case MB_PARAM_DISCRETE:
|
||||
if (mode != MB_PARAM_WRITE) {
|
||||
command = MB_FUNC_READ_DISCRETE_INPUTS;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "%s: Incorrect mode (%u)", __FUNCTION__, (unsigned)mode);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "%s: Incorrect param type (%u)", __FUNCTION__, (unsigned)param_type);
|
||||
break;
|
||||
}
|
||||
return command;
|
||||
}
|
||||
|
||||
// Helper to search parameter by name in the parameter description table
|
||||
// and fills Modbus request fields accordingly
|
||||
static esp_err_t mbc_serial_master_set_request(char* name, mb_param_mode_t mode,
|
||||
mb_param_request_t* request,
|
||||
mb_parameter_descriptor_t* reg_data)
|
||||
{
|
||||
MB_MASTER_CHECK((mbm_interface_ptr != NULL),
|
||||
ESP_ERR_INVALID_STATE,
|
||||
"Master interface uninitialized.");
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
esp_err_t error = ESP_ERR_NOT_FOUND;
|
||||
MB_MASTER_CHECK((name != NULL),
|
||||
ESP_ERR_INVALID_ARG, "mb incorrect parameter name.");
|
||||
MB_MASTER_CHECK((request != NULL),
|
||||
ESP_ERR_INVALID_ARG, "mb incorrect request parameter.");
|
||||
MB_MASTER_CHECK((mode <= MB_PARAM_WRITE),
|
||||
ESP_ERR_INVALID_ARG, "mb incorrect mode.");
|
||||
MB_MASTER_ASSERT(mbm_opts->mbm_param_descriptor_table != NULL);
|
||||
const mb_parameter_descriptor_t* reg_ptr = mbm_opts->mbm_param_descriptor_table;
|
||||
for (uint16_t counter = 0; counter < (mbm_opts->mbm_param_descriptor_size); counter++, reg_ptr++)
|
||||
{
|
||||
// Check the cid of the parameter is equal to record number in the table
|
||||
// Check the length of name and parameter key strings from table
|
||||
size_t param_key_len = strlen((const char*)reg_ptr->param_key);
|
||||
if (param_key_len != strlen((const char*)name)) {
|
||||
continue; // The length of strings is different then check next record in the table
|
||||
}
|
||||
// Compare the name of parameter with parameter key from table
|
||||
int comp_result = memcmp((const void*)name, (const void*)reg_ptr->param_key, (size_t)param_key_len);
|
||||
if (comp_result == 0) {
|
||||
// Returns an error in case of broadcast read request
|
||||
if (!reg_ptr->mb_slave_addr && (mode == MB_PARAM_READ)) {
|
||||
error = ESP_ERR_INVALID_ARG;
|
||||
break;
|
||||
}
|
||||
// The correct line is found in the table and reg_ptr points to the found parameter description
|
||||
request->slave_addr = reg_ptr->mb_slave_addr;
|
||||
request->reg_start = reg_ptr->mb_reg_start;
|
||||
request->reg_size = reg_ptr->mb_size;
|
||||
request->command = mbc_serial_master_get_command(reg_ptr->mb_param_type, mode);
|
||||
MB_MASTER_CHECK((request->command > 0), ESP_ERR_INVALID_ARG,
|
||||
"mb incorrect command or parameter type.");
|
||||
if (reg_data != NULL) {
|
||||
*reg_data = *reg_ptr; // Set the cid registered parameter data
|
||||
}
|
||||
error = ESP_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
static esp_err_t mbc_serial_master_get_parameter(uint16_t cid, char* name, uint8_t* value, 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.");
|
||||
MB_MASTER_CHECK((value != NULL), ESP_ERR_INVALID_ARG, "value pointer is incorrect.");
|
||||
esp_err_t error = ESP_ERR_INVALID_RESPONSE;
|
||||
mb_param_request_t request ;
|
||||
mb_parameter_descriptor_t reg_info = { 0 };
|
||||
uint8_t* pdata = NULL;
|
||||
|
||||
error = mbc_serial_master_set_request(name, MB_PARAM_READ, &request, ®_info);
|
||||
if ((error == ESP_OK) && (cid == reg_info.cid)) {
|
||||
MB_MASTER_CHECK(((reg_info.mb_size << 1) >= reg_info.param_size),
|
||||
MB_EILLSTATE,
|
||||
"Incorrect characteristic data.");
|
||||
// alloc buffer to store parameter data
|
||||
pdata = calloc(1, (reg_info.mb_size << 1));
|
||||
if (!pdata) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
error = mbc_serial_master_send_request(&request, pdata);
|
||||
if (error == ESP_OK) {
|
||||
// If data pointer is NULL then we don't need to set value (it is still in the cache of cid)
|
||||
if (value != NULL) {
|
||||
error = mbc_master_set_param_data((void*)value, (void*)pdata,
|
||||
reg_info.param_type, reg_info.param_size);
|
||||
if (error != ESP_OK) {
|
||||
ESP_LOGE(TAG, "fail to set parameter data.");
|
||||
error = ESP_ERR_INVALID_STATE;
|
||||
} else {
|
||||
ESP_LOGD(TAG, "%s: Good response for get cid(%u) = %s",
|
||||
__FUNCTION__, (unsigned)reg_info.cid, (char*)esp_err_to_name(error));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ESP_LOGD(TAG, "%s: Bad response to get cid(%u) = %s",
|
||||
__FUNCTION__, (unsigned)reg_info.cid, (char*)esp_err_to_name(error));
|
||||
}
|
||||
free(pdata);
|
||||
// Set the type of parameter found in the table
|
||||
*type = reg_info.param_type;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "%s: The requested cid(%u) is not configured correctly. Check data dictionary for correctness.",
|
||||
__FUNCTION__, (unsigned)cid);
|
||||
error = ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// Set parameter value for characteristic selected by name and cid
|
||||
static esp_err_t mbc_serial_master_set_parameter(uint16_t cid, char* name, uint8_t* value, uint8_t *type)
|
||||
{
|
||||
MB_MASTER_CHECK((name != NULL), ESP_ERR_INVALID_ARG, "mb incorrect descriptor.");
|
||||
MB_MASTER_CHECK((value != NULL), ESP_ERR_INVALID_ARG, "value pointer is incorrect.");
|
||||
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 };
|
||||
uint8_t* pdata = NULL;
|
||||
|
||||
error = mbc_serial_master_set_request(name, MB_PARAM_WRITE, &request, ®_info);
|
||||
if ((error == ESP_OK) && (cid == reg_info.cid)) {
|
||||
MB_MASTER_CHECK(((reg_info.mb_size << 1) >= reg_info.param_size),
|
||||
MB_EILLSTATE,
|
||||
"Incorrect characteristic data.");
|
||||
pdata = calloc(1, (reg_info.mb_size << 1)); // alloc parameter buffer
|
||||
if (!pdata) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
// Transfer value of characteristic into parameter buffer
|
||||
error = mbc_master_set_param_data((void*)pdata, (void*)value,
|
||||
reg_info.param_type, reg_info.param_size);
|
||||
if (error != ESP_OK) {
|
||||
ESP_LOGE(TAG, "fail to set parameter data.");
|
||||
free(pdata);
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
// Send request to write characteristic data
|
||||
error = mbc_serial_master_send_request(&request, pdata);
|
||||
if (error == ESP_OK) {
|
||||
ESP_LOGD(TAG, "%s: Good response for set cid(%u) = %s",
|
||||
__FUNCTION__, (unsigned)reg_info.cid, (char*)esp_err_to_name(error));
|
||||
} else {
|
||||
ESP_LOGD(TAG, "%s: Bad response to set 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;
|
||||
free(pdata);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "%s: The requested cid(%u) is not configured correctly. Check data dictionary for correctness.",
|
||||
__FUNCTION__, (unsigned)cid);
|
||||
error = ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
/* ----------------------- Callback functions for Modbus stack ---------------------------------*/
|
||||
// These are executed by modbus stack to read appropriate type of registers.
|
||||
|
||||
/**
|
||||
* Modbus master input register callback function.
|
||||
*
|
||||
* @param pucRegBuffer input register buffer
|
||||
* @param usAddress input register address
|
||||
* @param usNRegs input register number
|
||||
*
|
||||
* @return result
|
||||
*/
|
||||
// Callback function for reading of MB Input Registers
|
||||
eMBErrorCode eMBRegInputCBSerialMaster(UCHAR * pucRegBuffer, USHORT usAddress,
|
||||
USHORT usNRegs)
|
||||
{
|
||||
MB_MASTER_CHECK((mbm_interface_ptr != NULL),
|
||||
MB_EILLSTATE,
|
||||
"Master interface uninitialized.");
|
||||
MB_MASTER_CHECK((pucRegBuffer != NULL), MB_EINVAL,
|
||||
"Master stack processing error.");
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
// Number of input registers to be transferred
|
||||
USHORT usRegInputNregs = (USHORT)mbm_opts->mbm_reg_buffer_size;
|
||||
UCHAR* pucInputBuffer = (UCHAR*)mbm_opts->mbm_reg_buffer_ptr; // Get instance address
|
||||
USHORT usRegs = usNRegs;
|
||||
eMBErrorCode eStatus = MB_ENOERR;
|
||||
// If input or configuration parameters are incorrect then return an error to stack layer
|
||||
if ((pucInputBuffer != NULL)
|
||||
&& (usNRegs >= 1)
|
||||
&& (usRegInputNregs == usRegs)) {
|
||||
while (usRegs > 0) {
|
||||
_XFER_2_RD(pucInputBuffer, pucRegBuffer);
|
||||
usRegs -= 1;
|
||||
}
|
||||
} else {
|
||||
eStatus = MB_ENOREG;
|
||||
}
|
||||
return eStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modbus master holding register callback function.
|
||||
*
|
||||
* @param pucRegBuffer holding register buffer
|
||||
* @param usAddress holding register address
|
||||
* @param usNRegs holding register number
|
||||
* @param eMode read or write
|
||||
*
|
||||
* @return result
|
||||
*/
|
||||
// Callback function for reading of MB Holding Registers
|
||||
// Executed by stack when request to read/write holding registers is received
|
||||
eMBErrorCode eMBRegHoldingCBSerialMaster(UCHAR * pucRegBuffer, USHORT usAddress,
|
||||
USHORT usNRegs, eMBRegisterMode eMode)
|
||||
{
|
||||
MB_MASTER_CHECK((mbm_interface_ptr != NULL),
|
||||
MB_EILLSTATE,
|
||||
"Master interface uninitialized.");
|
||||
MB_MASTER_CHECK((pucRegBuffer != NULL), MB_EINVAL,
|
||||
"Master stack processing error.");
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
USHORT usRegHoldingNregs = (USHORT)mbm_opts->mbm_reg_buffer_size;
|
||||
UCHAR* pucHoldingBuffer = (UCHAR*)mbm_opts->mbm_reg_buffer_ptr;
|
||||
eMBErrorCode eStatus = MB_ENOERR;
|
||||
USHORT usRegs = usNRegs;
|
||||
// Check input and configuration parameters for correctness
|
||||
if ((pucHoldingBuffer != NULL)
|
||||
&& (usRegHoldingNregs == usNRegs)
|
||||
&& (usNRegs >= 1)) {
|
||||
switch (eMode) {
|
||||
case MB_REG_WRITE:
|
||||
while (usRegs > 0) {
|
||||
_XFER_2_RD(pucRegBuffer, pucHoldingBuffer);
|
||||
usRegs -= 1;
|
||||
};
|
||||
break;
|
||||
case MB_REG_READ:
|
||||
while (usRegs > 0) {
|
||||
_XFER_2_WR(pucHoldingBuffer, pucRegBuffer);
|
||||
pucHoldingBuffer += 2;
|
||||
usRegs -= 1;
|
||||
};
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
eStatus = MB_ENOREG;
|
||||
}
|
||||
return eStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modbus master coils callback function.
|
||||
*
|
||||
* @param pucRegBuffer coils buffer
|
||||
* @param usAddress coils address
|
||||
* @param usNCoils coils number
|
||||
* @param eMode read or write
|
||||
*
|
||||
* @return result
|
||||
*/
|
||||
// Callback function for reading of MB Coils Registers
|
||||
eMBErrorCode eMBRegCoilsCBSerialMaster(UCHAR* pucRegBuffer, USHORT usAddress,
|
||||
USHORT usNCoils, eMBRegisterMode eMode)
|
||||
{
|
||||
MB_MASTER_CHECK((mbm_interface_ptr != NULL),
|
||||
MB_EILLSTATE, "Master interface uninitialized.");
|
||||
MB_MASTER_CHECK((pucRegBuffer != NULL),
|
||||
MB_EINVAL, "Master stack processing error.");
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
USHORT usRegCoilNregs = (USHORT)mbm_opts->mbm_reg_buffer_size;
|
||||
UCHAR* pucRegCoilsBuf = (UCHAR*)mbm_opts->mbm_reg_buffer_ptr;
|
||||
eMBErrorCode eStatus = MB_ENOERR;
|
||||
USHORT iRegIndex;
|
||||
USHORT usCoils = usNCoils;
|
||||
usAddress--; // The address is already + 1
|
||||
if ((usRegCoilNregs >= 1)
|
||||
&& (pucRegCoilsBuf != NULL)
|
||||
&& (usNCoils == usRegCoilNregs)) {
|
||||
iRegIndex = (usAddress % 8);
|
||||
switch (eMode) {
|
||||
case MB_REG_WRITE:
|
||||
while (usCoils > 0) {
|
||||
UCHAR ucResult = xMBUtilGetBits(pucRegCoilsBuf, iRegIndex - (usAddress % 8), 1);
|
||||
xMBUtilSetBits(pucRegBuffer, iRegIndex - (usAddress % 8) , 1, ucResult);
|
||||
iRegIndex++;
|
||||
usCoils--;
|
||||
}
|
||||
break;
|
||||
case MB_REG_READ:
|
||||
while (usCoils > 0) {
|
||||
UCHAR ucResult = xMBUtilGetBits(pucRegBuffer, iRegIndex - (usAddress % 8), 1);
|
||||
xMBUtilSetBits(pucRegCoilsBuf, iRegIndex - (usAddress % 8), 1, ucResult);
|
||||
iRegIndex++;
|
||||
usCoils--;
|
||||
}
|
||||
break;
|
||||
} // switch ( eMode )
|
||||
} else {
|
||||
// If the configuration or input parameters are incorrect then return error to stack
|
||||
eStatus = MB_ENOREG;
|
||||
}
|
||||
return eStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modbus master discrete callback function.
|
||||
*
|
||||
* @param pucRegBuffer discrete buffer
|
||||
* @param usAddress discrete address
|
||||
* @param usNDiscrete discrete number
|
||||
*
|
||||
* @return result
|
||||
*/
|
||||
// Callback function for reading of MB Discrete Input Registers
|
||||
eMBErrorCode eMBRegDiscreteCBSerialMaster(UCHAR * pucRegBuffer, USHORT usAddress,
|
||||
USHORT usNDiscrete)
|
||||
{
|
||||
MB_MASTER_CHECK((mbm_interface_ptr != NULL),
|
||||
MB_EILLSTATE, "Master interface uninitialized.");
|
||||
MB_MASTER_CHECK((pucRegBuffer != NULL),
|
||||
MB_EINVAL, "Master stack processing error.");
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
USHORT usRegDiscreteNregs = (USHORT)mbm_opts->mbm_reg_buffer_size;
|
||||
UCHAR* pucRegDiscreteBuf = (UCHAR*)mbm_opts->mbm_reg_buffer_ptr;
|
||||
eMBErrorCode eStatus = MB_ENOERR;
|
||||
USHORT iRegBitIndex, iNReg;
|
||||
UCHAR* pucDiscreteInputBuf;
|
||||
iNReg = usNDiscrete / 8 + 1;
|
||||
pucDiscreteInputBuf = (UCHAR*) pucRegDiscreteBuf;
|
||||
// It is already plus one in Modbus function method.
|
||||
usAddress--;
|
||||
if ((usRegDiscreteNregs >= 1)
|
||||
&& (pucRegDiscreteBuf != NULL)
|
||||
&& (usNDiscrete >= 1)) {
|
||||
iRegBitIndex = (USHORT)(usAddress) % 8; // Get bit index
|
||||
while (iNReg > 1)
|
||||
{
|
||||
xMBUtilSetBits(pucDiscreteInputBuf++, iRegBitIndex - (usAddress % 8), 8, *pucRegBuffer++);
|
||||
iNReg--;
|
||||
}
|
||||
// last discrete
|
||||
usNDiscrete = usNDiscrete % 8;
|
||||
// xMBUtilSetBits has bug when ucNBits is zero
|
||||
if (usNDiscrete != 0)
|
||||
{
|
||||
xMBUtilSetBits(pucDiscreteInputBuf, iRegBitIndex - (usAddress % 8), usNDiscrete, *pucRegBuffer++);
|
||||
}
|
||||
} else {
|
||||
eStatus = MB_ENOREG;
|
||||
}
|
||||
return eStatus;
|
||||
}
|
||||
|
||||
// Initialization of resources for Modbus serial master controller
|
||||
esp_err_t mbc_serial_master_create(void** handler)
|
||||
{
|
||||
// Allocate space for master interface structure
|
||||
if (mbm_interface_ptr == NULL) {
|
||||
mbm_interface_ptr = malloc(sizeof(mb_master_interface_t));
|
||||
}
|
||||
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
|
||||
|
||||
// Initialize interface properties
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
mbm_opts->port_type = MB_PORT_SERIAL_MASTER;
|
||||
|
||||
vMBPortSetMode((UCHAR)MB_PORT_SERIAL_MASTER);
|
||||
|
||||
mbm_opts->mbm_comm.mode = MB_MODE_RTU;
|
||||
mbm_opts->mbm_comm.port = MB_UART_PORT;
|
||||
mbm_opts->mbm_comm.baudrate = MB_DEVICE_SPEED;
|
||||
mbm_opts->mbm_comm.parity = MB_PARITY_NONE;
|
||||
|
||||
// Initialization of active context of the modbus controller
|
||||
BaseType_t status = 0;
|
||||
// Parameter change notification queue
|
||||
mbm_opts->mbm_event_group = xEventGroupCreate();
|
||||
MB_MASTER_CHECK((mbm_opts->mbm_event_group != NULL),
|
||||
ESP_ERR_NO_MEM, "mb event group error.");
|
||||
// Create modbus controller task
|
||||
status = xTaskCreatePinnedToCore((void*)&modbus_master_task,
|
||||
"modbus_matask",
|
||||
MB_CONTROLLER_STACK_SIZE,
|
||||
NULL, // No parameters
|
||||
MB_CONTROLLER_PRIORITY,
|
||||
&mbm_opts->mbm_task_handle,
|
||||
MB_PORT_TASK_AFFINITY);
|
||||
if (status != pdPASS) {
|
||||
vTaskDelete(mbm_opts->mbm_task_handle);
|
||||
MB_MASTER_CHECK((status == pdPASS), ESP_ERR_NO_MEM,
|
||||
"mb controller task creation error, xTaskCreate() returns (0x%x).", (int)status);
|
||||
}
|
||||
MB_MASTER_ASSERT(mbm_opts->mbm_task_handle != NULL); // The task is created but handle is incorrect
|
||||
|
||||
// Initialize public interface methods of the interface
|
||||
mbm_interface_ptr->init = mbc_serial_master_create;
|
||||
mbm_interface_ptr->destroy = mbc_serial_master_destroy;
|
||||
mbm_interface_ptr->setup = mbc_serial_master_setup;
|
||||
mbm_interface_ptr->start = mbc_serial_master_start;
|
||||
mbm_interface_ptr->get_cid_info = mbc_serial_master_get_cid_info;
|
||||
mbm_interface_ptr->get_parameter = mbc_serial_master_get_parameter;
|
||||
mbm_interface_ptr->send_request = mbc_serial_master_send_request;
|
||||
mbm_interface_ptr->set_descriptor = mbc_serial_master_set_descriptor;
|
||||
mbm_interface_ptr->set_parameter = mbc_serial_master_set_parameter;
|
||||
|
||||
mbm_interface_ptr->master_reg_cb_discrete = eMBRegDiscreteCBSerialMaster;
|
||||
mbm_interface_ptr->master_reg_cb_input = eMBRegInputCBSerialMaster;
|
||||
mbm_interface_ptr->master_reg_cb_holding = eMBRegHoldingCBSerialMaster;
|
||||
mbm_interface_ptr->master_reg_cb_coils = eMBRegCoilsCBSerialMaster;
|
||||
|
||||
*handler = mbm_interface_ptr;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// mbc_serial_master.h Modbus controller serial master implementation header file
|
||||
|
||||
#ifndef _MODBUS_SERIAL_CONTROLLER_MASTER
|
||||
#define _MODBUS_SERIAL_CONTROLLER_MASTER
|
||||
|
||||
#include <stdint.h> // for standard int types definition
|
||||
#include <stddef.h> // for NULL and std defines
|
||||
#include "soc/soc.h" // for BITN definitions
|
||||
#include "esp_err.h" // for esp_err_t
|
||||
#include "esp_modbus_common.h" // for common defines
|
||||
|
||||
/**
|
||||
* @brief Initialize Modbus controller and stack
|
||||
*
|
||||
* @param[out] handler handler(pointer) to master data structure
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_NO_MEM Parameter error
|
||||
*/
|
||||
esp_err_t mbc_serial_master_create(void** handler);
|
||||
|
||||
#endif // _MODBUS_SERIAL_CONTROLLER_MASTER
|
||||
Reference in New Issue
Block a user