freemodbus: add support for list of area descriptors for each register area

add multi register area descriptors into concrete port (initial)
add create/destroy of area descriptors into concrete port
add  the list of descriptors in common slave interface structure and init/destroy in concrete slave port
move r/w callback functions into common slave
final update of common slave interface wrappers add override API option in concrete port
update slave examples to check new  feature
This commit is contained in:
Alex Lisitsyn
2021-01-28 12:29:32 +08:00
committed by Michael (XIAO Xufeng)
parent 445b1a6717
commit 96b77a28b1
7 changed files with 483 additions and 637 deletions

View File

@ -13,9 +13,11 @@
* limitations under the License.
*/
#include "esp_err.h" // for esp_err_t
#include "sdkconfig.h" // for KConfig defines
#include "esp_err.h" // for esp_err_t
#include "sdkconfig.h" // for KConfig defines
#include "mbc_slave.h" // for slave private type definitions
#include "mbutils.h" // for stack bit setting utilities
#include "esp_modbus_common.h" // for common defines
#include "esp_modbus_slave.h" // for public slave defines
#include "esp_modbus_callbacks.h" // for modbus callbacks function pointers declaration
@ -37,12 +39,59 @@ static uint8_t mb_slave_id[] = { MB_ID_BYTE0(MB_CONTROLLER_SLAVE_ID),
#endif
#define REG_SIZE(type, nregs) ((type == MB_PARAM_INPUT) || (type == MB_PARAM_HOLDING)) ? (nregs >> 1) : (nregs << 3)
// Common interface pointer for slave port
static mb_slave_interface_t* slave_interface_ptr = NULL;
// Searches the register in the area specified by type, returns descriptor if found, else NULL
static mb_descr_entry_t* mbc_slave_find_reg_descriptor(mb_param_type_t type, uint16_t addr, size_t regs)
{
mb_descr_entry_t* it;
uint16_t reg_size = 0;
mb_slave_options_t* mbs_opts = &slave_interface_ptr->opts;
if (LIST_EMPTY(&mbs_opts->mbs_area_descriptors[type])) {
return NULL;
}
// search for the register in each area
for (it = LIST_FIRST(&mbs_opts->mbs_area_descriptors[type]); it != NULL; it = LIST_NEXT(it, entries)) {
reg_size = REG_SIZE(type, it->size);
if ((addr >= it->start_offset)
&& (it->p_data)
&& (regs >= 1)
&& ((addr + regs) <= (it->start_offset + reg_size + 1))
&& (reg_size >= 1)) {
return it;
}
}
return NULL;
}
static void mbc_slave_free_descriptors(void) {
mb_descr_entry_t* it;
mb_slave_options_t* mbs_opts = &slave_interface_ptr->opts;
for (int descr_type = 0; descr_type < MB_PARAM_COUNT; descr_type++) {
for (it = LIST_FIRST(&mbs_opts->mbs_area_descriptors[descr_type]); it != NULL; it = LIST_NEXT(it, entries)) {
LIST_REMOVE(it, entries);
free(it);
}
}
}
void mbc_slave_init_iface(void* handler)
{
slave_interface_ptr = (mb_slave_interface_t*) handler;
mb_slave_options_t* mbs_opts = &slave_interface_ptr->opts;
// Initialize list head for register areas
LIST_INIT(&mbs_opts->mbs_area_descriptors[MB_PARAM_INPUT]);
LIST_INIT(&mbs_opts->mbs_area_descriptors[MB_PARAM_HOLDING]);
LIST_INIT(&mbs_opts->mbs_area_descriptors[MB_PARAM_COIL]);
LIST_INIT(&mbs_opts->mbs_area_descriptors[MB_PARAM_DISCRETE]);
}
/**
@ -65,6 +114,10 @@ esp_err_t mbc_slave_destroy(void)
ESP_ERR_INVALID_STATE,
"Slave destroy failure error=(0x%x).",
error);
// Destroy all opened descriptors
mbc_slave_free_descriptors();
free(slave_interface_ptr);
slave_interface_ptr = NULL;
return error;
}
@ -157,75 +210,328 @@ esp_err_t mbc_slave_set_descriptor(mb_register_area_descriptor_t descr_data)
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
MB_SLAVE_CHECK((slave_interface_ptr->set_descriptor != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
error = slave_interface_ptr->set_descriptor(descr_data);
MB_SLAVE_CHECK((error == ESP_OK),
ESP_ERR_INVALID_STATE,
"Slave set descriptor failure error=(0x%x).",
(uint16_t)error);
if (slave_interface_ptr->set_descriptor != NULL) {
error = slave_interface_ptr->set_descriptor(descr_data);
MB_SLAVE_CHECK((error == ESP_OK),
ESP_ERR_INVALID_STATE,
"Slave set descriptor failure error=(0x%x).",
(uint16_t)error);
} else {
mb_slave_options_t* mbs_opts = &slave_interface_ptr->opts;
// Check if the address is already in the descriptor list
mb_descr_entry_t* it = mbc_slave_find_reg_descriptor(descr_data.type, descr_data.start_offset, 1);
MB_SLAVE_CHECK((it == NULL), ESP_ERR_INVALID_ARG, "mb incorrect descriptor or already defined.");
mb_descr_entry_t* new_descr = (mb_descr_entry_t*) heap_caps_malloc(sizeof(mb_descr_entry_t),
MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT);
MB_SLAVE_CHECK((new_descr != NULL), ESP_ERR_NO_MEM, "mb can not allocate memory for descriptor.");
new_descr->start_offset = descr_data.start_offset;
new_descr->type = descr_data.type;
new_descr->p_data = descr_data.address;
new_descr->size = descr_data.size;
LIST_INSERT_HEAD(&mbs_opts->mbs_area_descriptors[descr_data.type], new_descr, entries);
error = ESP_OK;
}
return error;
}
/**
* Below are stack callback functions to read/write registers
// The helper function to get time stamp in microseconds
static uint64_t mbc_slave_get_time_stamp(void)
{
uint64_t time_stamp = esp_timer_get_time();
return time_stamp;
}
// Helper function to send parameter information to application task
static esp_err_t mbc_slave_send_param_info(mb_event_group_t par_type, uint16_t mb_offset,
uint8_t* par_address, uint16_t par_size)
{
MB_SLAVE_ASSERT(slave_interface_ptr != NULL);
mb_slave_options_t* mbs_opts = &slave_interface_ptr->opts;
esp_err_t error = ESP_FAIL;
mb_param_info_t par_info;
// Check if queue is not full the send parameter information
par_info.type = par_type;
par_info.size = par_size;
par_info.address = par_address;
par_info.time_stamp = mbc_slave_get_time_stamp();
par_info.mb_offset = mb_offset;
BaseType_t status = xQueueSend(mbs_opts->mbs_notification_queue_handle, &par_info, MB_PAR_INFO_TOUT);
if (pdTRUE == status) {
ESP_LOGD(MB_SLAVE_TAG, "Queue send parameter info (type, address, size): %d, 0x%.4x, %d",
par_type, (uint32_t)par_address, par_size);
error = ESP_OK;
} else if (errQUEUE_FULL == status) {
ESP_LOGD(MB_SLAVE_TAG, "Parameter queue is overflowed.");
}
return error;
}
// Helper function to send notification
static esp_err_t mbc_slave_send_param_access_notification(mb_event_group_t event)
{
MB_SLAVE_ASSERT(slave_interface_ptr != NULL);
mb_slave_options_t* mbs_opts = &slave_interface_ptr->opts;
esp_err_t err = ESP_FAIL;
mb_event_group_t bits = (mb_event_group_t)xEventGroupSetBits(mbs_opts->mbs_event_group, (EventBits_t)event);
if (bits & event) {
ESP_LOGD(MB_SLAVE_TAG, "The MB_REG_CHANGE_EVENT = 0x%.2x is set.", (uint8_t)event);
err = ESP_OK;
}
return err;
}
/*
* Below are the common slave read/write register callback functions
* The concrete slave port can override them using interface function pointers
*/
eMBErrorCode eMBRegDiscreteCB(UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNDiscrete)
// Callback function for reading of MB Input Registers
eMBErrorCode mbc_reg_input_slave_cb(UCHAR * reg_buffer, USHORT address, USHORT n_regs)
{
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
MB_EILLSTATE, "Slave stack uninitialized.");
MB_SLAVE_CHECK((reg_buffer != NULL),
MB_EINVAL, "Slave stack call failed.");
eMBErrorCode status = MB_ENOERR;
address--; // address of register is already +1
mb_descr_entry_t* it = mbc_slave_find_reg_descriptor(MB_PARAM_INPUT, address, n_regs);
if (it != NULL) {
uint16_t input_reg_start = (uint16_t)it->start_offset; // Get Modbus start address
uint8_t* input_buffer = (uint8_t*)it->p_data; // Get instance address
uint16_t regs = n_regs;
uint16_t reg_index;
// If input or configuration parameters are incorrect then return an error to stack layer
reg_index = (uint16_t)(address - input_reg_start);
reg_index <<= 1; // register Address to byte address
input_buffer += reg_index;
uint8_t* buffer_start = input_buffer;
while (regs > 0) {
_XFER_2_RD(reg_buffer, input_buffer);
reg_index += 2;
regs -= 1;
}
// Send access notification
(void)mbc_slave_send_param_access_notification(MB_EVENT_INPUT_REG_RD);
// Send parameter info to application task
(void)mbc_slave_send_param_info(MB_EVENT_INPUT_REG_RD, (uint16_t)address,
(uint8_t*)buffer_start, (uint16_t)n_regs);
} else {
status = MB_ENOREG;
}
return status;
}
// Callback function for reading of MB Holding Registers
// Executed by stack when request to read/write holding registers is received
eMBErrorCode mbc_reg_holding_slave_cb(UCHAR * reg_buffer, USHORT address, USHORT n_regs, eMBRegisterMode mode)
{
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
MB_EILLSTATE, "Slave stack uninitialized.");
MB_SLAVE_CHECK((reg_buffer != NULL),
MB_EINVAL, "Slave stack call failed.");
eMBErrorCode status = MB_ENOERR;
uint16_t reg_index;
address--; // address of register is already +1
mb_descr_entry_t* it = mbc_slave_find_reg_descriptor(MB_PARAM_HOLDING, address, n_regs);
if (it != NULL) {
uint16_t reg_holding_start = (uint16_t)it->start_offset; // Get Modbus start address
uint8_t* holding_buffer = (uint8_t*)it->p_data; // Get instance address
uint16_t regs = n_regs;
reg_index = (uint16_t) (address - reg_holding_start);
reg_index <<= 1; // register Address to byte address
holding_buffer += reg_index;
uint8_t* buffer_start = holding_buffer;
switch (mode) {
case MB_REG_READ:
while (regs > 0) {
_XFER_2_RD(reg_buffer, holding_buffer);
reg_index += 2;
regs -= 1;
};
// Send access notification
(void)mbc_slave_send_param_access_notification(MB_EVENT_HOLDING_REG_RD);
// Send parameter info
(void)mbc_slave_send_param_info(MB_EVENT_HOLDING_REG_RD, (uint16_t)address,
(uint8_t*)buffer_start, (uint16_t)n_regs);
break;
case MB_REG_WRITE:
while (regs > 0) {
_XFER_2_WR(holding_buffer, reg_buffer);
holding_buffer += 2;
reg_index += 2;
regs -= 1;
};
// Send access notification
(void)mbc_slave_send_param_access_notification(MB_EVENT_HOLDING_REG_WR);
// Send parameter info
(void)mbc_slave_send_param_info(MB_EVENT_HOLDING_REG_WR, (uint16_t)address,
(uint8_t*)buffer_start, (uint16_t)n_regs);
break;
}
} else {
status = MB_ENOREG;
}
return status;
}
// Callback function for reading of MB Coils Registers
eMBErrorCode mbc_reg_coils_slave_cb(UCHAR* reg_buffer, USHORT address, USHORT n_coils, eMBRegisterMode mode)
{
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
MB_EILLSTATE, "Slave stack uninitialized.");
MB_SLAVE_CHECK((reg_buffer != NULL),
MB_EINVAL, "Slave stack call failed.");
eMBErrorCode status = MB_ENOERR;
uint16_t reg_index;
uint16_t coils = n_coils;
address--; // The address is already +1
mb_descr_entry_t* it = mbc_slave_find_reg_descriptor(MB_PARAM_COIL, address, n_coils);
if (it != NULL) {
uint16_t reg_coils_start = (uint16_t)it->start_offset; // MB offset of coils
uint8_t* reg_coils_buf = (uint8_t*)it->p_data;
reg_index = (uint16_t) (address - it->start_offset);
CHAR* coils_data_buf = (CHAR*)(reg_coils_buf + (reg_index >> 3));
switch (mode) {
case MB_REG_READ:
while (coils > 0) {
uint8_t result = xMBUtilGetBits((uint8_t*)reg_coils_buf, reg_index, 1);
xMBUtilSetBits(reg_buffer, reg_index - (address - reg_coils_start), 1, result);
reg_index++;
coils--;
}
// Send an event to notify application task about event
(void)mbc_slave_send_param_access_notification(MB_EVENT_COILS_RD);
(void)mbc_slave_send_param_info(MB_EVENT_COILS_RD, (uint16_t)address,
(uint8_t*)(coils_data_buf), (uint16_t)n_coils);
break;
case MB_REG_WRITE:
while (coils > 0) {
uint8_t result = xMBUtilGetBits(reg_buffer,
reg_index - (address - reg_coils_start), 1);
xMBUtilSetBits((uint8_t*)reg_coils_buf, reg_index, 1, result);
reg_index++;
coils--;
}
// Send an event to notify application task about event
(void)mbc_slave_send_param_access_notification(MB_EVENT_COILS_WR);
(void)mbc_slave_send_param_info(MB_EVENT_COILS_WR, (uint16_t)address,
(uint8_t*)coils_data_buf, (uint16_t)n_coils);
break;
} // switch ( eMode )
} else {
// If the configuration or input parameters are incorrect then return error to stack
status = MB_ENOREG;
}
return status;
}
// Callback function for reading of MB Discrete Input Registers
eMBErrorCode mbc_reg_discrete_slave_cb(UCHAR* reg_buffer, USHORT address, USHORT n_discrete)
{
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
MB_EILLSTATE, "Slave stack uninitialized.");
MB_SLAVE_CHECK((reg_buffer != NULL),
MB_EINVAL, "Slave stack call failed.");
eMBErrorCode status = MB_ENOERR;
uint16_t reg_index;
uint16_t reg_bit_index;
uint16_t n_reg;
uint8_t* discrete_input_buf;
// It already plus one in modbus function method.
address--;
mb_descr_entry_t* it = mbc_slave_find_reg_descriptor(MB_PARAM_DISCRETE, address, n_discrete);
if (it != NULL) {
uint16_t reg_discrete_start = (uint16_t)it->start_offset; // MB offset of registers
n_reg = (n_discrete >> 3) + 1;
discrete_input_buf = (uint8_t*)it->p_data; // the storage address
reg_index = (uint16_t) (address - reg_discrete_start) / 8; // Get register index in the buffer for bit number
reg_bit_index = (uint16_t)(address - reg_discrete_start) % 8; // Get bit index
uint8_t* temp_buf = &discrete_input_buf[reg_index];
while (n_reg > 0) {
*reg_buffer++ = xMBUtilGetBits(&discrete_input_buf[reg_index++], reg_bit_index, 8);
n_reg--;
}
reg_buffer--;
// Last discrete
n_discrete = n_discrete % 8;
// Filling zero to high bit
*reg_buffer = *reg_buffer << (8 - n_discrete);
*reg_buffer = *reg_buffer >> (8 - n_discrete);
// Send an event to notify application task about event
(void)mbc_slave_send_param_access_notification(MB_EVENT_DISCRETE_RD);
(void)mbc_slave_send_param_info(MB_EVENT_DISCRETE_RD, (uint16_t)address,
(uint8_t*)temp_buf, (uint16_t)n_discrete);
} else {
status = MB_ENOREG;
}
return status;
}
/**
* Below are the stack callback functions to read/write registers
*/
eMBErrorCode eMBRegDiscreteCB(UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete)
{
eMBErrorCode error = MB_ENOERR;
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
MB_SLAVE_CHECK((slave_interface_ptr->slave_reg_cb_discrete != NULL),
error,
"Slave interface is not correctly initialized.");
error = slave_interface_ptr->slave_reg_cb_discrete(pucRegBuffer, usAddress, usNDiscrete);
// Check if the callback is overridden in concrete port
if (slave_interface_ptr->slave_reg_cb_discrete) {
error = slave_interface_ptr->slave_reg_cb_discrete(pucRegBuffer, usAddress, usNDiscrete);
} else {
error = mbc_reg_discrete_slave_cb(pucRegBuffer, usAddress, usNDiscrete);
}
return error;
}
eMBErrorCode eMBRegCoilsCB(UCHAR* pucRegBuffer, USHORT usAddress,
USHORT usNCoils, eMBRegisterMode eMode)
USHORT usNCoils, eMBRegisterMode eMode)
{
eMBErrorCode error = MB_ENOERR;
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
MB_SLAVE_CHECK((slave_interface_ptr->slave_reg_cb_coils != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
error = slave_interface_ptr->slave_reg_cb_coils(pucRegBuffer, usAddress,
usNCoils, eMode);
if (slave_interface_ptr->slave_reg_cb_coils) {
error = slave_interface_ptr->slave_reg_cb_coils(pucRegBuffer, usAddress, usNCoils, eMode);
} else {
error = mbc_reg_coils_slave_cb(pucRegBuffer, usAddress, usNCoils, eMode);
}
return error;
}
eMBErrorCode eMBRegHoldingCB(UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNRegs, eMBRegisterMode eMode)
USHORT usNRegs, eMBRegisterMode eMode)
{
eMBErrorCode error = MB_ENOERR;
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
MB_SLAVE_CHECK((slave_interface_ptr->slave_reg_cb_holding != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
error = slave_interface_ptr->slave_reg_cb_holding(pucRegBuffer, usAddress,
usNRegs, eMode);
if (slave_interface_ptr->slave_reg_cb_holding) {
error = slave_interface_ptr->slave_reg_cb_holding(pucRegBuffer, usAddress, usNRegs, eMode);
} else {
error = mbc_reg_holding_slave_cb(pucRegBuffer, usAddress, usNRegs, eMode);
}
return error;
}
eMBErrorCode eMBRegInputCB(UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNRegs)
eMBErrorCode eMBRegInputCB(UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs)
{
eMBErrorCode error = MB_ENOERR;
eMBErrorCode error = ESP_ERR_INVALID_STATE;
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
MB_SLAVE_CHECK((slave_interface_ptr->slave_reg_cb_input != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
error = slave_interface_ptr->slave_reg_cb_input(pucRegBuffer, usAddress, usNRegs);
if (slave_interface_ptr->slave_reg_cb_input) {
error = slave_interface_ptr->slave_reg_cb_input(pucRegBuffer, usAddress, usNRegs);
} else {
error = mbc_reg_input_slave_cb(pucRegBuffer, usAddress, usNRegs);
}
return error;
}

View File

@ -17,8 +17,10 @@
#include "driver/uart.h" // for uart defines
#include "errno.h" // for errno
#include "sys/queue.h" // for list
#include "esp_log.h" // for log write
#include "string.h" // for strerror()
#include "esp_modbus_slave.h" // for public type defines
#include "esp_modbus_callbacks.h" // for callback functions
@ -52,6 +54,17 @@ typedef struct {
uart_parity_t parity; /*!< Modbus UART parity settings */
} mb_slave_comm_info_t;
/**
* @brief Modbus area descriptor list item
*/
typedef struct mb_descr_entry_s{
uint16_t start_offset; /*!< Modbus start address for area descriptor */
mb_param_type_t type; /*!< Type of storage area descriptor */
void* p_data; /*!< Instance address for storage area descriptor */
size_t size; /*!< Instance size for area descriptor (bytes) */
LIST_ENTRY(mb_descr_entry_s) entries; /*!< The Modbus area descriptor entry */
} mb_descr_entry_t;
/**
* @brief Modbus controller handler structure
*/
@ -61,7 +74,7 @@ typedef struct {
TaskHandle_t mbs_task_handle; /*!< task handle */
EventGroupHandle_t mbs_event_group; /*!< controller event group */
QueueHandle_t mbs_notification_queue_handle; /*!< controller notification queue */
mb_register_area_descriptor_t mbs_area_descriptors[MB_PARAM_COUNT]; /*!< register area descriptors */
LIST_HEAD(mbs_area_descriptors_, mb_descr_entry_s) mbs_area_descriptors[MB_PARAM_COUNT]; /*!< register area descriptors */
} mb_slave_options_t;
typedef mb_event_group_t (*iface_check_event)(mb_event_group_t); /*!< Interface method check_event */

View File

@ -110,104 +110,6 @@ static esp_err_t mbc_serial_slave_start(void)
return ESP_OK;
}
// Modbus controller destroy function
static esp_err_t mbc_serial_slave_destroy(void)
{
MB_SLAVE_CHECK((mbs_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
eMBErrorCode mb_error = MB_ENOERR;
// Stop polling by clearing correspondent bit in the event group
EventBits_t flag = xEventGroupClearBits(mbs_opts->mbs_event_group,
(EventBits_t)MB_EVENT_STACK_STARTED);
MB_SLAVE_CHECK((flag & MB_EVENT_STACK_STARTED),
ESP_ERR_INVALID_STATE, "mb stack stop event failure.");
// Disable and then destroy the Modbus stack
mb_error = eMBDisable();
MB_SLAVE_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE, "mb stack disable failure.");
(void)vTaskDelete(mbs_opts->mbs_task_handle);
(void)vQueueDelete(mbs_opts->mbs_notification_queue_handle);
(void)vEventGroupDelete(mbs_opts->mbs_event_group);
mb_error = eMBClose();
MB_SLAVE_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE,
"mb stack close failure returned (0x%x).", (uint32_t)mb_error);
free(mbs_interface_ptr);
vMBPortSetMode((UCHAR)MB_PORT_INACTIVE);
mbs_interface_ptr = NULL;
return ESP_OK;
}
esp_err_t mbc_serial_slave_set_descriptor(const mb_register_area_descriptor_t descr_info)
{
MB_SLAVE_CHECK((mbs_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
MB_SLAVE_CHECK(((descr_info.type < MB_PARAM_COUNT) && (descr_info.type >= MB_PARAM_HOLDING)),
ESP_ERR_INVALID_ARG, "mb incorrect modbus instance type = (0x%x).",
(uint32_t)descr_info.type);
MB_SLAVE_CHECK((descr_info.address != NULL),
ESP_ERR_INVALID_ARG, "mb instance pointer is NULL.");
MB_SLAVE_CHECK((descr_info.size >= MB_INST_MIN_SIZE) && (descr_info.size < (MB_INST_MAX_SIZE)),
ESP_ERR_INVALID_ARG, "mb instance size is incorrect = (0x%x).",
(uint32_t)descr_info.size);
mbs_opts->mbs_area_descriptors[descr_info.type] = descr_info;
return ESP_OK;
}
// The helper function to get time stamp in microseconds
static uint64_t get_time_stamp(void)
{
uint64_t time_stamp = esp_timer_get_time();
return time_stamp;
}
// Helper function to send parameter information to application task
static esp_err_t send_param_info(mb_event_group_t par_type, uint16_t mb_offset,
uint8_t* par_address, uint16_t par_size)
{
MB_SLAVE_CHECK((mbs_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
esp_err_t error = ESP_FAIL;
mb_param_info_t par_info;
// Check if queue is not full the send parameter information
par_info.type = par_type;
par_info.size = par_size;
par_info.address = par_address;
par_info.time_stamp = get_time_stamp();
par_info.mb_offset = mb_offset;
BaseType_t status = xQueueSend(mbs_opts->mbs_notification_queue_handle,
&par_info, MB_PAR_INFO_TOUT);
if (pdTRUE == status) {
ESP_LOGD(MB_SLAVE_TAG, "Queue send parameter info (type, address, size): %d, 0x%.4x, %d",
par_type, (uint32_t)par_address, par_size);
error = ESP_OK;
} else if (errQUEUE_FULL == status) {
ESP_LOGD(MB_SLAVE_TAG, "Parameter queue is overflowed.");
}
return error;
}
// Helper function to send notification
static esp_err_t send_param_access_notification(mb_event_group_t event)
{
MB_SLAVE_CHECK((mbs_interface_ptr != NULL),
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
esp_err_t err = ESP_FAIL;
mb_event_group_t bits = (mb_event_group_t)xEventGroupSetBits(mbs_opts->mbs_event_group,
(EventBits_t)event);
if (bits & event) {
ESP_LOGD(MB_SLAVE_TAG, "The MB_REG_CHANGE_EVENT = 0x%.2x is set.", (uint8_t)event);
err = ESP_OK;
}
return err;
}
// Blocking function to get event on parameter group change for application task
static mb_event_group_t mbc_serial_slave_check_event(mb_event_group_t group)
{
@ -246,211 +148,33 @@ static esp_err_t mbc_serial_slave_get_param_info(mb_param_info_t* reg_info, uint
// This is required to suppress warning when register start address is zero
#pragma GCC diagnostic ignored "-Wtype-limits"
// Callback function for reading of MB Input Registers
eMBErrorCode eMBRegInputCBSerialSlave(UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNRegs)
// Modbus controller destroy function
static esp_err_t mbc_serial_slave_destroy(void)
{
MB_SLAVE_CHECK((mbs_interface_ptr != NULL),
MB_EILLSTATE, "Slave stack uninitialized.");
MB_SLAVE_CHECK((pucRegBuffer != NULL),
MB_EINVAL, "Slave stack call failed.");
ESP_ERR_INVALID_STATE,
"Slave interface is not correctly initialized.");
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
USHORT usRegInputNregs = (USHORT)(mbs_opts->mbs_area_descriptors[MB_PARAM_INPUT].size >> 1); // Number of input registers
USHORT usInputRegStart = (USHORT)mbs_opts->mbs_area_descriptors[MB_PARAM_INPUT].start_offset; // Get Modbus start address
UCHAR* pucInputBuffer = (UCHAR*)mbs_opts->mbs_area_descriptors[MB_PARAM_INPUT].address; // Get instance address
USHORT usRegs = usNRegs;
eMBErrorCode eStatus = MB_ENOERR;
USHORT iRegIndex;
// If input or configuration parameters are incorrect then return an error to stack layer
if ((usAddress >= usInputRegStart)
&& (pucInputBuffer != NULL)
&& (usNRegs >= 1)
&& ((usAddress + usRegs) <= (usInputRegStart + usRegInputNregs + 1))
&& (usRegInputNregs >= 1)) {
iRegIndex = (USHORT)(usAddress - usInputRegStart - 1);
iRegIndex <<= 1; // register Address to byte address
pucInputBuffer += iRegIndex;
UCHAR* pucBufferStart = pucInputBuffer;
while (usRegs > 0) {
_XFER_2_RD(pucRegBuffer, pucInputBuffer);
iRegIndex += 2;
usRegs -= 1;
}
// Send access notification
(void)send_param_access_notification(MB_EVENT_INPUT_REG_RD);
// Send parameter info to application task
(void)send_param_info(MB_EVENT_INPUT_REG_RD, (uint16_t)usAddress,
(uint8_t*)pucBufferStart, (uint16_t)usNRegs);
} else {
eStatus = MB_ENOREG;
}
return eStatus;
}
eMBErrorCode mb_error = MB_ENOERR;
// Stop polling by clearing correspondent bit in the event group
EventBits_t flag = xEventGroupClearBits(mbs_opts->mbs_event_group,
(EventBits_t)MB_EVENT_STACK_STARTED);
MB_SLAVE_CHECK((flag & MB_EVENT_STACK_STARTED),
ESP_ERR_INVALID_STATE, "mb stack stop event failure.");
// Disable and then destroy the Modbus stack
mb_error = eMBDisable();
MB_SLAVE_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE, "mb stack disable failure.");
(void)vTaskDelete(mbs_opts->mbs_task_handle);
(void)vQueueDelete(mbs_opts->mbs_notification_queue_handle);
(void)vEventGroupDelete(mbs_opts->mbs_event_group);
mb_error = eMBClose();
MB_SLAVE_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE,
"mb stack close failure returned (0x%x).", (uint32_t)mb_error);
// Callback function for reading of MB Holding Registers
// Executed by stack when request to read/write holding registers is received
eMBErrorCode eMBRegHoldingCBSerialSlave(UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNRegs, eMBRegisterMode eMode)
{
MB_SLAVE_CHECK((mbs_interface_ptr != NULL),
MB_EILLSTATE, "Slave stack uninitialized.");
MB_SLAVE_CHECK((pucRegBuffer != NULL),
MB_EINVAL, "Slave stack call failed.");
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
USHORT usRegHoldingNregs = (USHORT)(mbs_opts->mbs_area_descriptors[MB_PARAM_HOLDING].size >> 1);
USHORT usRegHoldingStart = (USHORT)mbs_opts->mbs_area_descriptors[MB_PARAM_HOLDING].start_offset;
UCHAR* pucHoldingBuffer = (UCHAR*)mbs_opts->mbs_area_descriptors[MB_PARAM_HOLDING].address;
eMBErrorCode eStatus = MB_ENOERR;
USHORT iRegIndex;
USHORT usRegs = usNRegs;
// Check input and configuration parameters for correctness
if ((usAddress >= usRegHoldingStart)
&& (pucHoldingBuffer != NULL)
&& ((usAddress + usRegs) <= (usRegHoldingStart + usRegHoldingNregs + 1))
&& (usRegHoldingNregs >= 1)
&& (usNRegs >= 1)) {
iRegIndex = (USHORT) (usAddress - usRegHoldingStart - 1);
iRegIndex <<= 1; // register Address to byte address
pucHoldingBuffer += iRegIndex;
UCHAR* pucBufferStart = pucHoldingBuffer;
switch (eMode) {
case MB_REG_READ:
while (usRegs > 0) {
_XFER_2_RD(pucRegBuffer, pucHoldingBuffer);
iRegIndex += 2;
usRegs -= 1;
};
// Send access notification
(void)send_param_access_notification(MB_EVENT_HOLDING_REG_RD);
// Send parameter info
(void)send_param_info(MB_EVENT_HOLDING_REG_RD, (uint16_t)usAddress,
(uint8_t*)pucBufferStart, (uint16_t)usNRegs);
break;
case MB_REG_WRITE:
while (usRegs > 0) {
_XFER_2_WR(pucHoldingBuffer, pucRegBuffer);
pucHoldingBuffer += 2;
iRegIndex += 2;
usRegs -= 1;
};
// Send access notification
(void)send_param_access_notification(MB_EVENT_HOLDING_REG_WR);
// Send parameter info
(void)send_param_info(MB_EVENT_HOLDING_REG_WR, (uint16_t)usAddress,
(uint8_t*)pucBufferStart, (uint16_t)usNRegs);
break;
}
} else {
eStatus = MB_ENOREG;
}
return eStatus;
vMBPortSetMode((UCHAR)MB_PORT_INACTIVE);
return ESP_OK;
}
// Callback function for reading of MB Coils Registers
eMBErrorCode eMBRegCoilsCBSerialSlave(UCHAR* pucRegBuffer, USHORT usAddress,
USHORT usNCoils, eMBRegisterMode eMode)
{
MB_SLAVE_CHECK((mbs_interface_ptr != NULL),
MB_EILLSTATE, "Slave stack uninitialized.");
MB_SLAVE_CHECK((pucRegBuffer != NULL),
MB_EINVAL, "Slave stack call failed.");
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
USHORT usRegCoilNregs = (USHORT)(mbs_opts->mbs_area_descriptors[MB_PARAM_COIL].size >> 1); // number of registers in storage area
USHORT usRegCoilsStart = (USHORT)mbs_opts->mbs_area_descriptors[MB_PARAM_COIL].start_offset; // MB offset of coils registers
UCHAR* pucRegCoilsBuf = (UCHAR*)mbs_opts->mbs_area_descriptors[MB_PARAM_COIL].address;
eMBErrorCode eStatus = MB_ENOERR;
USHORT iRegIndex;
USHORT usCoils = usNCoils;
usAddress--; // The address is already +1
if ((usAddress >= usRegCoilsStart)
&& (usRegCoilNregs >= 1)
&& ((usAddress + usCoils) <= (usRegCoilsStart + (usRegCoilNregs << 4) + 1))
&& (pucRegCoilsBuf != NULL)
&& (usNCoils >= 1)) {
iRegIndex = (USHORT) (usAddress - usRegCoilsStart);
CHAR* pucCoilsDataBuf = (CHAR*)(pucRegCoilsBuf + (iRegIndex >> 3));
switch (eMode) {
case MB_REG_READ:
while (usCoils > 0) {
UCHAR ucResult = xMBUtilGetBits((UCHAR*)pucRegCoilsBuf, iRegIndex, 1);
xMBUtilSetBits(pucRegBuffer, iRegIndex - (usAddress - usRegCoilsStart), 1, ucResult);
iRegIndex++;
usCoils--;
}
// Send an event to notify application task about event
(void)send_param_access_notification(MB_EVENT_COILS_RD);
(void)send_param_info(MB_EVENT_COILS_RD, (uint16_t)usAddress,
(uint8_t*)(pucCoilsDataBuf), (uint16_t)usNCoils);
break;
case MB_REG_WRITE:
while (usCoils > 0) {
UCHAR ucResult = xMBUtilGetBits(pucRegBuffer,
iRegIndex - (usAddress - usRegCoilsStart), 1);
xMBUtilSetBits((uint8_t*)pucRegCoilsBuf, iRegIndex, 1, ucResult);
iRegIndex++;
usCoils--;
}
// Send an event to notify application task about event
(void)send_param_access_notification(MB_EVENT_COILS_WR);
(void)send_param_info(MB_EVENT_COILS_WR, (uint16_t)usAddress,
(uint8_t*)pucCoilsDataBuf, (uint16_t)usNCoils);
break;
} // switch ( eMode )
} else {
// If the configuration or input parameters are incorrect then return error to stack
eStatus = MB_ENOREG;
}
return eStatus;
}
// Callback function for reading of MB Discrete Input Registers
eMBErrorCode eMBRegDiscreteCBSerialSlave(UCHAR* pucRegBuffer, USHORT usAddress,
USHORT usNDiscrete)
{
MB_SLAVE_CHECK((mbs_interface_ptr != NULL),
MB_EILLSTATE, "Slave stack uninitialized.");
MB_SLAVE_CHECK((pucRegBuffer != NULL),
MB_EINVAL, "Slave stack call failed.");
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
USHORT usRegDiscreteNregs = (USHORT)(mbs_opts->mbs_area_descriptors[MB_PARAM_DISCRETE].size >> 1); // number of registers in storage area
USHORT usRegDiscreteStart = (USHORT)mbs_opts->mbs_area_descriptors[MB_PARAM_DISCRETE].start_offset; // MB offset of registers
UCHAR* pucRegDiscreteBuf = (UCHAR*)mbs_opts->mbs_area_descriptors[MB_PARAM_DISCRETE].address; // the storage address
eMBErrorCode eStatus = MB_ENOERR;
USHORT iRegIndex, iRegBitIndex, iNReg;
UCHAR* pucDiscreteInputBuf;
iNReg = usNDiscrete / 8 + 1;
pucDiscreteInputBuf = (UCHAR*) pucRegDiscreteBuf;
// It already plus one in modbus function method.
usAddress--;
if ((usAddress >= usRegDiscreteStart)
&& (usRegDiscreteNregs >= 1)
&& (pucRegDiscreteBuf != NULL)
&& ((usAddress + usNDiscrete) <= (usRegDiscreteStart + (usRegDiscreteNregs * 16)))
&& (usNDiscrete >= 1)) {
iRegIndex = (USHORT) (usAddress - usRegDiscreteStart) / 8; // Get register index in the buffer for bit number
iRegBitIndex = (USHORT)(usAddress - usRegDiscreteStart) % 8; // Get bit index
UCHAR* pucTempBuf = &pucDiscreteInputBuf[iRegIndex];
while (iNReg > 0) {
*pucRegBuffer++ = xMBUtilGetBits(&pucDiscreteInputBuf[iRegIndex++], iRegBitIndex, 8);
iNReg--;
}
pucRegBuffer--;
// Last discrete
usNDiscrete = usNDiscrete % 8;
// Filling zero to high bit
*pucRegBuffer = *pucRegBuffer << (8 - usNDiscrete);
*pucRegBuffer = *pucRegBuffer >> (8 - usNDiscrete);
// Send an event to notify application task about event
(void)send_param_access_notification(MB_EVENT_DISCRETE_RD);
(void)send_param_info(MB_EVENT_DISCRETE_RD, (uint16_t)usAddress,
(uint8_t*)pucTempBuf, (uint16_t)usNDiscrete);
} else {
eStatus = MB_ENOREG;
}
return eStatus;
}
#pragma GCC diagnostic pop // require GCC
// Initialization of Modbus controller
esp_err_t mbc_serial_slave_create(void** handler)
{
@ -504,15 +228,15 @@ esp_err_t mbc_serial_slave_create(void** handler)
mbs_interface_ptr->destroy = mbc_serial_slave_destroy;
mbs_interface_ptr->get_param_info = mbc_serial_slave_get_param_info;
mbs_interface_ptr->init = mbc_serial_slave_create;
mbs_interface_ptr->set_descriptor = mbc_serial_slave_set_descriptor;
mbs_interface_ptr->set_descriptor = NULL; // Use common set descriptor function
mbs_interface_ptr->setup = mbc_serial_slave_setup;
mbs_interface_ptr->start = mbc_serial_slave_start;
// Initialize stack callback function pointers
mbs_interface_ptr->slave_reg_cb_discrete = eMBRegDiscreteCBSerialSlave;
mbs_interface_ptr->slave_reg_cb_input = eMBRegInputCBSerialSlave;
mbs_interface_ptr->slave_reg_cb_holding = eMBRegHoldingCBSerialSlave;
mbs_interface_ptr->slave_reg_cb_coils = eMBRegCoilsCBSerialSlave;
mbs_interface_ptr->slave_reg_cb_discrete = NULL; // implemented in common layer
mbs_interface_ptr->slave_reg_cb_input = NULL;
mbs_interface_ptr->slave_reg_cb_holding = NULL;
mbs_interface_ptr->slave_reg_cb_coils = NULL;
*handler = (void*)mbs_interface_ptr;

View File

@ -31,13 +31,6 @@
// Shared pointer to interface structure
static mb_slave_interface_t* mbs_interface_ptr = NULL;
// The helper function to get time stamp in microseconds
static uint64_t get_time_stamp(void)
{
uint64_t time_stamp = esp_timer_get_time();
return time_stamp;
}
// Modbus task function
static void modbus_tcp_slave_task(void *pvParameters)
{
@ -123,69 +116,11 @@ static esp_err_t mbc_tcp_slave_destroy(void)
(void)vQueueDelete(mbs_opts->mbs_notification_queue_handle);
(void)vEventGroupDelete(mbs_opts->mbs_event_group);
(void)vMBTCPPortClose();
free(mbs_interface_ptr);
vMBPortSetMode((UCHAR)MB_PORT_INACTIVE);
mbs_interface_ptr = NULL;
return ESP_OK;
}
esp_err_t mbc_tcp_slave_set_descriptor(const mb_register_area_descriptor_t descr_info)
{
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
MB_SLAVE_CHECK(((descr_info.type < MB_PARAM_COUNT) && (descr_info.type >= MB_PARAM_HOLDING)),
ESP_ERR_INVALID_ARG, "mb incorrect modbus instance type = (0x%x).",
(uint32_t)descr_info.type);
MB_SLAVE_CHECK((descr_info.address != NULL),
ESP_ERR_INVALID_ARG, "mb instance pointer is NULL.");
MB_SLAVE_CHECK((descr_info.size >= MB_INST_MIN_SIZE) && (descr_info.size < (MB_INST_MAX_SIZE)),
ESP_ERR_INVALID_ARG, "mb instance size is incorrect = (0x%x).",
(uint32_t)descr_info.size);
mbs_opts->mbs_area_descriptors[descr_info.type] = descr_info;
return ESP_OK;
}
// Helper function to send parameter information to application task
static esp_err_t send_param_info(mb_event_group_t par_type, uint16_t mb_offset,
uint8_t* par_address, uint16_t par_size)
{
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
esp_err_t error = ESP_FAIL;
mb_param_info_t par_info;
// Check if queue is not full the send parameter information
par_info.type = par_type;
par_info.size = par_size;
par_info.address = par_address;
par_info.time_stamp = get_time_stamp();
par_info.mb_offset = mb_offset;
BaseType_t status = xQueueSend(mbs_opts->mbs_notification_queue_handle,
&par_info, MB_PAR_INFO_TOUT);
if (pdTRUE == status) {
ESP_LOGD(MB_SLAVE_TAG, "Queue send parameter info (type, address, size): %d, 0x%.4x, %d",
par_type, (uint32_t)par_address, par_size);
error = ESP_OK;
} else if (errQUEUE_FULL == status) {
ESP_LOGD(MB_SLAVE_TAG, "Parameter queue is overflowed.");
}
return error;
}
// Helper function to send notification
static esp_err_t send_param_access_notification(mb_event_group_t event)
{
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
esp_err_t err = ESP_FAIL;
mb_event_group_t bits = (mb_event_group_t)xEventGroupSetBits(mbs_opts->mbs_event_group,
(EventBits_t)event);
if (bits & event) {
ESP_LOGD(MB_SLAVE_TAG, "The MB_REG_CHANGE_EVENT = 0x%.2x is set.", (uint8_t)event);
err = ESP_OK;
}
return err;
}
// Blocking function to get event on parameter group change for application task
static mb_event_group_t mbc_tcp_slave_check_event(mb_event_group_t group)
{
@ -220,203 +155,6 @@ static esp_err_t mbc_tcp_slave_get_param_info(mb_param_info_t* reg_info, uint32_
// This is required to suppress warning when register start address is zero
#pragma GCC diagnostic ignored "-Wtype-limits"
// Callback function for reading of MB Input Registers
eMBErrorCode eMBRegInputCBTcpSlave(UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNRegs)
{
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
MB_SLAVE_ASSERT(pucRegBuffer != NULL);
USHORT usRegInputNregs = (USHORT)(mbs_opts->mbs_area_descriptors[MB_PARAM_INPUT].size >> 1); // Number of input registers
USHORT usInputRegStart = (USHORT)mbs_opts->mbs_area_descriptors[MB_PARAM_INPUT].start_offset; // Get Modbus start address
UCHAR* pucInputBuffer = (UCHAR*)mbs_opts->mbs_area_descriptors[MB_PARAM_INPUT].address; // Get instance address
USHORT usRegs = usNRegs;
eMBErrorCode eStatus = MB_ENOERR;
USHORT iRegIndex;
// If input or configuration parameters are incorrect then return an error to stack layer
if ((usAddress >= usInputRegStart)
&& (pucInputBuffer != NULL)
&& (usNRegs >= 1)
&& ((usAddress + usRegs) <= (usInputRegStart + usRegInputNregs + 1))
&& (usRegInputNregs >= 1)) {
iRegIndex = (USHORT)(usAddress - usInputRegStart - 1);
iRegIndex <<= 1; // register Address to byte address
pucInputBuffer += iRegIndex;
UCHAR* pucBufferStart = pucInputBuffer;
while (usRegs > 0) {
_XFER_2_RD(pucRegBuffer, pucInputBuffer);
iRegIndex += 2;
usRegs -= 1;
}
// Send access notification
(void)send_param_access_notification(MB_EVENT_INPUT_REG_RD);
// Send parameter info to application task
(void)send_param_info(MB_EVENT_INPUT_REG_RD, (uint16_t)usAddress,
(uint8_t*)pucBufferStart, (uint16_t)usNRegs);
} else {
eStatus = MB_ENOREG;
}
return eStatus;
}
// Callback function for reading of MB Holding Registers
// Executed by stack when request to read/write holding registers is received
eMBErrorCode eMBRegHoldingCBTcpSlave(UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNRegs, eMBRegisterMode eMode)
{
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
MB_SLAVE_ASSERT(pucRegBuffer != NULL);
USHORT usRegHoldingNregs = (USHORT)(mbs_opts->mbs_area_descriptors[MB_PARAM_HOLDING].size >> 1);
USHORT usRegHoldingStart = (USHORT)mbs_opts->mbs_area_descriptors[MB_PARAM_HOLDING].start_offset;
UCHAR* pucHoldingBuffer = (UCHAR*)mbs_opts->mbs_area_descriptors[MB_PARAM_HOLDING].address;
eMBErrorCode eStatus = MB_ENOERR;
USHORT iRegIndex;
USHORT usRegs = usNRegs;
// Check input and configuration parameters for correctness
if ((usAddress >= usRegHoldingStart)
&& (pucHoldingBuffer != NULL)
&& ((usAddress + usRegs) <= (usRegHoldingStart + usRegHoldingNregs + 1))
&& (usRegHoldingNregs >= 1)
&& (usNRegs >= 1)) {
iRegIndex = (USHORT) (usAddress - usRegHoldingStart - 1);
iRegIndex <<= 1; // register Address to byte address
pucHoldingBuffer += iRegIndex;
UCHAR* pucBufferStart = pucHoldingBuffer;
switch (eMode) {
case MB_REG_READ:
while (usRegs > 0) {
_XFER_2_RD(pucRegBuffer, pucHoldingBuffer);
iRegIndex += 2;
usRegs -= 1;
};
// Send access notification
(void)send_param_access_notification(MB_EVENT_HOLDING_REG_RD);
// Send parameter info
(void)send_param_info(MB_EVENT_HOLDING_REG_RD, (uint16_t)usAddress,
(uint8_t*)pucBufferStart, (uint16_t)usNRegs);
break;
case MB_REG_WRITE:
while (usRegs > 0) {
_XFER_2_WR(pucHoldingBuffer, pucRegBuffer);
pucHoldingBuffer += 2;
iRegIndex += 2;
usRegs -= 1;
};
// Send access notification
(void)send_param_access_notification(MB_EVENT_HOLDING_REG_WR);
// Send parameter info
(void)send_param_info(MB_EVENT_HOLDING_REG_WR, (uint16_t)usAddress,
(uint8_t*)pucBufferStart, (uint16_t)usNRegs);
break;
}
} else {
eStatus = MB_ENOREG;
}
return eStatus;
}
// Callback function for reading of MB Coils Registers
eMBErrorCode eMBRegCoilsCBTcpSlave(UCHAR* pucRegBuffer, USHORT usAddress,
USHORT usNCoils, eMBRegisterMode eMode)
{
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
MB_SLAVE_ASSERT(NULL != pucRegBuffer);
USHORT usRegCoilNregs = (USHORT)(mbs_opts->mbs_area_descriptors[MB_PARAM_COIL].size >> 1); // number of registers in storage area
USHORT usRegCoilsStart = (USHORT)mbs_opts->mbs_area_descriptors[MB_PARAM_COIL].start_offset; // MB offset of coils registers
UCHAR* pucRegCoilsBuf = (UCHAR*)mbs_opts->mbs_area_descriptors[MB_PARAM_COIL].address;
eMBErrorCode eStatus = MB_ENOERR;
USHORT iRegIndex;
USHORT usCoils = usNCoils;
usAddress--; // The address is already +1
if ((usAddress >= usRegCoilsStart)
&& (usRegCoilNregs >= 1)
&& ((usAddress + usCoils) <= (usRegCoilsStart + (usRegCoilNregs << 4) + 1))
&& (pucRegCoilsBuf != NULL)
&& (usNCoils >= 1)) {
iRegIndex = (USHORT) (usAddress - usRegCoilsStart);
CHAR* pucCoilsDataBuf = (CHAR*)(pucRegCoilsBuf + (iRegIndex >> 3));
switch (eMode) {
case MB_REG_READ:
while (usCoils > 0) {
UCHAR ucResult = xMBUtilGetBits((UCHAR*)pucRegCoilsBuf, iRegIndex, 1);
xMBUtilSetBits(pucRegBuffer, iRegIndex - (usAddress - usRegCoilsStart), 1, ucResult);
iRegIndex++;
usCoils--;
}
// Send an event to notify application task about event
(void)send_param_access_notification(MB_EVENT_COILS_RD);
(void)send_param_info(MB_EVENT_COILS_RD, (uint16_t)usAddress,
(uint8_t*)(pucCoilsDataBuf), (uint16_t)usNCoils);
break;
case MB_REG_WRITE:
while (usCoils > 0) {
UCHAR ucResult = xMBUtilGetBits(pucRegBuffer,
iRegIndex - (usAddress - usRegCoilsStart), 1);
xMBUtilSetBits((uint8_t*)pucRegCoilsBuf, iRegIndex, 1, ucResult);
iRegIndex++;
usCoils--;
}
// Send an event to notify application task about event
(void)send_param_access_notification(MB_EVENT_COILS_WR);
(void)send_param_info(MB_EVENT_COILS_WR, (uint16_t)usAddress,
(uint8_t*)pucCoilsDataBuf, (uint16_t)usNCoils);
break;
} // switch ( eMode )
} else {
// If the configuration or input parameters are incorrect then return error to stack
eStatus = MB_ENOREG;
}
return eStatus;
}
// Callback function for reading of MB Discrete Input Registers
eMBErrorCode eMBRegDiscreteCBTcpSlave(UCHAR* pucRegBuffer, USHORT usAddress,
USHORT usNDiscrete)
{
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
MB_SLAVE_ASSERT(pucRegBuffer != NULL);
USHORT usRegDiscreteNregs = (USHORT)(mbs_opts->mbs_area_descriptors[MB_PARAM_DISCRETE].size >> 1); // number of registers in storage area
USHORT usRegDiscreteStart = (USHORT)mbs_opts->mbs_area_descriptors[MB_PARAM_DISCRETE].start_offset; // MB offset of registers
UCHAR* pucRegDiscreteBuf = (UCHAR*)mbs_opts->mbs_area_descriptors[MB_PARAM_DISCRETE].address; // the storage address
eMBErrorCode eStatus = MB_ENOERR;
USHORT iRegIndex, iRegBitIndex, iNReg;
UCHAR* pucDiscreteInputBuf;
iNReg = usNDiscrete / 8 + 1;
pucDiscreteInputBuf = (UCHAR*) pucRegDiscreteBuf;
// It already plus one in modbus function method.
usAddress--;
if ((usAddress >= usRegDiscreteStart)
&& (usRegDiscreteNregs >= 1)
&& (pucRegDiscreteBuf != NULL)
&& ((usAddress + usNDiscrete) <= (usRegDiscreteStart + (usRegDiscreteNregs * 16)))
&& (usNDiscrete >= 1)) {
iRegIndex = (USHORT) (usAddress - usRegDiscreteStart) / 8; // Get register index in the buffer for bit number
iRegBitIndex = (USHORT)(usAddress - usRegDiscreteStart) % 8; // Get bit index
UCHAR* pucTempBuf = &pucDiscreteInputBuf[iRegIndex];
while (iNReg > 0) {
*pucRegBuffer++ = xMBUtilGetBits(&pucDiscreteInputBuf[iRegIndex++], iRegBitIndex, 8);
iNReg--;
}
pucRegBuffer--;
// Last discrete
usNDiscrete = usNDiscrete % 8;
// Filling zero to high bit
*pucRegBuffer = *pucRegBuffer << (8 - usNDiscrete);
*pucRegBuffer = *pucRegBuffer >> (8 - usNDiscrete);
// Send an event to notify application task about event
(void)send_param_access_notification(MB_EVENT_DISCRETE_RD);
(void)send_param_info(MB_EVENT_DISCRETE_RD, (uint16_t)usAddress,
(uint8_t*)pucTempBuf, (uint16_t)usNDiscrete);
} else {
eStatus = MB_ENOREG;
}
return eStatus;
}
#pragma GCC diagnostic pop // require GCC
// Initialization of Modbus controller
esp_err_t mbc_tcp_slave_create(void** handler)
{
@ -470,13 +208,13 @@ esp_err_t mbc_tcp_slave_create(void** handler)
mbs_interface_ptr->start = mbc_tcp_slave_start;
mbs_interface_ptr->check_event = mbc_tcp_slave_check_event;
mbs_interface_ptr->get_param_info = mbc_tcp_slave_get_param_info;
mbs_interface_ptr->set_descriptor = mbc_tcp_slave_set_descriptor;
mbs_interface_ptr->set_descriptor = NULL; // Use common descriptor setter
// Initialize stack callback function pointers
mbs_interface_ptr->slave_reg_cb_discrete = eMBRegDiscreteCBTcpSlave;
mbs_interface_ptr->slave_reg_cb_input = eMBRegInputCBTcpSlave;
mbs_interface_ptr->slave_reg_cb_holding = eMBRegHoldingCBTcpSlave;
mbs_interface_ptr->slave_reg_cb_coils = eMBRegCoilsCBTcpSlave;
mbs_interface_ptr->slave_reg_cb_discrete = NULL; // implemented in common layer
mbs_interface_ptr->slave_reg_cb_input = NULL;
mbs_interface_ptr->slave_reg_cb_holding = NULL;
mbs_interface_ptr->slave_reg_cb_coils = NULL;
*handler = (void*)mbs_interface_ptr;

View File

@ -35,11 +35,15 @@ typedef struct
#pragma pack(push, 1)
typedef struct
{
float input_data0;
float input_data1;
float input_data2;
float input_data3;
uint16_t data[150];
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;
} input_reg_params_t;
#pragma pack(pop)
@ -51,6 +55,10 @@ typedef struct
float holding_data2;
float holding_data3;
uint16_t test_regs[150];
float holding_data4;
float holding_data5;
float holding_data6;
float holding_data7;
} holding_reg_params_t;
#pragma pack(pop)

View File

@ -20,10 +20,14 @@
// Please refer to documentation for selected board and target to configure pins using Kconfig.
// Defines below are used to define register start address for each type of Modbus registers
#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 MB_REG_DISCRETE_INPUT_START (0x0000)
#define MB_REG_INPUT_START (0x0000)
#define MB_REG_HOLDING_START (0x0000)
#define MB_REG_COILS_START (0x0000)
#define MB_REG_INPUT_START_AREA0 (INPUT_OFFSET(input_data0)) // register offset input area 0
#define MB_REG_INPUT_START_AREA1 (INPUT_OFFSET(input_data4)) // register offset input area 1
#define MB_REG_HOLDING_START_AREA0 (HOLD_OFFSET(holding_data0))
#define MB_REG_HOLDING_START_AREA1 (HOLD_OFFSET(holding_data4))
#define MB_PAR_INFO_GET_TOUT (10) // Timeout for get parameter info
#define MB_CHAN_DATA_MAX_VAL (6)
@ -44,16 +48,25 @@ static portMUX_TYPE param_lock = portMUX_INITIALIZER_UNLOCKED;
static void setup_reg_data(void)
{
// Define initial state of parameters
discrete_reg_params.discrete_input1 = 1;
discrete_reg_params.discrete_input3 = 1;
discrete_reg_params.discrete_input5 = 1;
discrete_reg_params.discrete_input7 = 1;
discrete_reg_params.discrete_input0 = 1;
discrete_reg_params.discrete_input1 = 0;
discrete_reg_params.discrete_input2 = 1;
discrete_reg_params.discrete_input3 = 0;
discrete_reg_params.discrete_input4 = 1;
discrete_reg_params.discrete_input5 = 0;
discrete_reg_params.discrete_input6 = 1;
discrete_reg_params.discrete_input7 = 0;
holding_reg_params.holding_data0 = 1.34;
holding_reg_params.holding_data1 = 2.56;
holding_reg_params.holding_data2 = 3.78;
holding_reg_params.holding_data3 = 4.90;
holding_reg_params.holding_data4 = 5.67;
holding_reg_params.holding_data5 = 6.78;
holding_reg_params.holding_data6 = 7.79;
holding_reg_params.holding_data7 = 8.80;
coil_reg_params.coils_port0 = 0x55;
coil_reg_params.coils_port1 = 0xAA;
@ -61,6 +74,11 @@ static void setup_reg_data(void)
input_reg_params.input_data1 = 2.34;
input_reg_params.input_data2 = 3.56;
input_reg_params.input_data3 = 4.78;
input_reg_params.input_data4 = 1.12;
input_reg_params.input_data5 = 2.34;
input_reg_params.input_data6 = 3.56;
input_reg_params.input_data7 = 4.78;
}
// An example application of Modbus slave. It is based on freemodbus stack.
@ -98,16 +116,27 @@ void app_main(void)
// by mbc_slave_set_descriptor() API call then Modbus stack
// will send exception response for this register area.
reg_area.type = MB_PARAM_HOLDING; // Set type of register area
reg_area.start_offset = MB_REG_HOLDING_START; // Offset of register area in Modbus protocol
reg_area.address = (void*)&holding_reg_params; // Set pointer to storage instance
reg_area.size = sizeof(holding_reg_params); // Set the size of register storage instance
reg_area.start_offset = MB_REG_HOLDING_START_AREA0; // Offset of register area in Modbus protocol
reg_area.address = (void*)&holding_reg_params.holding_data0; // Set pointer to storage instance
reg_area.size = sizeof(float) << 2; // Set the size of register storage instance
ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
reg_area.type = MB_PARAM_HOLDING; // Set type of register area
reg_area.start_offset = MB_REG_HOLDING_START_AREA1; // Offset of register area in Modbus protocol
reg_area.address = (void*)&holding_reg_params.holding_data4; // Set pointer to storage instance
reg_area.size = sizeof(float) << 2; // Set the size of register storage instance
ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
// Initialization of Input Registers area
reg_area.type = MB_PARAM_INPUT;
reg_area.start_offset = MB_REG_INPUT_START;
reg_area.address = (void*)&input_reg_params;
reg_area.size = sizeof(input_reg_params);
reg_area.start_offset = MB_REG_INPUT_START_AREA0;
reg_area.address = (void*)&input_reg_params.input_data0;
reg_area.size = sizeof(float) << 2;
ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
// Initialization of Input Registers area
reg_area.type = MB_PARAM_INPUT;
reg_area.start_offset = MB_REG_INPUT_START_AREA1;
reg_area.address = (void*)&input_reg_params.input_data4;
reg_area.size = sizeof(float) << 2;
ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
// Initialization of Coils register area

View File

@ -26,10 +26,14 @@
#define MB_MDNS_PORT (502)
// Defines below are used to define register start address for each type of Modbus registers
#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 MB_REG_DISCRETE_INPUT_START (0x0000)
#define MB_REG_INPUT_START (0x0000)
#define MB_REG_HOLDING_START (0x0000)
#define MB_REG_COILS_START (0x0000)
#define MB_REG_INPUT_START_AREA0 (INPUT_OFFSET(input_data0)) // register offset input area 0
#define MB_REG_INPUT_START_AREA1 (INPUT_OFFSET(input_data4)) // register offset input area 1
#define MB_REG_HOLDING_START_AREA0 (HOLD_OFFSET(holding_data0))
#define MB_REG_HOLDING_START_AREA1 (HOLD_OFFSET(holding_data4))
#define MB_PAR_INFO_GET_TOUT (10) // Timeout for get parameter info
#define MB_CHAN_DATA_MAX_VAL (10)
@ -117,16 +121,24 @@ static void start_mdns_service()
static void setup_reg_data(void)
{
// Define initial state of parameters
discrete_reg_params.discrete_input1 = 1;
discrete_reg_params.discrete_input3 = 1;
discrete_reg_params.discrete_input5 = 1;
discrete_reg_params.discrete_input7 = 1;
discrete_reg_params.discrete_input0 = 1;
discrete_reg_params.discrete_input1 = 0;
discrete_reg_params.discrete_input2 = 1;
discrete_reg_params.discrete_input3 = 0;
discrete_reg_params.discrete_input4 = 1;
discrete_reg_params.discrete_input5 = 0;
discrete_reg_params.discrete_input6 = 1;
discrete_reg_params.discrete_input7 = 0;
holding_reg_params.holding_data0 = 1.34;
holding_reg_params.holding_data1 = 2.56;
holding_reg_params.holding_data2 = 3.78;
holding_reg_params.holding_data3 = 4.90;
holding_reg_params.holding_data4 = 5.67;
holding_reg_params.holding_data5 = 6.78;
holding_reg_params.holding_data6 = 7.79;
holding_reg_params.holding_data7 = 8.80;
coil_reg_params.coils_port0 = 0x55;
coil_reg_params.coils_port1 = 0xAA;
@ -134,6 +146,10 @@ static void setup_reg_data(void)
input_reg_params.input_data1 = 2.34;
input_reg_params.input_data2 = 3.56;
input_reg_params.input_data3 = 4.78;
input_reg_params.input_data4 = 1.12;
input_reg_params.input_data5 = 2.34;
input_reg_params.input_data6 = 3.56;
input_reg_params.input_data7 = 4.78;
}
// An example application of Modbus slave. It is based on freemodbus stack.
@ -192,16 +208,28 @@ void app_main(void)
// by mbc_slave_set_descriptor() API call then Modbus stack
// will send exception response for this register area.
reg_area.type = MB_PARAM_HOLDING; // Set type of register area
reg_area.start_offset = MB_REG_HOLDING_START; // Offset of register area in Modbus protocol
reg_area.address = (void*)&holding_reg_params; // Set pointer to storage instance
reg_area.size = sizeof(holding_reg_params); // Set the size of register storage instance
reg_area.start_offset = MB_REG_HOLDING_START_AREA0; // Offset of register area in Modbus protocol
reg_area.address = (void*)&holding_reg_params.holding_data0; // Set pointer to storage instance
reg_area.size = sizeof(float) << 2; // Set the size of register storage instance
ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
reg_area.type = MB_PARAM_HOLDING; // Set type of register area
reg_area.start_offset = MB_REG_HOLDING_START_AREA1; // Offset of register area in Modbus protocol
reg_area.address = (void*)&holding_reg_params.holding_data4; // Set pointer to storage instance
reg_area.size = sizeof(float) << 2; // Set the size of register storage instance
ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
// Initialization of Input Registers area
reg_area.type = MB_PARAM_INPUT;
reg_area.start_offset = MB_REG_INPUT_START;
reg_area.address = (void*)&input_reg_params;
reg_area.size = sizeof(input_reg_params);
reg_area.start_offset = MB_REG_INPUT_START_AREA0;
reg_area.address = (void*)&input_reg_params.input_data0;
reg_area.size = sizeof(float) << 2;
ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
// Initialization of Input Registers area
reg_area.type = MB_PARAM_INPUT;
reg_area.start_offset = MB_REG_INPUT_START_AREA1;
reg_area.address = (void*)&input_reg_params.input_data4;
reg_area.size = sizeof(float) << 2;
ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
// Initialization of Coils register area