Contains two different component folders per each implementation (serial_master and serial_slave) with concrete ports. Added common public api for master and slave and common interface for master and slave implementation. Add support of cmake system (added cmake files). Added sdkconfig.defaults files for slave and master modbus examples. Updated make file and KConfig for freemodbus component Update according to review and fix doxygen warnings Fix Doxyfile to pass documentation build Update headers and change interface file names as per review comments Merge branch feature/freemodbus_move_rs485_mode_control Update after review: The stack modbus folder updated to support master and slave ports together and moved into freemodbus/modbus Stack and port files updated to remove duplicated simbols Make file, KConfig and CMakeLists.txt updated to compile master and slave stacks, common interface and concrete implementations of ports Stack callback functions execute callbacks using interface pointer from concrete port implementation User can instantiate any of concrete port using common API (only one concrete port at a time) and it does not require to select port by KConfig Port pins and mode configuration moved into example files from port files to allow user select pins and port mode (customer request) Changes tested using pymodbus, ModbusPoll and communication between two boards Updated DoxyFile according to public include path Fix maximum instance size for slave (merge from master of customer issue) Fix critical section issue TW#28622 (change spin lock based critical section to semaphore) Move serial port files into component port folder for master and slave accordingly Fix example issue showed in the log when IO slave is not configured correctly Fix conflicts while merging from origin/master Fix errors handling in modbus controller interface + some final corrections according to review Update maximum allowed number of slaves in the network segment Fix bug with incorrect coils read mask Closes https://github.com/espressif/esp-idf/issues/858
764 lines
33 KiB
C
764 lines
33 KiB
C
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
// 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----------------------*/
|
|
|
|
// The response time is average processing time + data transmission (higher on lower speeds)
|
|
// ~resp_time_ms = min_pcocessing_time_ms + ((2 packets * (header_size + packet_bytes)) * 11 bits in byte * 1000 ms_in_sec) / transmit_speed))
|
|
#define MB_RESPONSE_TIMEOUT(size) pdMS_TO_TICKS(30 + (2 * ((size << 1) + 8) * 11 * 1000 / mb_speed))
|
|
|
|
static mb_master_interface_t* mbm_interface_ptr = NULL; //&default_interface_inst;
|
|
|
|
// 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 response was transmitted out
|
|
(void)xMBMasterPortEventPost(EV_MASTER_FRAME_TRANSMITTED);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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 = (0x%x).",
|
|
(uint32_t)comm_info_ptr->mode);
|
|
MB_MASTER_CHECK((comm_info_ptr->port <= UART_NUM_2), ESP_ERR_INVALID_ARG,
|
|
"mb wrong port to set = (0x%x).", (uint32_t)comm_info_ptr->port);
|
|
MB_MASTER_CHECK((comm_info_ptr->parity <= UART_PARITY_EVEN), ESP_ERR_INVALID_ARG,
|
|
"mb wrong parity option = (0x%x).", (uint32_t)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 = eMBMasterInit((eMBMode)comm_info->mode, (UCHAR)comm_info->port,
|
|
(ULONG)comm_info->baudrate, (eMBParity)comm_info->parity);
|
|
MB_MASTER_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
|
|
"mb stack initialization failure, eMBInit() returns (0x%x).", status);
|
|
status = eMBMasterEnable();
|
|
MB_MASTER_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
|
|
"mb stack set slave ID failure, eMBEnable() returned (0x%x).", (uint32_t)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).", (uint32_t)mb_error);
|
|
free(mbm_interface_ptr); // free the memory allocated for options
|
|
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_NO_REG;
|
|
esp_err_t error = ESP_FAIL;
|
|
|
|
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;
|
|
uint32_t mb_speed = mbm_opts->mbm_comm.baudrate;
|
|
|
|
// Timeout value for packet processing
|
|
uint32_t timeout = 0;
|
|
size_t pack_length = 0;
|
|
|
|
// 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;
|
|
|
|
// Calls appropriate request function to send request and waits response
|
|
switch(mb_command)
|
|
{
|
|
case MB_FUNC_READ_COILS:
|
|
pack_length = (mb_size >= 8) ? (mb_size >> 3) : 1;
|
|
timeout = MB_RESPONSE_TIMEOUT(pack_length);
|
|
mb_error = eMBMasterReqReadCoils((UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
|
(USHORT)mb_size , (LONG)timeout );
|
|
break;
|
|
case MB_FUNC_WRITE_SINGLE_COIL:
|
|
timeout = MB_RESPONSE_TIMEOUT(1);
|
|
mb_error = eMBMasterReqWriteCoil((UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
|
*(USHORT*)data_ptr, (LONG)timeout );
|
|
break;
|
|
case MB_FUNC_WRITE_MULTIPLE_COILS:
|
|
pack_length = (mb_size >= 8) ? (mb_size >> 3) : 1;
|
|
timeout = MB_RESPONSE_TIMEOUT(pack_length);
|
|
mb_error = eMBMasterReqWriteMultipleCoils((UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
|
(USHORT)mb_size, (UCHAR*)data_ptr, (LONG)timeout);
|
|
break;
|
|
case MB_FUNC_READ_DISCRETE_INPUTS:
|
|
pack_length = (mb_size >= 8) ? (mb_size >> 3) : 1;
|
|
timeout = MB_RESPONSE_TIMEOUT(pack_length);
|
|
mb_error = eMBMasterReqReadDiscreteInputs((UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
|
(USHORT)mb_size, (LONG)timeout );
|
|
break;
|
|
case MB_FUNC_READ_HOLDING_REGISTER:
|
|
timeout = MB_RESPONSE_TIMEOUT(mb_size);
|
|
mb_error = eMBMasterReqReadHoldingRegister((UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
|
(USHORT)mb_size, (LONG)timeout );
|
|
break;
|
|
case MB_FUNC_WRITE_REGISTER:
|
|
timeout = MB_RESPONSE_TIMEOUT(1);
|
|
mb_error = eMBMasterReqWriteHoldingRegister( (UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
|
*(USHORT*)data_ptr, (LONG)timeout );
|
|
break;
|
|
|
|
case MB_FUNC_WRITE_MULTIPLE_REGISTERS:
|
|
timeout = MB_RESPONSE_TIMEOUT(mb_size);
|
|
mb_error = eMBMasterReqWriteMultipleHoldingRegister( (UCHAR)mb_slave_addr,
|
|
(USHORT)mb_offset, (USHORT)mb_size,
|
|
(USHORT*)data_ptr, (LONG)timeout );
|
|
break;
|
|
case MB_FUNC_READWRITE_MULTIPLE_REGISTERS:
|
|
timeout = MB_RESPONSE_TIMEOUT(mb_size << 1);
|
|
mb_error = eMBMasterReqReadWriteMultipleHoldingRegister( (UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
|
(USHORT)mb_size, (USHORT*)data_ptr,
|
|
(USHORT)mb_offset, (USHORT)mb_size,
|
|
(LONG)timeout );
|
|
break;
|
|
case MB_FUNC_READ_INPUT_REGISTER:
|
|
timeout = MB_RESPONSE_TIMEOUT(mb_size);
|
|
mb_error = eMBMasterReqReadInputRegister( (UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
|
(USHORT)mb_size, (LONG) timeout );
|
|
break;
|
|
default:
|
|
ESP_LOGE(MB_MASTER_TAG, "%s: Incorrect function in request (%u) ",
|
|
__FUNCTION__, 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;
|
|
break;
|
|
|
|
case MB_MRE_TIMEDOUT:
|
|
error = ESP_ERR_TIMEOUT;
|
|
break;
|
|
|
|
case MB_MRE_EXE_FUN:
|
|
case MB_MRE_REV_DATA:
|
|
error = ESP_ERR_INVALID_RESPONSE;
|
|
break;
|
|
default:
|
|
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(MB_MASTER_TAG, "%s: Incorrect mode (%u)",
|
|
__FUNCTION__, (uint8_t)mode);
|
|
}
|
|
break;
|
|
default:
|
|
ESP_LOGE(MB_MASTER_TAG, "%s: Incorrect param type (%u)",
|
|
__FUNCTION__, param_type);
|
|
break;
|
|
}
|
|
return command;
|
|
}
|
|
|
|
// Helper function to set parameter buffer according to its type
|
|
static esp_err_t mbc_serial_master_set_param_data(void* dest, void* src, mb_descr_type_t param_type, size_t param_size)
|
|
{
|
|
esp_err_t err = ESP_OK;
|
|
MB_MASTER_CHECK((dest != NULL),
|
|
ESP_ERR_INVALID_ARG, "incorrect parameter pointer.");
|
|
MB_MASTER_CHECK((src != NULL),
|
|
ESP_ERR_INVALID_ARG, "incorrect parameter pointer.");
|
|
// Transfer parameter data into value of characteristic
|
|
switch(param_type)
|
|
{
|
|
case PARAM_TYPE_U8:
|
|
*((uint8_t*)dest) = *((uint8_t*)src);
|
|
break;
|
|
case PARAM_TYPE_U16:
|
|
*((uint16_t*)dest) = *((uint16_t*)src);
|
|
break;
|
|
case PARAM_TYPE_U32:
|
|
*((uint32_t*)dest) = *((uint32_t*)src);
|
|
break;
|
|
case PARAM_TYPE_FLOAT:
|
|
*((float*)dest) = *(float*)src;
|
|
break;
|
|
case PARAM_TYPE_ASCII:
|
|
memcpy((void*)dest, (void*)src, (size_t)param_size);
|
|
break;
|
|
default:
|
|
ESP_LOGE(MB_MASTER_TAG, "%s: Incorrect param type (%u).",
|
|
__FUNCTION__, (uint16_t)param_type);
|
|
err = ESP_ERR_NOT_SUPPORTED;
|
|
break;
|
|
}
|
|
return err;
|
|
}
|
|
|
|
// 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
|
|
uint8_t comp_result = memcmp((const char*)name, (const char*)reg_ptr->param_key, (size_t)param_key_len);
|
|
if (comp_result == 0) {
|
|
// 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;
|
|
}
|
|
|
|
// Get parameter data for corresponding characteristic
|
|
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.");
|
|
esp_err_t error = ESP_ERR_INVALID_RESPONSE;
|
|
mb_param_request_t request ;
|
|
mb_parameter_descriptor_t reg_info = { 0 };
|
|
uint8_t param_buffer[PARAM_MAX_SIZE] = { 0 };
|
|
|
|
error = mbc_serial_master_set_request(name, MB_PARAM_READ, &request, ®_info);
|
|
if ((error == ESP_OK) && (cid == reg_info.cid)) {
|
|
error = mbc_serial_master_send_request(&request, ¶m_buffer[0]);
|
|
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_serial_master_set_param_data((void*)value, (void*)¶m_buffer[0],
|
|
reg_info.param_type, reg_info.param_size);
|
|
MB_MASTER_CHECK((error == ESP_OK), ESP_ERR_INVALID_STATE, "fail to set parameter data.");
|
|
}
|
|
ESP_LOGD(MB_MASTER_TAG, "%s: Good response for get cid(%u) = %s",
|
|
__FUNCTION__, (int)reg_info.cid, (char*)esp_err_to_name(error));
|
|
} else {
|
|
ESP_LOGD(MB_MASTER_TAG, "%s: Bad response to get cid(%u) = %s",
|
|
__FUNCTION__, 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_LOGD(MB_MASTER_TAG, "%s: The cid(%u) not found in the data dictionary.",
|
|
__FUNCTION__, reg_info.cid);
|
|
}
|
|
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 param_buffer[PARAM_MAX_SIZE] = { 0 };
|
|
|
|
error = mbc_serial_master_set_request(name, MB_PARAM_WRITE, &request, ®_info);
|
|
if ((error == ESP_OK) && (cid == reg_info.cid)) {
|
|
// Transfer value of characteristic into parameter buffer
|
|
error = mbc_serial_master_set_param_data((void*)¶m_buffer[0], (void*)value,
|
|
reg_info.param_type, reg_info.param_size);
|
|
MB_MASTER_CHECK((error == ESP_OK),
|
|
ESP_ERR_INVALID_STATE, "failure to set parameter data.");
|
|
// Send request to write characteristic data
|
|
error = mbc_serial_master_send_request(&request, ¶m_buffer[0]);
|
|
if (error == ESP_OK) {
|
|
ESP_LOGD(MB_MASTER_TAG, "%s: Good response for set cid(%u) = %s",
|
|
__FUNCTION__, (int)reg_info.cid, (char*)esp_err_to_name(error));
|
|
} else {
|
|
ESP_LOGD(MB_MASTER_TAG, "%s: Bad response to set cid(%u) = %s",
|
|
__FUNCTION__, 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(MB_MASTER_TAG, "%s: The requested cid(%u) not found in the data dictionary.",
|
|
__FUNCTION__, reg_info.cid);
|
|
}
|
|
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((UCHAR*)pucRegCoilsBuf, iRegIndex, 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((uint8_t*)pucRegCoilsBuf, iRegIndex, 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, 8, *pucRegBuffer++);
|
|
iNReg--;
|
|
}
|
|
// last discrete
|
|
usNDiscrete = usNDiscrete % 8;
|
|
// xMBUtilSetBits has bug when ucNBits is zero
|
|
if (usNDiscrete != 0)
|
|
{
|
|
xMBUtilSetBits(pucDiscreteInputBuf, iRegBitIndex, usNDiscrete, *pucRegBuffer++);
|
|
}
|
|
} else {
|
|
eStatus = MB_ENOREG;
|
|
}
|
|
return eStatus;
|
|
}
|
|
|
|
// Initialization of resources for Modbus serial master controller
|
|
esp_err_t mbc_serial_master_create(mb_port_type_t port_type, void** handler)
|
|
{
|
|
MB_MASTER_CHECK((port_type == MB_PORT_SERIAL_MASTER),
|
|
ESP_ERR_INVALID_STATE, "mb incorrect port selected = %u.",
|
|
(uint32_t)port_type);
|
|
// 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;
|
|
|
|
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 = xTaskCreate((void*)&modbus_master_task,
|
|
"modbus_matask",
|
|
MB_CONTROLLER_STACK_SIZE,
|
|
NULL, // No parameters
|
|
MB_CONTROLLER_PRIORITY,
|
|
&mbm_opts->mbm_task_handle);
|
|
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).",
|
|
(uint32_t)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;
|
|
}
|