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
255 lines
No EOL
9.9 KiB
C
255 lines
No EOL
9.9 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.
|
|
*/
|
|
|
|
#include "esp_err.h" // for esp_err_t
|
|
#include "sdkconfig.h" // for KConfig defines
|
|
#include "mbc_slave.h" // for slave private type definitions
|
|
#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
|
|
#include "mbc_serial_slave.h" // for create function of serial port
|
|
|
|
#ifdef CONFIG_MB_CONTROLLER_SLAVE_ID_SUPPORT
|
|
|
|
#define MB_ID_BYTE0(id) ((uint8_t)(id))
|
|
#define MB_ID_BYTE1(id) ((uint8_t)(((uint16_t)(id) >> 8) & 0xFF))
|
|
#define MB_ID_BYTE2(id) ((uint8_t)(((uint32_t)(id) >> 16) & 0xFF))
|
|
#define MB_ID_BYTE3(id) ((uint8_t)(((uint32_t)(id) >> 24) & 0xFF))
|
|
|
|
#define MB_CONTROLLER_SLAVE_ID (CONFIG_MB_CONTROLLER_SLAVE_ID)
|
|
#define MB_SLAVE_ID_SHORT (MB_ID_BYTE3(MB_CONTROLLER_SLAVE_ID))
|
|
|
|
// Slave ID constant
|
|
static uint8_t mb_slave_id[] = { MB_ID_BYTE0(MB_CONTROLLER_SLAVE_ID),
|
|
MB_ID_BYTE1(MB_CONTROLLER_SLAVE_ID),
|
|
MB_ID_BYTE2(MB_CONTROLLER_SLAVE_ID) };
|
|
|
|
#endif
|
|
|
|
// Common interface pointer for slave port
|
|
static mb_slave_interface_t* slave_interface_ptr = NULL;
|
|
|
|
/**
|
|
* Initialization of Modbus slave controller
|
|
*/
|
|
esp_err_t mbc_slave_init(mb_port_type_t port_type, void** handler)
|
|
{
|
|
void* port_handler = NULL;
|
|
esp_err_t error = ESP_ERR_NOT_SUPPORTED;
|
|
switch(port_type)
|
|
{
|
|
case MB_PORT_SERIAL_SLAVE:
|
|
// Call constructor function of actual port implementation
|
|
error = mbc_serial_slave_create(port_type, &port_handler);
|
|
break;
|
|
case MB_PORT_TCP_SLAVE:
|
|
// Not yet supported
|
|
//error = mbc_tcp_slave_create(port_type, &port_handler);
|
|
return ESP_ERR_NOT_SUPPORTED;
|
|
default:
|
|
return ESP_ERR_NOT_SUPPORTED;
|
|
}
|
|
MB_SLAVE_CHECK((port_handler != NULL),
|
|
ESP_ERR_INVALID_STATE,
|
|
"Slave interface initialization failure, error=(0x%x), port type=(0x%x).",
|
|
error, (uint16_t)port_type);
|
|
|
|
if ((port_handler != NULL) && (error == ESP_OK)) {
|
|
slave_interface_ptr = (mb_slave_interface_t*) port_handler;
|
|
*handler = port_handler;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
/**
|
|
* Modbus controller destroy function
|
|
*/
|
|
esp_err_t mbc_slave_destroy()
|
|
{
|
|
esp_err_t error = ESP_OK;
|
|
// Is initialization done?
|
|
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
|
|
ESP_ERR_INVALID_STATE,
|
|
"Slave interface is not correctly initialized.");
|
|
// Check if interface has been initialized
|
|
MB_SLAVE_CHECK((slave_interface_ptr->destroy != NULL),
|
|
ESP_ERR_INVALID_STATE,
|
|
"Slave interface is not correctly initialized.");
|
|
// Call the slave port destroy function
|
|
error = slave_interface_ptr->destroy();
|
|
MB_SLAVE_CHECK((error == ESP_OK),
|
|
ESP_ERR_INVALID_STATE,
|
|
"SERIAL slave destroy failure error=(0x%x).", error);
|
|
return error;
|
|
}
|
|
|
|
/**
|
|
* Setup Modbus controller parameters
|
|
*/
|
|
esp_err_t mbc_slave_setup(void* comm_info)
|
|
{
|
|
esp_err_t error = ESP_OK;
|
|
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
|
|
ESP_ERR_INVALID_STATE,
|
|
"Slave interface is not correctly initialized.");
|
|
MB_SLAVE_CHECK((slave_interface_ptr->setup != NULL),
|
|
ESP_ERR_INVALID_STATE,
|
|
"Slave interface is not correctly initialized.");
|
|
error = slave_interface_ptr->setup(comm_info);
|
|
MB_SLAVE_CHECK((error == ESP_OK),
|
|
ESP_ERR_INVALID_STATE,
|
|
"SERIAL slave setup failure error=(0x%x).", error);
|
|
return error;
|
|
}
|
|
|
|
/**
|
|
* Start Modbus controller start function
|
|
*/
|
|
esp_err_t mbc_slave_start()
|
|
{
|
|
esp_err_t error = ESP_OK;
|
|
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
|
|
ESP_ERR_INVALID_STATE,
|
|
"Slave interface is not correctly initialized.");
|
|
MB_SLAVE_CHECK((slave_interface_ptr->start != NULL),
|
|
ESP_ERR_INVALID_STATE,
|
|
"Slave interface is not correctly initialized.");
|
|
#ifdef CONFIG_MB_CONTROLLER_SLAVE_ID_SUPPORT
|
|
// Set the slave ID if the KConfig option is selected
|
|
eMBErrorCode status = eMBSetSlaveID(MB_SLAVE_ID_SHORT, TRUE, (UCHAR*)mb_slave_id, sizeof(mb_slave_id));
|
|
MB_SLAVE_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE, "mb stack set slave ID failure.");
|
|
#endif
|
|
error = slave_interface_ptr->start();
|
|
MB_SLAVE_CHECK((error == ESP_OK),
|
|
ESP_ERR_INVALID_STATE,
|
|
"SERIAL slave start failure error=(0x%x).", error);
|
|
return error;
|
|
}
|
|
|
|
/**
|
|
* Blocking function to get event on parameter group change for application task
|
|
*/
|
|
mb_event_group_t mbc_slave_check_event(mb_event_group_t group)
|
|
{
|
|
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
|
|
MB_EVENT_NO_EVENTS,
|
|
"Slave interface is not correctly initialized.");
|
|
MB_SLAVE_CHECK((slave_interface_ptr->check_event != NULL),
|
|
MB_EVENT_NO_EVENTS,
|
|
"Slave interface is not correctly initialized.");
|
|
mb_event_group_t event = slave_interface_ptr->check_event(group);
|
|
return event;
|
|
}
|
|
|
|
/**
|
|
* Function to get notification about parameter change from application task
|
|
*/
|
|
esp_err_t mbc_slave_get_param_info(mb_param_info_t* reg_info, uint32_t timeout)
|
|
{
|
|
esp_err_t error = ESP_OK;
|
|
MB_SLAVE_CHECK((slave_interface_ptr != NULL),
|
|
ESP_ERR_INVALID_STATE,
|
|
"Slave interface is not correctly initialized.");
|
|
MB_SLAVE_CHECK((slave_interface_ptr->get_param_info != NULL),
|
|
ESP_ERR_INVALID_STATE,
|
|
"Slave interface is not correctly initialized.");
|
|
error = slave_interface_ptr->get_param_info(reg_info, timeout);
|
|
MB_SLAVE_CHECK((error == ESP_OK),
|
|
ESP_ERR_INVALID_STATE,
|
|
"SERIAL slave get parameter info failure error=(0x%x).", error);
|
|
return error;
|
|
}
|
|
|
|
/**
|
|
* Function to set area descriptors for modbus parameters
|
|
*/
|
|
esp_err_t mbc_slave_set_descriptor(mb_register_area_descriptor_t descr_data)
|
|
{
|
|
esp_err_t error = ESP_OK;
|
|
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,
|
|
"SERIAL slave set descriptor failure error=(0x%x).", error);
|
|
return error;
|
|
}
|
|
|
|
/**
|
|
* Below are 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),
|
|
ESP_ERR_INVALID_STATE,
|
|
"Slave interface is not correctly initialized.");
|
|
error = slave_interface_ptr->slave_reg_cb_discrete(pucRegBuffer, usAddress, usNDiscrete);
|
|
|
|
return error;
|
|
}
|
|
|
|
eMBErrorCode eMBRegCoilsCB(UCHAR* pucRegBuffer, USHORT usAddress,
|
|
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);
|
|
return error;
|
|
}
|
|
|
|
eMBErrorCode eMBRegHoldingCB(UCHAR * pucRegBuffer, USHORT usAddress,
|
|
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);
|
|
return error;
|
|
}
|
|
|
|
eMBErrorCode eMBRegInputCB(UCHAR * pucRegBuffer, USHORT usAddress,
|
|
USHORT usNRegs)
|
|
{
|
|
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_input != NULL),
|
|
ESP_ERR_INVALID_STATE,
|
|
"Slave interface is not correctly initialized.");
|
|
error = slave_interface_ptr->slave_reg_cb_input(pucRegBuffer, usAddress, usNRegs);
|
|
return error;
|
|
} |