mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
freemodbus: Fix bug with incorrect coils read mask
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
This commit is contained in:
parent
936ee2884b
commit
449d2a6367
@ -1,26 +1,43 @@
|
|||||||
# The following five lines of boilerplate have to be in your project's
|
# The following five lines of boilerplate have to be in your project's
|
||||||
# CMakeLists in this exact order for cmake to work correctly
|
# CMakeLists in this exact order for cmake to work correctly
|
||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.5)
|
||||||
set(COMPONENT_SRCS "modbus/ascii/mbascii.c"
|
|
||||||
|
set(COMPONENT_SRCS "common/esp_modbus_master.c"
|
||||||
|
"common/esp_modbus_slave.c"
|
||||||
|
"modbus/mb.c"
|
||||||
|
"modbus/mb_m.c"
|
||||||
|
"modbus/ascii/mbascii.c"
|
||||||
|
"modbus/rtu/mbrtu_m.c"
|
||||||
|
"modbus/rtu/mbrtu.c"
|
||||||
"modbus/rtu/mbcrc.c"
|
"modbus/rtu/mbcrc.c"
|
||||||
|
"modbus/tcp/mbtcp.c"
|
||||||
|
"port/port.c"
|
||||||
|
"port/portevent.c"
|
||||||
|
"port/portevent_m.c"
|
||||||
|
"port/portother.c"
|
||||||
|
"port/portother_m.c"
|
||||||
|
"port/portserial.c"
|
||||||
|
"port/portserial_m.c"
|
||||||
|
"port/porttimer.c"
|
||||||
|
"port/porttimer_m.c"
|
||||||
"modbus/functions/mbfunccoils.c"
|
"modbus/functions/mbfunccoils.c"
|
||||||
|
"modbus/functions/mbfunccoils_m.c"
|
||||||
"modbus/functions/mbfuncdiag.c"
|
"modbus/functions/mbfuncdiag.c"
|
||||||
"modbus/functions/mbfuncdisc.c"
|
"modbus/functions/mbfuncdisc.c"
|
||||||
|
"modbus/functions/mbfuncdisc_m.c"
|
||||||
"modbus/functions/mbfuncholding.c"
|
"modbus/functions/mbfuncholding.c"
|
||||||
|
"modbus/functions/mbfuncholding_m.c"
|
||||||
"modbus/functions/mbfuncinput.c"
|
"modbus/functions/mbfuncinput.c"
|
||||||
|
"modbus/functions/mbfuncinput_m.c"
|
||||||
"modbus/functions/mbfuncother.c"
|
"modbus/functions/mbfuncother.c"
|
||||||
"modbus/rtu/mbrtu.c"
|
|
||||||
"modbus/tcp/mbtcp.c"
|
|
||||||
"modbus/functions/mbutils.c"
|
"modbus/functions/mbutils.c"
|
||||||
"port/portevent.c"
|
"serial_slave/modbus_controller/mbc_serial_slave.c"
|
||||||
"port/portother.c"
|
"serial_master/modbus_controller/mbc_serial_master.c")
|
||||||
"port/portserial.c"
|
set(COMPONENT_ADD_INCLUDEDIRS common/include)
|
||||||
"port/porttimer.c"
|
set(COMPONENT_PRIV_INCLUDEDIRS common port modbus modbus/ascii modbus/functions
|
||||||
"modbus_controller/mbcontroller.c"
|
modbus/rtu modbus/tcp modbus/include)
|
||||||
"modbus/mb.c")
|
list(APPEND COMPONENT_PRIV_INCLUDEDIRS serial_slave/port serial_slave/modbus_controller
|
||||||
|
serial_master/port serial_master/modbus_controller)
|
||||||
set(COMPONENT_ADD_INCLUDEDIRS modbus/include modbus_controller)
|
|
||||||
set(COMPONENT_PRIV_INCLUDEDIRS modbus port modbus/ascii modbus/functions modbus/rtu modbus/include)
|
|
||||||
set(COMPONENT_REQUIRES "driver")
|
set(COMPONENT_REQUIRES "driver")
|
||||||
|
|
||||||
register_component()
|
register_component()
|
||||||
|
@ -1,5 +1,23 @@
|
|||||||
menu "Modbus configuration"
|
menu "Modbus configuration"
|
||||||
|
|
||||||
|
config MB_MASTER_TIMEOUT_MS_RESPOND
|
||||||
|
int "Slave respond timeout (Milliseconds)"
|
||||||
|
default 150
|
||||||
|
range 50 400
|
||||||
|
|
||||||
|
help
|
||||||
|
If master sends a frame which is not broadcast, it has to wait sometime for slave response.
|
||||||
|
if slave is not respond in this time, the master will process timeout error.
|
||||||
|
|
||||||
|
config MB_MASTER_DELAY_MS_CONVERT
|
||||||
|
int "Slave conversion delay (Milliseconds)"
|
||||||
|
default 200
|
||||||
|
range 50 400
|
||||||
|
|
||||||
|
help
|
||||||
|
If master sends a broadcast frame, it has to wait conversion time to delay,
|
||||||
|
then master can send next frame.
|
||||||
|
|
||||||
config MB_QUEUE_LENGTH
|
config MB_QUEUE_LENGTH
|
||||||
int "Modbus serial task queue length"
|
int "Modbus serial task queue length"
|
||||||
range 0 200
|
range 0 200
|
||||||
@ -22,7 +40,7 @@ menu "Modbus configuration"
|
|||||||
default 256
|
default 256
|
||||||
help
|
help
|
||||||
Modbus serial task RX and TX buffer size for UART driver initialization.
|
Modbus serial task RX and TX buffer size for UART driver initialization.
|
||||||
This buffer is used for Modbus frame transfer. The Modbus protocol maximum
|
This buffer is used for modbus frame transfer. The Modbus protocol maximum
|
||||||
frame size is 256 bytes. Bigger size can be used for non standard implementations.
|
frame size is 256 bytes. Bigger size can be used for non standard implementations.
|
||||||
|
|
||||||
config MB_SERIAL_TASK_PRIO
|
config MB_SERIAL_TASK_PRIO
|
||||||
@ -84,7 +102,7 @@ menu "Modbus configuration"
|
|||||||
Modbus stack event processing time.
|
Modbus stack event processing time.
|
||||||
|
|
||||||
config MB_TIMER_PORT_ENABLED
|
config MB_TIMER_PORT_ENABLED
|
||||||
bool "Modbus stack use timer for 3.5T symbol time measurement"
|
bool "Modbus slave stack use timer for 3.5T symbol time measurement"
|
||||||
default y
|
default y
|
||||||
help
|
help
|
||||||
If this option is set the Modbus stack uses timer for T3.5 time measurement.
|
If this option is set the Modbus stack uses timer for T3.5 time measurement.
|
||||||
@ -93,7 +111,6 @@ menu "Modbus configuration"
|
|||||||
config MB_TIMER_GROUP
|
config MB_TIMER_GROUP
|
||||||
int "Modbus Timer group number"
|
int "Modbus Timer group number"
|
||||||
range 0 1
|
range 0 1
|
||||||
depends on MB_TIMER_PORT_ENABLED
|
|
||||||
default 0
|
default 0
|
||||||
help
|
help
|
||||||
Modbus Timer group number that is used for timeout measurement.
|
Modbus Timer group number that is used for timeout measurement.
|
||||||
@ -101,7 +118,6 @@ menu "Modbus configuration"
|
|||||||
config MB_TIMER_INDEX
|
config MB_TIMER_INDEX
|
||||||
int "Modbus Timer index in the group"
|
int "Modbus Timer index in the group"
|
||||||
range 0 1
|
range 0 1
|
||||||
depends on MB_TIMER_PORT_ENABLED
|
|
||||||
default 0
|
default 0
|
||||||
help
|
help
|
||||||
Modbus Timer Index in the group that is used for timeout measurement.
|
Modbus Timer Index in the group that is used for timeout measurement.
|
||||||
|
29
components/freemodbus/common/esp_modbus_callbacks.h
Normal file
29
components/freemodbus/common/esp_modbus_callbacks.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Stack callback functions prototypes
|
||||||
|
|
||||||
|
#ifndef _ESP_MODBUS_CALLBACKS_H_
|
||||||
|
#define _ESP_MODBUS_CALLBACKS_H_
|
||||||
|
|
||||||
|
#include "mb.h"
|
||||||
|
#include "mb_m.h"
|
||||||
|
|
||||||
|
typedef eMBErrorCode (*reg_input_cb)(UCHAR*, USHORT, USHORT);
|
||||||
|
typedef eMBErrorCode (*reg_holding_cb)(UCHAR*, USHORT, USHORT, eMBRegisterMode);
|
||||||
|
typedef eMBErrorCode (*reg_coils_cb)(UCHAR*, USHORT, USHORT, eMBRegisterMode);
|
||||||
|
typedef eMBErrorCode (*reg_discrete_cb)(UCHAR*, USHORT, USHORT);
|
||||||
|
|
||||||
|
#endif /* _ESP_MODBUS_CALLBACKS_H_ */
|
271
components/freemodbus/common/esp_modbus_master.c
Normal file
271
components/freemodbus/common/esp_modbus_master.c
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
/* 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 "mbc_master.h" // for master interface define
|
||||||
|
#include "esp_modbus_master.h" // for public interface defines
|
||||||
|
#include "mbc_serial_master.h" // for create function of the port
|
||||||
|
#include "esp_modbus_callbacks.h" // for callback functions
|
||||||
|
|
||||||
|
// This file implements public API for Modbus master controller.
|
||||||
|
// These functions are wrappers for interface functions of the controller
|
||||||
|
static mb_master_interface_t* master_interface_ptr = NULL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialization of Modbus controller resources
|
||||||
|
*/
|
||||||
|
esp_err_t mbc_master_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_MASTER:
|
||||||
|
error = mbc_serial_master_create(port_type, &port_handler);
|
||||||
|
break;
|
||||||
|
case MB_PORT_TCP_MASTER:
|
||||||
|
// TCP MAster is not yet supported
|
||||||
|
//error = mbc_tcp_master_create(port_type, &port_handler);
|
||||||
|
return ESP_ERR_NOT_SUPPORTED;
|
||||||
|
default:
|
||||||
|
return ESP_ERR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
MB_MASTER_CHECK((port_handler != NULL),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"Master interface initialization failure, error=(0x%x), port type=(0x%x).",
|
||||||
|
(uint16_t)error, (uint16_t)port_type);
|
||||||
|
|
||||||
|
if ((port_handler != NULL) && (error == ESP_OK)) {
|
||||||
|
master_interface_ptr = (mb_master_interface_t*) port_handler;
|
||||||
|
*handler = port_handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modbus controller destroy function
|
||||||
|
*/
|
||||||
|
esp_err_t mbc_master_destroy()
|
||||||
|
{
|
||||||
|
esp_err_t error = ESP_OK;
|
||||||
|
MB_MASTER_CHECK((master_interface_ptr != NULL),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"Master interface is not correctly initialized.");
|
||||||
|
MB_MASTER_CHECK((master_interface_ptr->destroy != NULL),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"Master interface is not correctly initialized.");
|
||||||
|
error = master_interface_ptr->destroy();
|
||||||
|
MB_MASTER_CHECK((error == ESP_OK),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"SERIAL master destroy failure error=(0x%x).",
|
||||||
|
(uint16_t)error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t mbc_master_get_cid_info(uint16_t cid, const mb_parameter_descriptor_t** param_info)
|
||||||
|
{
|
||||||
|
esp_err_t error = ESP_OK;
|
||||||
|
MB_MASTER_CHECK((master_interface_ptr != NULL),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"Master interface is not correctly initialized.");
|
||||||
|
MB_MASTER_CHECK((master_interface_ptr->get_cid_info != NULL),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"Master interface is not correctly initialized.");
|
||||||
|
error = master_interface_ptr->get_cid_info(cid, param_info);
|
||||||
|
MB_MASTER_CHECK((error == ESP_OK),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"SERIAL master get cid info failure error=(0x%x).",
|
||||||
|
(uint16_t)error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get parameter data for corresponding characteristic
|
||||||
|
*/
|
||||||
|
esp_err_t mbc_master_get_parameter(uint16_t cid, char* name, uint8_t* value, uint8_t* type)
|
||||||
|
{
|
||||||
|
esp_err_t error = ESP_OK;
|
||||||
|
MB_MASTER_CHECK((master_interface_ptr != NULL),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"Master interface is not correctly initialized.");
|
||||||
|
MB_MASTER_CHECK((master_interface_ptr->get_parameter != NULL),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"Master interface is not correctly initialized.");
|
||||||
|
error = master_interface_ptr->get_parameter(cid, name, value, type);
|
||||||
|
MB_MASTER_CHECK((error == ESP_OK),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"SERIAL master get parameter failure error=(0x%x).",
|
||||||
|
(uint16_t)error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send custom Modbus request defined as mb_param_request_t structure
|
||||||
|
*/
|
||||||
|
esp_err_t mbc_master_send_request(mb_param_request_t* request, void* data_ptr)
|
||||||
|
{
|
||||||
|
esp_err_t error = ESP_OK;
|
||||||
|
MB_MASTER_CHECK((master_interface_ptr != NULL),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"Master interface is not correctly initialized.");
|
||||||
|
MB_MASTER_CHECK((master_interface_ptr->send_request != NULL),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"Master interface is not correctly initialized.");
|
||||||
|
error = master_interface_ptr->send_request(request, data_ptr);
|
||||||
|
MB_MASTER_CHECK((error == ESP_OK),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"SERIAL master get parameter failure error=(0x%x).",
|
||||||
|
(uint16_t)error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Modbus parameter description table
|
||||||
|
*/
|
||||||
|
esp_err_t mbc_master_set_descriptor(const mb_parameter_descriptor_t* descriptor, const uint16_t num_elements)
|
||||||
|
{
|
||||||
|
esp_err_t error = ESP_OK;
|
||||||
|
MB_MASTER_CHECK((master_interface_ptr != NULL),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"Master interface is not correctly initialized.");
|
||||||
|
MB_MASTER_CHECK((master_interface_ptr->set_descriptor != NULL),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"Master interface is not correctly initialized.");
|
||||||
|
error = master_interface_ptr->set_descriptor(descriptor, num_elements);
|
||||||
|
MB_MASTER_CHECK((error == ESP_OK),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"SERIAL master set descriptor failure error=(0x%x).",
|
||||||
|
(uint16_t)error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set parameter value for characteristic selected by name and cid
|
||||||
|
*/
|
||||||
|
esp_err_t mbc_master_set_parameter(uint16_t cid, char* name, uint8_t* value, uint8_t* type)
|
||||||
|
{
|
||||||
|
esp_err_t error = ESP_OK;
|
||||||
|
MB_MASTER_CHECK((master_interface_ptr != NULL),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"Master interface is not correctly initialized.");
|
||||||
|
MB_MASTER_CHECK((master_interface_ptr->set_parameter != NULL),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"Master interface is not correctly initialized.");
|
||||||
|
error = master_interface_ptr->set_parameter(cid, name, value, type);
|
||||||
|
MB_MASTER_CHECK((error == ESP_OK),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"SERIAL master set parameter failure error=(0x%x).",
|
||||||
|
(uint16_t)error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup Modbus controller parameters
|
||||||
|
*/
|
||||||
|
esp_err_t mbc_master_setup(void* comm_info)
|
||||||
|
{
|
||||||
|
esp_err_t error = ESP_OK;
|
||||||
|
MB_MASTER_CHECK((master_interface_ptr != NULL),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"Master interface is not correctly initialized.");
|
||||||
|
MB_MASTER_CHECK((master_interface_ptr->setup != NULL),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"Master interface is not correctly initialized.");
|
||||||
|
error = master_interface_ptr->setup(comm_info);
|
||||||
|
MB_MASTER_CHECK((error == ESP_OK),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"SERIAL master setup failure error=(0x%x).",
|
||||||
|
(uint16_t)error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modbus controller stack start function
|
||||||
|
*/
|
||||||
|
esp_err_t mbc_master_start()
|
||||||
|
{
|
||||||
|
esp_err_t error = ESP_OK;
|
||||||
|
MB_MASTER_CHECK((master_interface_ptr != NULL),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"Master interface is not correctly initialized.");
|
||||||
|
MB_MASTER_CHECK((master_interface_ptr->start != NULL),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"Master interface is not correctly initialized.");
|
||||||
|
error = master_interface_ptr->start();
|
||||||
|
MB_MASTER_CHECK((error == ESP_OK),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"SERIAL master start failure error=(0x%x).",
|
||||||
|
(uint16_t)error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
eMBErrorCode eMBMasterRegDiscreteCB(UCHAR * pucRegBuffer, USHORT usAddress,
|
||||||
|
USHORT usNDiscrete)
|
||||||
|
{
|
||||||
|
eMBErrorCode error = MB_ENOERR;
|
||||||
|
MB_MASTER_CHECK((master_interface_ptr != NULL),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"Master interface is not correctly initialized.");
|
||||||
|
MB_MASTER_CHECK((master_interface_ptr->master_reg_cb_discrete != NULL),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"Master interface is not correctly initialized.");
|
||||||
|
error = master_interface_ptr->master_reg_cb_discrete(pucRegBuffer, usAddress, usNDiscrete);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
eMBErrorCode eMBMasterRegCoilsCB(UCHAR* pucRegBuffer, USHORT usAddress,
|
||||||
|
USHORT usNCoils, eMBRegisterMode eMode)
|
||||||
|
{
|
||||||
|
eMBErrorCode error = MB_ENOERR;
|
||||||
|
MB_MASTER_CHECK((master_interface_ptr != NULL),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"Master interface is not correctly initialized.");
|
||||||
|
MB_MASTER_CHECK((master_interface_ptr->master_reg_cb_coils != NULL),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"Master interface is not correctly initialized.");
|
||||||
|
error = master_interface_ptr->master_reg_cb_coils(pucRegBuffer, usAddress,
|
||||||
|
usNCoils, eMode);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
eMBErrorCode eMBMasterRegHoldingCB(UCHAR * pucRegBuffer, USHORT usAddress,
|
||||||
|
USHORT usNRegs, eMBRegisterMode eMode)
|
||||||
|
{
|
||||||
|
eMBErrorCode error = MB_ENOERR;
|
||||||
|
MB_MASTER_CHECK((master_interface_ptr != NULL),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"Master interface is not correctly initialized.");
|
||||||
|
MB_MASTER_CHECK((master_interface_ptr->master_reg_cb_holding != NULL),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"Master interface is not correctly initialized.");
|
||||||
|
error = master_interface_ptr->master_reg_cb_holding(pucRegBuffer, usAddress,
|
||||||
|
usNRegs, eMode);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
eMBErrorCode eMBMasterRegInputCB(UCHAR * pucRegBuffer, USHORT usAddress,
|
||||||
|
USHORT usNRegs)
|
||||||
|
{
|
||||||
|
eMBErrorCode error = MB_ENOERR;
|
||||||
|
MB_MASTER_CHECK((master_interface_ptr != NULL),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"Master interface is not correctly initialized.");
|
||||||
|
MB_MASTER_CHECK((master_interface_ptr->master_reg_cb_input != NULL),
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
"Master interface is not correctly initialized.");
|
||||||
|
error = master_interface_ptr->master_reg_cb_input(pucRegBuffer, usAddress, usNRegs);
|
||||||
|
return error;
|
||||||
|
}
|
255
components/freemodbus/common/esp_modbus_slave.c
Normal file
255
components/freemodbus/common/esp_modbus_slave.c
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
/* 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;
|
||||||
|
}
|
138
components/freemodbus/common/include/esp_modbus_common.h
Normal file
138
components/freemodbus/common/include/esp_modbus_common.h
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _MB_IFACE_COMMON_H
|
||||||
|
#define _MB_IFACE_COMMON_H
|
||||||
|
|
||||||
|
#include "driver/uart.h" // for UART types
|
||||||
|
|
||||||
|
#define MB_CONTROLLER_STACK_SIZE (CONFIG_MB_CONTROLLER_STACK_SIZE) // Stack size for Modbus controller
|
||||||
|
#define MB_CONTROLLER_PRIORITY (CONFIG_MB_SERIAL_TASK_PRIO - 1) // priority of MB controller task
|
||||||
|
|
||||||
|
// Default port defines
|
||||||
|
#define MB_DEVICE_ADDRESS (1) // Default slave device address in Modbus
|
||||||
|
#define MB_DEVICE_SPEED (115200) // Default Modbus speed for now hard defined
|
||||||
|
#define MB_UART_PORT (UART_NUM_2) // Default UART port number
|
||||||
|
#define MB_PAR_INFO_TOUT (10) // Timeout for get parameter info
|
||||||
|
#define MB_PARITY_NONE (UART_PARITY_DISABLE)
|
||||||
|
|
||||||
|
// The Macros below handle the endianness while transfer N byte data into buffer
|
||||||
|
#define _XFER_4_RD(dst, src) { \
|
||||||
|
*(uint8_t *)(dst)++ = *(uint8_t*)(src + 1); \
|
||||||
|
*(uint8_t *)(dst)++ = *(uint8_t*)(src + 0); \
|
||||||
|
*(uint8_t *)(dst)++ = *(uint8_t*)(src + 3); \
|
||||||
|
*(uint8_t *)(dst)++ = *(uint8_t*)(src + 2); \
|
||||||
|
(src) += 4; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define _XFER_2_RD(dst, src) { \
|
||||||
|
*(uint8_t *)(dst)++ = *(uint8_t *)(src + 1); \
|
||||||
|
*(uint8_t *)(dst)++ = *(uint8_t *)(src + 0); \
|
||||||
|
(src) += 2; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define _XFER_4_WR(dst, src) { \
|
||||||
|
*(uint8_t *)(dst + 1) = *(uint8_t *)(src)++; \
|
||||||
|
*(uint8_t *)(dst + 0) = *(uint8_t *)(src)++; \
|
||||||
|
*(uint8_t *)(dst + 3) = *(uint8_t *)(src)++; \
|
||||||
|
*(uint8_t *)(dst + 2) = *(uint8_t *)(src)++ ; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define _XFER_2_WR(dst, src) { \
|
||||||
|
*(uint8_t *)(dst + 1) = *(uint8_t *)(src)++; \
|
||||||
|
*(uint8_t *)(dst + 0) = *(uint8_t *)(src)++; \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Types of actual Modbus implementation
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
MB_PORT_SERIAL_MASTER = 0x00, /*!< Modbus port type serial master. */
|
||||||
|
MB_PORT_SERIAL_SLAVE, /*!< Modbus port type serial slave. */
|
||||||
|
MB_PORT_TCP_MASTER, /*!< Modbus port type TCP master. */
|
||||||
|
MB_PORT_TCP_SLAVE, /*!< Modbus port type TCP slave. */
|
||||||
|
MB_PORT_COUNT /*!< Modbus port count. */
|
||||||
|
} mb_port_type_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Event group for parameters notification
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
MB_EVENT_NO_EVENTS = 0x00,
|
||||||
|
MB_EVENT_HOLDING_REG_WR = BIT0, /*!< Modbus Event Write Holding registers. */
|
||||||
|
MB_EVENT_HOLDING_REG_RD = BIT1, /*!< Modbus Event Read Holding registers. */
|
||||||
|
MB_EVENT_INPUT_REG_RD = BIT3, /*!< Modbus Event Read Input registers. */
|
||||||
|
MB_EVENT_COILS_WR = BIT4, /*!< Modbus Event Write Coils. */
|
||||||
|
MB_EVENT_COILS_RD = BIT5, /*!< Modbus Event Read Coils. */
|
||||||
|
MB_EVENT_DISCRETE_RD = BIT6, /*!< Modbus Event Read Discrete bits. */
|
||||||
|
MB_EVENT_STACK_STARTED = BIT7 /*!< Modbus Event Stack started */
|
||||||
|
} mb_event_group_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Type of Modbus parameter
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
MB_PARAM_HOLDING = 0x00, /*!< Modbus Holding register. */
|
||||||
|
MB_PARAM_INPUT, /*!< Modbus Input register. */
|
||||||
|
MB_PARAM_COIL, /*!< Modbus Coils. */
|
||||||
|
MB_PARAM_DISCRETE, /*!< Modbus Discrete bits. */
|
||||||
|
MB_PARAM_COUNT,
|
||||||
|
MB_PARAM_UNKNOWN = 0xFF
|
||||||
|
} mb_param_type_t;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Modbus serial transmission modes (RTU/ASCII).
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
MB_MODE_RTU, /*!< RTU transmission mode. */
|
||||||
|
MB_MODE_ASCII, /*!< ASCII transmission mode. */
|
||||||
|
MB_MODE_TCP /*!< TCP mode. */
|
||||||
|
} mb_mode_type_t; // Todo: This is common type leave it here for now
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Device communication structure to setup Modbus controller
|
||||||
|
*/
|
||||||
|
typedef union {
|
||||||
|
// Serial communication structure
|
||||||
|
struct {
|
||||||
|
mb_mode_type_t mode; /*!< Modbus communication mode */
|
||||||
|
uint8_t slave_addr; /*!< Modbus slave address field (dummy for master) */
|
||||||
|
uart_port_t port; /*!< Modbus communication port (UART) number */
|
||||||
|
uint32_t baudrate; /*!< Modbus baudrate */
|
||||||
|
uart_parity_t parity; /*!< Modbus UART parity settings */
|
||||||
|
uint16_t dummy_port;
|
||||||
|
};
|
||||||
|
// Tcp communication structure
|
||||||
|
struct {
|
||||||
|
mb_mode_type_t tcp_mode; /*!< Modbus communication mode */
|
||||||
|
uint8_t dummy_addr; /*!< Modbus slave address field (dummy for master) */
|
||||||
|
uart_port_t dummy_uart_port; /*!< Modbus communication port (UART) number */
|
||||||
|
uint32_t dummy_baudrate; /*!< Modbus baudrate */
|
||||||
|
uart_parity_t dummy_parity; /*!< Modbus UART parity settings */
|
||||||
|
uint16_t tcp_port; /*!< Modbus TCP port */
|
||||||
|
};
|
||||||
|
} mb_communication_info_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* common interface method types
|
||||||
|
*/
|
||||||
|
typedef esp_err_t (*iface_init)(mb_port_type_t, void**); /*!< Interface method init */
|
||||||
|
typedef esp_err_t (*iface_destroy)(void); /*!< Interface method destroy */
|
||||||
|
typedef esp_err_t (*iface_setup)(void*); /*!< Interface method setup */
|
||||||
|
typedef esp_err_t (*iface_start)(void); /*!< Interface method start */
|
||||||
|
|
||||||
|
#endif // _MB_IFACE_COMMON_H
|
241
components/freemodbus/common/include/esp_modbus_master.h
Normal file
241
components/freemodbus/common/include/esp_modbus_master.h
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ESP_MB_MASTER_INTERFACE_H
|
||||||
|
#define _ESP_MB_MASTER_INTERFACE_H
|
||||||
|
|
||||||
|
#include <stdint.h> // for standard int types definition
|
||||||
|
#include <stddef.h> // for NULL and std defines
|
||||||
|
#include "soc/soc.h" // for BITN definitions
|
||||||
|
#include "esp_modbus_common.h" // for common types
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Modbus descriptor table parameter type defines.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
PARAM_TYPE_U8 = 0x00, /*!< Unsigned 8 */
|
||||||
|
PARAM_TYPE_U16 = 0x01, /*!< Unsigned 16 */
|
||||||
|
PARAM_TYPE_U32 = 0x02, /*!< Unsigned 32 */
|
||||||
|
PARAM_TYPE_FLOAT = 0x03, /*!< Float type */
|
||||||
|
PARAM_TYPE_ASCII = 0x04 /*!< ASCII type */
|
||||||
|
} mb_descr_type_t;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Modbus descriptor table parameter size in bytes.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
PARAM_SIZE_U8 = 0x01, /*!< Unsigned 8 */
|
||||||
|
PARAM_SIZE_U16 = 0x02, /*!< Unsigned 16 */
|
||||||
|
PARAM_SIZE_U32 = 0x04, /*!< Unsigned 32 */
|
||||||
|
PARAM_SIZE_FLOAT = 0x04, /*!< Float size */
|
||||||
|
PARAM_SIZE_ASCII = 0x08, /*!< ASCII size */
|
||||||
|
PARAM_SIZE_ASCII24 = 0x18, /*!< ASCII24 size */
|
||||||
|
PARAM_MAX_SIZE
|
||||||
|
} mb_descr_size_t;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Modbus parameter options for description table
|
||||||
|
*/
|
||||||
|
typedef union {
|
||||||
|
struct {
|
||||||
|
int opt1; /*!< Parameter option1 */
|
||||||
|
int opt2; /*!< Parameter option2 */
|
||||||
|
int opt3; /*!< Parameter option3 */
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
int min; /*!< Parameter minimum value */
|
||||||
|
int max; /*!< Parameter maximum value */
|
||||||
|
int step; /*!< Step of parameter change tracking */
|
||||||
|
};
|
||||||
|
} mb_parameter_opt_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Permissions for the characteristics
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
PAR_PERMS_READ = 1 << BIT0, /**< the characteristic of the device are readable */
|
||||||
|
PAR_PERMS_WRITE = 1 << BIT1, /**< the characteristic of the device are writable*/
|
||||||
|
PAR_PERMS_TRIGGER = 1 << BIT2, /**< the characteristic of the device are triggerable */
|
||||||
|
PAR_PERMS_READ_WRITE = PAR_PERMS_READ | PAR_PERMS_WRITE, /**< the characteristic of the device are readable & writable */
|
||||||
|
PAR_PERMS_READ_TRIGGER = PAR_PERMS_READ | PAR_PERMS_TRIGGER, /**< the characteristic of the device are readable & triggerable */
|
||||||
|
PAR_PERMS_WRITE_TRIGGER = PAR_PERMS_WRITE | PAR_PERMS_TRIGGER, /**< the characteristic of the device are writable & triggerable */
|
||||||
|
PAR_PERMS_READ_WRITE_TRIGGER = PAR_PERMS_READ_WRITE | PAR_PERMS_TRIGGER, /**< the characteristic of the device are readable & writable & triggerable */
|
||||||
|
} mb_param_perms_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Characteristics descriptor type is used to describe characteristic and
|
||||||
|
* link it with Modbus parameters that reflect its data.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint16_t cid; /*!< Characteristic cid */
|
||||||
|
const char* param_key; /*!< The key (name) of the parameter */
|
||||||
|
const char* param_units; /*!< The physical units of the parameter */
|
||||||
|
uint8_t mb_slave_addr; /*!< Slave address of device in the Modbus segment */
|
||||||
|
mb_param_type_t mb_param_type; /*!< Type of modbus parameter */
|
||||||
|
uint16_t mb_reg_start; /*!< This is the Modbus register address. This is the 0 based value. */
|
||||||
|
uint16_t mb_size; /*!< Size of mb parameter in registers */
|
||||||
|
uint16_t param_offset; /*!< Parameter name (OFFSET in the parameter structure) */
|
||||||
|
mb_descr_type_t param_type; /*!< Float, U8, U16, U32, ASCII, etc. */
|
||||||
|
mb_descr_size_t param_size; /*!< Number of bytes in the parameter. */
|
||||||
|
mb_parameter_opt_t param_opts; /*!< Parameter options used to check limits and etc. */
|
||||||
|
mb_param_perms_t access; /*!< Access permissions based on mode */
|
||||||
|
} mb_parameter_descriptor_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Modbus register request type structure
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint8_t slave_addr; /*!< Modbus slave address */
|
||||||
|
uint8_t command; /*!< Modbus command to send */
|
||||||
|
uint16_t reg_start; /*!< Modbus start register */
|
||||||
|
uint16_t reg_size; /*!< Modbus number of registers */
|
||||||
|
} mb_param_request_t;
|
||||||
|
|
||||||
|
// Master interface public functions
|
||||||
|
/**
|
||||||
|
* @brief Initialize Modbus controller and stack
|
||||||
|
*
|
||||||
|
* @param[out] handler handler(pointer) to master data structure
|
||||||
|
* @param[in] port_type the type of port
|
||||||
|
* @return
|
||||||
|
* - ESP_OK Success
|
||||||
|
* - ESP_ERR_NO_MEM Parameter error
|
||||||
|
*/
|
||||||
|
esp_err_t mbc_master_init(mb_port_type_t port_type, void** handler);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Destroy Modbus controller and stack
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK Success
|
||||||
|
* - ESP_ERR_INVALID_STATE Parameter error
|
||||||
|
*/
|
||||||
|
esp_err_t mbc_master_destroy(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Start Modbus communication stack
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK Success
|
||||||
|
* - ESP_ERR_INVALID_ARG Modbus stack start error
|
||||||
|
*/
|
||||||
|
esp_err_t mbc_master_start(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set Modbus communication parameters for the controller
|
||||||
|
*
|
||||||
|
* @param comm_info Communication parameters structure.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK Success
|
||||||
|
* - ESP_ERR_INVALID_ARG Incorrect parameter data
|
||||||
|
*/
|
||||||
|
esp_err_t mbc_master_setup(void* comm_info);
|
||||||
|
|
||||||
|
/***************************** Specific interface functions ********************************************
|
||||||
|
* Interface functions below provide basic methods to read/write access to slave devices in Modbus
|
||||||
|
* segment as well as API to read specific supported characteristics linked to Modbus parameters
|
||||||
|
* of devices in Modbus network.
|
||||||
|
*******************************************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Assign parameter description table for Modbus controller interface.
|
||||||
|
*
|
||||||
|
* @param[in] descriptor pointer to parameter description table
|
||||||
|
* @param num_elements number of elements in the table
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - esp_err_t ESP_OK - set descriptor successfully
|
||||||
|
* - esp_err_t ESP_ERR_INVALID_ARG - invalid argument in function call
|
||||||
|
*/
|
||||||
|
esp_err_t mbc_master_set_descriptor(const mb_parameter_descriptor_t* descriptor, const uint16_t num_elements);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Send data request as defined in parameter request, waits response
|
||||||
|
* from slave and returns status of command execution. This function provides standard way
|
||||||
|
* for read/write access to Modbus devices in the network.
|
||||||
|
*
|
||||||
|
* @param[in] request pointer to request structure of type mb_param_request_t
|
||||||
|
* @param[in] data_ptr pointer to data buffer to send or received data (dependent of command field in request)
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - esp_err_t ESP_OK - request was successful
|
||||||
|
* - esp_err_t ESP_ERR_INVALID_ARG - invalid argument of function
|
||||||
|
* - esp_err_t ESP_ERR_INVALID_RESPONSE - an invalid response from slave
|
||||||
|
* - esp_err_t ESP_ERR_TIMEOUT - operation timeout or no response from slave
|
||||||
|
* - esp_err_t ESP_ERR_NOT_SUPPORTED - the request command is not supported by slave
|
||||||
|
* - esp_err_t ESP_FAIL - slave returned an exception or other failure
|
||||||
|
*/
|
||||||
|
esp_err_t mbc_master_send_request(mb_param_request_t* request, void* data_ptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get information about supported characteristic defined as cid. Uses parameter description table to get
|
||||||
|
* this information. The function will check if characteristic defined as a cid parameter is supported
|
||||||
|
* and returns its description in param_info. Returns ESP_ERR_NOT_FOUND if characteristic is not supported.
|
||||||
|
*
|
||||||
|
* @param[in] cid characteristic id
|
||||||
|
* @param param_info pointer to pointer of characteristic data.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - esp_err_t ESP_OK - request was successful and buffer contains the supported characteristic name
|
||||||
|
* - esp_err_t ESP_ERR_INVALID_ARG - invalid argument of function
|
||||||
|
* - esp_err_t ESP_ERR_NOT_FOUND - the characteristic (cid) not found
|
||||||
|
* - esp_err_t ESP_FAIL - unknown error during lookup table processing
|
||||||
|
*/
|
||||||
|
esp_err_t mbc_master_get_cid_info(uint16_t cid, const mb_parameter_descriptor_t** param_info);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read parameter from modbus slave device whose name is defined by name and has cid.
|
||||||
|
* The additional data for request is taken from parameter description (lookup) table.
|
||||||
|
*
|
||||||
|
* @param[in] cid id of the characteristic for parameter
|
||||||
|
* @param[in] name pointer into string name (key) of parameter (null terminated)
|
||||||
|
* @param[out] value pointer to data buffer of parameter
|
||||||
|
* @param[out] type parameter type associated with the name returned from parameter description table.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - esp_err_t ESP_OK - request was successful and value buffer contains
|
||||||
|
* representation of actual parameter data from slave
|
||||||
|
* - esp_err_t ESP_ERR_INVALID_ARG - invalid argument of function
|
||||||
|
* - esp_err_t ESP_ERR_INVALID_RESPONSE - an invalid response from slave
|
||||||
|
* - esp_err_t ESP_ERR_INVALID_STATE - invalid state during data processing or allocation failure
|
||||||
|
* - esp_err_t ESP_ERR_TIMEOUT - operation timed out and no response from slave
|
||||||
|
* - esp_err_t ESP_ERR_NOT_SUPPORTED - the request command is not supported by slave
|
||||||
|
* - esp_err_t ESP_ERR_NOT_FOUND - the parameter is not found in the parameter description table
|
||||||
|
* - esp_err_t ESP_FAIL - slave returned an exception or other failure
|
||||||
|
*/
|
||||||
|
esp_err_t mbc_master_get_parameter(uint16_t cid, char* name, uint8_t* value, uint8_t *type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set characteristic's value defined as a name and cid parameter.
|
||||||
|
* The additional data for cid parameter request is taken from master parameter lookup table.
|
||||||
|
*
|
||||||
|
* @param[in] cid id of the characteristic for parameter
|
||||||
|
* @param[in] name pointer into string name (key) of parameter (null terminated)
|
||||||
|
* @param[out] value pointer to data buffer of parameter (actual representation of json value field in binary form)
|
||||||
|
* @param[out] type pointer to parameter type associated with the name returned from parameter lookup table.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - esp_err_t ESP_OK - request was successful and value was saved in the slave device registers
|
||||||
|
* - esp_err_t ESP_ERR_INVALID_ARG - invalid argument of function
|
||||||
|
* - esp_err_t ESP_ERR_INVALID_RESPONSE - an invalid response from slave during processing of parameter
|
||||||
|
* - esp_err_t ESP_ERR_INVALID_STATE - invalid state during data processing or allocation failure
|
||||||
|
* - esp_err_t ESP_ERR_TIMEOUT - operation timed out and no response from slave
|
||||||
|
* - esp_err_t ESP_ERR_NOT_SUPPORTED - the request command is not supported by slave
|
||||||
|
* - esp_err_t ESP_FAIL - slave returned an exception or other failure
|
||||||
|
*/
|
||||||
|
esp_err_t mbc_master_set_parameter(uint16_t cid, char* name, uint8_t* value, uint8_t *type);
|
||||||
|
|
||||||
|
#endif // _ESP_MB_MASTER_INTERFACE_H
|
123
components/freemodbus/common/include/esp_modbus_slave.h
Normal file
123
components/freemodbus/common/include/esp_modbus_slave.h
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ESP_MB_SLAVE_INTERFACE_H
|
||||||
|
#define _ESP_MB_SLAVE_INTERFACE_H
|
||||||
|
|
||||||
|
// Public interface header for slave
|
||||||
|
#include <stdint.h> // for standard int types definition
|
||||||
|
#include <stddef.h> // for NULL and std defines
|
||||||
|
#include "soc/soc.h" // for BITN definitions
|
||||||
|
#include "freertos/FreeRTOS.h" // for task creation and queues access
|
||||||
|
#include "freertos/event_groups.h" // for event groups
|
||||||
|
#include "esp_modbus_common.h" // for common types
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parameter access event information type
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint32_t time_stamp; /*!< Timestamp of Modbus Event (uS)*/
|
||||||
|
uint16_t mb_offset; /*!< Modbus register offset */
|
||||||
|
mb_event_group_t type; /*!< Modbus event type */
|
||||||
|
uint8_t* address; /*!< Modbus data storage address */
|
||||||
|
size_t size; /*!< Modbus event register size (number of registers)*/
|
||||||
|
} mb_param_info_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parameter storage area descriptor
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint16_t start_offset; /*!< Modbus start address for area descriptor */
|
||||||
|
mb_param_type_t type; /*!< Type of storage area descriptor */
|
||||||
|
void* address; /*!< Instance address for storage area descriptor */
|
||||||
|
size_t size; /*!< Instance size for area descriptor (bytes) */
|
||||||
|
} mb_register_area_descriptor_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize Modbus controller and stack
|
||||||
|
*
|
||||||
|
* @param[out] handler handler(pointer) to master data structure
|
||||||
|
* @param[in] port_type type of stack
|
||||||
|
* @return
|
||||||
|
* - ESP_OK Success
|
||||||
|
* - ESP_ERR_NO_MEM Parameter error
|
||||||
|
*/
|
||||||
|
//esp_err_t mbc_slave_init(mb_port_type_t port_type, void** handler);
|
||||||
|
esp_err_t mbc_slave_init(mb_port_type_t port_type, void** handler);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Destroy Modbus controller and stack
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK Success
|
||||||
|
* - ESP_ERR_INVALID_STATE Parameter error
|
||||||
|
*/
|
||||||
|
esp_err_t mbc_slave_destroy(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Start Modbus communication stack
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK Success
|
||||||
|
* - ESP_ERR_INVALID_ARG Modbus stack start error
|
||||||
|
*/
|
||||||
|
esp_err_t mbc_slave_start(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set Modbus communication parameters for the controller
|
||||||
|
*
|
||||||
|
* @param comm_info Communication parameters structure.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK Success
|
||||||
|
* - ESP_ERR_INVALID_ARG Incorrect parameter data
|
||||||
|
*/
|
||||||
|
esp_err_t mbc_slave_setup(void* comm_info);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Wait for specific event on parameter change.
|
||||||
|
*
|
||||||
|
* @param group Group event bit mask to wait for change
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - mb_event_group_t event bits triggered
|
||||||
|
*/
|
||||||
|
mb_event_group_t mbc_slave_check_event(mb_event_group_t group);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get parameter information
|
||||||
|
*
|
||||||
|
* @param[out] reg_info parameter info structure
|
||||||
|
* @param timeout Timeout in milliseconds to read information from
|
||||||
|
* parameter queue
|
||||||
|
* @return
|
||||||
|
* - ESP_OK Success
|
||||||
|
* - ESP_ERR_TIMEOUT Can not get data from parameter queue
|
||||||
|
* or queue overflow
|
||||||
|
*/
|
||||||
|
esp_err_t mbc_slave_get_param_info(mb_param_info_t* reg_info, uint32_t timeout);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set Modbus area descriptor
|
||||||
|
*
|
||||||
|
* @param descr_data Modbus registers area descriptor structure
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: The appropriate descriptor is set
|
||||||
|
* - ESP_ERR_INVALID_ARG: The argument is incorrect
|
||||||
|
*/
|
||||||
|
esp_err_t mbc_slave_set_descriptor(mb_register_area_descriptor_t descr_data);
|
||||||
|
|
||||||
|
#endif
|
32
components/freemodbus/common/include/mbcontroller.h
Normal file
32
components/freemodbus/common/include/mbcontroller.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
// mbcontroller.h
|
||||||
|
// mbcontroller common header
|
||||||
|
|
||||||
|
#ifndef _MODBUS_CONTROLLER_COMMON
|
||||||
|
#define _MODBUS_CONTROLLER_COMMON
|
||||||
|
|
||||||
|
#include <stdint.h> // for standard int types definition
|
||||||
|
#include <stddef.h> // for NULL and std defines
|
||||||
|
#include "string.h" // for strerror()
|
||||||
|
#include "errno.h" // for errno
|
||||||
|
#include "esp_err.h" // for error handling
|
||||||
|
#include "driver/uart.h" // for uart port number defines
|
||||||
|
#include "sdkconfig.h" // for KConfig options
|
||||||
|
|
||||||
|
#include "esp_modbus_master.h"
|
||||||
|
#include "esp_modbus_slave.h"
|
||||||
|
#endif
|
||||||
|
|
106
components/freemodbus/common/mbc_master.h
Normal file
106
components/freemodbus/common/mbc_master.h
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
#ifndef _MB_CONTROLLER_MASTER_H
|
||||||
|
#define _MB_CONTROLLER_MASTER_H
|
||||||
|
|
||||||
|
#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 "driver/uart.h" // for UART types
|
||||||
|
#include "errno.h" // for errno
|
||||||
|
#include "esp_log.h" // for log write
|
||||||
|
#include "string.h" // for strerror()
|
||||||
|
#include "esp_modbus_common.h" // for common types
|
||||||
|
#include "esp_modbus_master.h" // for public master types
|
||||||
|
#include "esp_modbus_callbacks.h"
|
||||||
|
|
||||||
|
/* ----------------------- Defines ------------------------------------------*/
|
||||||
|
|
||||||
|
#define MB_MASTER_TAG "MB_CONTROLLER_MASTER"
|
||||||
|
|
||||||
|
#define MB_MASTER_CHECK(a, ret_val, str, ...) \
|
||||||
|
if (!(a)) { \
|
||||||
|
ESP_LOGE(MB_MASTER_TAG, "%s(%u): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
|
||||||
|
return (ret_val); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MB_MASTER_ASSERT(con) do { \
|
||||||
|
if (!(con)) { ESP_LOGE(MB_MASTER_TAG, "assert errno:%d, errno_str: !(%s)", errno, strerror(errno)); assert(0 && #con); } \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Request mode for parameter to use in data dictionary
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
MB_PARAM_READ, /*!< Read parameter values. */
|
||||||
|
MB_PARAM_WRITE /*!< Write parameter values. */
|
||||||
|
} mb_param_mode_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Device communication parameters for master
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
mb_mode_type_t mode; /*!< Modbus communication mode */
|
||||||
|
uint8_t dummy; /*!< Dummy field */
|
||||||
|
uart_port_t port; /*!< Modbus communication port (UART) number */
|
||||||
|
uint32_t baudrate; /*!< Modbus baudrate */
|
||||||
|
uart_parity_t parity; /*!< Modbus UART parity settings */
|
||||||
|
} mb_master_comm_info_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Modbus controller handler structure
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
mb_port_type_t port_type; /*!< Modbus port type */
|
||||||
|
mb_communication_info_t mbm_comm; /*!< Modbus communication info */
|
||||||
|
uint8_t* mbm_reg_buffer_ptr; /*!< Modbus data buffer pointer */
|
||||||
|
uint16_t mbm_reg_buffer_size; /*!< Modbus data buffer size */
|
||||||
|
TaskHandle_t mbm_task_handle; /*!< Modbus task handle */
|
||||||
|
EventGroupHandle_t mbm_event_group; /*!< Modbus controller event group */
|
||||||
|
const mb_parameter_descriptor_t* mbm_param_descriptor_table; /*!< Modbus controller parameter description table */
|
||||||
|
size_t mbm_param_descriptor_size; /*!< Modbus controller parameter description table size*/
|
||||||
|
} mb_master_options_t;
|
||||||
|
|
||||||
|
typedef esp_err_t (*iface_get_cid_info)(uint16_t, const mb_parameter_descriptor_t**); /*!< Interface get_cid_info method */
|
||||||
|
typedef esp_err_t (*iface_get_parameter)(uint16_t, char*, uint8_t*, uint8_t*); /*!< Interface get_parameter method */
|
||||||
|
typedef esp_err_t (*iface_send_request)(mb_param_request_t*, void*); /*!< Interface send_request method */
|
||||||
|
typedef esp_err_t (*iface_set_descriptor)(const mb_parameter_descriptor_t*, const uint16_t); /*!< Interface set_descriptor method */
|
||||||
|
typedef esp_err_t (*iface_set_parameter)(uint16_t, char*, uint8_t*, uint8_t*); /*!< Interface set_parameter method */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Modbus controller interface structure
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
// Master object interface options
|
||||||
|
mb_master_options_t opts;
|
||||||
|
|
||||||
|
// Public interface methods
|
||||||
|
iface_init init; /*!< Interface method init */
|
||||||
|
iface_destroy destroy; /*!< Interface method destroy */
|
||||||
|
iface_setup setup; /*!< Interface method setup */
|
||||||
|
iface_start start; /*!< Interface method start */
|
||||||
|
iface_get_cid_info get_cid_info; /*!< Interface get_cid_info method */
|
||||||
|
iface_get_parameter get_parameter; /*!< Interface get_parameter method */
|
||||||
|
iface_send_request send_request; /*!< Interface send_request method */
|
||||||
|
iface_set_descriptor set_descriptor; /*!< Interface set_descriptor method */
|
||||||
|
iface_set_parameter set_parameter; /*!< Interface set_parameter method */
|
||||||
|
// Modbus register calback function pointers
|
||||||
|
reg_discrete_cb master_reg_cb_discrete; /*!< Stack callback discrete rw method */
|
||||||
|
reg_input_cb master_reg_cb_input; /*!< Stack callback input rw method */
|
||||||
|
reg_holding_cb master_reg_cb_holding; /*!< Stack callback holding rw method */
|
||||||
|
reg_coils_cb master_reg_cb_coils; /*!< Stack callback coils rw method */
|
||||||
|
} mb_master_interface_t;
|
||||||
|
|
||||||
|
#endif //_MB_CONTROLLER_MASTER_H
|
94
components/freemodbus/common/mbc_slave.h
Normal file
94
components/freemodbus/common/mbc_slave.h
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
#ifndef _MB_CONTROLLER_SLAVE_H
|
||||||
|
#define _MB_CONTROLLER_SLAVE_H
|
||||||
|
|
||||||
|
#include "driver/uart.h" // for uart defines
|
||||||
|
#include "errno.h" // for errno
|
||||||
|
#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
|
||||||
|
|
||||||
|
/* ----------------------- Defines ------------------------------------------*/
|
||||||
|
#define MB_INST_MIN_SIZE (2) // The minimal size of Modbus registers area in bytes
|
||||||
|
#define MB_INST_MAX_SIZE (65535 * 2) // The maximum size of Modbus area in bytes
|
||||||
|
|
||||||
|
#define MB_CONTROLLER_NOTIFY_QUEUE_SIZE (CONFIG_MB_CONTROLLER_NOTIFY_QUEUE_SIZE) // Number of messages in parameter notification queue
|
||||||
|
#define MB_CONTROLLER_NOTIFY_TIMEOUT (pdMS_TO_TICKS(CONFIG_MB_CONTROLLER_NOTIFY_TIMEOUT)) // notification timeout
|
||||||
|
|
||||||
|
#define MB_SLAVE_TAG "MB_CONTROLLER_SLAVE"
|
||||||
|
|
||||||
|
#define MB_SLAVE_CHECK(a, ret_val, str, ...) \
|
||||||
|
if (!(a)) { \
|
||||||
|
ESP_LOGE(MB_SLAVE_TAG, "%s(%u): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
|
||||||
|
return (ret_val); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MB_SLAVE_ASSERT(con) do { \
|
||||||
|
if (!(con)) { ESP_LOGE(MB_SLAVE_TAG, "assert errno:%d, errno_str: !(%s)", errno, strerror(errno)); assert(0 && #con); } \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Device communication parameters for master
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
mb_mode_type_t mode; /*!< Modbus communication mode */
|
||||||
|
uint8_t slave_addr; /*!< Slave address field */
|
||||||
|
uart_port_t port; /*!< Modbus communication port (UART) number */
|
||||||
|
uint32_t baudrate; /*!< Modbus baudrate */
|
||||||
|
uart_parity_t parity; /*!< Modbus UART parity settings */
|
||||||
|
} mb_slave_comm_info_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Modbus controller handler structure
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
mb_port_type_t port_type; /*!< port type */
|
||||||
|
mb_communication_info_t mbs_comm; /*!< communication info */
|
||||||
|
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 */
|
||||||
|
} mb_slave_options_t;
|
||||||
|
|
||||||
|
typedef mb_event_group_t (*iface_check_event)(mb_event_group_t); /*!< Interface method check_event */
|
||||||
|
typedef esp_err_t (*iface_get_param_info)(mb_param_info_t*, uint32_t); /*!< Interface method get_param_info */
|
||||||
|
typedef esp_err_t (*iface_set_descriptor)(mb_register_area_descriptor_t); /*!< Interface method set_descriptor */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Request mode for parameter to use in data dictionary
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
mb_slave_options_t opts; /*!< Modbus slave options */
|
||||||
|
|
||||||
|
// Functional pointers to internal static functions of the implementation (public interface methods)
|
||||||
|
iface_init init; /*!< Interface method init */
|
||||||
|
iface_destroy destroy; /*!< Interface method destroy */
|
||||||
|
iface_setup setup; /*!< Interface method setup */
|
||||||
|
iface_start start; /*!< Interface method start */
|
||||||
|
iface_check_event check_event; /*!< Interface method check_event */
|
||||||
|
iface_get_param_info get_param_info; /*!< Interface method get_param_info */
|
||||||
|
iface_set_descriptor set_descriptor; /*!< Interface method set_descriptor */
|
||||||
|
|
||||||
|
// Modbus register calback function pointers
|
||||||
|
reg_discrete_cb slave_reg_cb_discrete; /*!< Stack callback discrete rw method */
|
||||||
|
reg_input_cb slave_reg_cb_input; /*!< Stack callback input rw method */
|
||||||
|
reg_holding_cb slave_reg_cb_holding; /*!< Stack callback holding rw method */
|
||||||
|
reg_coils_cb slave_reg_cb_coils; /*!< Stack callback coils rw method */
|
||||||
|
} mb_slave_interface_t;
|
||||||
|
|
||||||
|
#endif
|
@ -1,3 +1,13 @@
|
|||||||
COMPONENT_ADD_INCLUDEDIRS := modbus/include modbus_controller
|
|
||||||
COMPONENT_PRIV_INCLUDEDIRS := . modbus port modbus/ascii modbus/functions modbus/rtu modbus/include
|
COMPONENT_ADD_INCLUDEDIRS := common/include
|
||||||
COMPONENT_SRCDIRS := . modbus port modbus/ascii modbus/functions modbus/rtu modbus_controller
|
COMPONENT_PRIV_INCLUDEDIRS := common port modbus modbus/ascii modbus/functions
|
||||||
|
COMPONENT_PRIV_INCLUDEDIRS += modbus/rtu modbus/tcp modbus/include
|
||||||
|
COMPONENT_PRIV_INCLUDEDIRS += serial_slave/port serial_slave/modbus_controller
|
||||||
|
COMPONENT_PRIV_INCLUDEDIRS += serial_master/port serial_master/modbus_controller
|
||||||
|
COMPONENT_SRCDIRS := common
|
||||||
|
COMPONENT_SRCDIRS += modbus modbus/ascii modbus/functions modbus/rtu modbus/tcp
|
||||||
|
COMPONENT_SRCDIRS += serial_slave/port
|
||||||
|
COMPONENT_SRCDIRS += serial_slave/modbus_controller
|
||||||
|
COMPONENT_SRCDIRS += serial_master/port
|
||||||
|
COMPONENT_SRCDIRS += serial_master/modbus_controller
|
||||||
|
COMPONENT_SRCDIRS += port
|
||||||
|
@ -44,7 +44,7 @@
|
|||||||
#include "mbcrc.h"
|
#include "mbcrc.h"
|
||||||
#include "mbport.h"
|
#include "mbport.h"
|
||||||
|
|
||||||
#if MB_ASCII_ENABLED > 0
|
#if MB_SLAVE_ASCII_ENABLED > 0
|
||||||
|
|
||||||
/* ----------------------- Defines ------------------------------------------*/
|
/* ----------------------- Defines ------------------------------------------*/
|
||||||
#define MB_ASCII_DEFAULT_CR '\r' /*!< Default CR character for Modbus ASCII. */
|
#define MB_ASCII_DEFAULT_CR '\r' /*!< Default CR character for Modbus ASCII. */
|
||||||
|
@ -34,8 +34,7 @@
|
|||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
PR_BEGIN_EXTERN_C
|
PR_BEGIN_EXTERN_C
|
||||||
#endif
|
#endif
|
||||||
|
#if MB_SLAVE_ASCII_ENABLED > 0
|
||||||
#if MB_ASCII_ENABLED > 0
|
|
||||||
eMBErrorCode eMBASCIIInit( UCHAR slaveAddress, UCHAR ucPort,
|
eMBErrorCode eMBASCIIInit( UCHAR slaveAddress, UCHAR ucPort,
|
||||||
ULONG ulBaudRate, eMBParity eParity );
|
ULONG ulBaudRate, eMBParity eParity );
|
||||||
void eMBASCIIStart( void );
|
void eMBASCIIStart( void );
|
||||||
|
391
components/freemodbus/modbus/functions/mbfunccoils_m.c
Normal file
391
components/freemodbus/modbus/functions/mbfunccoils_m.c
Normal file
@ -0,0 +1,391 @@
|
|||||||
|
/*
|
||||||
|
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
|
||||||
|
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* File: $Id: mbfunccoils_m.c,v 1.60 2013/10/12 15:10:12 Armink Add Master Functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* ----------------------- System includes ----------------------------------*/
|
||||||
|
#include "stdlib.h"
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
/* ----------------------- Platform includes --------------------------------*/
|
||||||
|
#include "port.h"
|
||||||
|
|
||||||
|
/* ----------------------- Modbus includes ----------------------------------*/
|
||||||
|
//#include "mb.h"
|
||||||
|
#include "mb_m.h"
|
||||||
|
#include "mbframe.h"
|
||||||
|
#include "mbproto.h"
|
||||||
|
#include "mbconfig.h"
|
||||||
|
#include "mbutils.h"
|
||||||
|
|
||||||
|
/* ----------------------- Defines ------------------------------------------*/
|
||||||
|
#define MB_PDU_REQ_READ_ADDR_OFF ( MB_PDU_DATA_OFF + 0 )
|
||||||
|
#define MB_PDU_REQ_READ_COILCNT_OFF ( MB_PDU_DATA_OFF + 2 )
|
||||||
|
#define MB_PDU_REQ_READ_SIZE ( 4 )
|
||||||
|
#define MB_PDU_FUNC_READ_COILCNT_OFF ( MB_PDU_DATA_OFF + 0 )
|
||||||
|
#define MB_PDU_FUNC_READ_VALUES_OFF ( MB_PDU_DATA_OFF + 1 )
|
||||||
|
#define MB_PDU_FUNC_READ_SIZE_MIN ( 1 )
|
||||||
|
|
||||||
|
#define MB_PDU_REQ_WRITE_ADDR_OFF ( MB_PDU_DATA_OFF )
|
||||||
|
#define MB_PDU_REQ_WRITE_VALUE_OFF ( MB_PDU_DATA_OFF + 2 )
|
||||||
|
#define MB_PDU_REQ_WRITE_SIZE ( 4 )
|
||||||
|
#define MB_PDU_FUNC_WRITE_ADDR_OFF ( MB_PDU_DATA_OFF )
|
||||||
|
#define MB_PDU_FUNC_WRITE_VALUE_OFF ( MB_PDU_DATA_OFF + 2 )
|
||||||
|
#define MB_PDU_FUNC_WRITE_SIZE ( 4 )
|
||||||
|
|
||||||
|
#define MB_PDU_REQ_WRITE_MUL_ADDR_OFF ( MB_PDU_DATA_OFF )
|
||||||
|
#define MB_PDU_REQ_WRITE_MUL_COILCNT_OFF ( MB_PDU_DATA_OFF + 2 )
|
||||||
|
#define MB_PDU_REQ_WRITE_MUL_BYTECNT_OFF ( MB_PDU_DATA_OFF + 4 )
|
||||||
|
#define MB_PDU_REQ_WRITE_MUL_VALUES_OFF ( MB_PDU_DATA_OFF + 5 )
|
||||||
|
#define MB_PDU_REQ_WRITE_MUL_SIZE_MIN ( 5 )
|
||||||
|
#define MB_PDU_REQ_WRITE_MUL_COILCNT_MAX ( 0x07B0 )
|
||||||
|
#define MB_PDU_FUNC_WRITE_MUL_ADDR_OFF ( MB_PDU_DATA_OFF )
|
||||||
|
#define MB_PDU_FUNC_WRITE_MUL_COILCNT_OFF ( MB_PDU_DATA_OFF + 2 )
|
||||||
|
#define MB_PDU_FUNC_WRITE_MUL_SIZE ( 5 )
|
||||||
|
|
||||||
|
/* ----------------------- Static functions ---------------------------------*/
|
||||||
|
eMBException prveMBError2Exception( eMBErrorCode eErrorCode );
|
||||||
|
|
||||||
|
/* ----------------------- Start implementation -----------------------------*/
|
||||||
|
#if MB_MASTER_RTU_ENABLED > 0 || MB_MASTER_ASCII_ENABLED > 0
|
||||||
|
#if MB_FUNC_READ_COILS_ENABLED > 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function will request read coil.
|
||||||
|
*
|
||||||
|
* @param ucSndAddr salve address
|
||||||
|
* @param usCoilAddr coil start address
|
||||||
|
* @param usNCoils coil total number
|
||||||
|
* @param lTimeOut timeout (-1 will waiting forever)
|
||||||
|
*
|
||||||
|
* @return error code
|
||||||
|
*/
|
||||||
|
eMBMasterReqErrCode
|
||||||
|
eMBMasterReqReadCoils( UCHAR ucSndAddr, USHORT usCoilAddr, USHORT usNCoils, LONG lTimeOut )
|
||||||
|
{
|
||||||
|
UCHAR *ucMBFrame;
|
||||||
|
eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
|
||||||
|
|
||||||
|
if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG;
|
||||||
|
else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vMBMasterGetPDUSndBuf(&ucMBFrame);
|
||||||
|
vMBMasterSetDestAddress(ucSndAddr);
|
||||||
|
ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_READ_COILS;
|
||||||
|
ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF] = usCoilAddr >> 8;
|
||||||
|
ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF + 1] = usCoilAddr;
|
||||||
|
ucMBFrame[MB_PDU_REQ_READ_COILCNT_OFF ] = usNCoils >> 8;
|
||||||
|
ucMBFrame[MB_PDU_REQ_READ_COILCNT_OFF + 1] = usNCoils;
|
||||||
|
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_READ_SIZE );
|
||||||
|
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_SENT );
|
||||||
|
eErrStatus = eMBMasterWaitRequestFinish( );
|
||||||
|
|
||||||
|
}
|
||||||
|
return eErrStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
eMBException
|
||||||
|
eMBMasterFuncReadCoils( UCHAR * pucFrame, USHORT * usLen )
|
||||||
|
{
|
||||||
|
UCHAR *ucMBFrame;
|
||||||
|
USHORT usRegAddress;
|
||||||
|
USHORT usCoilCount;
|
||||||
|
UCHAR ucByteCount;
|
||||||
|
|
||||||
|
eMBException eStatus = MB_EX_NONE;
|
||||||
|
eMBErrorCode eRegStatus;
|
||||||
|
|
||||||
|
/* If this request is broadcast, and it's read mode. This request don't need execute. */
|
||||||
|
if ( xMBMasterRequestIsBroadcast() )
|
||||||
|
{
|
||||||
|
eStatus = MB_EX_NONE;
|
||||||
|
}
|
||||||
|
else if ( *usLen >= MB_PDU_SIZE_MIN + MB_PDU_FUNC_READ_SIZE_MIN )
|
||||||
|
{
|
||||||
|
vMBMasterGetPDUSndBuf(&ucMBFrame);
|
||||||
|
usRegAddress = ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF] << 8 );
|
||||||
|
usRegAddress |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF + 1] );
|
||||||
|
usRegAddress++;
|
||||||
|
|
||||||
|
usCoilCount = ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_COILCNT_OFF] << 8 );
|
||||||
|
usCoilCount |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_COILCNT_OFF + 1] );
|
||||||
|
|
||||||
|
/* Test if the quantity of coils is a multiple of 8. If not last
|
||||||
|
* byte is only partially field with unused coils set to zero. */
|
||||||
|
if( ( usCoilCount & 0x0007 ) != 0 )
|
||||||
|
{
|
||||||
|
ucByteCount = ( UCHAR )( usCoilCount / 8 + 1 );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ucByteCount = ( UCHAR )( usCoilCount / 8 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if the number of registers to read is valid. If not
|
||||||
|
* return Modbus illegal data value exception.
|
||||||
|
*/
|
||||||
|
if( ( usCoilCount >= 1 ) &&
|
||||||
|
( ucByteCount == pucFrame[MB_PDU_FUNC_READ_COILCNT_OFF] ) )
|
||||||
|
{
|
||||||
|
/* Make callback to fill the buffer. */
|
||||||
|
eRegStatus = eMBMasterRegCoilsCB( &pucFrame[MB_PDU_FUNC_READ_VALUES_OFF], usRegAddress, usCoilCount, MB_REG_READ );
|
||||||
|
|
||||||
|
/* If an error occurred convert it into a Modbus exception. */
|
||||||
|
if( eRegStatus != MB_ENOERR )
|
||||||
|
{
|
||||||
|
eStatus = prveMBError2Exception( eRegStatus );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Can't be a valid read coil register request because the length
|
||||||
|
* is incorrect. */
|
||||||
|
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
|
||||||
|
}
|
||||||
|
return eStatus;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if MB_FUNC_WRITE_COIL_ENABLED > 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function will request write one coil.
|
||||||
|
*
|
||||||
|
* @param ucSndAddr salve address
|
||||||
|
* @param usCoilAddr coil start address
|
||||||
|
* @param usCoilData data to be written
|
||||||
|
* @param lTimeOut timeout (-1 will waiting forever)
|
||||||
|
*
|
||||||
|
* @return error code
|
||||||
|
*
|
||||||
|
* @see eMBMasterReqWriteMultipleCoils
|
||||||
|
*/
|
||||||
|
eMBMasterReqErrCode
|
||||||
|
eMBMasterReqWriteCoil( UCHAR ucSndAddr, USHORT usCoilAddr, USHORT usCoilData, LONG lTimeOut )
|
||||||
|
{
|
||||||
|
UCHAR *ucMBFrame;
|
||||||
|
eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
|
||||||
|
|
||||||
|
if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG;
|
||||||
|
else if ( ( usCoilData != 0xFF00 ) && ( usCoilData != 0x0000 ) ) eErrStatus = MB_MRE_ILL_ARG;
|
||||||
|
else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vMBMasterGetPDUSndBuf(&ucMBFrame);
|
||||||
|
vMBMasterSetDestAddress(ucSndAddr);
|
||||||
|
ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_WRITE_SINGLE_COIL;
|
||||||
|
ucMBFrame[MB_PDU_REQ_WRITE_ADDR_OFF] = usCoilAddr >> 8;
|
||||||
|
ucMBFrame[MB_PDU_REQ_WRITE_ADDR_OFF + 1] = usCoilAddr;
|
||||||
|
ucMBFrame[MB_PDU_REQ_WRITE_VALUE_OFF ] = usCoilData >> 8;
|
||||||
|
ucMBFrame[MB_PDU_REQ_WRITE_VALUE_OFF + 1] = usCoilData;
|
||||||
|
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_WRITE_SIZE );
|
||||||
|
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_SENT );
|
||||||
|
eErrStatus = eMBMasterWaitRequestFinish( );
|
||||||
|
}
|
||||||
|
return eErrStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
eMBException
|
||||||
|
eMBMasterFuncWriteCoil( UCHAR * pucFrame, USHORT * usLen )
|
||||||
|
{
|
||||||
|
USHORT usRegAddress;
|
||||||
|
UCHAR ucBuf[2];
|
||||||
|
|
||||||
|
eMBException eStatus = MB_EX_NONE;
|
||||||
|
eMBErrorCode eRegStatus;
|
||||||
|
|
||||||
|
if( *usLen == ( MB_PDU_FUNC_WRITE_SIZE + MB_PDU_SIZE_MIN ) )
|
||||||
|
{
|
||||||
|
usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF] << 8 );
|
||||||
|
usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF + 1] );
|
||||||
|
usRegAddress++;
|
||||||
|
|
||||||
|
if( ( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF + 1] == 0x00 ) &&
|
||||||
|
( ( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF] == 0xFF ) ||
|
||||||
|
( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF] == 0x00 ) ) )
|
||||||
|
{
|
||||||
|
ucBuf[1] = 0;
|
||||||
|
if( pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF] == 0xFF )
|
||||||
|
{
|
||||||
|
ucBuf[0] = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ucBuf[0] = 0;
|
||||||
|
}
|
||||||
|
eRegStatus =
|
||||||
|
eMBMasterRegCoilsCB( &ucBuf[0], usRegAddress, 1, MB_REG_WRITE );
|
||||||
|
|
||||||
|
/* If an error occured convert it into a Modbus exception. */
|
||||||
|
if( eRegStatus != MB_ENOERR )
|
||||||
|
{
|
||||||
|
eStatus = prveMBError2Exception( eRegStatus );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Can't be a valid write coil register request because the length
|
||||||
|
* is incorrect. */
|
||||||
|
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
|
||||||
|
}
|
||||||
|
return eStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // #if MB_FUNC_WRITE_COIL_ENABLED > 0
|
||||||
|
|
||||||
|
#if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED > 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function will request write multiple coils.
|
||||||
|
*
|
||||||
|
* @param ucSndAddr salve address
|
||||||
|
* @param usCoilAddr coil start address
|
||||||
|
* @param usNCoils coil total number
|
||||||
|
* @param usCoilData data to be written
|
||||||
|
* @param lTimeOut timeout (-1 will waiting forever)
|
||||||
|
*
|
||||||
|
* @return error code
|
||||||
|
*
|
||||||
|
* @see eMBMasterReqWriteCoil
|
||||||
|
*/
|
||||||
|
eMBMasterReqErrCode
|
||||||
|
eMBMasterReqWriteMultipleCoils( UCHAR ucSndAddr,
|
||||||
|
USHORT usCoilAddr, USHORT usNCoils, UCHAR * pucDataBuffer, LONG lTimeOut)
|
||||||
|
{
|
||||||
|
UCHAR *ucMBFrame;
|
||||||
|
USHORT usRegIndex = 0;
|
||||||
|
UCHAR ucByteCount;
|
||||||
|
eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
|
||||||
|
|
||||||
|
if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG;
|
||||||
|
else if ( usNCoils > MB_PDU_REQ_WRITE_MUL_COILCNT_MAX ) eErrStatus = MB_MRE_ILL_ARG;
|
||||||
|
else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vMBMasterGetPDUSndBuf(&ucMBFrame);
|
||||||
|
vMBMasterSetDestAddress(ucSndAddr);
|
||||||
|
ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_WRITE_MULTIPLE_COILS;
|
||||||
|
ucMBFrame[MB_PDU_REQ_WRITE_MUL_ADDR_OFF] = usCoilAddr >> 8;
|
||||||
|
ucMBFrame[MB_PDU_REQ_WRITE_MUL_ADDR_OFF + 1] = usCoilAddr;
|
||||||
|
ucMBFrame[MB_PDU_REQ_WRITE_MUL_COILCNT_OFF] = usNCoils >> 8;
|
||||||
|
ucMBFrame[MB_PDU_REQ_WRITE_MUL_COILCNT_OFF + 1] = usNCoils ;
|
||||||
|
if( ( usNCoils & 0x0007 ) != 0 )
|
||||||
|
{
|
||||||
|
ucByteCount = ( UCHAR )( usNCoils / 8 + 1 );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ucByteCount = ( UCHAR )( usNCoils / 8 );
|
||||||
|
}
|
||||||
|
ucMBFrame[MB_PDU_REQ_WRITE_MUL_BYTECNT_OFF] = ucByteCount;
|
||||||
|
ucMBFrame += MB_PDU_REQ_WRITE_MUL_VALUES_OFF;
|
||||||
|
while( ucByteCount > usRegIndex)
|
||||||
|
{
|
||||||
|
*ucMBFrame++ = pucDataBuffer[usRegIndex++];
|
||||||
|
}
|
||||||
|
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_WRITE_MUL_SIZE_MIN + ucByteCount );
|
||||||
|
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_SENT );
|
||||||
|
eErrStatus = eMBMasterWaitRequestFinish( );
|
||||||
|
}
|
||||||
|
return eErrStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
eMBException
|
||||||
|
eMBMasterFuncWriteMultipleCoils( UCHAR * pucFrame, USHORT * usLen )
|
||||||
|
{
|
||||||
|
USHORT usRegAddress;
|
||||||
|
USHORT usCoilCnt;
|
||||||
|
UCHAR ucByteCount;
|
||||||
|
UCHAR ucByteCountVerify;
|
||||||
|
UCHAR *ucMBFrame;
|
||||||
|
|
||||||
|
eMBException eStatus = MB_EX_NONE;
|
||||||
|
eMBErrorCode eRegStatus;
|
||||||
|
|
||||||
|
/* If this request is broadcast, the *usLen is not need check. */
|
||||||
|
if( ( *usLen == MB_PDU_FUNC_WRITE_MUL_SIZE ) || xMBMasterRequestIsBroadcast() )
|
||||||
|
{
|
||||||
|
vMBMasterGetPDUSndBuf(&ucMBFrame);
|
||||||
|
usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_ADDR_OFF] << 8 );
|
||||||
|
usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_ADDR_OFF + 1] );
|
||||||
|
usRegAddress++;
|
||||||
|
|
||||||
|
usCoilCnt = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_COILCNT_OFF] << 8 );
|
||||||
|
usCoilCnt |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_MUL_COILCNT_OFF + 1] );
|
||||||
|
|
||||||
|
ucByteCount = ucMBFrame[MB_PDU_REQ_WRITE_MUL_BYTECNT_OFF];
|
||||||
|
|
||||||
|
/* Compute the number of expected bytes in the request. */
|
||||||
|
if( ( usCoilCnt & 0x0007 ) != 0 )
|
||||||
|
{
|
||||||
|
ucByteCountVerify = ( UCHAR )( usCoilCnt / 8 + 1 );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ucByteCountVerify = ( UCHAR )( usCoilCnt / 8 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ( usCoilCnt >= 1 ) && ( ucByteCountVerify == ucByteCount ) )
|
||||||
|
{
|
||||||
|
eRegStatus =
|
||||||
|
eMBMasterRegCoilsCB( &ucMBFrame[MB_PDU_REQ_WRITE_MUL_VALUES_OFF],
|
||||||
|
usRegAddress, usCoilCnt, MB_REG_WRITE );
|
||||||
|
|
||||||
|
/* If an error occured convert it into a Modbus exception. */
|
||||||
|
if( eRegStatus != MB_ENOERR )
|
||||||
|
{
|
||||||
|
eStatus = prveMBError2Exception( eRegStatus );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Can't be a valid write coil register request because the length
|
||||||
|
* is incorrect. */
|
||||||
|
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
|
||||||
|
}
|
||||||
|
return eStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // #if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED > 0
|
||||||
|
#endif // #if MB_MASTER_RTU_ENABLED > 0 || MB_MASTER_ASCII_ENABLED > 0
|
161
components/freemodbus/modbus/functions/mbfuncdisc_m.c
Normal file
161
components/freemodbus/modbus/functions/mbfuncdisc_m.c
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
/*
|
||||||
|
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
|
||||||
|
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* File: $Id: mbfuncdisc_m.c,v 1.60 2013/10/15 8:48:20 Armink Add Master Functions Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* ----------------------- System includes ----------------------------------*/
|
||||||
|
#include "stdlib.h"
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
/* ----------------------- Platform includes --------------------------------*/
|
||||||
|
#include "port.h"
|
||||||
|
|
||||||
|
/* ----------------------- Modbus includes ----------------------------------*/
|
||||||
|
#include "mb_m.h"
|
||||||
|
#include "mbframe.h"
|
||||||
|
#include "mbproto.h"
|
||||||
|
#include "mbconfig.h"
|
||||||
|
|
||||||
|
/* ----------------------- Defines ------------------------------------------*/
|
||||||
|
#define MB_PDU_REQ_READ_ADDR_OFF ( MB_PDU_DATA_OFF + 0 )
|
||||||
|
#define MB_PDU_REQ_READ_DISCCNT_OFF ( MB_PDU_DATA_OFF + 2 )
|
||||||
|
#define MB_PDU_REQ_READ_SIZE ( 4 )
|
||||||
|
#define MB_PDU_FUNC_READ_DISCCNT_OFF ( MB_PDU_DATA_OFF + 0 )
|
||||||
|
#define MB_PDU_FUNC_READ_VALUES_OFF ( MB_PDU_DATA_OFF + 1 )
|
||||||
|
#define MB_PDU_FUNC_READ_SIZE_MIN ( 1 )
|
||||||
|
|
||||||
|
/* ----------------------- Static functions ---------------------------------*/
|
||||||
|
eMBException prveMBError2Exception( eMBErrorCode eErrorCode );
|
||||||
|
|
||||||
|
/* ----------------------- Start implementation -----------------------------*/
|
||||||
|
#if MB_MASTER_RTU_ENABLED > 0 || MB_MASTER_ASCII_ENABLED > 0
|
||||||
|
#if MB_FUNC_READ_DISCRETE_INPUTS_ENABLED > 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function will request read discrete inputs.
|
||||||
|
*
|
||||||
|
* @param ucSndAddr salve address
|
||||||
|
* @param usDiscreteAddr discrete start address
|
||||||
|
* @param usNDiscreteIn discrete total number
|
||||||
|
* @param lTimeOut timeout (-1 will waiting forever)
|
||||||
|
*
|
||||||
|
* @return error code
|
||||||
|
*/
|
||||||
|
eMBMasterReqErrCode
|
||||||
|
eMBMasterReqReadDiscreteInputs( UCHAR ucSndAddr, USHORT usDiscreteAddr, USHORT usNDiscreteIn, LONG lTimeOut )
|
||||||
|
{
|
||||||
|
UCHAR *ucMBFrame;
|
||||||
|
eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
|
||||||
|
|
||||||
|
if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG;
|
||||||
|
else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vMBMasterGetPDUSndBuf(&ucMBFrame);
|
||||||
|
vMBMasterSetDestAddress(ucSndAddr);
|
||||||
|
ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_READ_DISCRETE_INPUTS;
|
||||||
|
ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF] = usDiscreteAddr >> 8;
|
||||||
|
ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF + 1] = usDiscreteAddr;
|
||||||
|
ucMBFrame[MB_PDU_REQ_READ_DISCCNT_OFF ] = usNDiscreteIn >> 8;
|
||||||
|
ucMBFrame[MB_PDU_REQ_READ_DISCCNT_OFF + 1] = usNDiscreteIn;
|
||||||
|
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_READ_SIZE );
|
||||||
|
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_SENT );
|
||||||
|
eErrStatus = eMBMasterWaitRequestFinish( );
|
||||||
|
}
|
||||||
|
return eErrStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
eMBException
|
||||||
|
eMBMasterFuncReadDiscreteInputs( UCHAR * pucFrame, USHORT * usLen )
|
||||||
|
{
|
||||||
|
USHORT usRegAddress;
|
||||||
|
USHORT usDiscreteCnt;
|
||||||
|
UCHAR ucNBytes;
|
||||||
|
UCHAR *ucMBFrame;
|
||||||
|
|
||||||
|
eMBException eStatus = MB_EX_NONE;
|
||||||
|
eMBErrorCode eRegStatus;
|
||||||
|
|
||||||
|
/* If this request is broadcast, and it's read mode. This request don't need execute. */
|
||||||
|
if ( xMBMasterRequestIsBroadcast() )
|
||||||
|
{
|
||||||
|
eStatus = MB_EX_NONE;
|
||||||
|
}
|
||||||
|
else if( *usLen >= MB_PDU_SIZE_MIN + MB_PDU_FUNC_READ_SIZE_MIN )
|
||||||
|
{
|
||||||
|
vMBMasterGetPDUSndBuf(&ucMBFrame);
|
||||||
|
usRegAddress = ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF] << 8 );
|
||||||
|
usRegAddress |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF + 1] );
|
||||||
|
usRegAddress++;
|
||||||
|
|
||||||
|
usDiscreteCnt = ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_DISCCNT_OFF] << 8 );
|
||||||
|
usDiscreteCnt |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_DISCCNT_OFF + 1] );
|
||||||
|
|
||||||
|
/* Test if the quantity of coils is a multiple of 8. If not last
|
||||||
|
* byte is only partially field with unused coils set to zero. */
|
||||||
|
if( ( usDiscreteCnt & 0x0007 ) != 0 )
|
||||||
|
{
|
||||||
|
ucNBytes = ( UCHAR )( usDiscreteCnt / 8 + 1 );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ucNBytes = ( UCHAR )( usDiscreteCnt / 8 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if the number of registers to read is valid. If not
|
||||||
|
* return Modbus illegal data value exception.
|
||||||
|
*/
|
||||||
|
if ((usDiscreteCnt >= 1) && ucNBytes == pucFrame[MB_PDU_FUNC_READ_DISCCNT_OFF])
|
||||||
|
{
|
||||||
|
/* Make callback to fill the buffer. */
|
||||||
|
eRegStatus = eMBMasterRegDiscreteCB( &pucFrame[MB_PDU_FUNC_READ_VALUES_OFF], usRegAddress, usDiscreteCnt );
|
||||||
|
|
||||||
|
/* If an error occured convert it into a Modbus exception. */
|
||||||
|
if( eRegStatus != MB_ENOERR )
|
||||||
|
{
|
||||||
|
eStatus = prveMBError2Exception( eRegStatus );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Can't be a valid read coil register request because the length
|
||||||
|
* is incorrect. */
|
||||||
|
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
|
||||||
|
}
|
||||||
|
return eStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#endif // #if MB_SERIAL_MASTER_RTU_ENABLED > 0 || MB_SERIAL_MASTER_ASCII_ENABLED > 0
|
454
components/freemodbus/modbus/functions/mbfuncholding_m.c
Normal file
454
components/freemodbus/modbus/functions/mbfuncholding_m.c
Normal file
@ -0,0 +1,454 @@
|
|||||||
|
/*
|
||||||
|
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
|
||||||
|
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* File: $Id: mbfuncholding_m.c,v 1.60 2013/09/02 14:13:40 Armink Add Master Functions Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* ----------------------- System includes ----------------------------------*/
|
||||||
|
#include "stdlib.h"
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
/* ----------------------- Platform includes --------------------------------*/
|
||||||
|
#include "port.h"
|
||||||
|
|
||||||
|
/* ----------------------- Modbus includes ----------------------------------*/
|
||||||
|
//#include "mb.h"
|
||||||
|
#include "mb_m.h"
|
||||||
|
#include "mbframe.h"
|
||||||
|
#include "mbproto.h"
|
||||||
|
#include "mbconfig.h"
|
||||||
|
|
||||||
|
/* ----------------------- Defines ------------------------------------------*/
|
||||||
|
#define MB_PDU_REQ_READ_ADDR_OFF ( MB_PDU_DATA_OFF + 0 )
|
||||||
|
#define MB_PDU_REQ_READ_REGCNT_OFF ( MB_PDU_DATA_OFF + 2 )
|
||||||
|
#define MB_PDU_REQ_READ_SIZE ( 4 )
|
||||||
|
#define MB_PDU_FUNC_READ_REGCNT_MAX ( 0x007D )
|
||||||
|
#define MB_PDU_FUNC_READ_BYTECNT_OFF ( MB_PDU_DATA_OFF + 0 )
|
||||||
|
#define MB_PDU_FUNC_READ_VALUES_OFF ( MB_PDU_DATA_OFF + 1 )
|
||||||
|
#define MB_PDU_FUNC_READ_SIZE_MIN ( 1 )
|
||||||
|
|
||||||
|
#define MB_PDU_REQ_WRITE_ADDR_OFF ( MB_PDU_DATA_OFF + 0)
|
||||||
|
#define MB_PDU_REQ_WRITE_VALUE_OFF ( MB_PDU_DATA_OFF + 2 )
|
||||||
|
#define MB_PDU_REQ_WRITE_SIZE ( 4 )
|
||||||
|
#define MB_PDU_FUNC_WRITE_ADDR_OFF ( MB_PDU_DATA_OFF + 0)
|
||||||
|
#define MB_PDU_FUNC_WRITE_VALUE_OFF ( MB_PDU_DATA_OFF + 2 )
|
||||||
|
#define MB_PDU_FUNC_WRITE_SIZE ( 4 )
|
||||||
|
|
||||||
|
#define MB_PDU_REQ_WRITE_MUL_ADDR_OFF ( MB_PDU_DATA_OFF + 0 )
|
||||||
|
#define MB_PDU_REQ_WRITE_MUL_REGCNT_OFF ( MB_PDU_DATA_OFF + 2 )
|
||||||
|
#define MB_PDU_REQ_WRITE_MUL_BYTECNT_OFF ( MB_PDU_DATA_OFF + 4 )
|
||||||
|
#define MB_PDU_REQ_WRITE_MUL_VALUES_OFF ( MB_PDU_DATA_OFF + 5 )
|
||||||
|
#define MB_PDU_REQ_WRITE_MUL_SIZE_MIN ( 5 )
|
||||||
|
#define MB_PDU_REQ_WRITE_MUL_REGCNT_MAX ( 0x0078 )
|
||||||
|
#define MB_PDU_FUNC_WRITE_MUL_ADDR_OFF ( MB_PDU_DATA_OFF + 0 )
|
||||||
|
#define MB_PDU_FUNC_WRITE_MUL_REGCNT_OFF ( MB_PDU_DATA_OFF + 2 )
|
||||||
|
#define MB_PDU_FUNC_WRITE_MUL_SIZE ( 4 )
|
||||||
|
|
||||||
|
#define MB_PDU_REQ_READWRITE_READ_ADDR_OFF ( MB_PDU_DATA_OFF + 0 )
|
||||||
|
#define MB_PDU_REQ_READWRITE_READ_REGCNT_OFF ( MB_PDU_DATA_OFF + 2 )
|
||||||
|
#define MB_PDU_REQ_READWRITE_WRITE_ADDR_OFF ( MB_PDU_DATA_OFF + 4 )
|
||||||
|
#define MB_PDU_REQ_READWRITE_WRITE_REGCNT_OFF ( MB_PDU_DATA_OFF + 6 )
|
||||||
|
#define MB_PDU_REQ_READWRITE_WRITE_BYTECNT_OFF ( MB_PDU_DATA_OFF + 8 )
|
||||||
|
#define MB_PDU_REQ_READWRITE_WRITE_VALUES_OFF ( MB_PDU_DATA_OFF + 9 )
|
||||||
|
#define MB_PDU_REQ_READWRITE_SIZE_MIN ( 9 )
|
||||||
|
#define MB_PDU_FUNC_READWRITE_READ_BYTECNT_OFF ( MB_PDU_DATA_OFF + 0 )
|
||||||
|
#define MB_PDU_FUNC_READWRITE_READ_VALUES_OFF ( MB_PDU_DATA_OFF + 1 )
|
||||||
|
#define MB_PDU_FUNC_READWRITE_SIZE_MIN ( 1 )
|
||||||
|
|
||||||
|
/* ----------------------- Static functions ---------------------------------*/
|
||||||
|
eMBException prveMBError2Exception( eMBErrorCode eErrorCode );
|
||||||
|
|
||||||
|
/* ----------------------- Start implementation -----------------------------*/
|
||||||
|
#if MB_MASTER_RTU_ENABLED > 0 || MB_MASTER_ASCII_ENABLED > 0
|
||||||
|
#if MB_FUNC_WRITE_HOLDING_ENABLED > 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function will request write holding register.
|
||||||
|
*
|
||||||
|
* @param ucSndAddr salve address
|
||||||
|
* @param usRegAddr register start address
|
||||||
|
* @param usRegData register data to be written
|
||||||
|
* @param lTimeOut timeout (-1 will waiting forever)
|
||||||
|
*
|
||||||
|
* @return error code
|
||||||
|
*/
|
||||||
|
eMBMasterReqErrCode
|
||||||
|
eMBMasterReqWriteHoldingRegister( UCHAR ucSndAddr, USHORT usRegAddr, USHORT usRegData, LONG lTimeOut )
|
||||||
|
{
|
||||||
|
UCHAR *ucMBFrame;
|
||||||
|
eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
|
||||||
|
|
||||||
|
if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG;
|
||||||
|
else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vMBMasterGetPDUSndBuf(&ucMBFrame);
|
||||||
|
vMBMasterSetDestAddress(ucSndAddr);
|
||||||
|
ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_WRITE_REGISTER;
|
||||||
|
ucMBFrame[MB_PDU_REQ_WRITE_ADDR_OFF] = usRegAddr >> 8;
|
||||||
|
ucMBFrame[MB_PDU_REQ_WRITE_ADDR_OFF + 1] = usRegAddr;
|
||||||
|
ucMBFrame[MB_PDU_REQ_WRITE_VALUE_OFF] = usRegData >> 8;
|
||||||
|
ucMBFrame[MB_PDU_REQ_WRITE_VALUE_OFF + 1] = usRegData ;
|
||||||
|
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_WRITE_SIZE );
|
||||||
|
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_SENT );
|
||||||
|
eErrStatus = eMBMasterWaitRequestFinish( );
|
||||||
|
}
|
||||||
|
return eErrStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
eMBException
|
||||||
|
eMBMasterFuncWriteHoldingRegister( UCHAR * pucFrame, USHORT * usLen )
|
||||||
|
{
|
||||||
|
USHORT usRegAddress;
|
||||||
|
eMBException eStatus = MB_EX_NONE;
|
||||||
|
eMBErrorCode eRegStatus;
|
||||||
|
|
||||||
|
if( *usLen == ( MB_PDU_SIZE_MIN + MB_PDU_FUNC_WRITE_SIZE ) )
|
||||||
|
{
|
||||||
|
usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF] << 8 );
|
||||||
|
usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_WRITE_ADDR_OFF + 1] );
|
||||||
|
usRegAddress++;
|
||||||
|
|
||||||
|
/* Make callback to update the value. */
|
||||||
|
eRegStatus = eMBMasterRegHoldingCB( &pucFrame[MB_PDU_FUNC_WRITE_VALUE_OFF],
|
||||||
|
usRegAddress, 1, MB_REG_WRITE );
|
||||||
|
|
||||||
|
/* If an error occured convert it into a Modbus exception. */
|
||||||
|
if( eRegStatus != MB_ENOERR )
|
||||||
|
{
|
||||||
|
eStatus = prveMBError2Exception( eRegStatus );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Can't be a valid request because the length is incorrect. */
|
||||||
|
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
|
||||||
|
}
|
||||||
|
return eStatus;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED > 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function will request write multiple holding register.
|
||||||
|
*
|
||||||
|
* @param ucSndAddr salve address
|
||||||
|
* @param usRegAddr register start address
|
||||||
|
* @param usNRegs register total number
|
||||||
|
* @param pusDataBuffer data to be written
|
||||||
|
* @param lTimeOut timeout (-1 will waiting forever)
|
||||||
|
*
|
||||||
|
* @return error code
|
||||||
|
*/
|
||||||
|
eMBMasterReqErrCode
|
||||||
|
eMBMasterReqWriteMultipleHoldingRegister( UCHAR ucSndAddr,
|
||||||
|
USHORT usRegAddr, USHORT usNRegs, USHORT * pusDataBuffer, LONG lTimeOut )
|
||||||
|
{
|
||||||
|
UCHAR *ucMBFrame;
|
||||||
|
USHORT usRegIndex = 0;
|
||||||
|
eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
|
||||||
|
|
||||||
|
if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG;
|
||||||
|
else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vMBMasterGetPDUSndBuf(&ucMBFrame);
|
||||||
|
vMBMasterSetDestAddress(ucSndAddr);
|
||||||
|
ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_WRITE_MULTIPLE_REGISTERS;
|
||||||
|
ucMBFrame[MB_PDU_REQ_WRITE_MUL_ADDR_OFF] = usRegAddr >> 8;
|
||||||
|
ucMBFrame[MB_PDU_REQ_WRITE_MUL_ADDR_OFF + 1] = usRegAddr;
|
||||||
|
ucMBFrame[MB_PDU_REQ_WRITE_MUL_REGCNT_OFF] = usNRegs >> 8;
|
||||||
|
ucMBFrame[MB_PDU_REQ_WRITE_MUL_REGCNT_OFF + 1] = usNRegs ;
|
||||||
|
ucMBFrame[MB_PDU_REQ_WRITE_MUL_BYTECNT_OFF] = usNRegs * 2;
|
||||||
|
ucMBFrame += MB_PDU_REQ_WRITE_MUL_VALUES_OFF;
|
||||||
|
while( usNRegs > usRegIndex)
|
||||||
|
{
|
||||||
|
*ucMBFrame++ = pusDataBuffer[usRegIndex] >> 8;
|
||||||
|
*ucMBFrame++ = pusDataBuffer[usRegIndex++] ;
|
||||||
|
}
|
||||||
|
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_WRITE_MUL_SIZE_MIN + 2*usNRegs );
|
||||||
|
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_SENT );
|
||||||
|
eErrStatus = eMBMasterWaitRequestFinish( );
|
||||||
|
}
|
||||||
|
return eErrStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
eMBException
|
||||||
|
eMBMasterFuncWriteMultipleHoldingRegister( UCHAR * pucFrame, USHORT * usLen )
|
||||||
|
{
|
||||||
|
UCHAR *ucMBFrame;
|
||||||
|
USHORT usRegAddress;
|
||||||
|
USHORT usRegCount;
|
||||||
|
UCHAR ucRegByteCount;
|
||||||
|
|
||||||
|
eMBException eStatus = MB_EX_NONE;
|
||||||
|
eMBErrorCode eRegStatus;
|
||||||
|
|
||||||
|
/* If this request is broadcast, the *usLen is not need check. */
|
||||||
|
if( ( *usLen == MB_PDU_SIZE_MIN + MB_PDU_FUNC_WRITE_MUL_SIZE ) || xMBMasterRequestIsBroadcast() )
|
||||||
|
{
|
||||||
|
vMBMasterGetPDUSndBuf(&ucMBFrame);
|
||||||
|
usRegAddress = ( USHORT )( ucMBFrame[MB_PDU_REQ_WRITE_MUL_ADDR_OFF] << 8 );
|
||||||
|
usRegAddress |= ( USHORT )( ucMBFrame[MB_PDU_REQ_WRITE_MUL_ADDR_OFF + 1] );
|
||||||
|
usRegAddress++;
|
||||||
|
|
||||||
|
usRegCount = ( USHORT )( ucMBFrame[MB_PDU_REQ_WRITE_MUL_REGCNT_OFF] << 8 );
|
||||||
|
usRegCount |= ( USHORT )( ucMBFrame[MB_PDU_REQ_WRITE_MUL_REGCNT_OFF + 1] );
|
||||||
|
|
||||||
|
ucRegByteCount = ucMBFrame[MB_PDU_REQ_WRITE_MUL_BYTECNT_OFF];
|
||||||
|
|
||||||
|
if( ucRegByteCount == 2 * usRegCount )
|
||||||
|
{
|
||||||
|
/* Make callback to update the register values. */
|
||||||
|
eRegStatus = eMBMasterRegHoldingCB( &ucMBFrame[MB_PDU_REQ_WRITE_MUL_VALUES_OFF],
|
||||||
|
usRegAddress, usRegCount, MB_REG_WRITE );
|
||||||
|
|
||||||
|
/* If an error occured convert it into a Modbus exception. */
|
||||||
|
if( eRegStatus != MB_ENOERR )
|
||||||
|
{
|
||||||
|
eStatus = prveMBError2Exception( eRegStatus );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Can't be a valid request because the length is incorrect. */
|
||||||
|
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
|
||||||
|
}
|
||||||
|
return eStatus;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if MB_FUNC_READ_HOLDING_ENABLED > 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function will request read holding register.
|
||||||
|
*
|
||||||
|
* @param ucSndAddr salve address
|
||||||
|
* @param usRegAddr register start address
|
||||||
|
* @param usNRegs register total number
|
||||||
|
* @param lTimeOut timeout (-1 will waiting forever)
|
||||||
|
*
|
||||||
|
* @return error code
|
||||||
|
*/
|
||||||
|
eMBMasterReqErrCode
|
||||||
|
eMBMasterReqReadHoldingRegister( UCHAR ucSndAddr, USHORT usRegAddr, USHORT usNRegs, LONG lTimeOut )
|
||||||
|
{
|
||||||
|
UCHAR *ucMBFrame;
|
||||||
|
eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
|
||||||
|
|
||||||
|
if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG;
|
||||||
|
else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vMBMasterGetPDUSndBuf(&ucMBFrame);
|
||||||
|
vMBMasterSetDestAddress(ucSndAddr);
|
||||||
|
ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_READ_HOLDING_REGISTER;
|
||||||
|
ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF] = usRegAddr >> 8;
|
||||||
|
ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF + 1] = usRegAddr;
|
||||||
|
ucMBFrame[MB_PDU_REQ_READ_REGCNT_OFF] = usNRegs >> 8;
|
||||||
|
ucMBFrame[MB_PDU_REQ_READ_REGCNT_OFF + 1] = usNRegs;
|
||||||
|
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_READ_SIZE );
|
||||||
|
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_SENT );
|
||||||
|
eErrStatus = eMBMasterWaitRequestFinish( );
|
||||||
|
}
|
||||||
|
return eErrStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
eMBException
|
||||||
|
eMBMasterFuncReadHoldingRegister( UCHAR * pucFrame, USHORT * usLen )
|
||||||
|
{
|
||||||
|
UCHAR *ucMBFrame;
|
||||||
|
USHORT usRegAddress;
|
||||||
|
USHORT usRegCount;
|
||||||
|
|
||||||
|
eMBException eStatus = MB_EX_NONE;
|
||||||
|
eMBErrorCode eRegStatus;
|
||||||
|
|
||||||
|
/* If this request is broadcast, and it's read mode. This request don't need execute. */
|
||||||
|
if ( xMBMasterRequestIsBroadcast() )
|
||||||
|
{
|
||||||
|
eStatus = MB_EX_NONE;
|
||||||
|
}
|
||||||
|
else if( *usLen >= MB_PDU_SIZE_MIN + MB_PDU_FUNC_READ_SIZE_MIN )
|
||||||
|
{
|
||||||
|
vMBMasterGetPDUSndBuf(&ucMBFrame);
|
||||||
|
usRegAddress = ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF] << 8 );
|
||||||
|
usRegAddress |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF + 1] );
|
||||||
|
usRegAddress++;
|
||||||
|
|
||||||
|
usRegCount = ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_REGCNT_OFF] << 8 );
|
||||||
|
usRegCount |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_REGCNT_OFF + 1] );
|
||||||
|
|
||||||
|
/* Check if the number of registers to read is valid. If not
|
||||||
|
* return Modbus illegal data value exception.
|
||||||
|
*/
|
||||||
|
if( ( usRegCount >= 1 ) && ( 2 * usRegCount == pucFrame[MB_PDU_FUNC_READ_BYTECNT_OFF] ) )
|
||||||
|
{
|
||||||
|
/* Make callback to fill the buffer. */
|
||||||
|
eRegStatus = eMBMasterRegHoldingCB( &pucFrame[MB_PDU_FUNC_READ_VALUES_OFF], usRegAddress, usRegCount, MB_REG_READ );
|
||||||
|
/* If an error occured convert it into a Modbus exception. */
|
||||||
|
if( eRegStatus != MB_ENOERR )
|
||||||
|
{
|
||||||
|
eStatus = prveMBError2Exception( eRegStatus );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Can't be a valid request because the length is incorrect. */
|
||||||
|
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
|
||||||
|
}
|
||||||
|
return eStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if MB_FUNC_READWRITE_HOLDING_ENABLED > 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function will request read and write holding register.
|
||||||
|
*
|
||||||
|
* @param ucSndAddr salve address
|
||||||
|
* @param usReadRegAddr read register start address
|
||||||
|
* @param usNReadRegs read register total number
|
||||||
|
* @param pusDataBuffer data to be written
|
||||||
|
* @param usWriteRegAddr write register start address
|
||||||
|
* @param usNWriteRegs write register total number
|
||||||
|
* @param lTimeOut timeout (-1 will waiting forever)
|
||||||
|
*
|
||||||
|
* @return error code
|
||||||
|
*/
|
||||||
|
eMBMasterReqErrCode
|
||||||
|
eMBMasterReqReadWriteMultipleHoldingRegister( UCHAR ucSndAddr,
|
||||||
|
USHORT usReadRegAddr, USHORT usNReadRegs, USHORT * pusDataBuffer,
|
||||||
|
USHORT usWriteRegAddr, USHORT usNWriteRegs, LONG lTimeOut )
|
||||||
|
{
|
||||||
|
UCHAR *ucMBFrame;
|
||||||
|
USHORT usRegIndex = 0;
|
||||||
|
eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
|
||||||
|
|
||||||
|
if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG;
|
||||||
|
else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vMBMasterGetPDUSndBuf(&ucMBFrame);
|
||||||
|
vMBMasterSetDestAddress(ucSndAddr);
|
||||||
|
ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_READWRITE_MULTIPLE_REGISTERS;
|
||||||
|
ucMBFrame[MB_PDU_REQ_READWRITE_READ_ADDR_OFF] = usReadRegAddr >> 8;
|
||||||
|
ucMBFrame[MB_PDU_REQ_READWRITE_READ_ADDR_OFF + 1] = usReadRegAddr;
|
||||||
|
ucMBFrame[MB_PDU_REQ_READWRITE_READ_REGCNT_OFF] = usNReadRegs >> 8;
|
||||||
|
ucMBFrame[MB_PDU_REQ_READWRITE_READ_REGCNT_OFF + 1] = usNReadRegs ;
|
||||||
|
ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_ADDR_OFF] = usWriteRegAddr >> 8;
|
||||||
|
ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_ADDR_OFF + 1] = usWriteRegAddr;
|
||||||
|
ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_REGCNT_OFF] = usNWriteRegs >> 8;
|
||||||
|
ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_REGCNT_OFF + 1] = usNWriteRegs ;
|
||||||
|
ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_BYTECNT_OFF] = usNWriteRegs * 2;
|
||||||
|
ucMBFrame += MB_PDU_REQ_READWRITE_WRITE_VALUES_OFF;
|
||||||
|
while( usNWriteRegs > usRegIndex)
|
||||||
|
{
|
||||||
|
*ucMBFrame++ = pusDataBuffer[usRegIndex] >> 8;
|
||||||
|
*ucMBFrame++ = pusDataBuffer[usRegIndex++] ;
|
||||||
|
}
|
||||||
|
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_READWRITE_SIZE_MIN + 2*usNWriteRegs );
|
||||||
|
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_SENT );
|
||||||
|
eErrStatus = eMBMasterWaitRequestFinish( );
|
||||||
|
}
|
||||||
|
return eErrStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
eMBException
|
||||||
|
eMBMasterFuncReadWriteMultipleHoldingRegister( UCHAR * pucFrame, USHORT * usLen )
|
||||||
|
{
|
||||||
|
USHORT usRegReadAddress;
|
||||||
|
USHORT usRegReadCount;
|
||||||
|
USHORT usRegWriteAddress;
|
||||||
|
USHORT usRegWriteCount;
|
||||||
|
UCHAR *ucMBFrame;
|
||||||
|
|
||||||
|
eMBException eStatus = MB_EX_NONE;
|
||||||
|
eMBErrorCode eRegStatus;
|
||||||
|
|
||||||
|
/* If this request is broadcast, and it's read mode. This request don't need execute. */
|
||||||
|
if ( xMBMasterRequestIsBroadcast() )
|
||||||
|
{
|
||||||
|
eStatus = MB_EX_NONE;
|
||||||
|
}
|
||||||
|
else if( *usLen >= MB_PDU_SIZE_MIN + MB_PDU_FUNC_READWRITE_SIZE_MIN )
|
||||||
|
{
|
||||||
|
vMBMasterGetPDUSndBuf(&ucMBFrame);
|
||||||
|
usRegReadAddress = ( USHORT )( ucMBFrame[MB_PDU_REQ_READWRITE_READ_ADDR_OFF] << 8U );
|
||||||
|
usRegReadAddress |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READWRITE_READ_ADDR_OFF + 1] );
|
||||||
|
usRegReadAddress++;
|
||||||
|
|
||||||
|
usRegReadCount = ( USHORT )( ucMBFrame[MB_PDU_REQ_READWRITE_READ_REGCNT_OFF] << 8U );
|
||||||
|
usRegReadCount |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READWRITE_READ_REGCNT_OFF + 1] );
|
||||||
|
|
||||||
|
usRegWriteAddress = ( USHORT )( ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_ADDR_OFF] << 8U );
|
||||||
|
usRegWriteAddress |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_ADDR_OFF + 1] );
|
||||||
|
usRegWriteAddress++;
|
||||||
|
|
||||||
|
usRegWriteCount = ( USHORT )( ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_REGCNT_OFF] << 8U );
|
||||||
|
usRegWriteCount |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_REGCNT_OFF + 1] );
|
||||||
|
|
||||||
|
if( ( 2 * usRegReadCount ) == pucFrame[MB_PDU_FUNC_READWRITE_READ_BYTECNT_OFF] )
|
||||||
|
{
|
||||||
|
/* Make callback to update the register values. */
|
||||||
|
eRegStatus = eMBMasterRegHoldingCB( &ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_VALUES_OFF],
|
||||||
|
usRegWriteAddress, usRegWriteCount, MB_REG_WRITE );
|
||||||
|
|
||||||
|
if( eRegStatus == MB_ENOERR )
|
||||||
|
{
|
||||||
|
/* Make the read callback. */
|
||||||
|
eRegStatus = eMBMasterRegHoldingCB(&pucFrame[MB_PDU_FUNC_READWRITE_READ_VALUES_OFF],
|
||||||
|
usRegReadAddress, usRegReadCount, MB_REG_READ);
|
||||||
|
}
|
||||||
|
if( eRegStatus != MB_ENOERR )
|
||||||
|
{
|
||||||
|
eStatus = prveMBError2Exception( eRegStatus );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return eStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#endif // #if MB_MASTER_RTU_ENABLED > 0 || MB_MASTER_ASCII_ENABLED > 0
|
||||||
|
|
147
components/freemodbus/modbus/functions/mbfuncinput_m.c
Normal file
147
components/freemodbus/modbus/functions/mbfuncinput_m.c
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
/*
|
||||||
|
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
|
||||||
|
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* File: $Id: mbfuncinput_m.c,v 1.60 2013/10/12 14:23:40 Armink Add Master Functions Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* ----------------------- System includes ----------------------------------*/
|
||||||
|
#include "stdlib.h"
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
/* ----------------------- Platform includes --------------------------------*/
|
||||||
|
#include "port.h"
|
||||||
|
|
||||||
|
/* ----------------------- Modbus includes ----------------------------------*/
|
||||||
|
#include "mb_m.h"
|
||||||
|
#include "mbframe.h"
|
||||||
|
#include "mbproto.h"
|
||||||
|
#include "mbconfig.h"
|
||||||
|
|
||||||
|
/* ----------------------- Defines ------------------------------------------*/
|
||||||
|
#define MB_PDU_REQ_READ_ADDR_OFF ( MB_PDU_DATA_OFF + 0 )
|
||||||
|
#define MB_PDU_REQ_READ_REGCNT_OFF ( MB_PDU_DATA_OFF + 2 )
|
||||||
|
#define MB_PDU_REQ_READ_SIZE ( 4 )
|
||||||
|
#define MB_PDU_FUNC_READ_BYTECNT_OFF ( MB_PDU_DATA_OFF + 0 )
|
||||||
|
#define MB_PDU_FUNC_READ_VALUES_OFF ( MB_PDU_DATA_OFF + 1 )
|
||||||
|
#define MB_PDU_FUNC_READ_SIZE_MIN ( 1 )
|
||||||
|
|
||||||
|
#define MB_PDU_FUNC_READ_RSP_BYTECNT_OFF ( MB_PDU_DATA_OFF )
|
||||||
|
|
||||||
|
/* ----------------------- Static functions ---------------------------------*/
|
||||||
|
eMBException prveMBError2Exception( eMBErrorCode eErrorCode );
|
||||||
|
|
||||||
|
/* ----------------------- Start implementation -----------------------------*/
|
||||||
|
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED
|
||||||
|
#if MB_FUNC_READ_INPUT_ENABLED
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function will request read input register.
|
||||||
|
*
|
||||||
|
* @param ucSndAddr salve address
|
||||||
|
* @param usRegAddr register start address
|
||||||
|
* @param usNRegs register total number
|
||||||
|
* @param lTimeOut timeout (-1 will waiting forever)
|
||||||
|
*
|
||||||
|
* @return error code
|
||||||
|
*/
|
||||||
|
eMBMasterReqErrCode
|
||||||
|
eMBMasterReqReadInputRegister( UCHAR ucSndAddr, USHORT usRegAddr, USHORT usNRegs, LONG lTimeOut )
|
||||||
|
{
|
||||||
|
UCHAR *ucMBFrame;
|
||||||
|
eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
|
||||||
|
|
||||||
|
if ( ucSndAddr > MB_MASTER_TOTAL_SLAVE_NUM ) eErrStatus = MB_MRE_ILL_ARG;
|
||||||
|
else if ( xMBMasterRunResTake( lTimeOut ) == FALSE ) eErrStatus = MB_MRE_MASTER_BUSY;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vMBMasterGetPDUSndBuf(&ucMBFrame);
|
||||||
|
vMBMasterSetDestAddress(ucSndAddr);
|
||||||
|
ucMBFrame[MB_PDU_FUNC_OFF] = MB_FUNC_READ_INPUT_REGISTER;
|
||||||
|
ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF] = usRegAddr >> 8;
|
||||||
|
ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF + 1] = usRegAddr;
|
||||||
|
ucMBFrame[MB_PDU_REQ_READ_REGCNT_OFF] = usNRegs >> 8;
|
||||||
|
ucMBFrame[MB_PDU_REQ_READ_REGCNT_OFF + 1] = usNRegs;
|
||||||
|
vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_READ_SIZE );
|
||||||
|
( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_SENT );
|
||||||
|
eErrStatus = eMBMasterWaitRequestFinish( );
|
||||||
|
}
|
||||||
|
return eErrStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
eMBException
|
||||||
|
eMBMasterFuncReadInputRegister( UCHAR * pucFrame, USHORT * usLen )
|
||||||
|
{
|
||||||
|
UCHAR *ucMBFrame;
|
||||||
|
USHORT usRegAddress;
|
||||||
|
USHORT usRegCount;
|
||||||
|
|
||||||
|
eMBException eStatus = MB_EX_NONE;
|
||||||
|
eMBErrorCode eRegStatus;
|
||||||
|
|
||||||
|
/* If this request is broadcast, and it's read mode. This request don't need execute. */
|
||||||
|
if ( xMBMasterRequestIsBroadcast() )
|
||||||
|
{
|
||||||
|
eStatus = MB_EX_NONE;
|
||||||
|
}
|
||||||
|
else if( *usLen >= MB_PDU_SIZE_MIN + MB_PDU_FUNC_READ_SIZE_MIN )
|
||||||
|
{
|
||||||
|
vMBMasterGetPDUSndBuf(&ucMBFrame);
|
||||||
|
usRegAddress = ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF] << 8 );
|
||||||
|
usRegAddress |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_ADDR_OFF + 1] );
|
||||||
|
usRegAddress++;
|
||||||
|
|
||||||
|
usRegCount = ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_REGCNT_OFF] << 8 );
|
||||||
|
usRegCount |= ( USHORT )( ucMBFrame[MB_PDU_REQ_READ_REGCNT_OFF + 1] );
|
||||||
|
|
||||||
|
/* Check if the number of registers to read is valid. If not
|
||||||
|
* return Modbus illegal data value exception.
|
||||||
|
*/
|
||||||
|
if( ( usRegCount >= 1 ) && ( 2 * usRegCount == pucFrame[MB_PDU_FUNC_READ_BYTECNT_OFF] ) )
|
||||||
|
{
|
||||||
|
/* Make callback to fill the buffer. */
|
||||||
|
eRegStatus = eMBMasterRegInputCB( &pucFrame[MB_PDU_FUNC_READ_VALUES_OFF], usRegAddress, usRegCount );
|
||||||
|
/* If an error occured convert it into a Modbus exception. */
|
||||||
|
if( eRegStatus != MB_ENOERR )
|
||||||
|
{
|
||||||
|
eStatus = prveMBError2Exception( eRegStatus );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Can't be a valid request because the length is incorrect. */
|
||||||
|
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
|
||||||
|
}
|
||||||
|
return eStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#endif // #if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED
|
@ -74,6 +74,7 @@ PR_BEGIN_EXTERN_C
|
|||||||
#define MB_TCP_PORT_USE_DEFAULT 0
|
#define MB_TCP_PORT_USE_DEFAULT 0
|
||||||
|
|
||||||
/* ----------------------- Type definitions ---------------------------------*/
|
/* ----------------------- Type definitions ---------------------------------*/
|
||||||
|
#ifndef _MB_M_H
|
||||||
|
|
||||||
/*! \ingroup modbus
|
/*! \ingroup modbus
|
||||||
* \brief Modbus serial transmission modes (RTU/ASCII).
|
* \brief Modbus serial transmission modes (RTU/ASCII).
|
||||||
@ -121,6 +122,7 @@ typedef enum
|
|||||||
MB_ETIMEDOUT /*!< timeout error occurred. */
|
MB_ETIMEDOUT /*!< timeout error occurred. */
|
||||||
} eMBErrorCode;
|
} eMBErrorCode;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
/* ----------------------- Function prototypes ------------------------------*/
|
/* ----------------------- Function prototypes ------------------------------*/
|
||||||
/*! \ingroup modbus
|
/*! \ingroup modbus
|
||||||
|
455
components/freemodbus/modbus/include/mb_m.h
Normal file
455
components/freemodbus/modbus/include/mb_m.h
Normal file
@ -0,0 +1,455 @@
|
|||||||
|
/*
|
||||||
|
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
|
||||||
|
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* File: $Id: mb_m.h,v 1.60 2013/09/03 10:20:05 Armink Add Master Functions $
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _MB_M_H
|
||||||
|
#define _MB_M_H
|
||||||
|
|
||||||
|
#include "mbconfig.h"
|
||||||
|
#include "port.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
PR_BEGIN_EXTERN_C
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "mbport.h"
|
||||||
|
#include "mbproto.h"
|
||||||
|
/*! \defgroup modbus Modbus
|
||||||
|
* \code #include "mb.h" \endcode
|
||||||
|
*
|
||||||
|
* This module defines the interface for the application. It contains
|
||||||
|
* the basic functions and types required to use the Modbus Master protocol stack.
|
||||||
|
* A typical application will want to call eMBMasterInit() first. If the device
|
||||||
|
* is ready to answer network requests it must then call eMBEnable() to activate
|
||||||
|
* the protocol stack. In the main loop the function eMBMasterPoll() must be called
|
||||||
|
* periodically. The time interval between pooling depends on the configured
|
||||||
|
* Modbus timeout. If an RTOS is available a separate task should be created
|
||||||
|
* and the task should always call the function eMBMasterPoll().
|
||||||
|
*
|
||||||
|
* \code
|
||||||
|
* // Initialize protocol stack in RTU mode for a Master
|
||||||
|
* eMBMasterInit( MB_RTU, 38400, MB_PAR_EVEN );
|
||||||
|
* // Enable the Modbus Protocol Stack.
|
||||||
|
* eMBMasterEnable( );
|
||||||
|
* for( ;; )
|
||||||
|
* {
|
||||||
|
* // Call the main polling loop of the Modbus Master protocol stack.
|
||||||
|
* eMBMasterPoll( );
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
* \endcode
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* ----------------------- Defines ------------------------------------------*/
|
||||||
|
|
||||||
|
/*! \ingroup modbus
|
||||||
|
* \brief Use the default Modbus Master TCP port (502)
|
||||||
|
*/
|
||||||
|
#define MB_MASTER_TCP_PORT_USE_DEFAULT 0
|
||||||
|
|
||||||
|
#ifndef _MB_H
|
||||||
|
|
||||||
|
/* ----------------------- Type definitions ---------------------------------*/
|
||||||
|
/*! \ingroup modbus
|
||||||
|
* \brief Modbus serial transmission modes (RTU/ASCII).
|
||||||
|
*
|
||||||
|
* Modbus serial supports two transmission modes. Either ASCII or RTU. RTU
|
||||||
|
* is faster but has more hardware requirements and requires a network with
|
||||||
|
* a low jitter. ASCII is slower and more reliable on slower links (E.g. modems)
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
MB_RTU, /*!< RTU transmission mode. */
|
||||||
|
MB_ASCII, /*!< ASCII transmission mode. */
|
||||||
|
MB_TCP /*!< TCP mode. */
|
||||||
|
} eMBMode;
|
||||||
|
|
||||||
|
/*! \ingroup modbus
|
||||||
|
* \brief If register should be written or read.
|
||||||
|
*
|
||||||
|
* This value is passed to the callback functions which support either
|
||||||
|
* reading or writing register values. Writing means that the application
|
||||||
|
* registers should be updated and reading means that the modbus protocol
|
||||||
|
* stack needs to know the current register values.
|
||||||
|
*
|
||||||
|
* \see eMBRegHoldingCB( ), eMBRegCoilsCB( ), eMBRegDiscreteCB( ) and
|
||||||
|
* eMBRegInputCB( ).
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
MB_REG_READ, /*!< Read register values and pass to protocol stack. */
|
||||||
|
MB_REG_WRITE /*!< Update register values. */
|
||||||
|
} eMBRegisterMode;
|
||||||
|
|
||||||
|
/*! \ingroup modbus
|
||||||
|
* \brief Errorcodes used by all function in the protocol stack.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
MB_ENOERR, /*!< no error. */
|
||||||
|
MB_ENOREG, /*!< illegal register address. */
|
||||||
|
MB_EINVAL, /*!< illegal argument. */
|
||||||
|
MB_EPORTERR, /*!< porting layer error. */
|
||||||
|
MB_ENORES, /*!< insufficient resources. */
|
||||||
|
MB_EIO, /*!< I/O error. */
|
||||||
|
MB_EILLSTATE, /*!< protocol stack in illegal state. */
|
||||||
|
MB_ETIMEDOUT /*!< timeout error occurred. */
|
||||||
|
} eMBErrorCode;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*! \ingroup modbus
|
||||||
|
* \brief Errorcodes used by all function in the Master request.
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
MB_MRE_NO_ERR, /*!< no error. */
|
||||||
|
MB_MRE_NO_REG, /*!< illegal register address. */
|
||||||
|
MB_MRE_ILL_ARG, /*!< illegal argument. */
|
||||||
|
MB_MRE_REV_DATA, /*!< receive data error. */
|
||||||
|
MB_MRE_TIMEDOUT, /*!< timeout error occurred. */
|
||||||
|
MB_MRE_MASTER_BUSY, /*!< master is busy now. */
|
||||||
|
MB_MRE_EXE_FUN /*!< execute function error. */
|
||||||
|
} eMBMasterReqErrCode;
|
||||||
|
|
||||||
|
/*! \ingroup modbus
|
||||||
|
* \brief TimerMode is Master 3 kind of Timer modes.
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
MB_TMODE_T35, /*!< Master receive frame T3.5 timeout. */
|
||||||
|
MB_TMODE_RESPOND_TIMEOUT, /*!< Master wait respond for slave. */
|
||||||
|
MB_TMODE_CONVERT_DELAY /*!< Master sent broadcast ,then delay sometime.*/
|
||||||
|
}eMBMasterTimerMode;
|
||||||
|
|
||||||
|
/* ----------------------- Function prototypes ------------------------------*/
|
||||||
|
/*! \ingroup modbus
|
||||||
|
* \brief Initialize the Modbus Master protocol stack.
|
||||||
|
*
|
||||||
|
* This functions initializes the ASCII or RTU module and calls the
|
||||||
|
* init functions of the porting layer to prepare the hardware. Please
|
||||||
|
* note that the receiver is still disabled and no Modbus frames are
|
||||||
|
* processed until eMBMasterEnable( ) has been called.
|
||||||
|
*
|
||||||
|
* \param eMode If ASCII or RTU mode should be used.
|
||||||
|
* \param ucPort The port to use. E.g. 1 for COM1 on windows. This value
|
||||||
|
* is platform dependent and some ports simply choose to ignore it.
|
||||||
|
* \param ulBaudRate The baudrate. E.g. 19200. Supported baudrates depend
|
||||||
|
* on the porting layer.
|
||||||
|
* \param eParity Parity used for serial transmission.
|
||||||
|
*
|
||||||
|
* \return If no error occurs the function returns eMBErrorCode::MB_ENOERR.
|
||||||
|
* The protocol is then in the disabled state and ready for activation
|
||||||
|
* by calling eMBMasterEnable( ). Otherwise one of the following error codes
|
||||||
|
* is returned:
|
||||||
|
* - eMBErrorCode::MB_EPORTERR IF the porting layer returned an error.
|
||||||
|
*/
|
||||||
|
eMBErrorCode eMBMasterInit( eMBMode eMode, UCHAR ucPort,
|
||||||
|
ULONG ulBaudRate, eMBParity eParity );
|
||||||
|
|
||||||
|
/*! \ingroup modbus
|
||||||
|
* \brief Initialize the Modbus Master protocol stack for Modbus TCP.
|
||||||
|
*
|
||||||
|
* This function initializes the Modbus TCP Module. Please note that
|
||||||
|
* frame processing is still disabled until eMBEnable( ) is called.
|
||||||
|
*
|
||||||
|
* \param usTCPPort The TCP port to listen on.
|
||||||
|
* \return If the protocol stack has been initialized correctly the function
|
||||||
|
* returns eMBErrorCode::MB_ENOERR. Otherwise one of the following error
|
||||||
|
* codes is returned:
|
||||||
|
* - eMBErrorCode::MB_EINVAL If the slave address was not valid. Valid
|
||||||
|
* slave addresses are in the range 1 - 247.
|
||||||
|
* - eMBErrorCode::MB_EPORTERR IF the porting layer returned an error.
|
||||||
|
*/
|
||||||
|
eMBErrorCode eMBMasterTCPInit( USHORT usTCPPort );
|
||||||
|
|
||||||
|
/*! \ingroup modbus
|
||||||
|
* \brief Release resources used by the protocol stack.
|
||||||
|
*
|
||||||
|
* This function disables the Modbus Master protocol stack and release all
|
||||||
|
* hardware resources. It must only be called when the protocol stack
|
||||||
|
* is disabled.
|
||||||
|
*
|
||||||
|
* \note Note all ports implement this function. A port which wants to
|
||||||
|
* get an callback must define the macro MB_PORT_HAS_CLOSE to 1.
|
||||||
|
*
|
||||||
|
* \return If the resources where released it return eMBErrorCode::MB_ENOERR.
|
||||||
|
* If the protocol stack is not in the disabled state it returns
|
||||||
|
* eMBErrorCode::MB_EILLSTATE.
|
||||||
|
*/
|
||||||
|
eMBErrorCode eMBMasterClose( void );
|
||||||
|
|
||||||
|
/*! \ingroup modbus
|
||||||
|
* \brief Enable the Modbus Master protocol stack.
|
||||||
|
*
|
||||||
|
* This function enables processing of Modbus Master frames. Enabling the protocol
|
||||||
|
* stack is only possible if it is in the disabled state.
|
||||||
|
*
|
||||||
|
* \return If the protocol stack is now in the state enabled it returns
|
||||||
|
* eMBErrorCode::MB_ENOERR. If it was not in the disabled state it
|
||||||
|
* return eMBErrorCode::MB_EILLSTATE.
|
||||||
|
*/
|
||||||
|
eMBErrorCode eMBMasterEnable( void );
|
||||||
|
|
||||||
|
/*! \ingroup modbus
|
||||||
|
* \brief Disable the Modbus Master protocol stack.
|
||||||
|
*
|
||||||
|
* This function disables processing of Modbus frames.
|
||||||
|
*
|
||||||
|
* \return If the protocol stack has been disabled it returns
|
||||||
|
* eMBErrorCode::MB_ENOERR. If it was not in the enabled state it returns
|
||||||
|
* eMBErrorCode::MB_EILLSTATE.
|
||||||
|
*/
|
||||||
|
eMBErrorCode eMBMasterDisable( void );
|
||||||
|
|
||||||
|
/*! \ingroup modbus
|
||||||
|
* \brief The main pooling loop of the Modbus Master protocol stack.
|
||||||
|
*
|
||||||
|
* This function must be called periodically. The timer interval required
|
||||||
|
* is given by the application dependent Modbus slave timeout. Internally the
|
||||||
|
* function calls xMBMasterPortEventGet() and waits for an event from the receiver or
|
||||||
|
* transmitter state machines.
|
||||||
|
*
|
||||||
|
* \return If the protocol stack is not in the enabled state the function
|
||||||
|
* returns eMBErrorCode::MB_EILLSTATE. Otherwise it returns
|
||||||
|
* eMBErrorCode::MB_ENOERR.
|
||||||
|
*/
|
||||||
|
eMBErrorCode eMBMasterPoll( void );
|
||||||
|
|
||||||
|
/*! \ingroup modbus
|
||||||
|
* \brief Registers a callback handler for a given function code.
|
||||||
|
*
|
||||||
|
* This function registers a new callback handler for a given function code.
|
||||||
|
* The callback handler supplied is responsible for interpreting the Modbus PDU and
|
||||||
|
* the creation of an appropriate response. In case of an error it should return
|
||||||
|
* one of the possible Modbus exceptions which results in a Modbus exception frame
|
||||||
|
* sent by the protocol stack.
|
||||||
|
*
|
||||||
|
* \param ucFunctionCode The Modbus function code for which this handler should
|
||||||
|
* be registers. Valid function codes are in the range 1 to 127.
|
||||||
|
* \param pxHandler The function handler which should be called in case
|
||||||
|
* such a frame is received. If \c NULL a previously registered function handler
|
||||||
|
* for this function code is removed.
|
||||||
|
*
|
||||||
|
* \return eMBErrorCode::MB_ENOERR if the handler has been installed. If no
|
||||||
|
* more resources are available it returns eMBErrorCode::MB_ENORES. In this
|
||||||
|
* case the values in mbconfig.h should be adjusted. If the argument was not
|
||||||
|
* valid it returns eMBErrorCode::MB_EINVAL.
|
||||||
|
*/
|
||||||
|
eMBErrorCode eMBMasterRegisterCB( UCHAR ucFunctionCode,
|
||||||
|
pxMBFunctionHandler pxHandler );
|
||||||
|
|
||||||
|
/* ----------------------- Callback -----------------------------------------*/
|
||||||
|
|
||||||
|
/*! \defgroup modbus_master registers Modbus Registers
|
||||||
|
* \code #include "mb_m.h" \endcode
|
||||||
|
* The protocol stack does not internally allocate any memory for the
|
||||||
|
* registers. This makes the protocol stack very small and also usable on
|
||||||
|
* low end targets. In addition the values don't have to be in the memory
|
||||||
|
* and could for example be stored in a flash.<br>
|
||||||
|
* Whenever the protocol stack requires a value it calls one of the callback
|
||||||
|
* function with the register address and the number of registers to read
|
||||||
|
* as an argument. The application should then read the actual register values
|
||||||
|
* (for example the ADC voltage) and should store the result in the supplied
|
||||||
|
* buffer.<br>
|
||||||
|
* If the protocol stack wants to update a register value because a write
|
||||||
|
* register function was received a buffer with the new register values is
|
||||||
|
* passed to the callback function. The function should then use these values
|
||||||
|
* to update the application register values.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! \ingroup modbus_registers
|
||||||
|
* \brief Callback function used if the value of a <em>Input Register</em>
|
||||||
|
* is required by the protocol stack. The starting register address is given
|
||||||
|
* by \c usAddress and the last register is given by <tt>usAddress +
|
||||||
|
* usNRegs - 1</tt>.
|
||||||
|
*
|
||||||
|
* \param pucRegBuffer A buffer where the callback function should write
|
||||||
|
* the current value of the modbus registers to.
|
||||||
|
* \param usAddress The starting address of the register. Input registers
|
||||||
|
* are in the range 1 - 65535.
|
||||||
|
* \param usNRegs Number of registers the callback function must supply.
|
||||||
|
*
|
||||||
|
* \return The function must return one of the following error codes:
|
||||||
|
* - eMBErrorCode::MB_ENOERR If no error occurred. In this case a normal
|
||||||
|
* Modbus response is sent.
|
||||||
|
* - eMBErrorCode::MB_ENOREG If the application does not map an coils
|
||||||
|
* within the requested address range. In this case a
|
||||||
|
* <b>ILLEGAL DATA ADDRESS</b> is sent as a response.
|
||||||
|
*/
|
||||||
|
eMBErrorCode eMBMasterRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress,
|
||||||
|
USHORT usNRegs );
|
||||||
|
|
||||||
|
/*! \ingroup modbus_registers
|
||||||
|
* \brief Callback function used if a <em>Holding Register</em> value is
|
||||||
|
* read or written by the protocol stack. The starting register address
|
||||||
|
* is given by \c usAddress and the last register is given by
|
||||||
|
* <tt>usAddress + usNRegs - 1</tt>.
|
||||||
|
*
|
||||||
|
* \param pucRegBuffer If the application registers values should be updated the
|
||||||
|
* buffer points to the new registers values. If the protocol stack needs
|
||||||
|
* to now the current values the callback function should write them into
|
||||||
|
* this buffer.
|
||||||
|
* \param usAddress The starting address of the register.
|
||||||
|
* \param usNRegs Number of registers to read or write.
|
||||||
|
* \param eMode If eMBRegisterMode::MB_REG_WRITE the application register
|
||||||
|
* values should be updated from the values in the buffer. For example
|
||||||
|
* this would be the case when the Modbus master has issued an
|
||||||
|
* <b>WRITE SINGLE REGISTER</b> command.
|
||||||
|
* If the value eMBRegisterMode::MB_REG_READ the application should copy
|
||||||
|
* the current values into the buffer \c pucRegBuffer.
|
||||||
|
*
|
||||||
|
* \return The function must return one of the following error codes:
|
||||||
|
* - eMBErrorCode::MB_ENOERR If no error occurred. In this case a normal
|
||||||
|
* Modbus response is sent.
|
||||||
|
* - eMBErrorCode::MB_ENOREG If the application does not map an coils
|
||||||
|
* within the requested address range. In this case a
|
||||||
|
* <b>ILLEGAL DATA ADDRESS</b> is sent as a response.
|
||||||
|
*/
|
||||||
|
eMBErrorCode eMBMasterRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress,
|
||||||
|
USHORT usNRegs, eMBRegisterMode eMode );
|
||||||
|
|
||||||
|
/*! \ingroup modbus_registers
|
||||||
|
* \brief Callback function used if a <em>Coil Register</em> value is
|
||||||
|
* read or written by the protocol stack. If you are going to use
|
||||||
|
* this function you might use the functions xMBUtilSetBits( ) and
|
||||||
|
* xMBUtilGetBits( ) for working with bitfields.
|
||||||
|
*
|
||||||
|
* \param pucRegBuffer The bits are packed in bytes where the first coil
|
||||||
|
* starting at address \c usAddress is stored in the LSB of the
|
||||||
|
* first byte in the buffer <code>pucRegBuffer</code>.
|
||||||
|
* If the buffer should be written by the callback function unused
|
||||||
|
* coil values (I.e. if not a multiple of eight coils is used) should be set
|
||||||
|
* to zero.
|
||||||
|
* \param usAddress The first coil number.
|
||||||
|
* \param usNCoils Number of coil values requested.
|
||||||
|
* \param eMode If eMBRegisterMode::MB_REG_WRITE the application values should
|
||||||
|
* be updated from the values supplied in the buffer \c pucRegBuffer.
|
||||||
|
* If eMBRegisterMode::MB_REG_READ the application should store the current
|
||||||
|
* values in the buffer \c pucRegBuffer.
|
||||||
|
*
|
||||||
|
* \return The function must return one of the following error codes:
|
||||||
|
* - eMBErrorCode::MB_ENOERR If no error occurred. In this case a normal
|
||||||
|
* Modbus response is sent.
|
||||||
|
* - eMBErrorCode::MB_ENOREG If the application does not map an coils
|
||||||
|
* within the requested address range. In this case a
|
||||||
|
* <b>ILLEGAL DATA ADDRESS</b> is sent as a response.
|
||||||
|
*/
|
||||||
|
eMBErrorCode eMBMasterRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress,
|
||||||
|
USHORT usNCoils, eMBRegisterMode eMode );
|
||||||
|
|
||||||
|
/*! \ingroup modbus_registers
|
||||||
|
* \brief Callback function used if a <em>Input Discrete Register</em> value is
|
||||||
|
* read by the protocol stack.
|
||||||
|
*
|
||||||
|
* If you are going to use his function you might use the functions
|
||||||
|
* xMBUtilSetBits( ) and xMBUtilGetBits( ) for working with bitfields.
|
||||||
|
*
|
||||||
|
* \param pucRegBuffer The buffer should be updated with the current
|
||||||
|
* coil values. The first discrete input starting at \c usAddress must be
|
||||||
|
* stored at the LSB of the first byte in the buffer. If the requested number
|
||||||
|
* is not a multiple of eight the remaining bits should be set to zero.
|
||||||
|
* \param usAddress The starting address of the first discrete input.
|
||||||
|
* \param usNDiscrete Number of discrete input values.
|
||||||
|
* \return The function must return one of the following error codes:
|
||||||
|
* - eMBErrorCode::MB_ENOERR If no error occurred. In this case a normal
|
||||||
|
* Modbus response is sent.
|
||||||
|
* - eMBErrorCode::MB_ENOREG If the application does not map an coils
|
||||||
|
* within the requested address range. In this case a
|
||||||
|
* <b>ILLEGAL DATA ADDRESS</b> is sent as a response.
|
||||||
|
*/
|
||||||
|
eMBErrorCode eMBMasterRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress,
|
||||||
|
USHORT usNDiscrete );
|
||||||
|
|
||||||
|
/*! \ingroup modbus
|
||||||
|
*\brief These Modbus functions are called for user when Modbus run in Master Mode.
|
||||||
|
*/
|
||||||
|
eMBMasterReqErrCode
|
||||||
|
eMBMasterReqReadInputRegister( UCHAR ucSndAddr, USHORT usRegAddr, USHORT usNRegs, LONG lTimeOut );
|
||||||
|
eMBMasterReqErrCode
|
||||||
|
eMBMasterReqWriteHoldingRegister( UCHAR ucSndAddr, USHORT usRegAddr, USHORT usRegData, LONG lTimeOut );
|
||||||
|
eMBMasterReqErrCode
|
||||||
|
eMBMasterReqWriteMultipleHoldingRegister( UCHAR ucSndAddr, USHORT usRegAddr,
|
||||||
|
USHORT usNRegs, USHORT * pusDataBuffer, LONG lTimeOut );
|
||||||
|
eMBMasterReqErrCode
|
||||||
|
eMBMasterReqReadHoldingRegister( UCHAR ucSndAddr, USHORT usRegAddr, USHORT usNRegs, LONG lTimeOut );
|
||||||
|
eMBMasterReqErrCode
|
||||||
|
eMBMasterReqReadWriteMultipleHoldingRegister( UCHAR ucSndAddr,
|
||||||
|
USHORT usReadRegAddr, USHORT usNReadRegs, USHORT * pusDataBuffer,
|
||||||
|
USHORT usWriteRegAddr, USHORT usNWriteRegs, LONG lTimeOut );
|
||||||
|
eMBMasterReqErrCode
|
||||||
|
eMBMasterReqReadCoils( UCHAR ucSndAddr, USHORT usCoilAddr, USHORT usNCoils, LONG lTimeOut );
|
||||||
|
eMBMasterReqErrCode
|
||||||
|
eMBMasterReqWriteCoil( UCHAR ucSndAddr, USHORT usCoilAddr, USHORT usCoilData, LONG lTimeOut );
|
||||||
|
eMBMasterReqErrCode
|
||||||
|
eMBMasterReqWriteMultipleCoils( UCHAR ucSndAddr,
|
||||||
|
USHORT usCoilAddr, USHORT usNCoils, UCHAR * pucDataBuffer, LONG lTimeOut );
|
||||||
|
eMBMasterReqErrCode
|
||||||
|
eMBMasterReqReadDiscreteInputs( UCHAR ucSndAddr, USHORT usDiscreteAddr, USHORT usNDiscreteIn, LONG lTimeOut );
|
||||||
|
|
||||||
|
eMBException
|
||||||
|
eMBMasterFuncReportSlaveID( UCHAR * pucFrame, USHORT * usLen );
|
||||||
|
eMBException
|
||||||
|
eMBMasterFuncReadInputRegister( UCHAR * pucFrame, USHORT * usLen );
|
||||||
|
eMBException
|
||||||
|
eMBMasterFuncReadHoldingRegister( UCHAR * pucFrame, USHORT * usLen );
|
||||||
|
eMBException
|
||||||
|
eMBMasterFuncWriteHoldingRegister( UCHAR * pucFrame, USHORT * usLen );
|
||||||
|
eMBException
|
||||||
|
eMBMasterFuncWriteMultipleHoldingRegister( UCHAR * pucFrame, USHORT * usLen );
|
||||||
|
eMBException
|
||||||
|
eMBMasterFuncReadCoils( UCHAR * pucFrame, USHORT * usLen );
|
||||||
|
eMBException
|
||||||
|
eMBMasterFuncWriteCoil( UCHAR * pucFrame, USHORT * usLen );
|
||||||
|
eMBException
|
||||||
|
eMBMasterFuncWriteMultipleCoils( UCHAR * pucFrame, USHORT * usLen );
|
||||||
|
eMBException
|
||||||
|
eMBMasterFuncReadDiscreteInputs( UCHAR * pucFrame, USHORT * usLen );
|
||||||
|
eMBException
|
||||||
|
eMBMasterFuncReadWriteMultipleHoldingRegister( UCHAR * pucFrame, USHORT * usLen );
|
||||||
|
|
||||||
|
/* \ingroup modbus
|
||||||
|
* \brief These functions are interface for Modbus Master
|
||||||
|
*/
|
||||||
|
void vMBMasterGetPDUSndBuf( UCHAR ** pucFrame );
|
||||||
|
UCHAR ucMBMasterGetDestAddress( void );
|
||||||
|
void vMBMasterSetDestAddress( UCHAR Address );
|
||||||
|
BOOL xMBMasterGetCBRunInMasterMode( void );
|
||||||
|
void vMBMasterSetCBRunInMasterMode( BOOL IsMasterMode );
|
||||||
|
USHORT usMBMasterGetPDUSndLength( void );
|
||||||
|
void vMBMasterSetPDUSndLength( USHORT SendPDULength );
|
||||||
|
void vMBMasterSetCurTimerMode( eMBMasterTimerMode eMBTimerMode );
|
||||||
|
BOOL xMBMasterRequestIsBroadcast( void );
|
||||||
|
eMBMasterErrorEventType eMBMasterGetErrorType( void );
|
||||||
|
void vMBMasterSetErrorType( eMBMasterErrorEventType errorType );
|
||||||
|
eMBMasterReqErrCode eMBMasterWaitRequestFinish( void );
|
||||||
|
|
||||||
|
/* ----------------------- Callback -----------------------------------------*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
PR_END_EXTERN_C
|
||||||
|
#endif
|
||||||
|
#endif
|
@ -26,11 +26,14 @@
|
|||||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*
|
*
|
||||||
* File: $Id: mbconfig.h,v 1.15 2010/06/06 13:54:40 wolti Exp $
|
* File: $Id: mbconfig.h,v 1.15 2010/06/06 13:54:40 wolti Exp $
|
||||||
|
* $Id: mbconfig.h,v 1.60 2013/08/13 21:19:55 Armink Add Master Functions $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _MB_CONFIG_H
|
#ifndef _MB_CONFIG_H
|
||||||
#define _MB_CONFIG_H
|
#define _MB_CONFIG_H
|
||||||
|
|
||||||
|
#include "sdkconfig.h" // for KConfig options
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
PR_BEGIN_EXTERN_C
|
PR_BEGIN_EXTERN_C
|
||||||
#endif
|
#endif
|
||||||
@ -46,15 +49,18 @@ PR_BEGIN_EXTERN_C
|
|||||||
/*! \addtogroup modbus_cfg
|
/*! \addtogroup modbus_cfg
|
||||||
* @{
|
* @{
|
||||||
*/
|
*/
|
||||||
/*! \brief If Modbus ASCII support is enabled. */
|
/*! \brief If Modbus Master ASCII support is enabled. */
|
||||||
#define MB_ASCII_ENABLED ( 0 )
|
#define MB_MASTER_ASCII_ENABLED ( 0 )
|
||||||
|
/*! \brief If Modbus Master RTU support is enabled. */
|
||||||
/*! \brief If Modbus RTU support is enabled. */
|
#define MB_MASTER_RTU_ENABLED ( 1 )
|
||||||
#define MB_RTU_ENABLED ( 1 )
|
/*! \brief If Modbus Master TCP support is enabled. */
|
||||||
|
#define MB_MASTER_TCP_ENABLED ( 0 )
|
||||||
/*! \brief If Modbus TCP support is enabled. */
|
/*! \brief If Modbus Slave ASCII support is enabled. */
|
||||||
#define MB_TCP_ENABLED ( 0 )
|
#define MB_SLAVE_ASCII_ENABLED ( 1 )
|
||||||
|
/*! \brief If Modbus Slave RTU support is enabled. */
|
||||||
|
#define MB_SLAVE_RTU_ENABLED ( 1 )
|
||||||
|
/*! \brief If Modbus Slave TCP support is enabled. */
|
||||||
|
#define MB_TCP_ENABLED ( 1 )
|
||||||
/*! \brief The character timeout value for Modbus ASCII.
|
/*! \brief The character timeout value for Modbus ASCII.
|
||||||
*
|
*
|
||||||
* The character timeout value is not fixed for Modbus ASCII and is therefore
|
* The character timeout value is not fixed for Modbus ASCII and is therefore
|
||||||
@ -62,7 +68,6 @@ PR_BEGIN_EXTERN_C
|
|||||||
* time of the network.
|
* time of the network.
|
||||||
*/
|
*/
|
||||||
#define MB_ASCII_TIMEOUT_SEC ( 1 )
|
#define MB_ASCII_TIMEOUT_SEC ( 1 )
|
||||||
|
|
||||||
/*! \brief Timeout to wait in ASCII prior to enabling transmitter.
|
/*! \brief Timeout to wait in ASCII prior to enabling transmitter.
|
||||||
*
|
*
|
||||||
* If defined the function calls vMBPortSerialDelay with the argument
|
* If defined the function calls vMBPortSerialDelay with the argument
|
||||||
@ -129,4 +134,18 @@ PR_BEGIN_EXTERN_C
|
|||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
PR_END_EXTERN_C
|
PR_END_EXTERN_C
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED
|
||||||
|
/*! \brief If master send a broadcast frame, the master will wait time of convert to delay,
|
||||||
|
* then master can send other frame */
|
||||||
|
#define MB_MASTER_DELAY_MS_CONVERT ( CONFIG_MB_MASTER_DELAY_MS_CONVERT )
|
||||||
|
/*! \brief If master send a frame which is not broadcast,the master will wait sometime for slave.
|
||||||
|
* And if slave is not respond in this time,the master will process this timeout error.
|
||||||
|
* Then master can send other frame */
|
||||||
|
#define MB_MASTER_TIMEOUT_MS_RESPOND ( CONFIG_MB_MASTER_TIMEOUT_MS_RESPOND )
|
||||||
|
/*! \brief The total slaves in Modbus Master system.
|
||||||
|
* \note : The slave ID must be continuous from 1.*/
|
||||||
|
#define MB_MASTER_TOTAL_SLAVE_NUM ( 247 )
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -31,6 +31,8 @@
|
|||||||
#ifndef _MB_PORT_H
|
#ifndef _MB_PORT_H
|
||||||
#define _MB_PORT_H
|
#define _MB_PORT_H
|
||||||
|
|
||||||
|
#include "mbconfig.h" // for options
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
PR_BEGIN_EXTERN_C
|
PR_BEGIN_EXTERN_C
|
||||||
#endif
|
#endif
|
||||||
@ -39,12 +41,34 @@ PR_BEGIN_EXTERN_C
|
|||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
EV_READY, /*!< Startup finished. */
|
EV_READY = 0x01, /*!< Startup finished. */
|
||||||
EV_FRAME_RECEIVED, /*!< Frame received. */
|
EV_FRAME_RECEIVED = 0x02, /*!< Frame received. */
|
||||||
EV_EXECUTE, /*!< Execute function. */
|
EV_EXECUTE = 0x04, /*!< Execute function. */
|
||||||
EV_FRAME_SENT /*!< Frame sent. */
|
EV_FRAME_SENT = 0x08 /*!< Frame sent. */
|
||||||
} eMBEventType;
|
} eMBEventType;
|
||||||
|
|
||||||
|
#if MB_MASTER_RTU_ENABLED > 0 || MB_MASTER_ASCII_ENABLED > 0
|
||||||
|
typedef enum {
|
||||||
|
EV_MASTER_NO_EVENT = 0x0000,
|
||||||
|
EV_MASTER_READY = 0x0001, /*!< Startup finished. */
|
||||||
|
EV_MASTER_FRAME_RECEIVED = 0x0002, /*!< Frame received. */
|
||||||
|
EV_MASTER_EXECUTE = 0x0004, /*!< Execute function. */
|
||||||
|
EV_MASTER_FRAME_SENT = 0x0008, /*!< Frame sent. */
|
||||||
|
EV_MASTER_FRAME_TRANSMITTED = 0x0010, /*!< Request execute function error. */
|
||||||
|
EV_MASTER_ERROR_PROCESS = 0x0020, /*!< Frame error process. */
|
||||||
|
EV_MASTER_PROCESS_SUCCESS = 0x0040, /*!< Request process success. */
|
||||||
|
EV_MASTER_ERROR_RESPOND_TIMEOUT = 0x0080, /*!< Request respond timeout. */
|
||||||
|
EV_MASTER_ERROR_RECEIVE_DATA = 0x0100, /*!< Request receive data error. */
|
||||||
|
EV_MASTER_ERROR_EXECUTE_FUNCTION = 0x0200, /*!< Request execute function error. */
|
||||||
|
} eMBMasterEventType;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
EV_ERROR_RESPOND_TIMEOUT, /*!< Slave respond timeout. */
|
||||||
|
EV_ERROR_RECEIVE_DATA, /*!< Receive frame data erroe. */
|
||||||
|
EV_ERROR_EXECUTE_FUNCTION, /*!< Execute function error. */
|
||||||
|
} eMBMasterErrorEventType;
|
||||||
|
#endif
|
||||||
|
|
||||||
/*! \ingroup modbus
|
/*! \ingroup modbus
|
||||||
* \brief Parity used for characters in serial mode.
|
* \brief Parity used for characters in serial mode.
|
||||||
*
|
*
|
||||||
@ -66,6 +90,19 @@ BOOL xMBPortEventPost( eMBEventType eEvent );
|
|||||||
|
|
||||||
BOOL xMBPortEventGet( /*@out@ */ eMBEventType * eEvent );
|
BOOL xMBPortEventGet( /*@out@ */ eMBEventType * eEvent );
|
||||||
|
|
||||||
|
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED
|
||||||
|
BOOL xMBMasterPortEventInit( void );
|
||||||
|
|
||||||
|
BOOL xMBMasterPortEventPost( eMBMasterEventType eEvent );
|
||||||
|
|
||||||
|
BOOL xMBMasterPortEventGet( /*@out@ */ eMBMasterEventType * eEvent );
|
||||||
|
|
||||||
|
void vMBMasterOsResInit( void );
|
||||||
|
|
||||||
|
BOOL xMBMasterRunResTake( LONG time );
|
||||||
|
|
||||||
|
void vMBMasterRunResRelease( void );
|
||||||
|
#endif // #if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED
|
||||||
/* ----------------------- Serial port functions ----------------------------*/
|
/* ----------------------- Serial port functions ----------------------------*/
|
||||||
|
|
||||||
BOOL xMBPortSerialInit( UCHAR ucPort, ULONG ulBaudRate,
|
BOOL xMBPortSerialInit( UCHAR ucPort, ULONG ulBaudRate,
|
||||||
@ -81,6 +118,21 @@ BOOL xMBPortSerialGetByte( CHAR * pucByte );
|
|||||||
|
|
||||||
BOOL xMBPortSerialPutByte( CHAR ucByte );
|
BOOL xMBPortSerialPutByte( CHAR ucByte );
|
||||||
|
|
||||||
|
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED
|
||||||
|
BOOL xMBMasterPortSerialInit( UCHAR ucPort, ULONG ulBaudRate,
|
||||||
|
UCHAR ucDataBits, eMBParity eParity );
|
||||||
|
|
||||||
|
void vMBMasterPortClose( void );
|
||||||
|
|
||||||
|
void xMBMasterPortSerialClose( void );
|
||||||
|
|
||||||
|
void vMBMasterPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable );
|
||||||
|
|
||||||
|
BOOL xMBMasterPortSerialGetByte( CHAR * pucByte );
|
||||||
|
|
||||||
|
BOOL xMBMasterPortSerialPutByte( CHAR ucByte );
|
||||||
|
#endif
|
||||||
|
|
||||||
/* ----------------------- Timers functions ---------------------------------*/
|
/* ----------------------- Timers functions ---------------------------------*/
|
||||||
BOOL xMBPortTimersInit( USHORT usTimeOut50us );
|
BOOL xMBPortTimersInit( USHORT usTimeOut50us );
|
||||||
|
|
||||||
@ -92,6 +144,32 @@ void vMBPortTimersDisable( void );
|
|||||||
|
|
||||||
void vMBPortTimersDelay( USHORT usTimeOutMS );
|
void vMBPortTimersDelay( USHORT usTimeOutMS );
|
||||||
|
|
||||||
|
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED
|
||||||
|
BOOL xMBMasterPortTimersInit( USHORT usTimeOut50us );
|
||||||
|
|
||||||
|
void xMBMasterPortTimersClose( void );
|
||||||
|
|
||||||
|
void vMBMasterPortTimersT35Enable( void );
|
||||||
|
|
||||||
|
void vMBMasterPortTimersConvertDelayEnable( void );
|
||||||
|
|
||||||
|
void vMBMasterPortTimersRespondTimeoutEnable( void );
|
||||||
|
|
||||||
|
void vMBMasterPortTimersDisable( void );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ----------------- Callback for the master error process ------------------*/
|
||||||
|
void vMBMasterErrorCBRespondTimeout( UCHAR ucDestAddress, const UCHAR* pucPDUData,
|
||||||
|
USHORT ucPDULength );
|
||||||
|
|
||||||
|
void vMBMasterErrorCBReceiveData( UCHAR ucDestAddress, const UCHAR* pucPDUData,
|
||||||
|
USHORT ucPDULength );
|
||||||
|
|
||||||
|
void vMBMasterErrorCBExecuteFunction( UCHAR ucDestAddress, const UCHAR* pucPDUData,
|
||||||
|
USHORT ucPDULength );
|
||||||
|
|
||||||
|
void vMBMasterCBRequestSuccess( void );
|
||||||
|
|
||||||
/* ----------------------- Callback for the protocol stack ------------------*/
|
/* ----------------------- Callback for the protocol stack ------------------*/
|
||||||
/*!
|
/*!
|
||||||
* \brief Callback function for the porting layer when a new byte is
|
* \brief Callback function for the porting layer when a new byte is
|
||||||
@ -110,9 +188,15 @@ extern BOOL( *pxMBFrameCBByteReceived ) ( void );
|
|||||||
extern BOOL( *pxMBFrameCBTransmitterEmpty ) ( void );
|
extern BOOL( *pxMBFrameCBTransmitterEmpty ) ( void );
|
||||||
|
|
||||||
extern BOOL( *pxMBPortCBTimerExpired ) ( void );
|
extern BOOL( *pxMBPortCBTimerExpired ) ( void );
|
||||||
|
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED
|
||||||
|
extern BOOL( *pxMBMasterFrameCBByteReceived ) ( void );
|
||||||
|
|
||||||
|
extern BOOL( *pxMBMasterFrameCBTransmitterEmpty ) ( void );
|
||||||
|
|
||||||
|
extern BOOL( *pxMBMasterPortCBTimerExpired ) ( void );
|
||||||
|
#endif
|
||||||
/* ----------------------- TCP port functions -------------------------------*/
|
/* ----------------------- TCP port functions -------------------------------*/
|
||||||
#if MB_TCP_ENABLED == 1
|
#if MB_TCP_ENABLED
|
||||||
BOOL xMBTCPPortInit( USHORT usTCPPort );
|
BOOL xMBTCPPortInit( USHORT usTCPPort );
|
||||||
|
|
||||||
void vMBTCPPortClose( void );
|
void vMBTCPPortClose( void );
|
||||||
|
@ -41,12 +41,12 @@
|
|||||||
#include "mbframe.h"
|
#include "mbframe.h"
|
||||||
#include "mbproto.h"
|
#include "mbproto.h"
|
||||||
#include "mbfunc.h"
|
#include "mbfunc.h"
|
||||||
|
|
||||||
#include "mbport.h"
|
#include "mbport.h"
|
||||||
#if MB_RTU_ENABLED == 1
|
|
||||||
|
#if MB_SLAVE_RTU_ENABLED == 1
|
||||||
#include "mbrtu.h"
|
#include "mbrtu.h"
|
||||||
#endif
|
#endif
|
||||||
#if MB_ASCII_ENABLED == 1
|
#if MB_SLAVE_ASCII_ENABLED == 1
|
||||||
#include "mbascii.h"
|
#include "mbascii.h"
|
||||||
#endif
|
#endif
|
||||||
#if MB_TCP_ENABLED == 1
|
#if MB_TCP_ENABLED == 1
|
||||||
@ -85,7 +85,6 @@ static pvMBFrameClose pvMBFrameCloseCur;
|
|||||||
BOOL( *pxMBFrameCBByteReceived ) ( void );
|
BOOL( *pxMBFrameCBByteReceived ) ( void );
|
||||||
BOOL( *pxMBFrameCBTransmitterEmpty ) ( void );
|
BOOL( *pxMBFrameCBTransmitterEmpty ) ( void );
|
||||||
BOOL( *pxMBPortCBTimerExpired ) ( void );
|
BOOL( *pxMBPortCBTimerExpired ) ( void );
|
||||||
|
|
||||||
BOOL( *pxMBFrameCBReceiveFSMCur ) ( void );
|
BOOL( *pxMBFrameCBReceiveFSMCur ) ( void );
|
||||||
BOOL( *pxMBFrameCBTransmitFSMCur ) ( void );
|
BOOL( *pxMBFrameCBTransmitFSMCur ) ( void );
|
||||||
|
|
||||||
@ -143,7 +142,7 @@ eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eM
|
|||||||
|
|
||||||
switch ( eMode )
|
switch ( eMode )
|
||||||
{
|
{
|
||||||
#if MB_RTU_ENABLED > 0
|
#if MB_SLAVE_RTU_ENABLED > 0
|
||||||
case MB_RTU:
|
case MB_RTU:
|
||||||
pvMBFrameStartCur = eMBRTUStart;
|
pvMBFrameStartCur = eMBRTUStart;
|
||||||
pvMBFrameStopCur = eMBRTUStop;
|
pvMBFrameStopCur = eMBRTUStop;
|
||||||
@ -157,7 +156,7 @@ eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eM
|
|||||||
eStatus = eMBRTUInit( ucMBAddress, ucPort, ulBaudRate, eParity );
|
eStatus = eMBRTUInit( ucMBAddress, ucPort, ulBaudRate, eParity );
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
#if MB_ASCII_ENABLED > 0
|
#if MB_SLAVE_ASCII_ENABLED > 0
|
||||||
case MB_ASCII:
|
case MB_ASCII:
|
||||||
pvMBFrameStartCur = eMBASCIIStart;
|
pvMBFrameStartCur = eMBASCIIStart;
|
||||||
pvMBFrameStopCur = eMBASCIIStop;
|
pvMBFrameStopCur = eMBASCIIStop;
|
||||||
|
446
components/freemodbus/modbus/mb_m.c
Normal file
446
components/freemodbus/modbus/mb_m.c
Normal file
@ -0,0 +1,446 @@
|
|||||||
|
/*
|
||||||
|
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
|
||||||
|
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* File: $Id: mbrtu_m.c,v 1.60 2013/08/20 11:18:10 Armink Add Master Functions $
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* ----------------------- System includes ----------------------------------*/
|
||||||
|
#include "stdlib.h"
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
/* ----------------------- Platform includes --------------------------------*/
|
||||||
|
#include "port.h"
|
||||||
|
|
||||||
|
/* ----------------------- Modbus includes ----------------------------------*/
|
||||||
|
|
||||||
|
#include "mb_m.h"
|
||||||
|
#include "mbconfig.h"
|
||||||
|
#include "mbframe.h"
|
||||||
|
#include "mbproto.h"
|
||||||
|
#include "mbfunc.h"
|
||||||
|
|
||||||
|
#include "mbport.h"
|
||||||
|
#if MB_MASTER_RTU_ENABLED == 1
|
||||||
|
#include "mbrtu.h"
|
||||||
|
#endif
|
||||||
|
#if MB_MASTER_ASCII_ENABLED == 1
|
||||||
|
#include "mbascii.h"
|
||||||
|
#endif
|
||||||
|
#if MB_MASTER_TCP_ENABLED == 1
|
||||||
|
#include "mbtcp.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if MB_MASTER_RTU_ENABLED > 0 || MB_MASTER_ASCII_ENABLED > 0
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef MB_PORT_HAS_CLOSE
|
||||||
|
#define MB_PORT_HAS_CLOSE 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ----------------------- Static variables ---------------------------------*/
|
||||||
|
|
||||||
|
static UCHAR ucMBMasterDestAddress;
|
||||||
|
static BOOL xMBRunInMasterMode = FALSE;
|
||||||
|
static volatile eMBMasterErrorEventType eMBMasterCurErrorType;
|
||||||
|
|
||||||
|
static enum
|
||||||
|
{
|
||||||
|
STATE_ENABLED,
|
||||||
|
STATE_DISABLED,
|
||||||
|
STATE_NOT_INITIALIZED
|
||||||
|
} eMBState = STATE_NOT_INITIALIZED;
|
||||||
|
|
||||||
|
/* Functions pointer which are initialized in eMBInit( ). Depending on the
|
||||||
|
* mode (RTU or ASCII) the are set to the correct implementations.
|
||||||
|
* Using for Modbus Master,Add by Armink 20130813
|
||||||
|
*/
|
||||||
|
static peMBFrameSend peMBMasterFrameSendCur;
|
||||||
|
static pvMBFrameStart pvMBMasterFrameStartCur;
|
||||||
|
static pvMBFrameStop pvMBMasterFrameStopCur;
|
||||||
|
static peMBFrameReceive peMBMasterFrameReceiveCur;
|
||||||
|
static pvMBFrameClose pvMBMasterFrameCloseCur;
|
||||||
|
|
||||||
|
/* Callback functions required by the porting layer. They are called when
|
||||||
|
* an external event has happend which includes a timeout or the reception
|
||||||
|
* or transmission of a character.
|
||||||
|
* Using for Modbus Master,Add by Armink 20130813
|
||||||
|
*/
|
||||||
|
BOOL( *pxMBMasterFrameCBByteReceived ) ( void );
|
||||||
|
|
||||||
|
BOOL( *pxMBMasterFrameCBTransmitterEmpty ) ( void );
|
||||||
|
|
||||||
|
BOOL( *pxMBMasterPortCBTimerExpired ) ( void );
|
||||||
|
|
||||||
|
BOOL( *pxMBMasterFrameCBReceiveFSMCur ) ( void );
|
||||||
|
|
||||||
|
BOOL( *pxMBMasterFrameCBTransmitFSMCur ) ( void );
|
||||||
|
|
||||||
|
/* An array of Modbus functions handlers which associates Modbus function
|
||||||
|
* codes with implementing functions.
|
||||||
|
*/
|
||||||
|
static xMBFunctionHandler xMasterFuncHandlers[MB_FUNC_HANDLERS_MAX] = {
|
||||||
|
#if MB_FUNC_OTHER_REP_SLAVEID_ENABLED > 0
|
||||||
|
{MB_FUNC_OTHER_REPORT_SLAVEID, eMBFuncReportSlaveID},
|
||||||
|
#endif
|
||||||
|
#if MB_FUNC_READ_INPUT_ENABLED > 0
|
||||||
|
{MB_FUNC_READ_INPUT_REGISTER, eMBMasterFuncReadInputRegister},
|
||||||
|
#endif
|
||||||
|
#if MB_FUNC_READ_HOLDING_ENABLED > 0
|
||||||
|
{MB_FUNC_READ_HOLDING_REGISTER, eMBMasterFuncReadHoldingRegister},
|
||||||
|
#endif
|
||||||
|
#if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED > 0
|
||||||
|
{MB_FUNC_WRITE_MULTIPLE_REGISTERS, eMBMasterFuncWriteMultipleHoldingRegister},
|
||||||
|
#endif
|
||||||
|
#if MB_FUNC_WRITE_HOLDING_ENABLED > 0
|
||||||
|
{MB_FUNC_WRITE_REGISTER, eMBMasterFuncWriteHoldingRegister},
|
||||||
|
#endif
|
||||||
|
#if MB_FUNC_READWRITE_HOLDING_ENABLED > 0
|
||||||
|
{MB_FUNC_READWRITE_MULTIPLE_REGISTERS, eMBMasterFuncReadWriteMultipleHoldingRegister},
|
||||||
|
#endif
|
||||||
|
#if MB_FUNC_READ_COILS_ENABLED > 0
|
||||||
|
{MB_FUNC_READ_COILS, eMBMasterFuncReadCoils},
|
||||||
|
#endif
|
||||||
|
#if MB_FUNC_WRITE_COIL_ENABLED > 0
|
||||||
|
{MB_FUNC_WRITE_SINGLE_COIL, eMBMasterFuncWriteCoil},
|
||||||
|
#endif
|
||||||
|
#if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED > 0
|
||||||
|
{MB_FUNC_WRITE_MULTIPLE_COILS, eMBMasterFuncWriteMultipleCoils},
|
||||||
|
#endif
|
||||||
|
#if MB_FUNC_READ_DISCRETE_INPUTS_ENABLED > 0
|
||||||
|
{MB_FUNC_READ_DISCRETE_INPUTS, eMBMasterFuncReadDiscreteInputs},
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ----------------------- Start implementation -----------------------------*/
|
||||||
|
eMBErrorCode
|
||||||
|
eMBMasterInit( eMBMode eMode, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
|
||||||
|
{
|
||||||
|
eMBErrorCode eStatus = MB_ENOERR;
|
||||||
|
|
||||||
|
switch (eMode)
|
||||||
|
{
|
||||||
|
#if MB_MASTER_RTU_ENABLED > 0
|
||||||
|
case MB_RTU:
|
||||||
|
pvMBMasterFrameStartCur = eMBMasterRTUStart;
|
||||||
|
pvMBMasterFrameStopCur = eMBMasterRTUStop;
|
||||||
|
peMBMasterFrameSendCur = eMBMasterRTUSend;
|
||||||
|
peMBMasterFrameReceiveCur = eMBMasterRTUReceive;
|
||||||
|
pvMBMasterFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBMasterPortClose : NULL;
|
||||||
|
pxMBMasterFrameCBByteReceived = xMBMasterRTUReceiveFSM;
|
||||||
|
pxMBMasterFrameCBTransmitterEmpty = xMBMasterRTUTransmitFSM;
|
||||||
|
pxMBMasterPortCBTimerExpired = xMBMasterRTUTimerExpired;
|
||||||
|
|
||||||
|
eStatus = eMBMasterRTUInit(ucPort, ulBaudRate, eParity);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
#if MB_MASTER_ASCII_ENABLED > 0
|
||||||
|
case MB_ASCII:
|
||||||
|
pvMBMasterFrameStartCur = eMBMasterASCIIStart;
|
||||||
|
pvMBMasterFrameStopCur = eMBMasterASCIIStop;
|
||||||
|
peMBMasterFrameSendCur = eMBMasterASCIISend;
|
||||||
|
peMBMasterFrameReceiveCur = eMBMasterASCIIReceive;
|
||||||
|
pvMBMasterFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBMasterPortClose : NULL;
|
||||||
|
pxMBMasterFrameCBByteReceived = xMBMasterASCIIReceiveFSM;
|
||||||
|
pxMBMasterFrameCBTransmitterEmpty = xMBMasterASCIITransmitFSM;
|
||||||
|
pxMBMasterPortCBTimerExpired = xMBMasterASCIITimerT1SExpired;
|
||||||
|
|
||||||
|
eStatus = eMBMasterASCIIInit(ucPort, ulBaudRate, eParity );
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
eStatus = MB_EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eStatus == MB_ENOERR)
|
||||||
|
{
|
||||||
|
if (!xMBMasterPortEventInit())
|
||||||
|
{
|
||||||
|
/* port dependent event module initalization failed. */
|
||||||
|
eStatus = MB_EPORTERR;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eMBState = STATE_DISABLED;
|
||||||
|
}
|
||||||
|
/* initialize the OS resource for modbus master. */
|
||||||
|
vMBMasterOsResInit();
|
||||||
|
}
|
||||||
|
return eStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
eMBErrorCode
|
||||||
|
eMBMasterClose( void )
|
||||||
|
{
|
||||||
|
eMBErrorCode eStatus = MB_ENOERR;
|
||||||
|
|
||||||
|
if( eMBState == STATE_DISABLED )
|
||||||
|
{
|
||||||
|
if( pvMBMasterFrameCloseCur != NULL )
|
||||||
|
{
|
||||||
|
pvMBMasterFrameCloseCur( );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eStatus = MB_EILLSTATE;
|
||||||
|
}
|
||||||
|
return eStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
eMBErrorCode
|
||||||
|
eMBMasterEnable( void )
|
||||||
|
{
|
||||||
|
eMBErrorCode eStatus = MB_ENOERR;
|
||||||
|
|
||||||
|
if( eMBState == STATE_DISABLED )
|
||||||
|
{
|
||||||
|
/* Activate the protocol stack. */
|
||||||
|
pvMBMasterFrameStartCur( );
|
||||||
|
/* Release the resource, because it created in busy state */
|
||||||
|
vMBMasterRunResRelease( );
|
||||||
|
eMBState = STATE_ENABLED;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eStatus = MB_EILLSTATE;
|
||||||
|
}
|
||||||
|
return eStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
eMBErrorCode
|
||||||
|
eMBMasterDisable( void )
|
||||||
|
{
|
||||||
|
eMBErrorCode eStatus;
|
||||||
|
|
||||||
|
if( eMBState == STATE_ENABLED )
|
||||||
|
{
|
||||||
|
pvMBMasterFrameStopCur( );
|
||||||
|
eMBState = STATE_DISABLED;
|
||||||
|
eStatus = MB_ENOERR;
|
||||||
|
}
|
||||||
|
else if( eMBState == STATE_DISABLED )
|
||||||
|
{
|
||||||
|
eStatus = MB_ENOERR;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eStatus = MB_EILLSTATE;
|
||||||
|
}
|
||||||
|
return eStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
eMBErrorCode
|
||||||
|
eMBMasterPoll( void )
|
||||||
|
{
|
||||||
|
static UCHAR *ucMBFrame;
|
||||||
|
static UCHAR ucRcvAddress;
|
||||||
|
static UCHAR ucFunctionCode;
|
||||||
|
static USHORT usLength;
|
||||||
|
static eMBException eException;
|
||||||
|
|
||||||
|
int i , j;
|
||||||
|
eMBErrorCode eStatus = MB_ENOERR;
|
||||||
|
eMBMasterEventType eEvent;
|
||||||
|
eMBMasterErrorEventType errorType;
|
||||||
|
|
||||||
|
/* Check if the protocol stack is ready. */
|
||||||
|
if( eMBState != STATE_ENABLED )
|
||||||
|
{
|
||||||
|
return MB_EILLSTATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if there is a event available. If not return control to caller.
|
||||||
|
* Otherwise we will handle the event. */
|
||||||
|
if( xMBMasterPortEventGet( &eEvent ) == TRUE )
|
||||||
|
{
|
||||||
|
switch ( eEvent )
|
||||||
|
{
|
||||||
|
case EV_MASTER_NO_EVENT:
|
||||||
|
ESP_LOGD(MB_PORT_TAG, "%s:EV_MASTER_NO_EVENT", __func__);
|
||||||
|
// Something went wrong and task unblocked but there are no any events set
|
||||||
|
assert(0);
|
||||||
|
break;
|
||||||
|
case EV_MASTER_PROCESS_SUCCESS:
|
||||||
|
ESP_LOGD(MB_PORT_TAG, "%s:EV_MASTER_PROCESS_SUCCESS", __func__);
|
||||||
|
break;
|
||||||
|
case EV_MASTER_ERROR_RESPOND_TIMEOUT:
|
||||||
|
ESP_LOGD(MB_PORT_TAG, "%s:EV_MASTER_ERROR_RESPOND_TIMEOUT", __func__);
|
||||||
|
break;
|
||||||
|
case EV_MASTER_ERROR_RECEIVE_DATA:
|
||||||
|
ESP_LOGD(MB_PORT_TAG, "%s:EV_MASTER_ERROR_RECEIVE_DATA", __func__);
|
||||||
|
break;
|
||||||
|
case EV_MASTER_ERROR_EXECUTE_FUNCTION:
|
||||||
|
ESP_LOGD(MB_PORT_TAG, "%s:EV_MASTER_ERROR_EXECUTE_FUNCTION", __func__);
|
||||||
|
break;
|
||||||
|
case EV_MASTER_READY:
|
||||||
|
ESP_LOGD(MB_PORT_TAG, "%s:EV_MASTER_READY", __func__);
|
||||||
|
break;
|
||||||
|
case EV_MASTER_FRAME_RECEIVED:
|
||||||
|
eStatus = peMBMasterFrameReceiveCur( &ucRcvAddress, &ucMBFrame, &usLength );
|
||||||
|
// Check if the frame is for us. If not ,send an error process event.
|
||||||
|
if ( ( eStatus == MB_ENOERR ) && ( ucRcvAddress == ucMBMasterGetDestAddress() ) )
|
||||||
|
{
|
||||||
|
ESP_LOGD(MB_PORT_TAG, "%s: Packet data received successfully (%u).", __func__, eStatus);
|
||||||
|
( void ) xMBMasterPortEventPost( EV_MASTER_EXECUTE );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vMBMasterSetErrorType(EV_ERROR_RECEIVE_DATA);
|
||||||
|
ESP_LOGD(MB_PORT_TAG, "%s: Packet data receive failed (addr=%u)(%u).", __func__, ucRcvAddress, eStatus);
|
||||||
|
( void ) xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case EV_MASTER_EXECUTE:
|
||||||
|
ESP_LOGD(MB_PORT_TAG, "%s:EV_MASTER_EXECUTE", __func__);
|
||||||
|
ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF];
|
||||||
|
eException = MB_EX_ILLEGAL_FUNCTION;
|
||||||
|
/* If receive frame has exception. The receive function code highest bit is 1.*/
|
||||||
|
if(ucFunctionCode >> 7) {
|
||||||
|
eException = (eMBException)ucMBFrame[MB_PDU_DATA_OFF];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (i = 0; i < MB_FUNC_HANDLERS_MAX; i++)
|
||||||
|
{
|
||||||
|
/* No more function handlers registered. Abort. */
|
||||||
|
if (xMasterFuncHandlers[i].ucFunctionCode == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (xMasterFuncHandlers[i].ucFunctionCode == ucFunctionCode) {
|
||||||
|
vMBMasterSetCBRunInMasterMode(TRUE);
|
||||||
|
/* If master request is broadcast,
|
||||||
|
* the master need execute function for all slave.
|
||||||
|
*/
|
||||||
|
if ( xMBMasterRequestIsBroadcast() ) {
|
||||||
|
usLength = usMBMasterGetPDUSndLength();
|
||||||
|
for(j = 1; j <= MB_MASTER_TOTAL_SLAVE_NUM; j++){
|
||||||
|
vMBMasterSetDestAddress(j);
|
||||||
|
eException = xMasterFuncHandlers[i].pxHandler(ucMBFrame, &usLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
eException = xMasterFuncHandlers[i].pxHandler(ucMBFrame, &usLength);
|
||||||
|
}
|
||||||
|
vMBMasterSetCBRunInMasterMode(FALSE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* If master has exception ,Master will send error process.Otherwise the Master is idle.*/
|
||||||
|
if (eException != MB_EX_NONE) {
|
||||||
|
vMBMasterSetErrorType(EV_ERROR_EXECUTE_FUNCTION);
|
||||||
|
( void ) xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
vMBMasterCBRequestSuccess( );
|
||||||
|
vMBMasterRunResRelease( );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case EV_MASTER_FRAME_SENT:
|
||||||
|
ESP_LOGD(MB_PORT_TAG, "%s:EV_MASTER_FRAME_SENT", __func__);
|
||||||
|
/* Master is busy now. */
|
||||||
|
vMBMasterGetPDUSndBuf( &ucMBFrame );
|
||||||
|
eStatus = peMBMasterFrameSendCur( ucMBMasterGetDestAddress(), ucMBFrame, usMBMasterGetPDUSndLength() );
|
||||||
|
if (eStatus != MB_ENOERR)
|
||||||
|
{
|
||||||
|
ESP_LOGD(MB_PORT_TAG, "%s:Frame send error. %d", __func__, eStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case EV_MASTER_FRAME_TRANSMITTED:
|
||||||
|
ESP_LOGD(MB_PORT_TAG, "%s:EV_MASTER_FRAME_TRANSMITTED", __func__);
|
||||||
|
break;
|
||||||
|
case EV_MASTER_ERROR_PROCESS:
|
||||||
|
ESP_LOGD(MB_PORT_TAG, "%s:EV_MASTER_ERROR_PROCESS", __func__);
|
||||||
|
/* Execute specified error process callback function. */
|
||||||
|
errorType = eMBMasterGetErrorType();
|
||||||
|
vMBMasterGetPDUSndBuf( &ucMBFrame );
|
||||||
|
switch (errorType) {
|
||||||
|
case EV_ERROR_RESPOND_TIMEOUT:
|
||||||
|
vMBMasterErrorCBRespondTimeout(ucMBMasterGetDestAddress(),
|
||||||
|
ucMBFrame, usMBMasterGetPDUSndLength());
|
||||||
|
break;
|
||||||
|
case EV_ERROR_RECEIVE_DATA:
|
||||||
|
vMBMasterErrorCBReceiveData(ucMBMasterGetDestAddress(),
|
||||||
|
ucMBFrame, usMBMasterGetPDUSndLength());
|
||||||
|
break;
|
||||||
|
case EV_ERROR_EXECUTE_FUNCTION:
|
||||||
|
vMBMasterErrorCBExecuteFunction(ucMBMasterGetDestAddress(),
|
||||||
|
ucMBFrame, usMBMasterGetPDUSndLength());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ESP_LOGD(MB_PORT_TAG, "%s: incorrect error type.", __func__);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
vMBMasterRunResRelease();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// xMBMasterPortEventGet has unbloked the task but the event bits are not set
|
||||||
|
ESP_LOGD(MB_PORT_TAG, "%s: task event wait failure.", __func__);
|
||||||
|
}
|
||||||
|
return MB_ENOERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get whether the Modbus Master is run in master mode.
|
||||||
|
BOOL xMBMasterGetCBRunInMasterMode( void )
|
||||||
|
{
|
||||||
|
return xMBRunInMasterMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set whether the Modbus Master is run in master mode.
|
||||||
|
void vMBMasterSetCBRunInMasterMode( BOOL IsMasterMode )
|
||||||
|
{
|
||||||
|
xMBRunInMasterMode = IsMasterMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get Modbus Master send destination address.
|
||||||
|
UCHAR ucMBMasterGetDestAddress( void )
|
||||||
|
{
|
||||||
|
return ucMBMasterDestAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Modbus Master send destination address.
|
||||||
|
void vMBMasterSetDestAddress( UCHAR Address )
|
||||||
|
{
|
||||||
|
ucMBMasterDestAddress = Address;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get Modbus Master current error event type.
|
||||||
|
eMBMasterErrorEventType eMBMasterGetErrorType( void )
|
||||||
|
{
|
||||||
|
return eMBMasterCurErrorType;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Modbus Master current error event type.
|
||||||
|
void vMBMasterSetErrorType( eMBMasterErrorEventType errorType )
|
||||||
|
{
|
||||||
|
eMBMasterCurErrorType = errorType;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MB_MASTER_RTU_ENABLED > 0 || MB_MASTER_ASCII_ENABLED > 0
|
@ -147,14 +147,9 @@ eMBRTUStop( void )
|
|||||||
EXIT_CRITICAL_SECTION( );
|
EXIT_CRITICAL_SECTION( );
|
||||||
}
|
}
|
||||||
|
|
||||||
// The lines below are required to suppress GCC warnings about unused but set variable 'xFrameReceived'
|
|
||||||
// This warning is treated as error during compilation.
|
|
||||||
#pragma GCC diagnostic push // required for GCC
|
|
||||||
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
|
||||||
eMBErrorCode
|
eMBErrorCode
|
||||||
eMBRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength )
|
eMBRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength )
|
||||||
{
|
{
|
||||||
BOOL xFrameReceived = FALSE;
|
|
||||||
eMBErrorCode eStatus = MB_ENOERR;
|
eMBErrorCode eStatus = MB_ENOERR;
|
||||||
|
|
||||||
ENTER_CRITICAL_SECTION( );
|
ENTER_CRITICAL_SECTION( );
|
||||||
@ -176,7 +171,6 @@ eMBRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength )
|
|||||||
|
|
||||||
/* Return the start of the Modbus PDU to the caller. */
|
/* Return the start of the Modbus PDU to the caller. */
|
||||||
*pucFrame = ( UCHAR * ) & ucRTUBuf[MB_SER_PDU_PDU_OFF];
|
*pucFrame = ( UCHAR * ) & ucRTUBuf[MB_SER_PDU_PDU_OFF];
|
||||||
xFrameReceived = TRUE;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -186,7 +180,6 @@ eMBRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength )
|
|||||||
EXIT_CRITICAL_SECTION( );
|
EXIT_CRITICAL_SECTION( );
|
||||||
return eStatus;
|
return eStatus;
|
||||||
}
|
}
|
||||||
#pragma GCC diagnostic pop // require GCC
|
|
||||||
|
|
||||||
eMBErrorCode
|
eMBErrorCode
|
||||||
eMBRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength )
|
eMBRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength )
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
*
|
*
|
||||||
* File: $Id: mbrtu.h,v 1.9 2006/12/07 22:10:34 wolti Exp $
|
* File: $Id: mbrtu.h,v 1.9 2006/12/07 22:10:34 wolti Exp $
|
||||||
*/
|
*/
|
||||||
|
#include "mbconfig.h"
|
||||||
|
|
||||||
#ifndef _MB_RTU_H
|
#ifndef _MB_RTU_H
|
||||||
#define _MB_RTU_H
|
#define _MB_RTU_H
|
||||||
@ -45,6 +46,17 @@ BOOL xMBRTUTransmitFSM( void );
|
|||||||
BOOL xMBRTUTimerT15Expired( void );
|
BOOL xMBRTUTimerT15Expired( void );
|
||||||
BOOL xMBRTUTimerT35Expired( void );
|
BOOL xMBRTUTimerT35Expired( void );
|
||||||
|
|
||||||
|
#if MB_MASTER_RTU_ENABLED > 0
|
||||||
|
eMBErrorCode eMBMasterRTUInit( UCHAR ucPort, ULONG ulBaudRate,eMBParity eParity );
|
||||||
|
void eMBMasterRTUStart( void );
|
||||||
|
void eMBMasterRTUStop( void );
|
||||||
|
eMBErrorCode eMBMasterRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength );
|
||||||
|
eMBErrorCode eMBMasterRTUSend( UCHAR slaveAddress, const UCHAR * pucFrame, USHORT usLength );
|
||||||
|
BOOL xMBMasterRTUReceiveFSM( void );
|
||||||
|
BOOL xMBMasterRTUTransmitFSM( void );
|
||||||
|
BOOL xMBMasterRTUTimerExpired( void );
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
PR_END_EXTERN_C
|
PR_END_EXTERN_C
|
||||||
#endif
|
#endif
|
||||||
|
448
components/freemodbus/modbus/rtu/mbrtu_m.c
Normal file
448
components/freemodbus/modbus/rtu/mbrtu_m.c
Normal file
@ -0,0 +1,448 @@
|
|||||||
|
/*
|
||||||
|
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
|
||||||
|
* Copyright (c) 2013 China Beijing Armink <armink.ztl@gmail.com>
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* File: $Id: mbrtu_m.c,v 1.60 2013/08/17 11:42:56 Armink Add Master Functions $
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* ----------------------- System includes ----------------------------------*/
|
||||||
|
#include "stdlib.h"
|
||||||
|
#include "string.h"
|
||||||
|
#include "stdio.h"
|
||||||
|
|
||||||
|
/* ----------------------- Platform includes --------------------------------*/
|
||||||
|
#include "port.h"
|
||||||
|
|
||||||
|
/* ----------------------- Modbus includes ----------------------------------*/
|
||||||
|
|
||||||
|
#include "mb_m.h"
|
||||||
|
#include "mbrtu.h"
|
||||||
|
#include "mbframe.h"
|
||||||
|
|
||||||
|
#include "mbcrc.h"
|
||||||
|
#include "mbport.h"
|
||||||
|
|
||||||
|
#if MB_MASTER_RTU_ENABLED > 0
|
||||||
|
/* ----------------------- Defines ------------------------------------------*/
|
||||||
|
#define MB_SER_PDU_SIZE_MIN 4 /*!< Minimum size of a Modbus RTU frame. */
|
||||||
|
#define MB_SER_PDU_SIZE_MAX 256 /*!< Maximum size of a Modbus RTU frame. */
|
||||||
|
#define MB_SER_PDU_SIZE_CRC 2 /*!< Size of CRC field in PDU. */
|
||||||
|
#define MB_SER_PDU_ADDR_OFF 0 /*!< Offset of slave address in Ser-PDU. */
|
||||||
|
#define MB_SER_PDU_PDU_OFF 1 /*!< Offset of Modbus-PDU in Ser-PDU. */
|
||||||
|
|
||||||
|
/* ----------------------- Type definitions ---------------------------------*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
STATE_M_RX_INIT, /*!< Receiver is in initial state. */
|
||||||
|
STATE_M_RX_IDLE, /*!< Receiver is in idle state. */
|
||||||
|
STATE_M_RX_RCV, /*!< Frame is beeing received. */
|
||||||
|
STATE_M_RX_ERROR, /*!< If the frame is invalid. */
|
||||||
|
} eMBMasterRcvState;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
STATE_M_TX_IDLE, /*!< Transmitter is in idle state. */
|
||||||
|
STATE_M_TX_XMIT, /*!< Transmitter is in transfer state. */
|
||||||
|
STATE_M_TX_XFWR, /*!< Transmitter is in transfer finish and wait receive state. */
|
||||||
|
} eMBMasterSndState;
|
||||||
|
|
||||||
|
/* ----------------------- Static variables ---------------------------------*/
|
||||||
|
static volatile eMBMasterSndState eSndState;
|
||||||
|
static volatile eMBMasterRcvState eRcvState;
|
||||||
|
|
||||||
|
static volatile UCHAR ucMasterRTUSndBuf[MB_PDU_SIZE_MAX];
|
||||||
|
static volatile UCHAR ucMasterRTURcvBuf[MB_SER_PDU_SIZE_MAX];
|
||||||
|
static volatile USHORT usMasterSendPDULength;
|
||||||
|
|
||||||
|
static volatile UCHAR *pucMasterSndBufferCur;
|
||||||
|
static volatile USHORT usMasterSndBufferCount;
|
||||||
|
|
||||||
|
static volatile USHORT usMasterRcvBufferPos;
|
||||||
|
static volatile BOOL xFrameIsBroadcast = FALSE;
|
||||||
|
|
||||||
|
static volatile eMBMasterTimerMode eMasterCurTimerMode;
|
||||||
|
|
||||||
|
/* ----------------------- Start implementation -----------------------------*/
|
||||||
|
eMBErrorCode
|
||||||
|
eMBMasterRTUInit(UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
|
||||||
|
{
|
||||||
|
eMBErrorCode eStatus = MB_ENOERR;
|
||||||
|
ULONG usTimerT35_50us;
|
||||||
|
|
||||||
|
ENTER_CRITICAL_SECTION( );
|
||||||
|
|
||||||
|
/* Modbus RTU uses 8 Databits. */
|
||||||
|
if( xMBMasterPortSerialInit( ucPort, ulBaudRate, 8, eParity ) != TRUE )
|
||||||
|
{
|
||||||
|
eStatus = MB_EPORTERR;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* If baudrate > 19200 then we should use the fixed timer values
|
||||||
|
* t35 = 1750us. Otherwise t35 must be 3.5 times the character time.
|
||||||
|
*/
|
||||||
|
if( ulBaudRate > 19200 )
|
||||||
|
{
|
||||||
|
usTimerT35_50us = 35; /* 1800us. */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* The timer reload value for a character is given by:
|
||||||
|
*
|
||||||
|
* ChTimeValue = Ticks_per_1s / ( Baudrate / 11 )
|
||||||
|
* = 11 * Ticks_per_1s / Baudrate
|
||||||
|
* = 220000 / Baudrate
|
||||||
|
* The reload for t3.5 is 1.5 times this value and similary
|
||||||
|
* for t3.5.
|
||||||
|
*/
|
||||||
|
usTimerT35_50us = ( 7UL * 220000UL ) / ( 2UL * ulBaudRate );
|
||||||
|
}
|
||||||
|
if( xMBMasterPortTimersInit( ( USHORT ) usTimerT35_50us ) != TRUE )
|
||||||
|
{
|
||||||
|
eStatus = MB_EPORTERR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EXIT_CRITICAL_SECTION( );
|
||||||
|
|
||||||
|
return eStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
eMBMasterRTUStart( void )
|
||||||
|
{
|
||||||
|
ENTER_CRITICAL_SECTION( );
|
||||||
|
/* Initially the receiver is in the state STATE_M_RX_INIT. we start
|
||||||
|
* the timer and if no character is received within t3.5 we change
|
||||||
|
* to STATE_M_RX_IDLE. This makes sure that we delay startup of the
|
||||||
|
* modbus protocol stack until the bus is free.
|
||||||
|
*/
|
||||||
|
eRcvState = STATE_M_RX_INIT;
|
||||||
|
vMBMasterPortSerialEnable( TRUE, FALSE );
|
||||||
|
vMBMasterPortTimersT35Enable( );
|
||||||
|
|
||||||
|
EXIT_CRITICAL_SECTION( );
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
eMBMasterRTUStop( void )
|
||||||
|
{
|
||||||
|
ENTER_CRITICAL_SECTION( );
|
||||||
|
vMBMasterPortSerialEnable( FALSE, FALSE );
|
||||||
|
vMBMasterPortTimersDisable( );
|
||||||
|
EXIT_CRITICAL_SECTION( );
|
||||||
|
}
|
||||||
|
|
||||||
|
eMBErrorCode
|
||||||
|
eMBMasterRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength )
|
||||||
|
{
|
||||||
|
eMBErrorCode eStatus = MB_ENOERR;
|
||||||
|
|
||||||
|
ENTER_CRITICAL_SECTION( );
|
||||||
|
assert( usMasterRcvBufferPos < MB_SER_PDU_SIZE_MAX );
|
||||||
|
|
||||||
|
/* Length and CRC check */
|
||||||
|
if( ( usMasterRcvBufferPos >= MB_SER_PDU_SIZE_MIN )
|
||||||
|
&& ( usMBCRC16( ( UCHAR * ) ucMasterRTURcvBuf, usMasterRcvBufferPos ) == 0 ) )
|
||||||
|
{
|
||||||
|
/* Save the address field. All frames are passed to the upper layed
|
||||||
|
* and the decision if a frame is used is done there.
|
||||||
|
*/
|
||||||
|
*pucRcvAddress = ucMasterRTURcvBuf[MB_SER_PDU_ADDR_OFF];
|
||||||
|
|
||||||
|
/* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus
|
||||||
|
* size of address field and CRC checksum.
|
||||||
|
*/
|
||||||
|
*pusLength = ( USHORT )( usMasterRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_CRC );
|
||||||
|
|
||||||
|
/* Return the start of the Modbus PDU to the caller. */
|
||||||
|
*pucFrame = ( UCHAR * ) & ucMasterRTURcvBuf[MB_SER_PDU_PDU_OFF];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eStatus = MB_EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
EXIT_CRITICAL_SECTION( );
|
||||||
|
return eStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
eMBErrorCode
|
||||||
|
eMBMasterRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength )
|
||||||
|
{
|
||||||
|
eMBErrorCode eStatus = MB_ENOERR;
|
||||||
|
USHORT usCRC16;
|
||||||
|
|
||||||
|
if ( ucSlaveAddress > MB_MASTER_TOTAL_SLAVE_NUM ) return MB_EINVAL;
|
||||||
|
|
||||||
|
ENTER_CRITICAL_SECTION( );
|
||||||
|
|
||||||
|
/* Check if the receiver is still in idle state. If not we where to
|
||||||
|
* slow with processing the received frame and the master sent another
|
||||||
|
* frame on the network. We have to abort sending the frame.
|
||||||
|
*/
|
||||||
|
if( eRcvState == STATE_M_RX_IDLE )
|
||||||
|
{
|
||||||
|
/* First byte before the Modbus-PDU is the slave address. */
|
||||||
|
pucMasterSndBufferCur = ( UCHAR * ) pucFrame - 1;
|
||||||
|
usMasterSndBufferCount = 1;
|
||||||
|
|
||||||
|
/* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */
|
||||||
|
pucMasterSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress;
|
||||||
|
usMasterSndBufferCount += usLength;
|
||||||
|
|
||||||
|
/* Calculate CRC16 checksum for Modbus-Serial-Line-PDU. */
|
||||||
|
usCRC16 = usMBCRC16( ( UCHAR * ) pucMasterSndBufferCur, usMasterSndBufferCount );
|
||||||
|
ucMasterRTUSndBuf[usMasterSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF );
|
||||||
|
ucMasterRTUSndBuf[usMasterSndBufferCount++] = ( UCHAR )( usCRC16 >> 8 );
|
||||||
|
|
||||||
|
/* Activate the transmitter. */
|
||||||
|
eSndState = STATE_M_TX_XMIT;
|
||||||
|
// The place to enable RS485 driver
|
||||||
|
vMBMasterPortSerialEnable( FALSE, TRUE );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eStatus = MB_EIO;
|
||||||
|
}
|
||||||
|
EXIT_CRITICAL_SECTION( );
|
||||||
|
return eStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL
|
||||||
|
xMBMasterRTUReceiveFSM( void )
|
||||||
|
{
|
||||||
|
BOOL xTaskNeedSwitch = FALSE;
|
||||||
|
UCHAR ucByte;
|
||||||
|
|
||||||
|
assert(( eSndState == STATE_M_TX_IDLE ) || ( eSndState == STATE_M_TX_XFWR ));
|
||||||
|
|
||||||
|
/* Always read the character. */
|
||||||
|
( void )xMBMasterPortSerialGetByte( ( CHAR * ) & ucByte );
|
||||||
|
|
||||||
|
switch ( eRcvState )
|
||||||
|
{
|
||||||
|
/* If we have received a character in the init state we have to
|
||||||
|
* wait until the frame is finished.
|
||||||
|
*/
|
||||||
|
case STATE_M_RX_INIT:
|
||||||
|
vMBMasterPortTimersT35Enable( );
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* In the error state we wait until all characters in the
|
||||||
|
* damaged frame are transmitted.
|
||||||
|
*/
|
||||||
|
case STATE_M_RX_ERROR:
|
||||||
|
vMBMasterPortTimersT35Enable( );
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* In the idle state we wait for a new character. If a character
|
||||||
|
* is received the t1.5 and t3.5 timers are started and the
|
||||||
|
* receiver is in the state STATE_RX_RECEIVCE and disable early
|
||||||
|
* the timer of respond timeout .
|
||||||
|
*/
|
||||||
|
case STATE_M_RX_IDLE:
|
||||||
|
/* In time of respond timeout,the receiver receive a frame.
|
||||||
|
* Disable timer of respond timeout and change the transmiter state to idle.
|
||||||
|
*/
|
||||||
|
vMBMasterPortTimersDisable( );
|
||||||
|
eSndState = STATE_M_TX_IDLE;
|
||||||
|
|
||||||
|
usMasterRcvBufferPos = 0;
|
||||||
|
ucMasterRTURcvBuf[usMasterRcvBufferPos++] = ucByte;
|
||||||
|
eRcvState = STATE_M_RX_RCV;
|
||||||
|
|
||||||
|
/* Enable t3.5 timers. */
|
||||||
|
vMBMasterPortTimersT35Enable( );
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* We are currently receiving a frame. Reset the timer after
|
||||||
|
* every character received. If more than the maximum possible
|
||||||
|
* number of bytes in a modbus frame is received the frame is
|
||||||
|
* ignored.
|
||||||
|
*/
|
||||||
|
case STATE_M_RX_RCV:
|
||||||
|
if( usMasterRcvBufferPos < MB_SER_PDU_SIZE_MAX )
|
||||||
|
{
|
||||||
|
ucMasterRTURcvBuf[usMasterRcvBufferPos++] = ucByte;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eRcvState = STATE_M_RX_ERROR;
|
||||||
|
}
|
||||||
|
vMBMasterPortTimersT35Enable( );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return xTaskNeedSwitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL
|
||||||
|
xMBMasterRTUTransmitFSM( void )
|
||||||
|
{
|
||||||
|
BOOL xNeedPoll = FALSE;
|
||||||
|
|
||||||
|
assert( eRcvState == STATE_M_RX_IDLE );
|
||||||
|
|
||||||
|
switch ( eSndState )
|
||||||
|
{
|
||||||
|
/* We should not get a transmitter event if the transmitter is in
|
||||||
|
* idle state. */
|
||||||
|
case STATE_M_TX_XFWR:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STATE_M_TX_IDLE:
|
||||||
|
/* enable receiver/disable transmitter. */
|
||||||
|
vMBMasterPortSerialEnable( TRUE, FALSE );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STATE_M_TX_XMIT:
|
||||||
|
/* check if we are finished. */
|
||||||
|
if( usMasterSndBufferCount != 0 )
|
||||||
|
{
|
||||||
|
xMBMasterPortSerialPutByte( ( CHAR )*pucMasterSndBufferCur );
|
||||||
|
pucMasterSndBufferCur++; /* next byte in sendbuffer. */
|
||||||
|
usMasterSndBufferCount--;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
xFrameIsBroadcast = ( ucMasterRTUSndBuf[MB_SER_PDU_ADDR_OFF] == MB_ADDRESS_BROADCAST ) ? TRUE : FALSE;
|
||||||
|
/* Disable transmitter. This prevents another transmit buffer
|
||||||
|
* empty interrupt. */
|
||||||
|
vMBMasterPortSerialEnable( TRUE, FALSE );
|
||||||
|
eSndState = STATE_M_TX_XFWR;
|
||||||
|
/* If the frame is broadcast ,master will enable timer of convert delay,
|
||||||
|
* else master will enable timer of respond timeout. */
|
||||||
|
if ( xFrameIsBroadcast == TRUE )
|
||||||
|
{
|
||||||
|
vMBMasterPortTimersConvertDelayEnable( );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vMBMasterPortTimersRespondTimeoutEnable( );
|
||||||
|
}
|
||||||
|
xNeedPoll = TRUE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return xNeedPoll;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL
|
||||||
|
xMBMasterRTUTimerExpired(void)
|
||||||
|
{
|
||||||
|
BOOL xNeedPoll = FALSE;
|
||||||
|
|
||||||
|
switch (eRcvState)
|
||||||
|
{
|
||||||
|
/* Timer t35 expired. Startup phase is finished. */
|
||||||
|
case STATE_M_RX_INIT:
|
||||||
|
xNeedPoll = xMBMasterPortEventPost(EV_MASTER_READY);
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* A frame was received and t35 expired. Notify the listener that
|
||||||
|
* a new frame was received. */
|
||||||
|
case STATE_M_RX_RCV:
|
||||||
|
xNeedPoll = xMBMasterPortEventPost(EV_MASTER_FRAME_RECEIVED);
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* An error occured while receiving the frame. */
|
||||||
|
case STATE_M_RX_ERROR:
|
||||||
|
vMBMasterSetErrorType(EV_ERROR_RECEIVE_DATA);
|
||||||
|
xNeedPoll = xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Function called in an illegal state. */
|
||||||
|
default:
|
||||||
|
assert((eRcvState == STATE_M_RX_INIT) || (eRcvState == STATE_M_RX_RCV)
|
||||||
|
|| (eRcvState == STATE_M_RX_ERROR) || (eRcvState == STATE_M_RX_IDLE));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
eRcvState = STATE_M_RX_IDLE;
|
||||||
|
|
||||||
|
switch (eSndState)
|
||||||
|
{
|
||||||
|
/* A frame was send finish and convert delay or respond timeout expired.
|
||||||
|
* If the frame is broadcast,The master will idle,and if the frame is not
|
||||||
|
* broadcast.Notify the listener process error.*/
|
||||||
|
case STATE_M_TX_XFWR:
|
||||||
|
if ( xFrameIsBroadcast == FALSE ) {
|
||||||
|
vMBMasterSetErrorType(EV_ERROR_RESPOND_TIMEOUT);
|
||||||
|
xNeedPoll = xMBMasterPortEventPost(EV_MASTER_ERROR_PROCESS);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
/* Function called in an illegal state. */
|
||||||
|
default:
|
||||||
|
assert(
|
||||||
|
( eSndState == STATE_M_TX_XFWR ) || ( eSndState == STATE_M_TX_IDLE ));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
eSndState = STATE_M_TX_IDLE;
|
||||||
|
|
||||||
|
vMBMasterPortTimersDisable( );
|
||||||
|
/* If timer mode is convert delay, the master event then turns EV_MASTER_EXECUTE status. */
|
||||||
|
if (eMasterCurTimerMode == MB_TMODE_CONVERT_DELAY) {
|
||||||
|
xNeedPoll = xMBMasterPortEventPost( EV_MASTER_EXECUTE );
|
||||||
|
}
|
||||||
|
|
||||||
|
return xNeedPoll;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get Modbus Master send RTU's buffer address pointer.*/
|
||||||
|
void vMBMasterGetRTUSndBuf( UCHAR ** pucFrame )
|
||||||
|
{
|
||||||
|
*pucFrame = ( UCHAR * ) ucMasterRTUSndBuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get Modbus Master send PDU's buffer address pointer.*/
|
||||||
|
void vMBMasterGetPDUSndBuf( UCHAR ** pucFrame )
|
||||||
|
{
|
||||||
|
*pucFrame = ( UCHAR * ) &ucMasterRTUSndBuf[MB_SER_PDU_PDU_OFF];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set Modbus Master send PDU's buffer length.*/
|
||||||
|
void vMBMasterSetPDUSndLength( USHORT SendPDULength )
|
||||||
|
{
|
||||||
|
usMasterSendPDULength = SendPDULength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get Modbus Master send PDU's buffer length.*/
|
||||||
|
USHORT usMBMasterGetPDUSndLength( void )
|
||||||
|
{
|
||||||
|
return usMasterSendPDULength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set Modbus Master current timer mode.*/
|
||||||
|
void vMBMasterSetCurTimerMode( eMBMasterTimerMode eMBTimerMode )
|
||||||
|
{
|
||||||
|
eMasterCurTimerMode = eMBTimerMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The master request is broadcast? */
|
||||||
|
BOOL xMBMasterRequestIsBroadcast( void ){
|
||||||
|
return xFrameIsBroadcast;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
@ -1,186 +0,0 @@
|
|||||||
// Copyright 2015-2016 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.
|
|
||||||
|
|
||||||
// mbcontroller.h
|
|
||||||
// Implementation of the MbController
|
|
||||||
|
|
||||||
#ifndef _MODBUS_CONTROLLER
|
|
||||||
#define _MODBUS_CONTROLLER
|
|
||||||
|
|
||||||
#include <stdint.h> // for standard int types definition
|
|
||||||
#include <stddef.h> // for NULL and std defines
|
|
||||||
#include "soc/soc.h" // for BITN definitions
|
|
||||||
#include "sdkconfig.h" // for KConfig options
|
|
||||||
#include "driver/uart.h" // for uart port number defines
|
|
||||||
|
|
||||||
/* ----------------------- Defines ------------------------------------------*/
|
|
||||||
#define MB_INST_MIN_SIZE (2) // The minimal size of Modbus registers area in bytes
|
|
||||||
#define MB_INST_MAX_SIZE (65535 * 2) // The maximum size of Modbus area in bytes
|
|
||||||
|
|
||||||
#define MB_CONTROLLER_STACK_SIZE (CONFIG_MB_CONTROLLER_STACK_SIZE) // Stack size for Modbus controller
|
|
||||||
#define MB_CONTROLLER_PRIORITY (CONFIG_MB_SERIAL_TASK_PRIO - 1) // priority of MB controller task
|
|
||||||
#define MB_CONTROLLER_NOTIFY_QUEUE_SIZE (CONFIG_MB_CONTROLLER_NOTIFY_QUEUE_SIZE) // Number of messages in parameter notification queue
|
|
||||||
#define MB_CONTROLLER_NOTIFY_TIMEOUT (pdMS_TO_TICKS(CONFIG_MB_CONTROLLER_NOTIFY_TIMEOUT)) // notification timeout
|
|
||||||
|
|
||||||
// Default port defines
|
|
||||||
#define MB_DEVICE_ADDRESS (1) // Default slave device address in Modbus
|
|
||||||
#define MB_DEVICE_SPEED (115200) // Default Modbus speed for now hard defined
|
|
||||||
#define MB_UART_PORT (UART_NUM_2) // Default UART port number
|
|
||||||
#define MB_PAR_INFO_TOUT (10) // Timeout for get parameter info
|
|
||||||
#define MB_PARITY_NONE (UART_PARITY_DISABLE)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Event group for parameters notification
|
|
||||||
*/
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
MB_EVENT_NO_EVENTS = 0x00,
|
|
||||||
MB_EVENT_HOLDING_REG_WR = BIT0, /*!< Modbus Event Write Holding registers. */
|
|
||||||
MB_EVENT_HOLDING_REG_RD = BIT1, /*!< Modbus Event Read Holding registers. */
|
|
||||||
MB_EVENT_INPUT_REG_RD = BIT3, /*!< Modbus Event Read Input registers. */
|
|
||||||
MB_EVENT_COILS_WR = BIT4, /*!< Modbus Event Write Coils. */
|
|
||||||
MB_EVENT_COILS_RD = BIT5, /*!< Modbus Event Read Coils. */
|
|
||||||
MB_EVENT_DISCRETE_RD = BIT6, /*!< Modbus Event Read Discrete bits. */
|
|
||||||
MB_EVENT_STACK_STARTED = BIT7 /*!< Modbus Event Stack started */
|
|
||||||
} mb_event_group_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Type of Modbus parameter
|
|
||||||
*/
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
MB_PARAM_HOLDING, /*!< Modbus Holding register. */
|
|
||||||
MB_PARAM_INPUT, /*!< Modbus Input register. */
|
|
||||||
MB_PARAM_COIL, /*!< Modbus Coils. */
|
|
||||||
MB_PARAM_DISCRETE, /*!< Modbus Discrete bits. */
|
|
||||||
MB_PARAM_COUNT,
|
|
||||||
MB_PARAM_UNKNOWN = 0xFF
|
|
||||||
} mb_param_type_t;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Modbus serial transmission modes (RTU/ASCII).
|
|
||||||
*/
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
MB_MODE_RTU, /*!< RTU transmission mode. */
|
|
||||||
MB_MODE_ASCII, /*!< ASCII transmission mode. */
|
|
||||||
MB_MODE_TCP /*!< TCP mode. */
|
|
||||||
} mb_mode_type_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Parameter access event information type
|
|
||||||
*/
|
|
||||||
typedef struct {
|
|
||||||
uint32_t time_stamp; /*!< Timestamp of Modbus Event (uS)*/
|
|
||||||
uint16_t mb_offset; /*!< Modbus register offset */
|
|
||||||
mb_event_group_t type; /*!< Modbus event type */
|
|
||||||
uint8_t* address; /*!< Modbus data storage address */
|
|
||||||
size_t size; /*!< Modbus event register size (number of registers)*/
|
|
||||||
} mb_param_info_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Parameter storage area descriptor
|
|
||||||
*/
|
|
||||||
typedef struct {
|
|
||||||
uint16_t start_offset; /*!< Modbus start address for area descriptor */
|
|
||||||
mb_param_type_t type; /*!< Type of storage area descriptor */
|
|
||||||
void* address; /*!< Instance address for storage area descriptor */
|
|
||||||
size_t size; /*!< Instance size for area descriptor (bytes) */
|
|
||||||
} mb_register_area_descriptor_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Device communication parameters
|
|
||||||
*/
|
|
||||||
typedef struct {
|
|
||||||
mb_mode_type_t mode; /*!< Modbus communication mode */
|
|
||||||
uint8_t slave_addr; /*!< Modbus Slave Address */
|
|
||||||
uart_port_t port; /*!< Modbus communication port (UART) number */
|
|
||||||
uint32_t baudrate; /*!< Modbus baudrate */
|
|
||||||
uart_parity_t parity; /*!< Modbus UART parity settings */
|
|
||||||
} mb_communication_info_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Initialize modbus controller and stack
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* - ESP_OK Success
|
|
||||||
* - ESP_FAIL Parameter error
|
|
||||||
*/
|
|
||||||
esp_err_t mbcontroller_init(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Destroy Modbus controller and stack
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* - ESP_OK Success
|
|
||||||
* - ESP_FAIL Parameter error
|
|
||||||
*/
|
|
||||||
esp_err_t mbcontroller_destroy(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Start Modbus communication stack
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* - ESP_OK Success
|
|
||||||
* - ESP_ERR_INVALID_ARG Modbus stack start error
|
|
||||||
*/
|
|
||||||
esp_err_t mbcontroller_start(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set Modbus communication parameters for the controller
|
|
||||||
*
|
|
||||||
* @param comm_info Communication parameters structure.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* - ESP_OK Success
|
|
||||||
* - ESP_ERR_INVALID_ARG Incorrect parameter data
|
|
||||||
*/
|
|
||||||
esp_err_t mbcontroller_setup(mb_communication_info_t comm_info);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Wait for specific event on parameter change.
|
|
||||||
*
|
|
||||||
* @param group Group event bit mask to wait for change
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* - mb_event_group_t event bits triggered
|
|
||||||
*/
|
|
||||||
mb_event_group_t mbcontroller_check_event(mb_event_group_t group);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get parameter information
|
|
||||||
*
|
|
||||||
* @param[out] reg_info parameter info structure
|
|
||||||
* @param timeout Timeout in milliseconds to read information from
|
|
||||||
* parameter queue
|
|
||||||
* @return
|
|
||||||
* - ESP_OK Success
|
|
||||||
* - ESP_ERR_TIMEOUT Can not get data from parameter queue
|
|
||||||
* or queue overflow
|
|
||||||
*/
|
|
||||||
esp_err_t mbcontroller_get_param_info(mb_param_info_t* reg_info, uint32_t timeout);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set Modbus area descriptor
|
|
||||||
*
|
|
||||||
* @param descr_data Modbus registers area descriptor structure
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* - ESP_OK: The appropriate descriptor is set
|
|
||||||
* - ESP_ERR_INVALID_ARG: The argument is incorrect
|
|
||||||
*/
|
|
||||||
esp_err_t mbcontroller_set_descriptor(mb_register_area_descriptor_t descr_data);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
58
components/freemodbus/port/port.c
Normal file
58
components/freemodbus/port/port.c
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* FreeModbus Libary: RT-Thread Port
|
||||||
|
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* File: $Id: port.c,v 1.60 2015/02/01 9:18:05 Armink $
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* ----------------------- System includes --------------------------------*/
|
||||||
|
|
||||||
|
/* ----------------------- Modbus includes ----------------------------------*/
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/portmacro.h"
|
||||||
|
#include "sys/lock.h"
|
||||||
|
#include "port.h"
|
||||||
|
|
||||||
|
/* ----------------------- Variables ----------------------------------------*/
|
||||||
|
static _lock_t s_port_lock;
|
||||||
|
|
||||||
|
/* ----------------------- Start implementation -----------------------------*/
|
||||||
|
inline void
|
||||||
|
vMBPortEnterCritical( )
|
||||||
|
{
|
||||||
|
_lock_acquire(&s_port_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
vMBPortExitCritical( )
|
||||||
|
{
|
||||||
|
_lock_release(&s_port_lock);
|
||||||
|
}
|
@ -1,49 +1,31 @@
|
|||||||
/*
|
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||||
* FreeModbus Libary: ESP32 Port Demo Application
|
|
||||||
* Copyright (C) 2010 Christian Walter <cwalter@embedded-solutions.at>
|
|
||||||
*
|
*
|
||||||
|
* 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
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. The name of the author may not be used to endorse or promote products
|
|
||||||
* derived from this software without specific prior written permission.
|
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
* See the License for the specific language governing permissions and
|
||||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
* limitations under the License.
|
||||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
||||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
* File: $Id: portother.c,v 1.1 2010/06/06 13:07:20 wolti Exp $
|
|
||||||
*/
|
*/
|
||||||
#ifndef _PORT_H
|
|
||||||
#define _PORT_H
|
#ifndef PORT_COMMON_H_
|
||||||
/* ----------------------- Platform includes --------------------------------*/
|
#define PORT_COMMON_H_
|
||||||
#include "driver/uart.h"
|
|
||||||
#include "driver/timer.h"
|
|
||||||
#include "esp_log.h"
|
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/xtensa_api.h"
|
#include "freertos/xtensa_api.h"
|
||||||
#include "freertos/portmacro.h"
|
#include "freertos/portmacro.h"
|
||||||
#include "sdkconfig.h"
|
#include "esp_log.h" // for ESP_LOGE macro
|
||||||
|
|
||||||
/* ----------------------- Defines ------------------------------------------*/
|
#define INLINE inline
|
||||||
#ifdef __cplusplus
|
#define PR_BEGIN_EXTERN_C extern "C" {
|
||||||
extern "C" {
|
#define PR_END_EXTERN_C }
|
||||||
#endif /* __cplusplus */
|
|
||||||
|
|
||||||
#define MB_PORT_TAG "MB_PORT"
|
#define MB_PORT_TAG "MB_PORT_COMMON"
|
||||||
|
|
||||||
#define MB_PORT_CHECK(a, ret_val, str, ...) \
|
#define MB_PORT_CHECK(a, ret_val, str, ...) \
|
||||||
if (!(a)) { \
|
if (!(a)) { \
|
||||||
@ -51,20 +33,17 @@
|
|||||||
return (ret_val); \
|
return (ret_val); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define INLINE inline
|
#ifdef __cplusplus
|
||||||
#define PR_BEGIN_EXTERN_C extern "C" {
|
PR_BEGIN_EXTERN_C
|
||||||
#define PR_END_EXTERN_C }
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
#define MB_ENTER_CRITICAL_ISR(mux) portENTER_CRITICAL_ISR(mux)
|
#ifndef TRUE
|
||||||
#define MB_EXIT_CRITICAL_ISR(mux) portEXIT_CRITICAL_ISR(mux)
|
#define TRUE 1
|
||||||
#define MB_ENTER_CRITICAL(mux) portENTER_CRITICAL(mux)
|
#endif
|
||||||
#define MB_EXIT_CRITICAL(mux) portEXIT_CRITICAL(mux)
|
|
||||||
|
|
||||||
#define ENTER_CRITICAL_SECTION( ) { ESP_LOGD(MB_PORT_TAG,"%s: Port enter critical.", __func__); \
|
#ifndef FALSE
|
||||||
vMBPortEnterCritical(); }
|
#define FALSE 0
|
||||||
|
#endif
|
||||||
#define EXIT_CRITICAL_SECTION( ) { vMBPortExitCritical(); \
|
|
||||||
ESP_LOGD(MB_PORT_TAG,"%s: Port exit critical", __func__); }
|
|
||||||
|
|
||||||
typedef char BOOL;
|
typedef char BOOL;
|
||||||
|
|
||||||
@ -77,25 +56,17 @@ typedef short SHORT;
|
|||||||
typedef unsigned long ULONG;
|
typedef unsigned long ULONG;
|
||||||
typedef long LONG;
|
typedef long LONG;
|
||||||
|
|
||||||
#ifndef TRUE
|
void vMBPortEnterCritical( );
|
||||||
#define TRUE 1
|
void vMBPortExitCritical( );
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef FALSE
|
#define ENTER_CRITICAL_SECTION( ) { ESP_LOGD(MB_PORT_TAG,"%s: Port enter critical.", __func__); \
|
||||||
#define FALSE 0
|
vMBPortEnterCritical(); }
|
||||||
#endif
|
|
||||||
|
|
||||||
void vMBPortSetWithinException( BOOL bInException );
|
|
||||||
|
|
||||||
void vMBPortEnterCritical(void);
|
|
||||||
|
|
||||||
void vMBPortExitCritical(void);
|
|
||||||
|
|
||||||
BOOL xMBPortSerialTxPoll();
|
|
||||||
|
|
||||||
|
#define EXIT_CRITICAL_SECTION( ) { vMBPortExitCritical(); \
|
||||||
|
ESP_LOGD(MB_PORT_TAG,"%s: Port exit critical", __func__); }
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
PR_END_EXTERN_C
|
||||||
#endif /* __cplusplus */
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
#endif
|
#endif /* PORT_COMMON_H_ */
|
||||||
|
@ -1,3 +1,17 @@
|
|||||||
|
/* 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.
|
||||||
|
*/
|
||||||
/*
|
/*
|
||||||
* FreeModbus Libary: ESP32 Port Demo Application
|
* FreeModbus Libary: ESP32 Port Demo Application
|
||||||
* Copyright (C) 2010 Christian Walter <cwalter@embedded-solutions.at>
|
* Copyright (C) 2010 Christian Walter <cwalter@embedded-solutions.at>
|
||||||
@ -38,14 +52,13 @@
|
|||||||
#include "mbport.h"
|
#include "mbport.h"
|
||||||
#include "port.h"
|
#include "port.h"
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
|
#include "port_serial_slave.h"
|
||||||
/* ----------------------- Variables ----------------------------------------*/
|
/* ----------------------- Variables ----------------------------------------*/
|
||||||
static xQueueHandle xQueueHdl;
|
static xQueueHandle xQueueHdl;
|
||||||
|
|
||||||
#define MB_EVENT_QUEUE_SIZE (1)
|
#define MB_EVENT_QUEUE_SIZE (1)
|
||||||
#define MB_EVENT_QUEUE_TIMEOUT (pdMS_TO_TICKS(CONFIG_MB_EVENT_QUEUE_TIMEOUT))
|
#define MB_EVENT_QUEUE_TIMEOUT (pdMS_TO_TICKS(CONFIG_MB_EVENT_QUEUE_TIMEOUT))
|
||||||
|
|
||||||
BOOL bMBPortIsWithinException(void);
|
|
||||||
|
|
||||||
/* ----------------------- Start implementation -----------------------------*/
|
/* ----------------------- Start implementation -----------------------------*/
|
||||||
BOOL
|
BOOL
|
||||||
xMBPortEventInit( void )
|
xMBPortEventInit( void )
|
||||||
@ -75,7 +88,7 @@ xMBPortEventPost( eMBEventType eEvent )
|
|||||||
BOOL bStatus = TRUE;
|
BOOL bStatus = TRUE;
|
||||||
assert(xQueueHdl != NULL);
|
assert(xQueueHdl != NULL);
|
||||||
|
|
||||||
if(bMBPortIsWithinException())
|
if( (BOOL)xPortInIsrContext() == TRUE )
|
||||||
{
|
{
|
||||||
xQueueSendFromISR(xQueueHdl, (const void*)&eEvent, pdFALSE);
|
xQueueSendFromISR(xQueueHdl, (const void*)&eEvent, pdFALSE);
|
||||||
}
|
}
|
||||||
|
299
components/freemodbus/port/portevent_m.c
Normal file
299
components/freemodbus/port/portevent_m.c
Normal file
@ -0,0 +1,299 @@
|
|||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* FreeModbus Libary: ESP32 Port Demo Application
|
||||||
|
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* File: $Id: portevent.c v 1.60 2013/08/13 15:07:05 Armink add Master Functions$
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* ----------------------- Modbus includes ----------------------------------*/
|
||||||
|
#include "mb_m.h"
|
||||||
|
#include "mbport.h"
|
||||||
|
#include "mbconfig.h"
|
||||||
|
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "freertos/event_groups.h"
|
||||||
|
#include "port.h"
|
||||||
|
#include "mbport.h"
|
||||||
|
#include "freertos/semphr.h"
|
||||||
|
#include "port_serial_master.h"
|
||||||
|
|
||||||
|
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED
|
||||||
|
/* ----------------------- Defines ------------------------------------------*/
|
||||||
|
// Event bit mask for xMBMasterPortEventGet()
|
||||||
|
#define MB_EVENT_POLL_MASK (EventBits_t)( EV_MASTER_READY | \
|
||||||
|
EV_MASTER_FRAME_RECEIVED | \
|
||||||
|
EV_MASTER_EXECUTE | \
|
||||||
|
EV_MASTER_FRAME_SENT | \
|
||||||
|
EV_MASTER_FRAME_TRANSMITTED | \
|
||||||
|
EV_MASTER_ERROR_PROCESS )
|
||||||
|
|
||||||
|
// Event bit mask for eMBMasterWaitRequestFinish()
|
||||||
|
#define MB_EVENT_REQ_MASK (EventBits_t)( EV_MASTER_PROCESS_SUCCESS | \
|
||||||
|
EV_MASTER_ERROR_RESPOND_TIMEOUT | \
|
||||||
|
EV_MASTER_ERROR_RECEIVE_DATA | \
|
||||||
|
EV_MASTER_ERROR_EXECUTE_FUNCTION )
|
||||||
|
|
||||||
|
#define MB_CHECK_EVENT(event, mask) (event & mask)
|
||||||
|
|
||||||
|
/* ----------------------- Variables ----------------------------------------*/
|
||||||
|
static SemaphoreHandle_t xSemaphorMasterHdl;
|
||||||
|
static EventGroupHandle_t xEventGroupMasterHdl;
|
||||||
|
|
||||||
|
/* ----------------------- Start implementation -----------------------------*/
|
||||||
|
|
||||||
|
BOOL
|
||||||
|
xMBMasterPortEventInit( void )
|
||||||
|
{
|
||||||
|
xEventGroupMasterHdl = xEventGroupCreate();
|
||||||
|
MB_PORT_CHECK((xEventGroupMasterHdl != NULL),
|
||||||
|
FALSE, "mb stack event group creation error.");
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL
|
||||||
|
xMBMasterPortEventPost( eMBMasterEventType eEvent )
|
||||||
|
{
|
||||||
|
BOOL bStatus = FALSE;
|
||||||
|
eMBMasterEventType eTempEvent = eEvent;
|
||||||
|
|
||||||
|
if( (BOOL)xPortInIsrContext() == TRUE )
|
||||||
|
{
|
||||||
|
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||||
|
BaseType_t xResult = xEventGroupSetBitsFromISR( xEventGroupMasterHdl,
|
||||||
|
(EventBits_t) eTempEvent,
|
||||||
|
&xHigherPriorityTaskWoken );
|
||||||
|
// Was the message posted successfully?
|
||||||
|
if( xResult == pdPASS ) {
|
||||||
|
// If xHigherPriorityTaskWoken is now set to pdTRUE
|
||||||
|
// then a context switch should be requested.
|
||||||
|
if (xHigherPriorityTaskWoken) portYIELD_FROM_ISR();
|
||||||
|
bStatus = TRUE;
|
||||||
|
} else {
|
||||||
|
bStatus = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Set event bits if the function is called from task
|
||||||
|
// The return result is not checked here because
|
||||||
|
// It might be that event bit was cleared automatically as a
|
||||||
|
// task that was waiting for the bit was removed from the Blocked state.
|
||||||
|
(void) xEventGroupSetBits( xEventGroupMasterHdl, (EventBits_t)eTempEvent );
|
||||||
|
bStatus = TRUE;
|
||||||
|
}
|
||||||
|
return bStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL
|
||||||
|
xMBMasterPortEventGet( eMBMasterEventType * eEvent)
|
||||||
|
{
|
||||||
|
EventBits_t uxBits;
|
||||||
|
BOOL xEventHappened = FALSE;
|
||||||
|
uxBits = xEventGroupWaitBits(
|
||||||
|
xEventGroupMasterHdl, // The event group being tested.
|
||||||
|
MB_EVENT_POLL_MASK, // The bits within the event group to wait for.
|
||||||
|
pdTRUE, // Masked bits should be cleared before returning.
|
||||||
|
pdFALSE, // Don't wait for both bits, either bit will do.
|
||||||
|
portMAX_DELAY); // Wait forever for either bit to be set.
|
||||||
|
|
||||||
|
// Check if poll event is correct
|
||||||
|
if (uxBits & MB_EVENT_POLL_MASK) {
|
||||||
|
*eEvent = (eMBMasterEventType)(uxBits);
|
||||||
|
xEventHappened = TRUE;
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(MB_PORT_TAG,"%s: Incorrect event triggered.", __func__);
|
||||||
|
xEventHappened = FALSE;
|
||||||
|
}
|
||||||
|
return xEventHappened;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function is initialize the OS resource for modbus master.
|
||||||
|
void vMBMasterOsResInit( void )
|
||||||
|
{
|
||||||
|
xSemaphorMasterHdl = xSemaphoreCreateBinary();
|
||||||
|
if (xSemaphorMasterHdl == NULL)
|
||||||
|
{
|
||||||
|
ESP_LOGE(MB_PORT_TAG,"%s: OS semaphore create error.", __func__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is take Mobus Master running resource.
|
||||||
|
* Note:The resource is define by Operating System.
|
||||||
|
*
|
||||||
|
* @param lTimeOut the waiting time.
|
||||||
|
*
|
||||||
|
* @return resource take result
|
||||||
|
*/
|
||||||
|
BOOL xMBMasterRunResTake( LONG lTimeOut )
|
||||||
|
{
|
||||||
|
BOOL xResult = FALSE;
|
||||||
|
BaseType_t xStatus = pdTRUE;
|
||||||
|
|
||||||
|
// If waiting time is -1. It will wait forever
|
||||||
|
xStatus = xSemaphoreTake(xSemaphorMasterHdl, lTimeOut );
|
||||||
|
if (xStatus == pdTRUE) {
|
||||||
|
xResult = TRUE;
|
||||||
|
}
|
||||||
|
return xResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is release Mobus Master running resource.
|
||||||
|
* Note:The resource is define by Operating System.If you not use OS this function can be empty.
|
||||||
|
*/
|
||||||
|
void vMBMasterRunResRelease( void )
|
||||||
|
{
|
||||||
|
BaseType_t xStatus = pdTRUE;
|
||||||
|
xStatus = xSemaphoreGive(xSemaphorMasterHdl);
|
||||||
|
if (xStatus != pdTRUE) {
|
||||||
|
ESP_LOGE(MB_PORT_TAG,"%s: resource release failure.", __func__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is modbus master respond timeout error process callback function.
|
||||||
|
* @note There functions will block modbus master poll while execute OS waiting.
|
||||||
|
*
|
||||||
|
* @param ucDestAddress destination salve address
|
||||||
|
* @param pucPDUData PDU buffer data
|
||||||
|
* @param ucPDULength PDU buffer length
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void vMBMasterErrorCBRespondTimeout(UCHAR ucDestAddress, const UCHAR* pucPDUData, USHORT ucPDULength)
|
||||||
|
{
|
||||||
|
BOOL ret = xMBMasterPortEventPost(EV_MASTER_ERROR_RESPOND_TIMEOUT);
|
||||||
|
if(ret != TRUE) {
|
||||||
|
ESP_LOGE(MB_PORT_TAG, "Post event 'EV_MASTER_ERROR_RESPOND_TIMEOUT' failed!!!");
|
||||||
|
}
|
||||||
|
ESP_LOGD(MB_PORT_TAG,"%s:Callback respond timeout.", __func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is modbus master receive data error process callback function.
|
||||||
|
* @note There functions will block modbus master poll while execute OS waiting.
|
||||||
|
*
|
||||||
|
* @param ucDestAddress destination salve address
|
||||||
|
* @param pucPDUData PDU buffer data
|
||||||
|
* @param ucPDULength PDU buffer length
|
||||||
|
*/
|
||||||
|
void vMBMasterErrorCBReceiveData(UCHAR ucDestAddress, const UCHAR* pucPDUData, USHORT ucPDULength)
|
||||||
|
{
|
||||||
|
BOOL ret = xMBMasterPortEventPost(EV_MASTER_ERROR_RECEIVE_DATA);
|
||||||
|
if(ret != TRUE) {
|
||||||
|
ESP_LOGE(MB_PORT_TAG,"xMBMasterPortEventPost event 'EV_MASTER_ERROR_RECEIVE_DATA' failed!!!");
|
||||||
|
}
|
||||||
|
ESP_LOGD(MB_PORT_TAG,"%s:Callback receive data timeout failure.", __func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is modbus master execute function error process callback function.
|
||||||
|
* @note There functions will block modbus master poll while execute OS waiting.
|
||||||
|
* So,for real-time of system.Do not execute too much waiting process.
|
||||||
|
*
|
||||||
|
* @param ucDestAddress destination salve address
|
||||||
|
* @param pucPDUData PDU buffer data
|
||||||
|
* @param ucPDULength PDU buffer length
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void vMBMasterErrorCBExecuteFunction(UCHAR ucDestAddress, const UCHAR* pucPDUData, USHORT ucPDULength)
|
||||||
|
{
|
||||||
|
BOOL ret = xMBMasterPortEventPost(EV_MASTER_ERROR_EXECUTE_FUNCTION);
|
||||||
|
if(ret != TRUE) {
|
||||||
|
ESP_LOGE(MB_PORT_TAG,"xMBMasterPortEventPost event 'EV_MASTER_ERROR_EXECUTE_FUNCTION' failed!!!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is modbus master request process success callback function.
|
||||||
|
* @note There functions will block modbus master poll while execute OS waiting.
|
||||||
|
* So,for real-time of system. Do not execute too much waiting process.
|
||||||
|
*/
|
||||||
|
void vMBMasterCBRequestSuccess( void ) {
|
||||||
|
/**
|
||||||
|
* @note This code is use OS's event mechanism for modbus master protocol stack.
|
||||||
|
* If you don't use OS, you can change it.
|
||||||
|
*/
|
||||||
|
BOOL ret = xMBMasterPortEventPost(EV_MASTER_PROCESS_SUCCESS);
|
||||||
|
if (ret != TRUE) {
|
||||||
|
ESP_LOGE(MB_PORT_TAG,"xMBMasterPortEventPost event 'EV_MASTER_PROCESS_SUCCESS' failed!!!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is wait for modbus master request finish and return result.
|
||||||
|
* Waiting result include request process success, request respond timeout,
|
||||||
|
* receive data error and execute function error.You can use the above callback function.
|
||||||
|
* @note If you are use OS, you can use OS's event mechanism. Otherwise you have to run
|
||||||
|
* much user custom delay for waiting.
|
||||||
|
*
|
||||||
|
* @return request error code
|
||||||
|
*/
|
||||||
|
eMBMasterReqErrCode eMBMasterWaitRequestFinish( void ) {
|
||||||
|
eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
|
||||||
|
eMBMasterEventType xRecvedEvent;
|
||||||
|
|
||||||
|
EventBits_t uxBits = xEventGroupWaitBits( xEventGroupMasterHdl, // The event group being tested.
|
||||||
|
MB_EVENT_REQ_MASK, // The bits within the event group to wait for.
|
||||||
|
pdTRUE, // Masked bits should be cleared before returning.
|
||||||
|
pdFALSE, // Don't wait for both bits, either bit will do.
|
||||||
|
portMAX_DELAY ); // Wait forever for either bit to be set.
|
||||||
|
xRecvedEvent = (eMBMasterEventType)(uxBits);
|
||||||
|
if (xRecvedEvent) {
|
||||||
|
ESP_LOGD(MB_PORT_TAG,"%s: returned event = 0x%x", __func__, xRecvedEvent);
|
||||||
|
if (!(xRecvedEvent & MB_EVENT_REQ_MASK)) {
|
||||||
|
// if we wait for certain event bits but get other then set in mask
|
||||||
|
ESP_LOGE(MB_PORT_TAG,"%s: incorrect event set = 0x%x", __func__, xRecvedEvent);
|
||||||
|
}
|
||||||
|
if MB_CHECK_EVENT(xRecvedEvent, EV_MASTER_PROCESS_SUCCESS) {
|
||||||
|
eErrStatus = MB_MRE_NO_ERR;
|
||||||
|
} else if MB_CHECK_EVENT(xRecvedEvent, EV_MASTER_ERROR_RESPOND_TIMEOUT){
|
||||||
|
eErrStatus = MB_MRE_TIMEDOUT;
|
||||||
|
} else if MB_CHECK_EVENT(xRecvedEvent, EV_MASTER_ERROR_RECEIVE_DATA){
|
||||||
|
eErrStatus = MB_MRE_REV_DATA;
|
||||||
|
} else if MB_CHECK_EVENT(xRecvedEvent, EV_MASTER_ERROR_EXECUTE_FUNCTION){
|
||||||
|
eErrStatus = MB_MRE_EXE_FUN;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(MB_PORT_TAG,"%s: Incorrect event or timeout xRecvedEvent = 0x%x", __func__, xRecvedEvent);
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
return eErrStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vMBMasterPortEventClose(void)
|
||||||
|
{
|
||||||
|
vEventGroupDelete(xEventGroupMasterHdl);
|
||||||
|
vSemaphoreDelete(xSemaphorMasterHdl);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -1,3 +1,17 @@
|
|||||||
|
/* 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.
|
||||||
|
*/
|
||||||
/*
|
/*
|
||||||
* FreeModbus Libary: ESP32 Demo Application
|
* FreeModbus Libary: ESP32 Demo Application
|
||||||
* Copyright (C) 2010 Christian Walter <cwalter@embedded-solutions.at>
|
* Copyright (C) 2010 Christian Walter <cwalter@embedded-solutions.at>
|
||||||
@ -36,12 +50,12 @@
|
|||||||
/* ----------------------- Modbus includes ----------------------------------*/
|
/* ----------------------- Modbus includes ----------------------------------*/
|
||||||
#include "mb.h"
|
#include "mb.h"
|
||||||
#include "mbport.h"
|
#include "mbport.h"
|
||||||
#include "sys/lock.h"
|
#include "port_serial_slave.h"
|
||||||
|
|
||||||
/* ----------------------- Modbus includes ----------------------------------*/
|
/* ----------------------- Modbus includes ----------------------------------*/
|
||||||
|
|
||||||
/* ----------------------- Variables ----------------------------------------*/
|
/* ----------------------- Variables ----------------------------------------*/
|
||||||
static _lock_t s_port_lock;
|
static UCHAR ucPortMode = 0;
|
||||||
|
|
||||||
/* ----------------------- Start implementation -----------------------------*/
|
/* ----------------------- Start implementation -----------------------------*/
|
||||||
|
|
||||||
@ -52,16 +66,20 @@ bMBPortIsWithinException( void )
|
|||||||
return bIsWithinException;
|
return bIsWithinException;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void
|
/* ----------------------- Start implementation -----------------------------*/
|
||||||
vMBPortEnterCritical( void )
|
|
||||||
|
UCHAR
|
||||||
|
ucMBPortGetMode( void )
|
||||||
{
|
{
|
||||||
_lock_acquire(&s_port_lock);
|
return ucPortMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void
|
void
|
||||||
vMBPortExitCritical( void )
|
vMBPortSetMode( UCHAR ucMode )
|
||||||
{
|
{
|
||||||
_lock_release(&s_port_lock);
|
ENTER_CRITICAL_SECTION();
|
||||||
|
ucPortMode = ucMode;
|
||||||
|
EXIT_CRITICAL_SECTION();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
87
components/freemodbus/port/portother_m.c
Normal file
87
components/freemodbus/port/portother_m.c
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* FreeModbus Libary: ESP32 Demo Application
|
||||||
|
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* File: $Id: portother.c,v 1.1 2010/06/06 13:07:20 wolti Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* ----------------------- System includes ----------------------------------*/
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/task.h>
|
||||||
|
#include <freertos/semphr.h>
|
||||||
|
|
||||||
|
/* ----------------------- Modbus includes ----------------------------------*/
|
||||||
|
#include "mb_m.h"
|
||||||
|
#include "mbport.h"
|
||||||
|
|
||||||
|
/* ----------------------- Modbus includes ----------------------------------*/
|
||||||
|
|
||||||
|
/* ----------------------- Variables ----------------------------------------*/
|
||||||
|
static UCHAR ucPortMode = 0;
|
||||||
|
|
||||||
|
/* ----------------------- Start implementation -----------------------------*/
|
||||||
|
|
||||||
|
UCHAR
|
||||||
|
ucMBPortGetMode( void )
|
||||||
|
{
|
||||||
|
return ucPortMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
vMBPortSetMode( UCHAR ucMode )
|
||||||
|
{
|
||||||
|
ENTER_CRITICAL_SECTION();
|
||||||
|
ucPortMode = ucMode;
|
||||||
|
EXIT_CRITICAL_SECTION();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
vMBMasterPortClose( void )
|
||||||
|
{
|
||||||
|
extern void vMBMasterPortSerialClose( void );
|
||||||
|
extern void vMBMasterPortTimerClose( void );
|
||||||
|
extern void vMBMasterPortEventClose( void );
|
||||||
|
vMBMasterPortSerialClose( );
|
||||||
|
vMBMasterPortTimerClose( );
|
||||||
|
vMBMasterPortEventClose( );
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,17 @@
|
|||||||
|
/* 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.
|
||||||
|
*/
|
||||||
/*
|
/*
|
||||||
* FreeModbus Libary: ESP32 Port Demo Application
|
* FreeModbus Libary: ESP32 Port Demo Application
|
||||||
* Copyright (C) 2010 Christian Walter <cwalter@embedded-solutions.at>
|
* Copyright (C) 2010 Christian Walter <cwalter@embedded-solutions.at>
|
||||||
@ -39,6 +53,7 @@
|
|||||||
#include "mb.h"
|
#include "mb.h"
|
||||||
#include "mbport.h"
|
#include "mbport.h"
|
||||||
#include "sdkconfig.h" // for KConfig options
|
#include "sdkconfig.h" // for KConfig options
|
||||||
|
#include "port_serial_slave.h"
|
||||||
|
|
||||||
// Definitions of UART default pin numbers
|
// Definitions of UART default pin numbers
|
||||||
#define MB_UART_RXD (CONFIG_MB_UART_RXD)
|
#define MB_UART_RXD (CONFIG_MB_UART_RXD)
|
||||||
@ -231,7 +246,7 @@ BOOL xMBPortSerialInit(UCHAR ucPORT, ULONG ulBaudRate,
|
|||||||
FALSE, "mb config failure, uart_param_config() returned (0x%x).", (uint32_t)xErr);
|
FALSE, "mb config failure, uart_param_config() returned (0x%x).", (uint32_t)xErr);
|
||||||
// Install UART driver, and get the queue.
|
// Install UART driver, and get the queue.
|
||||||
xErr = uart_driver_install(ucUartNumber, MB_SERIAL_BUF_SIZE, MB_SERIAL_BUF_SIZE,
|
xErr = uart_driver_install(ucUartNumber, MB_SERIAL_BUF_SIZE, MB_SERIAL_BUF_SIZE,
|
||||||
MB_QUEUE_LENGTH, &xMbUartQueue, ESP_INTR_FLAG_LOWMED);
|
MB_QUEUE_LENGTH, &xMbUartQueue, ESP_INTR_FLAG_LEVEL3);
|
||||||
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
||||||
"mb serial driver failure, uart_driver_install() returned (0x%x).", (uint32_t)xErr);
|
"mb serial driver failure, uart_driver_install() returned (0x%x).", (uint32_t)xErr);
|
||||||
#ifndef MB_TIMER_PORT_ENABLED
|
#ifndef MB_TIMER_PORT_ENABLED
|
||||||
|
291
components/freemodbus/port/portserial_m.c
Normal file
291
components/freemodbus/port/portserial_m.c
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* FreeModbus Libary: ESP32 Port Demo Application
|
||||||
|
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* File: $Id: portserial.c,v 1.60 2013/08/13 15:07:05 Armink add Master Functions $
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "port.h"
|
||||||
|
|
||||||
|
/* ----------------------- Modbus includes ----------------------------------*/
|
||||||
|
#include "mb_m.h"
|
||||||
|
#include "mbport.h"
|
||||||
|
#include "mbrtu.h"
|
||||||
|
#include "mbconfig.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "driver/uart.h"
|
||||||
|
#include "soc/dport_access.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "freertos/queue.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#include "port_serial_master.h"
|
||||||
|
|
||||||
|
/* ----------------------- Defines ------------------------------------------*/
|
||||||
|
|
||||||
|
#define MB_BAUD_RATE_DEFAULT (115200)
|
||||||
|
#define MB_QUEUE_LENGTH (CONFIG_MB_QUEUE_LENGTH)
|
||||||
|
|
||||||
|
#define MB_SERIAL_TASK_PRIO (CONFIG_MB_SERIAL_TASK_PRIO)
|
||||||
|
#define MB_SERIAL_TASK_STACK_SIZE (CONFIG_MB_SERIAL_TASK_STACK_SIZE)
|
||||||
|
#define MB_SERIAL_TOUT (3) // 3.5*8 = 28 ticks, TOUT=3 -> ~24..33 ticks
|
||||||
|
|
||||||
|
// Set buffer size for transmission
|
||||||
|
#define MB_SERIAL_BUF_SIZE (CONFIG_MB_SERIAL_BUF_SIZE)
|
||||||
|
#define MB_SERIAL_TX_TOUT_MS (100)
|
||||||
|
#define MB_SERIAL_TX_TOUT_TICKS pdMS_TO_TICKS(MB_SERIAL_TX_TOUT_MS) // timeout for transmission
|
||||||
|
|
||||||
|
/* ----------------------- Static variables ---------------------------------*/
|
||||||
|
static const CHAR *TAG = "MB_MASTER_SERIAL";
|
||||||
|
|
||||||
|
// A queue to handle UART event.
|
||||||
|
static QueueHandle_t xMbUartQueue;
|
||||||
|
static TaskHandle_t xMbTaskHandle;
|
||||||
|
|
||||||
|
// The UART hardware port number
|
||||||
|
static UCHAR ucUartNumber = UART_NUM_2;
|
||||||
|
|
||||||
|
static BOOL bRxStateEnabled = FALSE; // Receiver enabled flag
|
||||||
|
static BOOL bTxStateEnabled = FALSE; // Transmitter enabled flag
|
||||||
|
|
||||||
|
static UCHAR ucBuffer[MB_SERIAL_BUF_SIZE]; // Temporary buffer to transfer received data to modbus stack
|
||||||
|
static USHORT uiRxBufferPos = 0; // position in the receiver buffer
|
||||||
|
|
||||||
|
void vMBMasterPortSerialEnable(BOOL bRxEnable, BOOL bTxEnable)
|
||||||
|
{
|
||||||
|
// This function can be called from xMBRTUTransmitFSM() of different task
|
||||||
|
if (bTxEnable) {
|
||||||
|
bTxStateEnabled = TRUE;
|
||||||
|
} else {
|
||||||
|
bTxStateEnabled = FALSE;
|
||||||
|
}
|
||||||
|
if (bRxEnable) {
|
||||||
|
bRxStateEnabled = TRUE;
|
||||||
|
vTaskResume(xMbTaskHandle); // Resume receiver task
|
||||||
|
} else {
|
||||||
|
vTaskSuspend(xMbTaskHandle); // Block receiver task
|
||||||
|
bRxStateEnabled = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vMBMasterPortSerialRxPoll(size_t xEventSize)
|
||||||
|
{
|
||||||
|
USHORT usLength;
|
||||||
|
|
||||||
|
if (bRxStateEnabled) {
|
||||||
|
if (xEventSize > 0) {
|
||||||
|
xEventSize = (xEventSize > MB_SERIAL_BUF_SIZE) ? MB_SERIAL_BUF_SIZE : xEventSize;
|
||||||
|
// Get received packet into Rx buffer
|
||||||
|
usLength = uart_read_bytes(ucUartNumber, &ucBuffer[0], xEventSize, portMAX_DELAY);
|
||||||
|
uiRxBufferPos = 0;
|
||||||
|
for(USHORT usCnt = 0; usCnt < usLength; usCnt++ ) {
|
||||||
|
// Call the Modbus stack callback function and let it fill the stack buffers.
|
||||||
|
( void )pxMBMasterFrameCBByteReceived(); // calls callback xMBRTUReceiveFSM()
|
||||||
|
}
|
||||||
|
// The buffer is transferred into Modbus stack and is not needed here any more
|
||||||
|
uart_flush_input(ucUartNumber);
|
||||||
|
ESP_LOGD(TAG, "RX_T35_timeout: %d(bytes in buffer)\n", (uint32_t)usLength);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "%s: bRxState disabled but junk data (%d bytes) received. ", __func__, (uint16_t)xEventSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL xMBMasterPortSerialTxPoll()
|
||||||
|
{
|
||||||
|
BOOL bStatus = FALSE;
|
||||||
|
USHORT usCount = 0;
|
||||||
|
BOOL bNeedPoll = FALSE;
|
||||||
|
|
||||||
|
if( bTxStateEnabled ) {
|
||||||
|
// Continue while all response bytes put in buffer or out of buffer
|
||||||
|
while((bNeedPoll == FALSE) && (usCount++ < MB_SERIAL_BUF_SIZE)) {
|
||||||
|
// Calls the modbus stack callback function to let it fill the UART transmit buffer.
|
||||||
|
bNeedPoll = pxMBMasterFrameCBTransmitterEmpty( ); // calls callback xMBRTUTransmitFSM();
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "MB_TX_buffer sent: (%d) bytes.", (uint16_t)(usCount - 1));
|
||||||
|
// Waits while UART sending the packet
|
||||||
|
esp_err_t xTxStatus = uart_wait_tx_done(ucUartNumber, MB_SERIAL_TX_TOUT_TICKS);
|
||||||
|
bTxStateEnabled = FALSE;
|
||||||
|
MB_PORT_CHECK((xTxStatus == ESP_OK), FALSE, "mb serial sent buffer failure.");
|
||||||
|
bStatus = TRUE;
|
||||||
|
}
|
||||||
|
return bStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
// UART receive event task
|
||||||
|
static void vUartTask(void* pvParameters)
|
||||||
|
{
|
||||||
|
uart_event_t xEvent;
|
||||||
|
for(;;) {
|
||||||
|
if (xQueueReceive(xMbUartQueue, (void*)&xEvent, portMAX_DELAY) == pdTRUE) { // portMAX_DELAY
|
||||||
|
ESP_LOGD(TAG, "MB_uart[%d] event:", ucUartNumber);
|
||||||
|
switch(xEvent.type) {
|
||||||
|
//Event of UART receiving data
|
||||||
|
case UART_DATA:
|
||||||
|
ESP_LOGD(TAG,"Receive data, len: %d.", xEvent.size);
|
||||||
|
// Read received data and send it to modbus stack
|
||||||
|
vMBMasterPortSerialRxPoll(xEvent.size);
|
||||||
|
break;
|
||||||
|
//Event of HW FIFO overflow detected
|
||||||
|
case UART_FIFO_OVF:
|
||||||
|
ESP_LOGD(TAG, "hw fifo overflow.");
|
||||||
|
xQueueReset(xMbUartQueue);
|
||||||
|
break;
|
||||||
|
//Event of UART ring buffer full
|
||||||
|
case UART_BUFFER_FULL:
|
||||||
|
ESP_LOGD(TAG, "ring buffer full.");
|
||||||
|
xQueueReset(xMbUartQueue);
|
||||||
|
uart_flush_input(ucUartNumber);
|
||||||
|
break;
|
||||||
|
//Event of UART RX break detected
|
||||||
|
case UART_BREAK:
|
||||||
|
ESP_LOGD(TAG, "uart rx break.");
|
||||||
|
break;
|
||||||
|
//Event of UART parity check error
|
||||||
|
case UART_PARITY_ERR:
|
||||||
|
ESP_LOGD(TAG, "uart parity error.");
|
||||||
|
break;
|
||||||
|
//Event of UART frame error
|
||||||
|
case UART_FRAME_ERR:
|
||||||
|
ESP_LOGD(TAG, "uart frame error.");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ESP_LOGD(TAG, "uart event type: %d.", xEvent.type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------- Start implementation -----------------------------*/
|
||||||
|
BOOL xMBMasterPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
|
||||||
|
{
|
||||||
|
esp_err_t xErr = ESP_OK;
|
||||||
|
MB_PORT_CHECK((eParity <= MB_PAR_EVEN), FALSE, "mb serial set parity failure.");
|
||||||
|
// Set communication port number
|
||||||
|
ucUartNumber = ucPORT;
|
||||||
|
// Configure serial communication parameters
|
||||||
|
UCHAR ucParity = UART_PARITY_DISABLE;
|
||||||
|
UCHAR ucData = UART_DATA_8_BITS;
|
||||||
|
switch(eParity){
|
||||||
|
case MB_PAR_NONE:
|
||||||
|
ucParity = UART_PARITY_DISABLE;
|
||||||
|
break;
|
||||||
|
case MB_PAR_ODD:
|
||||||
|
ucParity = UART_PARITY_ODD;
|
||||||
|
break;
|
||||||
|
case MB_PAR_EVEN:
|
||||||
|
ucParity = UART_PARITY_EVEN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch(ucDataBits){
|
||||||
|
case 5:
|
||||||
|
ucData = UART_DATA_5_BITS;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
ucData = UART_DATA_6_BITS;
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
ucData = UART_DATA_7_BITS;
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
ucData = UART_DATA_8_BITS;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ucData = UART_DATA_8_BITS;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
uart_config_t xUartConfig = {
|
||||||
|
.baud_rate = ulBaudRate,
|
||||||
|
.data_bits = ucData,
|
||||||
|
.parity = ucParity,
|
||||||
|
.stop_bits = UART_STOP_BITS_1,
|
||||||
|
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
||||||
|
.rx_flow_ctrl_thresh = 2,
|
||||||
|
};
|
||||||
|
// Set UART config
|
||||||
|
xErr = uart_param_config(ucUartNumber, &xUartConfig);
|
||||||
|
MB_PORT_CHECK((xErr == ESP_OK),
|
||||||
|
FALSE, "mb config failure, uart_param_config() returned (0x%x).", (uint32_t)xErr);
|
||||||
|
// Install UART driver, and get the queue.
|
||||||
|
xErr = uart_driver_install(ucUartNumber, MB_SERIAL_BUF_SIZE, MB_SERIAL_BUF_SIZE,
|
||||||
|
MB_QUEUE_LENGTH, &xMbUartQueue, ESP_INTR_FLAG_LEVEL3);
|
||||||
|
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
||||||
|
"mb serial driver failure, uart_driver_install() returned (0x%x).", (uint32_t)xErr);
|
||||||
|
// Set timeout for TOUT interrupt (T3.5 modbus time)
|
||||||
|
xErr = uart_set_rx_timeout(ucUartNumber, MB_SERIAL_TOUT);
|
||||||
|
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
||||||
|
"mb serial set rx timeout failure, uart_set_rx_timeout() returned (0x%x).", (uint32_t)xErr);
|
||||||
|
// Create a task to handle UART events
|
||||||
|
BaseType_t xStatus = xTaskCreate(vUartTask, "uart_queue_task", MB_SERIAL_TASK_STACK_SIZE,
|
||||||
|
NULL, MB_SERIAL_TASK_PRIO, &xMbTaskHandle);
|
||||||
|
if (xStatus != pdPASS) {
|
||||||
|
vTaskDelete(xMbTaskHandle);
|
||||||
|
// Force exit from function with failure
|
||||||
|
MB_PORT_CHECK(FALSE, FALSE,
|
||||||
|
"mb stack serial task creation error. xTaskCreate() returned (0x%x).",
|
||||||
|
(uint32_t)xStatus);
|
||||||
|
} else {
|
||||||
|
vTaskSuspend(xMbTaskHandle); // Suspend serial task while stack is not started
|
||||||
|
}
|
||||||
|
uiRxBufferPos = 0;
|
||||||
|
ESP_LOGD(MB_PORT_TAG,"%s Init serial.", __func__);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vMBMasterPortSerialClose()
|
||||||
|
{
|
||||||
|
(void)vTaskDelete(xMbTaskHandle);
|
||||||
|
ESP_ERROR_CHECK(uart_driver_delete(ucUartNumber));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL xMBMasterPortSerialPutByte(CHAR ucByte)
|
||||||
|
{
|
||||||
|
// Send one byte to UART transmission buffer
|
||||||
|
// This function is called by Modbus stack
|
||||||
|
UCHAR ucLength = uart_write_bytes(ucUartNumber, &ucByte, 1);
|
||||||
|
return (ucLength == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get one byte from intermediate RX buffer
|
||||||
|
BOOL xMBMasterPortSerialGetByte(CHAR* pucByte)
|
||||||
|
{
|
||||||
|
assert(pucByte != NULL);
|
||||||
|
MB_PORT_CHECK((uiRxBufferPos < MB_SERIAL_BUF_SIZE),
|
||||||
|
FALSE, "mb stack serial get byte failure.");
|
||||||
|
*pucByte = ucBuffer[uiRxBufferPos];
|
||||||
|
uiRxBufferPos++;
|
||||||
|
return TRUE;
|
||||||
|
}
|
@ -1,3 +1,17 @@
|
|||||||
|
/* 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.
|
||||||
|
*/
|
||||||
/*
|
/*
|
||||||
* FreeModbus Libary: ESP32 Port Demo Application
|
* FreeModbus Libary: ESP32 Port Demo Application
|
||||||
* Copyright (C) 2010 Christian Walter <cwalter@embedded-solutions.at>
|
* Copyright (C) 2010 Christian Walter <cwalter@embedded-solutions.at>
|
||||||
@ -35,6 +49,7 @@
|
|||||||
#include "mbport.h"
|
#include "mbport.h"
|
||||||
#include "driver/timer.h"
|
#include "driver/timer.h"
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
|
#include "port_serial_slave.h"
|
||||||
|
|
||||||
#ifdef CONFIG_MB_TIMER_PORT_ENABLED
|
#ifdef CONFIG_MB_TIMER_PORT_ENABLED
|
||||||
|
|
||||||
|
201
components/freemodbus/port/porttimer_m.c
Normal file
201
components/freemodbus/port/porttimer_m.c
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* FreeModbus Libary: ESP32 Port Demo Application
|
||||||
|
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* File: $Id: porttimer_m.c,v 1.60 2013/08/13 15:07:05 Armink add Master Functions$
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* ----------------------- Platform includes --------------------------------*/
|
||||||
|
#include "port.h"
|
||||||
|
|
||||||
|
/* ----------------------- Modbus includes ----------------------------------*/
|
||||||
|
#include "mb_m.h"
|
||||||
|
#include "mbport.h"
|
||||||
|
#include "port_serial_master.h"
|
||||||
|
|
||||||
|
#define MB_US50_FREQ (20000) // 20kHz 1/20000 = 50mks
|
||||||
|
#define MB_TICK_TIME_US (50) // 50uS = one tick for timer
|
||||||
|
|
||||||
|
#define MB_TIMER_PRESCALLER ((TIMER_BASE_CLK / MB_US50_FREQ) - 1);
|
||||||
|
#define MB_TIMER_SCALE (TIMER_BASE_CLK / TIMER_DIVIDER)
|
||||||
|
#define MB_TIMER_DIVIDER ((TIMER_BASE_CLK / 1000000UL) * MB_TICK_TIME_US - 1) // divider for 50uS
|
||||||
|
#define MB_TIMER_WITH_RELOAD (1)
|
||||||
|
|
||||||
|
// Timer group and timer number to measure time (configurable in KConfig)
|
||||||
|
#define MB_TIMER_INDEX CONFIG_MB_TIMER_INDEX
|
||||||
|
#define MB_TIMER_GROUP CONFIG_MB_TIMER_GROUP
|
||||||
|
|
||||||
|
#define MB_TIMER_IO_LED 0
|
||||||
|
|
||||||
|
/* ----------------------- Variables ----------------------------------------*/
|
||||||
|
static USHORT usT35TimeOut50us;
|
||||||
|
|
||||||
|
static const USHORT usTimerIndex = MB_TIMER_INDEX; // Initialize Modbus Timer index used by stack,
|
||||||
|
static const USHORT usTimerGroupIndex = MB_TIMER_GROUP; // Timer group index used by stack
|
||||||
|
|
||||||
|
static timg_dev_t *MB_TG[2] = { &TIMERG0, &TIMERG1 };
|
||||||
|
|
||||||
|
/* ----------------------- static functions ---------------------------------*/
|
||||||
|
|
||||||
|
static void IRAM_ATTR vTimerGroupIsr(void *param)
|
||||||
|
{
|
||||||
|
// Retrieve the interrupt status and the counter value
|
||||||
|
// from the timer that reported the interrupt
|
||||||
|
uint32_t intr_status = MB_TG[usTimerGroupIndex]->int_st_timers.val;
|
||||||
|
if (intr_status & BIT(usTimerIndex)) {
|
||||||
|
MB_TG[usTimerGroupIndex]->int_clr_timers.val |= BIT(usTimerIndex);
|
||||||
|
MB_TG[usTimerGroupIndex]->hw_timer[usTimerIndex].update = 1;
|
||||||
|
(void)pxMBMasterPortCBTimerExpired(); // Timer expired callback function
|
||||||
|
// Enable alarm
|
||||||
|
MB_TG[usTimerGroupIndex]->hw_timer[usTimerIndex].config.alarm_en = TIMER_ALARM_EN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------- Start implementation -----------------------------*/
|
||||||
|
BOOL xMBMasterPortTimersInit(USHORT usTimeOut50us)
|
||||||
|
{
|
||||||
|
MB_PORT_CHECK((usTimeOut50us > 0), FALSE,
|
||||||
|
"Modbus timeout discreet is incorrect.");
|
||||||
|
// Save timer reload value for Modbus T35 period
|
||||||
|
usT35TimeOut50us = usTimeOut50us;
|
||||||
|
esp_err_t xErr;
|
||||||
|
timer_config_t config;
|
||||||
|
config.alarm_en = TIMER_ALARM_EN;
|
||||||
|
config.auto_reload = MB_TIMER_WITH_RELOAD;
|
||||||
|
config.counter_dir = TIMER_COUNT_UP;
|
||||||
|
config.divider = MB_TIMER_PRESCALLER;
|
||||||
|
config.intr_type = TIMER_INTR_LEVEL;
|
||||||
|
config.counter_en = TIMER_PAUSE;
|
||||||
|
// Configure timer
|
||||||
|
xErr = timer_init(usTimerGroupIndex, usTimerIndex, &config);
|
||||||
|
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
||||||
|
"timer init failure, timer_init() returned (0x%x).", (uint32_t)xErr);
|
||||||
|
// Stop timer counter
|
||||||
|
xErr = timer_pause(usTimerGroupIndex, usTimerIndex);
|
||||||
|
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
||||||
|
"stop timer failure, timer_pause() returned (0x%x).", (uint32_t)xErr);
|
||||||
|
// Reset counter value
|
||||||
|
xErr = timer_set_counter_value(usTimerGroupIndex, usTimerIndex, 0x00000000ULL);
|
||||||
|
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
||||||
|
"timer set value failure, timer_set_counter_value() returned (0x%x).",
|
||||||
|
(uint32_t)xErr);
|
||||||
|
// wait3T5_us = 35 * 11 * 100000 / baud; // the 3.5T symbol time for baudrate
|
||||||
|
// Set alarm value for usTimeOut50us * 50uS
|
||||||
|
xErr = timer_set_alarm_value(usTimerGroupIndex, usTimerIndex, (uint32_t)(usTimeOut50us));
|
||||||
|
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
||||||
|
"failure to set alarm failure, timer_set_alarm_value() returned (0x%x).",
|
||||||
|
(uint32_t)xErr);
|
||||||
|
// Register ISR for timer
|
||||||
|
xErr = timer_isr_register(usTimerGroupIndex, usTimerIndex,
|
||||||
|
vTimerGroupIsr, NULL, ESP_INTR_FLAG_IRAM, NULL);
|
||||||
|
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
||||||
|
"timer set value failure, timer_isr_register() returned (0x%x).",
|
||||||
|
(uint32_t)xErr);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set alarm value for usTimerTimeOut50us * 50uS
|
||||||
|
static BOOL xMBMasterPortTimersEnable(USHORT usTimerTics50us)
|
||||||
|
{
|
||||||
|
MB_PORT_CHECK((usTimerTics50us > 0), FALSE,
|
||||||
|
"incorrect tick value for timer = (0x%x).",
|
||||||
|
(uint32_t)usTimerTics50us);
|
||||||
|
esp_err_t xErr;
|
||||||
|
xErr = timer_pause(usTimerGroupIndex, usTimerIndex); // stop timer
|
||||||
|
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
||||||
|
"timer pause failure, timer_pause() returned (0x%x).",
|
||||||
|
(uint32_t)xErr);
|
||||||
|
xErr = timer_set_counter_value(usTimerGroupIndex, usTimerIndex, 0ULL); // reset timer
|
||||||
|
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
||||||
|
"timer set counter failure, timer_set_counter_value() returned (0x%x).",
|
||||||
|
(uint32_t)xErr);
|
||||||
|
// Set alarm value to number of 50uS ticks
|
||||||
|
xErr = timer_set_alarm_value(usTimerGroupIndex, usTimerIndex,
|
||||||
|
(uint32_t)(usTimerTics50us));
|
||||||
|
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
||||||
|
"timer set alarm failure, timer_set_alarm_value() returned (0x%x).",
|
||||||
|
(uint32_t)xErr);
|
||||||
|
xErr = timer_enable_intr(usTimerGroupIndex, usTimerIndex);
|
||||||
|
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
||||||
|
"timer enable interrupt failure, timer_enable_intr() returned (0x%x).",
|
||||||
|
(uint32_t)xErr);
|
||||||
|
xErr = timer_start(usTimerGroupIndex, usTimerIndex); // start timer
|
||||||
|
MB_PORT_CHECK((xErr == ESP_OK), FALSE,
|
||||||
|
"timer start failure, timer_start() returned (0x%x).",
|
||||||
|
(uint32_t)xErr);
|
||||||
|
//ESP_LOGD(MB_PORT_TAG,"%s Init timer.", __func__);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vMBMasterPortTimersT35Enable()
|
||||||
|
{
|
||||||
|
USHORT usTimerTicks = usT35TimeOut50us;
|
||||||
|
|
||||||
|
// Set current timer mode, don't change it.
|
||||||
|
vMBMasterSetCurTimerMode(MB_TMODE_T35);
|
||||||
|
// Set timer period
|
||||||
|
(void)xMBMasterPortTimersEnable(usTimerTicks);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vMBMasterPortTimersConvertDelayEnable()
|
||||||
|
{
|
||||||
|
// Covert time in milliseconds into ticks
|
||||||
|
USHORT usTimerTicks = ((MB_MASTER_DELAY_MS_CONVERT * 1000) / MB_TICK_TIME_US);
|
||||||
|
|
||||||
|
// Set current timer mode
|
||||||
|
vMBMasterSetCurTimerMode(MB_TMODE_CONVERT_DELAY);
|
||||||
|
ESP_LOGD(MB_PORT_TAG,"%s Convert delay enable.", __func__);
|
||||||
|
(void)xMBMasterPortTimersEnable(usTimerTicks);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vMBMasterPortTimersRespondTimeoutEnable()
|
||||||
|
{
|
||||||
|
USHORT usTimerTicks = (MB_MASTER_TIMEOUT_MS_RESPOND * 1000 / MB_TICK_TIME_US);
|
||||||
|
|
||||||
|
vMBMasterSetCurTimerMode(MB_TMODE_RESPOND_TIMEOUT);
|
||||||
|
ESP_LOGD(MB_PORT_TAG,"%s Respond enable timeout.", __func__);
|
||||||
|
(void)xMBMasterPortTimersEnable(usTimerTicks);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vMBMasterPortTimersDisable()
|
||||||
|
{
|
||||||
|
// Stop timer and then reload timer counter value
|
||||||
|
ESP_ERROR_CHECK(timer_pause(usTimerGroupIndex, usTimerIndex));
|
||||||
|
ESP_ERROR_CHECK(timer_set_counter_value(usTimerGroupIndex, usTimerIndex, 0ULL));
|
||||||
|
// Disable timer interrupt
|
||||||
|
ESP_ERROR_CHECK(timer_disable_intr(usTimerGroupIndex, usTimerIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
void vMBMasterPortTimerClose()
|
||||||
|
{
|
||||||
|
ESP_ERROR_CHECK(timer_pause(usTimerGroupIndex, usTimerIndex));
|
||||||
|
ESP_ERROR_CHECK(timer_disable_intr(usTimerGroupIndex, usTimerIndex));
|
||||||
|
}
|
@ -0,0 +1,764 @@
|
|||||||
|
/* 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;
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
/* 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.h Modbus controller serial master implementation header file
|
||||||
|
|
||||||
|
#ifndef _MODBUS_SERIAL_CONTROLLER_MASTER
|
||||||
|
#define _MODBUS_SERIAL_CONTROLLER_MASTER
|
||||||
|
|
||||||
|
#include <stdint.h> // for standard int types definition
|
||||||
|
#include <stddef.h> // for NULL and std defines
|
||||||
|
#include "soc/soc.h" // for BITN definitions
|
||||||
|
#include "esp_err.h" // for esp_err_t
|
||||||
|
#include "esp_modbus_common.h" // for common defines
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize Modbus controller and stack
|
||||||
|
*
|
||||||
|
* @param[out] handler handler(pointer) to master data structure
|
||||||
|
* @return
|
||||||
|
* - ESP_OK Success
|
||||||
|
* - ESP_ERR_NO_MEM Parameter error
|
||||||
|
*/
|
||||||
|
esp_err_t mbc_serial_master_create(mb_port_type_t port_type, void** handler);
|
||||||
|
|
||||||
|
#endif // _MODBUS_SERIAL_CONTROLLER_MASTER
|
||||||
|
|
@ -0,0 +1,65 @@
|
|||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* FreeModbus Libary: ESP32 Port Demo Application
|
||||||
|
* Copyright (C) 2010 Christian Walter <cwalter@embedded-solutions.at>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* File: $Id: portother.c,v 1.1 2010/06/06 13:07:20 wolti Exp $
|
||||||
|
*/
|
||||||
|
#ifndef _PORT_SERIAL_MASTER_H
|
||||||
|
#define _PORT_SERIAL_MASTER_H
|
||||||
|
|
||||||
|
/* ----------------------- Platform includes --------------------------------*/
|
||||||
|
#include "driver/uart.h"
|
||||||
|
#include "driver/timer.h"
|
||||||
|
#include "esp_log.h" // for ESP_LOGE macro
|
||||||
|
#include "mb_m.h"
|
||||||
|
#include "port.h"
|
||||||
|
|
||||||
|
/* ----------------------- Defines ------------------------------------------*/
|
||||||
|
#ifdef __cplusplus
|
||||||
|
PR_BEGIN_EXTERN_C
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
void vMBPortSetMode( UCHAR ucMode );
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
PR_END_EXTERN_C
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
#endif
|
@ -1,145 +1,46 @@
|
|||||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
/* Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||||
//
|
*
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
// You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
//
|
*
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
//
|
*
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
// mbcontroller.c
|
// mbc_serial_slave.c
|
||||||
// Implementation of the modbus controller
|
// Implementation of the Modbus controller serial slave
|
||||||
// The modbus controller is responsible for processing of modbus packet and transfer data
|
|
||||||
// into parameter instance.
|
|
||||||
|
|
||||||
#include <sys/time.h> // for calculation of time stamp in milliseconds
|
#include <sys/time.h> // for calculation of time stamp in milliseconds
|
||||||
#include "esp_log.h" // for log_write
|
#include "esp_log.h" // for log_write
|
||||||
#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.h" // for mb types definition
|
#include "mb.h" // for mb types definition
|
||||||
#include "mbutils.h" // for mbutils functions definition for stack callback
|
#include "mbutils.h" // for mbutils functions definition for stack callback
|
||||||
#include "sdkconfig.h" // for KConfig values
|
#include "sdkconfig.h" // for KConfig values
|
||||||
#include "mbcontroller.h"
|
#include "esp_modbus_common.h" // for common defines
|
||||||
|
#include "esp_modbus_slave.h" // for public slave interface types
|
||||||
|
#include "mbc_slave.h" // for private slave interface types
|
||||||
|
#include "mbc_serial_slave.h" // for serial slave implementation definitions
|
||||||
|
#include "port_serial_slave.h"
|
||||||
|
|
||||||
static const char* TAG = "MB_CONTROLLER";
|
// Shared pointer to interface structure
|
||||||
|
static mb_slave_interface_t* mbs_interface_ptr = NULL; // &default_interface_inst;
|
||||||
#define MB_CHECK(a, ret_val, str, ...) \
|
|
||||||
if (!(a)) { \
|
|
||||||
ESP_LOGE(TAG, "%s(%u): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
|
|
||||||
return (ret_val); \
|
|
||||||
}
|
|
||||||
|
|
||||||
// The Macros below handle the endianness while transfer N byte data into buffer
|
|
||||||
#define _XFER_4_RD(dst, src) { \
|
|
||||||
*(uint8_t *)(dst)++ = *(uint8_t*)(src + 1); \
|
|
||||||
*(uint8_t *)(dst)++ = *(uint8_t*)(src + 0); \
|
|
||||||
*(uint8_t *)(dst)++ = *(uint8_t*)(src + 3); \
|
|
||||||
*(uint8_t *)(dst)++ = *(uint8_t*)(src + 2); \
|
|
||||||
(src) += 4; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define _XFER_2_RD(dst, src) { \
|
|
||||||
*(uint8_t *)(dst)++ = *(uint8_t *)(src + 1); \
|
|
||||||
*(uint8_t *)(dst)++ = *(uint8_t *)(src + 0); \
|
|
||||||
(src) += 2; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define _XFER_4_WR(dst, src) { \
|
|
||||||
*(uint8_t *)(dst + 1) = *(uint8_t *)(src)++; \
|
|
||||||
*(uint8_t *)(dst + 0) = *(uint8_t *)(src)++; \
|
|
||||||
*(uint8_t *)(dst + 3) = *(uint8_t *)(src)++; \
|
|
||||||
*(uint8_t *)(dst + 2) = *(uint8_t *)(src)++ ; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define _XFER_2_WR(dst, src) { \
|
|
||||||
*(uint8_t *)(dst + 1) = *(uint8_t *)(src)++; \
|
|
||||||
*(uint8_t *)(dst + 0) = *(uint8_t *)(src)++; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#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
|
|
||||||
|
|
||||||
// Event group parameters
|
|
||||||
static TaskHandle_t mb_controller_task_handle = NULL;
|
|
||||||
static EventGroupHandle_t mb_controller_event_group = NULL;
|
|
||||||
static QueueHandle_t mb_controller_notification_queue_handle = NULL;
|
|
||||||
|
|
||||||
static uint8_t mb_type = 0;
|
|
||||||
static uint8_t mb_address = 0;
|
|
||||||
static uint8_t mb_port = 0;
|
|
||||||
static uint32_t mb_speed = 0;
|
|
||||||
static uint16_t mb_parity = 0;
|
|
||||||
|
|
||||||
// This is array of Modbus address area descriptors
|
|
||||||
static mb_register_area_descriptor_t mb_area_descriptors[MB_PARAM_COUNT] = { 0 };
|
|
||||||
|
|
||||||
// The helper function to get time stamp in microseconds
|
|
||||||
static uint64_t get_time_stamp()
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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(mb_controller_notification_queue_handle, &par_info, MB_PAR_INFO_TOUT);
|
|
||||||
if (pdTRUE == status) {
|
|
||||||
ESP_LOGD(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(TAG, "Parameter queue is overflowed.");
|
|
||||||
}
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t send_param_access_notification(mb_event_group_t event)
|
|
||||||
{
|
|
||||||
esp_err_t err = ESP_FAIL;
|
|
||||||
mb_event_group_t bits = (mb_event_group_t)xEventGroupSetBits(mb_controller_event_group, (EventBits_t)event);
|
|
||||||
if (bits & event) {
|
|
||||||
ESP_LOGD(TAG, "The MB_REG_CHANGE_EVENT = 0x%.2x is set.", (uint8_t)event);
|
|
||||||
err = ESP_OK;
|
|
||||||
}
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Modbus task function
|
// Modbus task function
|
||||||
static void modbus_task(void *pvParameters) {
|
static void modbus_slave_task(void *pvParameters)
|
||||||
|
{
|
||||||
|
// Modbus interface must be initialized before start
|
||||||
|
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
|
||||||
|
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
|
||||||
|
|
||||||
|
MB_SLAVE_ASSERT(mbs_opts != NULL);
|
||||||
// Main Modbus stack processing cycle
|
// Main Modbus stack processing cycle
|
||||||
for (;;) {
|
for (;;) {
|
||||||
BaseType_t status = xEventGroupWaitBits(mb_controller_event_group,
|
BaseType_t status = xEventGroupWaitBits(mbs_opts->mbs_event_group,
|
||||||
(BaseType_t)(MB_EVENT_STACK_STARTED),
|
(BaseType_t)(MB_EVENT_STACK_STARTED),
|
||||||
pdFALSE, // do not clear bits
|
pdFALSE, // do not clear bits
|
||||||
pdFALSE,
|
pdFALSE,
|
||||||
@ -152,150 +53,192 @@ static void modbus_task(void *pvParameters) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Blocking function to get event on parameter group change for application task
|
// Setup Modbus controller parameters
|
||||||
mb_event_group_t mbcontroller_check_event(mb_event_group_t group)
|
static esp_err_t mbc_serial_slave_setup(void* comm_info)
|
||||||
{
|
{
|
||||||
assert(mb_controller_event_group != NULL);
|
MB_SLAVE_CHECK((mbs_interface_ptr != NULL),
|
||||||
BaseType_t status = xEventGroupWaitBits(mb_controller_event_group, (BaseType_t)group,
|
ESP_ERR_INVALID_STATE,
|
||||||
pdTRUE , pdFALSE, portMAX_DELAY);
|
"Slave interface is not correctly initialized.");
|
||||||
return (mb_event_group_t)status;
|
MB_SLAVE_CHECK((comm_info != NULL), ESP_ERR_INVALID_ARG,
|
||||||
}
|
"mb wrong communication settings.");
|
||||||
|
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
|
||||||
|
mb_slave_comm_info_t* comm_settings = (mb_slave_comm_info_t*)comm_info;
|
||||||
|
MB_SLAVE_CHECK(((comm_settings->mode == MB_MODE_RTU) || (comm_settings->mode == MB_MODE_ASCII)),
|
||||||
|
ESP_ERR_INVALID_ARG, "mb incorrect mode = (0x%x).",
|
||||||
|
(uint32_t)comm_settings->mode);
|
||||||
|
MB_SLAVE_CHECK((comm_settings->slave_addr <= MB_ADDRESS_MAX),
|
||||||
|
ESP_ERR_INVALID_ARG, "mb wrong slave address = (0x%x).",
|
||||||
|
(uint32_t)comm_settings->slave_addr);
|
||||||
|
MB_SLAVE_CHECK((comm_settings->port <= UART_NUM_2), ESP_ERR_INVALID_ARG,
|
||||||
|
"mb wrong port to set = (0x%x).", (uint32_t)comm_settings->port);
|
||||||
|
MB_SLAVE_CHECK((comm_settings->parity <= UART_PARITY_EVEN), ESP_ERR_INVALID_ARG,
|
||||||
|
"mb wrong parity option = (0x%x).", (uint32_t)comm_settings->parity);
|
||||||
|
|
||||||
esp_err_t mbcontroller_set_descriptor(const mb_register_area_descriptor_t descr_info)
|
// Set communication options of the controller
|
||||||
{
|
mbs_opts->mbs_comm = *(mb_communication_info_t*)comm_settings;
|
||||||
MB_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_CHECK((descr_info.address != NULL),
|
|
||||||
ESP_ERR_INVALID_ARG, "mb instance pointer is NULL.");
|
|
||||||
MB_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);
|
|
||||||
mb_area_descriptors[descr_info.type].type = descr_info.type;
|
|
||||||
mb_area_descriptors[descr_info.type].start_offset = descr_info.start_offset;
|
|
||||||
mb_area_descriptors[descr_info.type].address = (uint8_t*)descr_info.address;
|
|
||||||
mb_area_descriptors[descr_info.type].size = descr_info.size;
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialization of Modbus controller
|
|
||||||
esp_err_t mbcontroller_init(void) {
|
|
||||||
mb_type = MB_MODE_RTU;
|
|
||||||
mb_address = MB_DEVICE_ADDRESS;
|
|
||||||
mb_port = MB_UART_PORT;
|
|
||||||
mb_speed = MB_DEVICE_SPEED;
|
|
||||||
mb_parity = MB_PARITY_NONE;
|
|
||||||
|
|
||||||
// Initialization of active context of the modbus controller
|
|
||||||
BaseType_t status = 0;
|
|
||||||
// Parameter change notification queue
|
|
||||||
mb_controller_event_group = xEventGroupCreate();
|
|
||||||
MB_CHECK((mb_controller_event_group != NULL),
|
|
||||||
ESP_ERR_NO_MEM, "mb event group error.");
|
|
||||||
// Parameter change notification queue
|
|
||||||
mb_controller_notification_queue_handle = xQueueCreate(
|
|
||||||
MB_CONTROLLER_NOTIFY_QUEUE_SIZE,
|
|
||||||
sizeof(mb_param_info_t));
|
|
||||||
MB_CHECK((mb_controller_notification_queue_handle != NULL),
|
|
||||||
ESP_ERR_NO_MEM, "mb notify queue creation error.");
|
|
||||||
// Create modbus controller task
|
|
||||||
status = xTaskCreate((void*)&modbus_task,
|
|
||||||
"modbus_task",
|
|
||||||
MB_CONTROLLER_STACK_SIZE,
|
|
||||||
NULL,
|
|
||||||
MB_CONTROLLER_PRIORITY,
|
|
||||||
&mb_controller_task_handle);
|
|
||||||
if (status != pdPASS) {
|
|
||||||
vTaskDelete(mb_controller_task_handle);
|
|
||||||
MB_CHECK((status == pdPASS), ESP_ERR_NO_MEM,
|
|
||||||
"mb controller task creation error, xTaskCreate() returns (0x%x).",
|
|
||||||
(uint32_t)status);
|
|
||||||
}
|
|
||||||
assert(mb_controller_task_handle != NULL); // The task is created but handle is incorrect
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to get notification about parameter change from application task
|
|
||||||
esp_err_t mbcontroller_get_param_info(mb_param_info_t* reg_info, uint32_t timeout)
|
|
||||||
{
|
|
||||||
esp_err_t err = ESP_ERR_TIMEOUT;
|
|
||||||
MB_CHECK((mb_controller_notification_queue_handle != NULL),
|
|
||||||
ESP_ERR_INVALID_ARG, "mb queue handle is invalid.");
|
|
||||||
MB_CHECK((reg_info != NULL), ESP_ERR_INVALID_ARG, "mb register information is invalid.");
|
|
||||||
BaseType_t status = xQueueReceive(mb_controller_notification_queue_handle,
|
|
||||||
reg_info, pdMS_TO_TICKS(timeout));
|
|
||||||
if (status == pdTRUE) {
|
|
||||||
err = ESP_OK;
|
|
||||||
}
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start Modbus controller start function
|
// Start Modbus controller start function
|
||||||
esp_err_t mbcontroller_start(void)
|
static esp_err_t mbc_serial_slave_start(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 status = MB_EIO;
|
eMBErrorCode status = MB_EIO;
|
||||||
// Initialize Modbus stack using mbcontroller parameters
|
// Initialize Modbus stack using mbcontroller parameters
|
||||||
status = eMBInit((eMBMode)mb_type, (UCHAR)mb_address, (UCHAR)mb_port,
|
status = eMBInit((eMBMode)mbs_opts->mbs_comm.mode,
|
||||||
(ULONG)mb_speed, (eMBParity)mb_parity);
|
(UCHAR)mbs_opts->mbs_comm.slave_addr,
|
||||||
MB_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
|
(UCHAR)mbs_opts->mbs_comm.port,
|
||||||
|
(ULONG)mbs_opts->mbs_comm.baudrate,
|
||||||
|
(eMBParity)mbs_opts->mbs_comm.parity);
|
||||||
|
MB_SLAVE_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
|
||||||
"mb stack initialization failure, eMBInit() returns (0x%x).", status);
|
"mb stack initialization failure, eMBInit() returns (0x%x).", status);
|
||||||
#ifdef CONFIG_MB_CONTROLLER_SLAVE_ID_SUPPORT
|
#ifdef CONFIG_MB_CONTROLLER_SLAVE_ID_SUPPORT
|
||||||
status = eMBSetSlaveID(MB_SLAVE_ID_SHORT, TRUE, (UCHAR*)mb_slave_id, sizeof(mb_slave_id));
|
status = eMBSetSlaveID(MB_SLAVE_ID_SHORT, TRUE, (UCHAR*)mb_slave_id, sizeof(mb_slave_id));
|
||||||
MB_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE, "mb stack set slave ID failure.");
|
MB_SLAVE_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE, "mb stack set slave ID failure.");
|
||||||
#endif
|
#endif
|
||||||
status = eMBEnable();
|
status = eMBEnable();
|
||||||
MB_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
|
MB_SLAVE_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
|
||||||
"mb stack set slave ID failure, eMBEnable() returned (0x%x).", (uint32_t)status);
|
"mb stack set slave ID failure, eMBEnable() returned (0x%x).", (uint32_t)status);
|
||||||
// Set the mbcontroller start flag
|
// Set the mbcontroller start flag
|
||||||
EventBits_t flag = xEventGroupSetBits(mb_controller_event_group,
|
EventBits_t flag = xEventGroupSetBits(mbs_opts->mbs_event_group,
|
||||||
(EventBits_t)MB_EVENT_STACK_STARTED);
|
(EventBits_t)MB_EVENT_STACK_STARTED);
|
||||||
MB_CHECK((flag & MB_EVENT_STACK_STARTED),
|
MB_SLAVE_CHECK((flag & MB_EVENT_STACK_STARTED),
|
||||||
ESP_ERR_INVALID_STATE, "mb stack start event set error.");
|
ESP_ERR_INVALID_STATE, "mb stack start event set error.");
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modbus controller destroy function
|
// Modbus controller destroy function
|
||||||
esp_err_t mbcontroller_destroy(void)
|
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;
|
eMBErrorCode mb_error = MB_ENOERR;
|
||||||
// Stop polling by clearing correspondent bit in the event group
|
// Stop polling by clearing correspondent bit in the event group
|
||||||
EventBits_t flag = xEventGroupClearBits(mb_controller_event_group,
|
EventBits_t flag = xEventGroupClearBits(mbs_opts->mbs_event_group,
|
||||||
(EventBits_t)MB_EVENT_STACK_STARTED);
|
(EventBits_t)MB_EVENT_STACK_STARTED);
|
||||||
MB_CHECK((flag & MB_EVENT_STACK_STARTED),
|
MB_SLAVE_CHECK((flag & MB_EVENT_STACK_STARTED),
|
||||||
ESP_ERR_INVALID_STATE, "mb stack stop event failure.");
|
ESP_ERR_INVALID_STATE, "mb stack stop event failure.");
|
||||||
// Desable and then destroy the Modbus stack
|
// Disable and then destroy the Modbus stack
|
||||||
mb_error = eMBDisable();
|
mb_error = eMBDisable();
|
||||||
MB_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE, "mb stack disable failure.");
|
MB_SLAVE_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE, "mb stack disable failure.");
|
||||||
(void)vTaskDelete(mb_controller_task_handle);
|
(void)vTaskDelete(mbs_opts->mbs_task_handle);
|
||||||
(void)vQueueDelete(mb_controller_notification_queue_handle);
|
(void)vQueueDelete(mbs_opts->mbs_notification_queue_handle);
|
||||||
(void)vEventGroupDelete(mb_controller_event_group);
|
(void)vEventGroupDelete(mbs_opts->mbs_event_group);
|
||||||
mb_error = eMBClose();
|
mb_error = eMBClose();
|
||||||
MB_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE,
|
MB_SLAVE_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE,
|
||||||
"mb stack close failure returned (0x%x).", (uint32_t)mb_error);
|
"mb stack close failure returned (0x%x).", (uint32_t)mb_error);
|
||||||
|
free(mbs_interface_ptr);
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup modbus controller parameters
|
esp_err_t mbc_serial_slave_set_descriptor(const mb_register_area_descriptor_t descr_info)
|
||||||
esp_err_t mbcontroller_setup(const mb_communication_info_t comm_info)
|
|
||||||
{
|
{
|
||||||
MB_CHECK(((comm_info.mode == MB_MODE_RTU) || (comm_info.mode == MB_MODE_ASCII)),
|
MB_SLAVE_CHECK((mbs_interface_ptr != NULL),
|
||||||
ESP_ERR_INVALID_ARG, "mb incorrect mode = (0x%x).",
|
ESP_ERR_INVALID_STATE,
|
||||||
(uint32_t)comm_info.mode);
|
"Slave interface is not correctly initialized.");
|
||||||
MB_CHECK((comm_info.slave_addr <= MB_ADDRESS_MAX),
|
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
|
||||||
ESP_ERR_INVALID_ARG, "mb wrong slave address = (0x%x).",
|
MB_SLAVE_CHECK(((descr_info.type < MB_PARAM_COUNT) && (descr_info.type >= MB_PARAM_HOLDING)),
|
||||||
(uint32_t)comm_info.slave_addr);
|
ESP_ERR_INVALID_ARG, "mb incorrect modbus instance type = (0x%x).",
|
||||||
MB_CHECK((comm_info.port <= UART_NUM_2), ESP_ERR_INVALID_ARG,
|
(uint32_t)descr_info.type);
|
||||||
"mb wrong port to set = (0x%x).", (uint32_t)comm_info.port);
|
MB_SLAVE_CHECK((descr_info.address != NULL),
|
||||||
MB_CHECK((comm_info.parity <= UART_PARITY_EVEN), ESP_ERR_INVALID_ARG,
|
ESP_ERR_INVALID_ARG, "mb instance pointer is NULL.");
|
||||||
"mb wrong parity option = (0x%x).", (uint32_t)comm_info.parity);
|
MB_SLAVE_CHECK((descr_info.size >= MB_INST_MIN_SIZE) && (descr_info.size < (MB_INST_MAX_SIZE)),
|
||||||
mb_type = (uint8_t)comm_info.mode;
|
ESP_ERR_INVALID_ARG, "mb instance size is incorrect = (0x%x).",
|
||||||
mb_address = (uint8_t)comm_info.slave_addr;
|
(uint32_t)descr_info.size);
|
||||||
mb_port = (uint8_t)comm_info.port;
|
mbs_opts->mbs_area_descriptors[descr_info.type] = descr_info;
|
||||||
mb_speed = (uint32_t)comm_info.baudrate;
|
|
||||||
mb_parity = (uint8_t)comm_info.parity;
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The helper function to get time stamp in microseconds
|
||||||
|
static uint64_t get_time_stamp()
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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_ASSERT(mbs_opts->mbs_event_group != NULL);
|
||||||
|
BaseType_t status = xEventGroupWaitBits(mbs_opts->mbs_event_group, (BaseType_t)group,
|
||||||
|
pdTRUE , pdFALSE, portMAX_DELAY);
|
||||||
|
return (mb_event_group_t)status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to get notification about parameter change from application task
|
||||||
|
static esp_err_t mbc_serial_slave_get_param_info(mb_param_info_t* reg_info, uint32_t timeout)
|
||||||
|
{
|
||||||
|
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_ERR_TIMEOUT;
|
||||||
|
MB_SLAVE_CHECK((mbs_opts->mbs_notification_queue_handle != NULL),
|
||||||
|
ESP_ERR_INVALID_ARG, "mb queue handle is invalid.");
|
||||||
|
MB_SLAVE_CHECK((reg_info != NULL), ESP_ERR_INVALID_ARG, "mb register information is invalid.");
|
||||||
|
BaseType_t status = xQueueReceive(mbs_opts->mbs_notification_queue_handle,
|
||||||
|
reg_info, pdMS_TO_TICKS(timeout));
|
||||||
|
if (status == pdTRUE) {
|
||||||
|
err = ESP_OK;
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
/* ----------------------- Callback functions for Modbus stack ---------------------------------*/
|
/* ----------------------- Callback functions for Modbus stack ---------------------------------*/
|
||||||
// These are executed by modbus stack to read appropriate type of registers.
|
// These are executed by modbus stack to read appropriate type of registers.
|
||||||
|
|
||||||
@ -303,13 +246,17 @@ esp_err_t mbcontroller_setup(const mb_communication_info_t comm_info)
|
|||||||
#pragma GCC diagnostic ignored "-Wtype-limits"
|
#pragma GCC diagnostic ignored "-Wtype-limits"
|
||||||
|
|
||||||
// Callback function for reading of MB Input Registers
|
// Callback function for reading of MB Input Registers
|
||||||
eMBErrorCode eMBRegInputCB(UCHAR * pucRegBuffer, USHORT usAddress,
|
eMBErrorCode eMBRegInputCBSerialSlave(UCHAR * pucRegBuffer, USHORT usAddress,
|
||||||
USHORT usNRegs)
|
USHORT usNRegs)
|
||||||
{
|
{
|
||||||
assert(pucRegBuffer != NULL);
|
MB_SLAVE_CHECK((mbs_interface_ptr != NULL),
|
||||||
USHORT usRegInputNregs = (USHORT)(mb_area_descriptors[MB_PARAM_INPUT].size >> 1); // Number of input registers
|
MB_EILLSTATE, "Slave stack uninitialized.");
|
||||||
USHORT usInputRegStart = (USHORT)mb_area_descriptors[MB_PARAM_INPUT].start_offset; // Get Modbus start address
|
MB_SLAVE_CHECK((pucRegBuffer != NULL),
|
||||||
UCHAR* pucInputBuffer = (UCHAR*)mb_area_descriptors[MB_PARAM_INPUT].address; // Get instance address
|
MB_EINVAL, "Slave stack call failed.");
|
||||||
|
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;
|
USHORT usRegs = usNRegs;
|
||||||
eMBErrorCode eStatus = MB_ENOERR;
|
eMBErrorCode eStatus = MB_ENOERR;
|
||||||
USHORT iRegIndex;
|
USHORT iRegIndex;
|
||||||
@ -341,13 +288,17 @@ eMBErrorCode eMBRegInputCB(UCHAR * pucRegBuffer, USHORT usAddress,
|
|||||||
|
|
||||||
// Callback function for reading of MB Holding Registers
|
// Callback function for reading of MB Holding Registers
|
||||||
// Executed by stack when request to read/write holding registers is received
|
// Executed by stack when request to read/write holding registers is received
|
||||||
eMBErrorCode eMBRegHoldingCB(UCHAR * pucRegBuffer, USHORT usAddress,
|
eMBErrorCode eMBRegHoldingCBSerialSlave(UCHAR * pucRegBuffer, USHORT usAddress,
|
||||||
USHORT usNRegs, eMBRegisterMode eMode)
|
USHORT usNRegs, eMBRegisterMode eMode)
|
||||||
{
|
{
|
||||||
assert(pucRegBuffer != NULL);
|
MB_SLAVE_CHECK((mbs_interface_ptr != NULL),
|
||||||
USHORT usRegHoldingNregs = (USHORT)(mb_area_descriptors[MB_PARAM_HOLDING].size >> 1);
|
MB_EILLSTATE, "Slave stack uninitialized.");
|
||||||
USHORT usRegHoldingStart = (USHORT)mb_area_descriptors[MB_PARAM_HOLDING].start_offset;
|
MB_SLAVE_CHECK((pucRegBuffer != NULL),
|
||||||
UCHAR* pucHoldingBuffer = (UCHAR*)mb_area_descriptors[MB_PARAM_HOLDING].address;
|
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;
|
eMBErrorCode eStatus = MB_ENOERR;
|
||||||
USHORT iRegIndex;
|
USHORT iRegIndex;
|
||||||
USHORT usRegs = usNRegs;
|
USHORT usRegs = usNRegs;
|
||||||
@ -395,13 +346,17 @@ eMBErrorCode eMBRegHoldingCB(UCHAR * pucRegBuffer, USHORT usAddress,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Callback function for reading of MB Coils Registers
|
// Callback function for reading of MB Coils Registers
|
||||||
eMBErrorCode eMBRegCoilsCB(UCHAR* pucRegBuffer, USHORT usAddress,
|
eMBErrorCode eMBRegCoilsCBSerialSlave(UCHAR* pucRegBuffer, USHORT usAddress,
|
||||||
USHORT usNCoils, eMBRegisterMode eMode)
|
USHORT usNCoils, eMBRegisterMode eMode)
|
||||||
{
|
{
|
||||||
assert(NULL != pucRegBuffer);
|
MB_SLAVE_CHECK((mbs_interface_ptr != NULL),
|
||||||
USHORT usRegCoilNregs = (USHORT)(mb_area_descriptors[MB_PARAM_COIL].size >> 1); // number of registers in storage area
|
MB_EILLSTATE, "Slave stack uninitialized.");
|
||||||
USHORT usRegCoilsStart = (USHORT)mb_area_descriptors[MB_PARAM_COIL].start_offset; // MB offset of coils registers
|
MB_SLAVE_CHECK((pucRegBuffer != NULL),
|
||||||
UCHAR* pucRegCoilsBuf = (UCHAR*)mb_area_descriptors[MB_PARAM_COIL].address;
|
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;
|
eMBErrorCode eStatus = MB_ENOERR;
|
||||||
USHORT iRegIndex;
|
USHORT iRegIndex;
|
||||||
USHORT usCoils = usNCoils;
|
USHORT usCoils = usNCoils;
|
||||||
@ -448,13 +403,17 @@ eMBErrorCode eMBRegCoilsCB(UCHAR* pucRegBuffer, USHORT usAddress,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Callback function for reading of MB Discrete Input Registers
|
// Callback function for reading of MB Discrete Input Registers
|
||||||
eMBErrorCode eMBRegDiscreteCB(UCHAR * pucRegBuffer, USHORT usAddress,
|
eMBErrorCode eMBRegDiscreteCBSerialSlave(UCHAR* pucRegBuffer, USHORT usAddress,
|
||||||
USHORT usNDiscrete)
|
USHORT usNDiscrete)
|
||||||
{
|
{
|
||||||
assert(pucRegBuffer != NULL);
|
MB_SLAVE_CHECK((mbs_interface_ptr != NULL),
|
||||||
USHORT usRegDiscreteNregs = (USHORT)(mb_area_descriptors[MB_PARAM_DISCRETE].size >> 1); // number of registers in storage area
|
MB_EILLSTATE, "Slave stack uninitialized.");
|
||||||
USHORT usRegDiscreteStart = (USHORT)mb_area_descriptors[MB_PARAM_DISCRETE].start_offset; // MB offset of registers
|
MB_SLAVE_CHECK((pucRegBuffer != NULL),
|
||||||
UCHAR* pucRegDiscreteBuf = (UCHAR*)mb_area_descriptors[MB_PARAM_DISCRETE].address; // the storage address
|
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;
|
eMBErrorCode eStatus = MB_ENOERR;
|
||||||
USHORT iRegIndex, iRegBitIndex, iNReg;
|
USHORT iRegIndex, iRegBitIndex, iNReg;
|
||||||
UCHAR* pucDiscreteInputBuf;
|
UCHAR* pucDiscreteInputBuf;
|
||||||
@ -490,3 +449,73 @@ eMBErrorCode eMBRegDiscreteCB(UCHAR * pucRegBuffer, USHORT usAddress,
|
|||||||
return eStatus;
|
return eStatus;
|
||||||
}
|
}
|
||||||
#pragma GCC diagnostic pop // require GCC
|
#pragma GCC diagnostic pop // require GCC
|
||||||
|
|
||||||
|
// Initialization of Modbus controller
|
||||||
|
esp_err_t mbc_serial_slave_create(mb_port_type_t port_type, void** handler)
|
||||||
|
{
|
||||||
|
MB_SLAVE_CHECK((port_type == MB_PORT_SERIAL_SLAVE),
|
||||||
|
ESP_ERR_NOT_SUPPORTED,
|
||||||
|
"mb port not supported = %u.", (uint32_t)port_type);
|
||||||
|
// Allocate space for options
|
||||||
|
if (mbs_interface_ptr == NULL) {
|
||||||
|
mbs_interface_ptr = malloc(sizeof(mb_slave_interface_t));
|
||||||
|
}
|
||||||
|
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
|
||||||
|
vMBPortSetMode((UCHAR)port_type);
|
||||||
|
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
|
||||||
|
mbs_opts->port_type = MB_PORT_SERIAL_SLAVE; // set interface port type
|
||||||
|
|
||||||
|
// Set default values of communication options
|
||||||
|
mbs_opts->mbs_comm.mode = MB_MODE_RTU;
|
||||||
|
mbs_opts->mbs_comm.slave_addr = MB_DEVICE_ADDRESS;
|
||||||
|
mbs_opts->mbs_comm.port = MB_UART_PORT;
|
||||||
|
mbs_opts->mbs_comm.baudrate = MB_DEVICE_SPEED;
|
||||||
|
mbs_opts->mbs_comm.parity = MB_PARITY_NONE;
|
||||||
|
|
||||||
|
// Initialization of active context of the Modbus controller
|
||||||
|
BaseType_t status = 0;
|
||||||
|
// Parameter change notification queue
|
||||||
|
mbs_opts->mbs_event_group = xEventGroupCreate();
|
||||||
|
MB_SLAVE_CHECK((mbs_opts->mbs_event_group != NULL),
|
||||||
|
ESP_ERR_NO_MEM, "mb event group error.");
|
||||||
|
// Parameter change notification queue
|
||||||
|
mbs_opts->mbs_notification_queue_handle = xQueueCreate(
|
||||||
|
MB_CONTROLLER_NOTIFY_QUEUE_SIZE,
|
||||||
|
sizeof(mb_param_info_t));
|
||||||
|
MB_SLAVE_CHECK((mbs_opts->mbs_notification_queue_handle != NULL),
|
||||||
|
ESP_ERR_NO_MEM, "mb notify queue creation error.");
|
||||||
|
// Create Modbus controller task
|
||||||
|
status = xTaskCreate((void*)&modbus_slave_task,
|
||||||
|
"modbus_slave_task",
|
||||||
|
MB_CONTROLLER_STACK_SIZE,
|
||||||
|
NULL,
|
||||||
|
MB_CONTROLLER_PRIORITY,
|
||||||
|
&mbs_opts->mbs_task_handle);
|
||||||
|
if (status != pdPASS) {
|
||||||
|
vTaskDelete(mbs_opts->mbs_task_handle);
|
||||||
|
MB_SLAVE_CHECK((status == pdPASS), ESP_ERR_NO_MEM,
|
||||||
|
"mb controller task creation error, xTaskCreate() returns (0x%x).",
|
||||||
|
(uint32_t)status);
|
||||||
|
}
|
||||||
|
MB_SLAVE_ASSERT(mbs_opts->mbs_task_handle != NULL); // The task is created but handle is incorrect
|
||||||
|
|
||||||
|
// Initialize interface function pointers
|
||||||
|
mbs_interface_ptr->check_event = mbc_serial_slave_check_event;
|
||||||
|
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->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;
|
||||||
|
|
||||||
|
*handler = (void*)mbs_interface_ptr;
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
|||||||
|
/* 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_slave.h Modbus controller serial slave implementation header file
|
||||||
|
|
||||||
|
#ifndef _MODBUS_SERIAL_CONTROLLER_SLAVE
|
||||||
|
#define _MODBUS_SERIAL_CONTROLLER_SLAVE
|
||||||
|
|
||||||
|
#include <stdint.h> // for standard int types definition
|
||||||
|
#include <stddef.h> // for NULL and std defines
|
||||||
|
#include "esp_modbus_common.h" // for common defines
|
||||||
|
|
||||||
|
/* ----------------------- Defines ------------------------------------------*/
|
||||||
|
#define MB_CONTROLLER_NOTIFY_QUEUE_SIZE (CONFIG_MB_CONTROLLER_NOTIFY_QUEUE_SIZE) // Number of messages in parameter notification queue
|
||||||
|
#define MB_CONTROLLER_NOTIFY_TIMEOUT (pdMS_TO_TICKS(CONFIG_MB_CONTROLLER_NOTIFY_TIMEOUT)) // notification timeout
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief Initialize Modbus controller and stack
|
||||||
|
*
|
||||||
|
* @param[out] handler handler(pointer) to master data structure
|
||||||
|
* @return
|
||||||
|
* - ESP_OK Success
|
||||||
|
* - ESP_ERR_NO_MEM Parameter error
|
||||||
|
*/
|
||||||
|
esp_err_t mbc_serial_slave_create(mb_port_type_t port_type, void** handler);
|
||||||
|
|
||||||
|
#endif // _MODBUS_SERIAL_CONTROLLER_SLAVE
|
||||||
|
|
67
components/freemodbus/serial_slave/port/port_serial_slave.h
Normal file
67
components/freemodbus/serial_slave/port/port_serial_slave.h
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* FreeModbus Libary: ESP32 Port Demo Application
|
||||||
|
* Copyright (C) 2010 Christian Walter <cwalter@embedded-solutions.at>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* IF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* File: $Id: portother.c,v 1.1 2010/06/06 13:07:20 wolti Exp $
|
||||||
|
*/
|
||||||
|
#ifndef _PORT_SERIAL_SLAVE_H
|
||||||
|
#define _PORT_SERIAL_SLAVE_H
|
||||||
|
|
||||||
|
/* ----------------------- Platform includes --------------------------------*/
|
||||||
|
|
||||||
|
#include "driver/uart.h"
|
||||||
|
#include "driver/timer.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "port.h"
|
||||||
|
|
||||||
|
/* ----------------------- Defines ------------------------------------------*/
|
||||||
|
#ifdef __cplusplus
|
||||||
|
PR_BEGIN_EXTERN_C
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
BOOL xMBPortSerialTxPoll( void );
|
||||||
|
|
||||||
|
void vMBPortSetMode( UCHAR ucMode );
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
PR_END_EXTERN_C
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
#endif
|
@ -216,7 +216,10 @@ INPUT = \
|
|||||||
### System APIs
|
### System APIs
|
||||||
../../components/esp_common/include/esp_system.h \
|
../../components/esp_common/include/esp_system.h \
|
||||||
### Modbus controller component header file
|
### Modbus controller component header file
|
||||||
../../components/freemodbus/modbus_controller/mbcontroller.h
|
../../components/freemodbus/common/include/esp_modbus_common.h \
|
||||||
|
../../components/freemodbus/common/include/esp_modbus_slave.h \
|
||||||
|
../../components/freemodbus/common/include/esp_modbus_master.h
|
||||||
|
|
||||||
|
|
||||||
## Get warnings for functions that have no documentation for their parameters or return value
|
## Get warnings for functions that have no documentation for their parameters or return value
|
||||||
##
|
##
|
||||||
|
@ -12,7 +12,7 @@ Application Protocols
|
|||||||
HTTPS Server <esp_https_server>
|
HTTPS Server <esp_https_server>
|
||||||
ASIO <asio>
|
ASIO <asio>
|
||||||
ESP-MQTT <mqtt>
|
ESP-MQTT <mqtt>
|
||||||
Modbus slave <modbus>
|
Modbus <modbus>
|
||||||
|
|
||||||
Example code for this API section is provided in :example:`protocols` directory of ESP-IDF examples.
|
Example code for this API section is provided in :example:`protocols` directory of ESP-IDF examples.
|
||||||
|
|
||||||
|
@ -14,49 +14,98 @@ There are many variants of Modbus protocols, some of them are:
|
|||||||
* ``Modbus TCP/IP or Modbus TCP`` — This is a Modbus variant used for communications over TCP/IP networks, connecting over port 502. It does not require a checksum calculation, as lower layers already provide checksum protection.
|
* ``Modbus TCP/IP or Modbus TCP`` — This is a Modbus variant used for communications over TCP/IP networks, connecting over port 502. It does not require a checksum calculation, as lower layers already provide checksum protection.
|
||||||
|
|
||||||
|
|
||||||
Modbus slave interface API overview
|
Modbus common interface API overview
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
|
||||||
ESP-IDF supports Modbus slave protocol and provides modbus_controller interface API to interact with user application. The interface API functions below are used to setup and use Modbus slave stack from application and could be executed in next order:
|
The API functions below provide common functionality to setup Modbus stack for slave and master implementation accordingly. ISP-IDF supports Modbus serial slave and master protocol stacks and provides modbus_controller interface API to interact with user application.
|
||||||
|
|
||||||
|
.. doxygenfunction:: mbc_slave_init
|
||||||
The files deviceparams.c/h contain the user structures which represent Modbus parameters accessed by stack. These parameters should be prepared by user and be assigned to the modbus_controller interface using :cpp:func:`mbcontroller_set_descriptor()` API call before start of communication.
|
.. doxygenfunction:: mbc_master_init
|
||||||
|
|
||||||
.. doxygenfunction:: mbcontroller_init
|
|
||||||
|
|
||||||
The function initializes the Modbus controller interface and its active context (tasks, RTOS objects and other resources).
|
The function initializes the Modbus controller interface and its active context (tasks, RTOS objects and other resources).
|
||||||
|
|
||||||
.. doxygenfunction:: mbcontroller_setup
|
.. doxygenfunction:: mbc_slave_setup
|
||||||
|
.. doxygenfunction:: mbc_master_setup
|
||||||
|
|
||||||
The function is used to setup communication parameters of the Modbus stack. See the Modbus controller API documentation for more information.
|
The function is used to setup communication parameters of the Modbus stack. See the Modbus controller API documentation for more information.
|
||||||
|
|
||||||
.. doxygenfunction:: mbcontroller_set_descriptor
|
:cpp:func:`mbc_slave_set_descriptor`: Initialization of slave descriptor.
|
||||||
|
|
||||||
The function initializes Modbus communication descriptors for each type of Modbus register area (Holding Registers, Input Registers, Coils (single bit output), Discrete Inputs). Once areas are initialized and the :cpp:func:`mbcontroller_start()` API is called the Modbus stack can access the data in user data structures by request from master. See the :cpp:type:`mb_register_area_descriptor_t` for more information.
|
:cpp:func:`mbc_master_set_descriptor`: Initialization of master descriptor.
|
||||||
|
|
||||||
.. doxygenfunction:: mbcontroller_start
|
The Modbus stack uses parameter description tables (descriptors) for communication. These are different for master and slave implementation of stack and should be assigned by the API call before start of communication.
|
||||||
|
|
||||||
|
.. doxygenfunction:: mbc_slave_start
|
||||||
|
.. doxygenfunction:: mbc_master_start
|
||||||
|
|
||||||
Modbus controller start function. Starts stack and interface and allows communication.
|
Modbus controller start function. Starts stack and interface and allows communication.
|
||||||
|
|
||||||
.. doxygenfunction:: mbcontroller_check_event
|
.. doxygenfunction:: mbc_slave_destroy
|
||||||
|
.. doxygenfunction:: mbc_master_destroy
|
||||||
The blocking call to function waits for event specified in the input parameter as event mask. Once master access the parameter and event mask matches the parameter the application task will be unblocked and function will return ESP_OK. See the :cpp:type:`mb_event_group_t` for more information about Modbus event masks.
|
|
||||||
|
|
||||||
.. doxygenfunction:: mbcontroller_get_param_info
|
|
||||||
|
|
||||||
The function gets information about accessed parameters from modbus controller event queue. The KConfig 'CONFIG_MB_CONTROLLER_NOTIFY_QUEUE_SIZE' key can be used to configure the notification queue size. The timeout parameter allows to specify timeout for waiting notification. The :cpp:type:`mb_param_info_t` structure contain information about accessed parameter.
|
|
||||||
|
|
||||||
.. doxygenfunction:: mbcontroller_destroy
|
|
||||||
|
|
||||||
This function stops Modbus communication stack and destroys controller interface.
|
This function stops Modbus communication stack and destroys controller interface.
|
||||||
|
|
||||||
There are some configuration parameters modbus_controller interface and Modbus stack can be configured using KConfig values in "Modbus configuration" menu. See the example application for more information about how to use these API functions.
|
There are some configurable parameters of modbus_controller interface and Modbus stack that can be configured using KConfig values in "Modbus configuration" menu. The most important option in KConfig menu is "Selection of Modbus stack support mode" that allows to select master or slave stack for implementation. See the examples for more information about how to use these API functions.
|
||||||
|
|
||||||
|
|
||||||
|
Modbus serial slave interface API overview
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
|
||||||
|
The slave stack requires the user defined structures which represent Modbus parameters accessed by stack. These structures should be prepared by user and be assigned to the modbus_controller interface using :cpp:func:`mbc_slave_set_descriptor()` API call before start of communication.
|
||||||
|
The interface API functions below are used for Modbus slave application:
|
||||||
|
|
||||||
|
.. doxygenfunction:: mbc_slave_set_descriptor
|
||||||
|
|
||||||
|
The function initializes Modbus communication descriptors for each type of Modbus register area (Holding Registers, Input Registers, Coils (single bit output), Discrete Inputs). Once areas are initialized and the :cpp:func:`mbc_slave_start()` API is called the Modbus stack can access the data in user data structures by request from master. See the :cpp:type:`mb_register_area_descriptor_t` and example for more information.
|
||||||
|
|
||||||
|
.. doxygenfunction:: mbc_slave_check_event
|
||||||
|
|
||||||
|
The blocking call to function waits for event specified in the input parameter as event mask. Once master access the parameter and event mask matches the parameter the application task will be unblocked and function will return ESP_OK. See the :cpp:type:`mb_event_group_t` for more information about Modbus event masks.
|
||||||
|
|
||||||
|
.. doxygenfunction:: mbc_slave_get_param_info
|
||||||
|
|
||||||
|
The function gets information about accessed parameters from modbus controller event queue. The KConfig 'CONFIG_MB_CONTROLLER_NOTIFY_QUEUE_SIZE' key can be used to configure the notification queue size. The timeout parameter allows to specify timeout for waiting notification. The :cpp:type:`mb_param_info_t` structure contain information about accessed parameter.
|
||||||
|
|
||||||
|
|
||||||
|
Modbus serial master interface API overview
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
|
||||||
|
The Modbus master implementation requires parameter description table be defined before start of stack. This table describes characteristics (physical parameters like temperature, humidity, etc.) and links them to Modbus registers in specific slave device in the Modbus segment. The table has to be assigned to the modbus_controller interface using :cpp:func:`mbc_master_set_descriptor()` API call before start of communication.
|
||||||
|
|
||||||
|
Below are the interface API functions that are used to setup and use Modbus master stack from user application and can be executed in next order:
|
||||||
|
|
||||||
|
.. doxygenfunction:: mbc_master_set_descriptor
|
||||||
|
|
||||||
|
Assigns parameter description table for Modbus controller interface. The table has to be prepared by user according to particular
|
||||||
|
|
||||||
|
.. doxygenfunction:: mbc_master_send_request
|
||||||
|
|
||||||
|
This function sends data request as defined in parameter request, waits response from corresponded slave and returns status of command execution. This function provides a standard way for read/write access to Modbus devices in the network.
|
||||||
|
|
||||||
|
.. doxygenfunction:: mbc_master_get_cid_info
|
||||||
|
|
||||||
|
The function gets information about supported characteristic defined as cid. It will check if characteristic is supported and returns its description.
|
||||||
|
|
||||||
|
.. doxygenfunction:: mbc_master_get_parameter
|
||||||
|
|
||||||
|
The function reads data of characteristic defined in parameters from Modbus slave device and returns its data. The additional data for request is taken from parameter description table.
|
||||||
|
|
||||||
|
.. doxygenfunction:: mbc_master_set_parameter
|
||||||
|
|
||||||
|
The function writes characteristic's value defined as a name and cid parameter in corresponded slave device. The additional data for parameter request is taken from master parameter description table.
|
||||||
|
|
||||||
Application Example
|
Application Example
|
||||||
-------------------
|
-------------------
|
||||||
The example uses the FreeModbus library port for slave implementation:
|
|
||||||
|
|
||||||
|
The examples below use the FreeModbus library port for slave and master implementation accordingly. The selection of stack is performed through KConfig menu "Selection of Modbus stack support mode" and related configuration keys.
|
||||||
|
|
||||||
:example:`protocols/modbus_slave`
|
:example:`protocols/modbus_slave`
|
||||||
|
|
||||||
|
:example:`protocols/modbus_master`
|
||||||
|
|
||||||
|
Please refer to the specific example README.md for details.
|
||||||
|
|
||||||
|
6
examples/protocols/modbus_master/CMakeLists.txt
Normal file
6
examples/protocols/modbus_master/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# The following five lines of boilerplate have to be in your project's
|
||||||
|
# CMakeLists in this exact order for cmake to work correctly
|
||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(modbus_master)
|
8
examples/protocols/modbus_master/Makefile
Normal file
8
examples/protocols/modbus_master/Makefile
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#
|
||||||
|
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||||
|
# project subdirectory.
|
||||||
|
#
|
||||||
|
|
||||||
|
PROJECT_NAME := modbus_master_sense
|
||||||
|
|
||||||
|
include $(IDF_PATH)/make/project.mk
|
134
examples/protocols/modbus_master/README.md
Normal file
134
examples/protocols/modbus_master/README.md
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
# Modbus Master Example
|
||||||
|
|
||||||
|
This example demonstrates using of FreeModbus stack port implementation for ESP32 as a master device.
|
||||||
|
This implementation is able to read/write values of slave devices connected into Modbus segment. All parameters to be accessed are defined in data dictionary in the files /main/device_params.h/c.
|
||||||
|
The values represented as characteristics with its name and characteristic CID which are linked into registers of slave devices connected into Modbus segment.
|
||||||
|
|
||||||
|
The example implements simple control algorithm and checks humidity and temperature from two sensors and set alarm (relay in third device) when values exceeded limits.
|
||||||
|
|
||||||
|
Device parameters definition:
|
||||||
|
--------------------------------------------------------------------------------------------------
|
||||||
|
| Slave address | Characteristic ID | Characteristic name | Description |
|
||||||
|
| --------------------|----------------------|----------------------|----------------------------|
|
||||||
|
| MB_DEVICE_ADDR1 | CID_DATA_CHAN_0, | Data_channel_0 | Data channel 1 |
|
||||||
|
| MB_DEVICE_ADDR1 | CID_HUMIDITY_1, | Humidity_1 | Humidity from sensor 1 |
|
||||||
|
| | CID_TEMPERATURE_1 | Temperature_1 | Sensor 1 temperature |
|
||||||
|
| MB_DEVICE_ADDR2 | CID_HUMIDITY_2, | Humidity_2 | Humidity from sensor 2 |
|
||||||
|
| | CID_TEMPERATURE_2 | Temperature_2 | Sensor 2 temperature |
|
||||||
|
| MB_DEVICE_ADDR3 | CID_RELAY_P1 | RelayP1 | Alarm Relay outputs on/off |
|
||||||
|
--------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Modbus segment device connection schematic:
|
||||||
|
```
|
||||||
|
MB_DEVICE_ADDR1
|
||||||
|
-------------
|
||||||
|
| |
|
||||||
|
| Slave 1 |---<>--+
|
||||||
|
| | |
|
||||||
|
------------- |
|
||||||
|
MB_DEVICE_ADDR2 |
|
||||||
|
------------- | -------------
|
||||||
|
| | | | |
|
||||||
|
| Slave 2 |---<>--+---<>---| Master |
|
||||||
|
| | | | |
|
||||||
|
------------- | -------------
|
||||||
|
MB_DEVICE_ADDR3 |
|
||||||
|
------------- RS485 network
|
||||||
|
| | |
|
||||||
|
| Slave 3 |---<>--+
|
||||||
|
| |
|
||||||
|
-------------
|
||||||
|
```
|
||||||
|
|
||||||
|
## Hardware required :
|
||||||
|
Option 1:
|
||||||
|
PC (Modbus Slave app) + USB Serial adapter connected to USB port + RS485 line drivers + ESP32 WROVER-KIT board.
|
||||||
|
Option 2:
|
||||||
|
Three ESP32 WROVER-KIT board flashed with modbus_slave example software to represent slave device with specific slave address. The slave addresses for each board have to be configured as defined in "Device parameters definition" table above.
|
||||||
|
One ESP32 WROVER-KIT board flashed with modbus_master example. All the boards require connection of RS485 line drivers (see below).
|
||||||
|
|
||||||
|
The MAX485 line driver is used as an example below but other similar chips can be used as well.
|
||||||
|
RS485 example circuit schematic for connection of master and slave devices into segment:
|
||||||
|
```
|
||||||
|
VCC ---------------+ +--------------- VCC
|
||||||
|
| |
|
||||||
|
+-------x-------+ +-------x-------+
|
||||||
|
RXD <------| RO | DIFFERENTIAL | RO|-----> RXD
|
||||||
|
| B|---------------|B |
|
||||||
|
TXD ------>| DI MAX485 | \ / | MAX485 DI|<----- TXD
|
||||||
|
ESP32 WROVER KIT 1 | | RS-485 side | | External PC (emulator) with USB to serial or
|
||||||
|
RTS --+--->| DE | / \ | DE|---+ ESP32 WROVER KIT 2 (slave)
|
||||||
|
| | A|---------------|A | |
|
||||||
|
+----| /RE | PAIR | /RE|---+-- RTS
|
||||||
|
+-------x-------+ +-------x-------+
|
||||||
|
| |
|
||||||
|
--- ---
|
||||||
|
Modbus Master device Modbus Slave device
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## How to setup and use an example:
|
||||||
|
|
||||||
|
### Configure the application
|
||||||
|
Configure the UART pins used for modbus communication using command and table below.
|
||||||
|
```
|
||||||
|
make menuconfig
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
------------------------------------------------------------------------------------------------
|
||||||
|
| ESP32 Interface | #define | Default ESP32 Pin | External RS485 Pin|
|
||||||
|
| ----------------------|------------------------------|-------------------|-------------------|
|
||||||
|
| Transmit Data (TxD) | CONFIG_MB_UART_TXD | GPIO23 | DI |
|
||||||
|
| Receive Data (RxD) | CONFIG_MB_UART_RXD | GPIO22 | RO |
|
||||||
|
| Request To Send (RTS) | CONFIG_MB_UART_RTS | GPIO18 | ~RE/DE |
|
||||||
|
| | | | |
|
||||||
|
| Ground | n/a | GND | GND |
|
||||||
|
------------------------------------------------------------------------------------------------
|
||||||
|
```
|
||||||
|
The communication parameters below allow to configure Modbus stack appropriately but usually it is enough to use default settings.
|
||||||
|
See the help string of parameters for more information.
|
||||||
|
|
||||||
|
### Setup external Modbus slave devices or emulator
|
||||||
|
Option 1:
|
||||||
|
Configure the external Modbus master software according to port configuration parameters used in the example. The Modbus Slave application can be used with this example to emulate slave devices with its parameters. Use official documentation for software to setup emulation of slave devices.
|
||||||
|
Option 2:
|
||||||
|
Other option is to have the modbus_slave example flashed into ESP32 WROVER KIT board and connect boards together as showed in "Modbus segment connection schematic above". See the modbus slave API documentation to configure communication parameters and slave addresses as defined in "Device parameters definition" table above.
|
||||||
|
|
||||||
|
### Build and flash software of master device
|
||||||
|
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||||
|
```
|
||||||
|
make -j4 flash monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||||
|
|
||||||
|
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||||
|
|
||||||
|
## Example Output
|
||||||
|
Example output of the application:
|
||||||
|
```
|
||||||
|
I (51493) SENSE_MAIN: cid: 0, Data_channel_0(Volts) = 6.00
|
||||||
|
I (51543) SENSE_MAIN: cid: 1, Humidity_1(%rH) = 22.00
|
||||||
|
I (51573) SENSE_MAIN: cid: 2, Temperature_1(C) = 0.00
|
||||||
|
I (51603) SENSE_MAIN: cid: 3, Humidity_2(%rH) = 1.00
|
||||||
|
I (51633) SENSE_MAIN: cid: 4, Temperature_2(C) = 33.00
|
||||||
|
I (51673) SENSE_MAIN: cid: 5, (RelayP1) = OFF
|
||||||
|
I (61713) SENSE_MAIN: cid: 0, Data_channel_0(Volts) = 50.00
|
||||||
|
I (61763) SENSE_MAIN: cid: 1, Humidity_1(%rH) = 22.00
|
||||||
|
I (61793) SENSE_MAIN: cid: 2, Temperature_1(C) = 0.00
|
||||||
|
I (61823) SENSE_MAIN: cid: 3, Humidity_2(%rH) = 1.00
|
||||||
|
I (61853) SENSE_MAIN: cid: 4, Temperature_2(C) = 33.00
|
||||||
|
I (61893) SENSE_MAIN: cid: 5, (RelayP1) = OFF
|
||||||
|
I (62893) SENSE_MAIN: The value exceeds limit, then set relay.
|
||||||
|
I (71953) SENSE_MAIN: cid: 0, Data_channel_0(Volts) = 50.00
|
||||||
|
I (71993) SENSE_MAIN: cid: 1, Humidity_1(%rH) = 22.00
|
||||||
|
I (72023) SENSE_MAIN: cid: 2, Temperature_1(C) = 0.00
|
||||||
|
I (72063) SENSE_MAIN: cid: 3, Humidity_2(%rH) = 1.00
|
||||||
|
I (72093) SENSE_MAIN: cid: 4, Temperature_2(C) = 33.00
|
||||||
|
I (73143) SENSE_MAIN: cid: 5, (RelayP1) = ON
|
||||||
|
|
||||||
|
```
|
||||||
|
The example refreshes the characteristics from devices every 10 seconds, verifies if they exceeded limits and sets alarm accordingly. The output line describes Timestamp, Cid of characteristic, Characteristic name(Units), Characteristic value.
|
||||||
|
|
||||||
|
|
6
examples/protocols/modbus_master/main/CMakeLists.txt
Normal file
6
examples/protocols/modbus_master/main/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
set(COMPONENT_SRCS "sense_main.c"
|
||||||
|
"sense_modbus.c"
|
||||||
|
"device_params.c")
|
||||||
|
set(COMPONENT_ADD_INCLUDEDIRS "." "include")
|
||||||
|
|
||||||
|
register_component()
|
27
examples/protocols/modbus_master/main/Kconfig.projbuild
Normal file
27
examples/protocols/modbus_master/main/Kconfig.projbuild
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
menu "Modbus Example Configuration"
|
||||||
|
|
||||||
|
config MB_UART_RXD
|
||||||
|
int "UART RXD pin number"
|
||||||
|
range 0 34
|
||||||
|
default 22
|
||||||
|
help
|
||||||
|
GPIO number for UART RX pin. See UART documentation for more information
|
||||||
|
about available pin numbers for UART.
|
||||||
|
|
||||||
|
config MB_UART_TXD
|
||||||
|
int "UART TXD pin number"
|
||||||
|
range 0 34
|
||||||
|
default 23
|
||||||
|
help
|
||||||
|
GPIO number for UART TX pin. See UART documentation for more information
|
||||||
|
about available pin numbers for UART.
|
||||||
|
|
||||||
|
config MB_UART_RTS
|
||||||
|
int "UART RTS pin number"
|
||||||
|
range 0 34
|
||||||
|
default 18
|
||||||
|
help
|
||||||
|
GPIO number for UART RTS pin. This pin is connected to
|
||||||
|
~RE/DE pin of RS485 transceiver to switch direction.
|
||||||
|
|
||||||
|
endmenu
|
4
examples/protocols/modbus_master/main/component.mk
Normal file
4
examples/protocols/modbus_master/main/component.mk
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#
|
||||||
|
# "main" pseudo-component makefile.
|
||||||
|
#
|
||||||
|
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
70
examples/protocols/modbus_master/main/device_params.c
Normal file
70
examples/protocols/modbus_master/main/device_params.c
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*======================================================================================
|
||||||
|
* Description:
|
||||||
|
* This C file contains user defined parameters for Modbus example and data dictionary
|
||||||
|
* which describes each value (characteristic) and links it to modbus registers in
|
||||||
|
* in corresponded slave device.
|
||||||
|
*=====================================================================================*/
|
||||||
|
|
||||||
|
#include "mbcontroller.h"
|
||||||
|
#include "device_params.h"
|
||||||
|
|
||||||
|
// Here are the user instances defined as structures for device parameters packed by 1 byte
|
||||||
|
// These are keep the values that can be accessed from Modbus master
|
||||||
|
|
||||||
|
holding_reg_params_t holding_reg_params = { 0 };
|
||||||
|
|
||||||
|
input_reg_params_t input_reg_params = { 0 };
|
||||||
|
|
||||||
|
coil_reg_params_t coil_reg_params = { 0 };
|
||||||
|
|
||||||
|
discrete_reg_params_t discrete_reg_params = { 0 };
|
||||||
|
|
||||||
|
#define HOLD_OFFSET(field) ((uint16_t)(offsetof(holding_reg_params_t, field) + 1))
|
||||||
|
#define INPUT_OFFSET(field) ((uint16_t)(offsetof(input_reg_params_t, field) + 1))
|
||||||
|
#define COIL_OFFSET(field) ((uint16_t)(offsetof(coil_reg_params_t, field) + 1))
|
||||||
|
// Discrete offset macro (options can be used as bit masks)
|
||||||
|
#define DISCR_OFFSET(field) ((uint16_t)(offsetof(discrete_reg_params_t, field) + 1))
|
||||||
|
|
||||||
|
#define STR(fieldname) ((const char*)( fieldname ))
|
||||||
|
#define OPTS(min_val, max_val, step_val) { .opt1 = min_val, .opt2 = max_val, .opt3 = step_val }
|
||||||
|
|
||||||
|
// This table below defines the characteristics supported by this Modbus master device (Data dictionary).
|
||||||
|
// These characteristics are linked to Modbus parameters of external slave devices in Modbus network.
|
||||||
|
// For this example next devices are supported:
|
||||||
|
// MB_DEVICE_ADDR1 : (CID_HUMIDITY_1, CID_TEMPERATURE_1) : Modbus sensor 1 (Humidity and Temperature)
|
||||||
|
// MB_DEVICE_ADDR2 : (CID_HUMIDITY_2, CID_TEMPERATURE_2) : Modbus sensor 2 (Humidity and Temperature)
|
||||||
|
// MB_DEVICE_ADDR3 : (CID_RELAY_P1, CID_RELAY_P2) : Modbus output device (Relay outputs on/off)
|
||||||
|
|
||||||
|
// There are two options to define instance for parameter:
|
||||||
|
// 1. Define offset (param_offset field) to the field in the parameter's structure (see example below)
|
||||||
|
// Once set it will be used as to store parameter value.
|
||||||
|
// 2. Set param_offset field in characteristics table to zero.
|
||||||
|
// This will allow to allocate space for parameter storage during initialization and use it as a cache.
|
||||||
|
|
||||||
|
// Example Data (Object) Dictionary for Modbus parameters
|
||||||
|
const mb_parameter_descriptor_t device_parameters[] = {
|
||||||
|
// { Cid, Param Name, Units, Modbus Slave Addr, Modbus Reg Type, Reg Start, Reg Size, Instance Offset, Data Type, Data Size, Parameter Options, Access Mode}
|
||||||
|
// Parameter: Data channel 0 : Data channel 0 = Voltage
|
||||||
|
{ CID_DATA_CHAN_0, STR("Data_channel_0"), STR("Volts"), MB_DEVICE_ADDR1, MB_PARAM_INPUT, 0, 2,
|
||||||
|
INPUT_OFFSET(data_chan0), PARAM_TYPE_FLOAT, 4, OPTS( -10, 10, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||||
|
{ CID_HUMIDITY_1, STR("Humidity_1"), STR("%rH"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 0, 2,
|
||||||
|
HOLD_OFFSET(mb_device1_humidity), PARAM_TYPE_FLOAT, 4, OPTS( 0, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||||
|
// Parameter: Temperature_2 : Temperature from device slave address = 1
|
||||||
|
{ CID_TEMPERATURE_1, STR("Temperature_1"), STR("°C"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, 2, 2,
|
||||||
|
HOLD_OFFSET(mb_device1_temperature), PARAM_TYPE_FLOAT, 4, OPTS( -40, 80, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||||
|
// Parameter: Humidity_2 : Humidity from device slave address = 2
|
||||||
|
{ CID_HUMIDITY_2, STR("Humidity_2"), STR("%rH"), MB_DEVICE_ADDR2, MB_PARAM_HOLDING, 0, 2,
|
||||||
|
HOLD_OFFSET(mb_device2_humidity), PARAM_TYPE_FLOAT, 4, OPTS( 0, 100, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||||
|
// Parameter: Temperature_2 : Temperature from device slave address = 2
|
||||||
|
{ CID_TEMPERATURE_2, STR("Temperature_2"), STR("°C"), MB_DEVICE_ADDR2, MB_PARAM_HOLDING, 2, 2,
|
||||||
|
HOLD_OFFSET(mb_device2_temperature), PARAM_TYPE_FLOAT, 4, OPTS( -40, 80, 1 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||||
|
// Parameter: Relay P1 : Alarm on/off channel 1 : Output device 1
|
||||||
|
{ CID_RELAY_P1, STR("RelayP1"), STR("on/off"), MB_DEVICE_ADDR3, MB_PARAM_COIL, 0, 3,
|
||||||
|
COIL_OFFSET(coils_port1), PARAM_TYPE_U16, 2, OPTS( BIT0 | BIT1 | BIT2, 0, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||||
|
// Parameter: Relay P2 : Alarm on/off channel 2 : Output device 1
|
||||||
|
{ CID_RELAY_P2, STR("RelayP2"), STR("on/off"), MB_DEVICE_ADDR3, MB_PARAM_COIL, 3, 8,
|
||||||
|
COIL_OFFSET(coils_port2), PARAM_TYPE_U16, 2, OPTS( BIT3, 0, 0 ), PAR_PERMS_READ_WRITE_TRIGGER },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Calculate number of parameters in the table
|
||||||
|
const uint16_t num_device_parameters = (sizeof(device_parameters)/sizeof(device_parameters[0]));
|
140
examples/protocols/modbus_master/main/include/device_params.h
Normal file
140
examples/protocols/modbus_master/main/include/device_params.h
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
/*=====================================================================================
|
||||||
|
* Description:
|
||||||
|
* C file to define user defined parameters for Modbus example
|
||||||
|
*====================================================================================*/
|
||||||
|
|
||||||
|
#ifndef _DEVICEPARAMS_H_
|
||||||
|
#define _DEVICEPARAMS_H_
|
||||||
|
|
||||||
|
#include "mbcontroller.h" // for common Modbus defines
|
||||||
|
|
||||||
|
// Enumeration of modbus device addresses accessed by master device
|
||||||
|
enum {
|
||||||
|
MB_DEVICE_ADDR1 = 1,
|
||||||
|
MB_DEVICE_ADDR2 = 2,
|
||||||
|
MB_DEVICE_ADDR3 = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Enumeration of all supported CIDs for device (used in parameter definition table)
|
||||||
|
enum {
|
||||||
|
CID_DATA_CHAN_0 = 0,
|
||||||
|
CID_HUMIDITY_1,
|
||||||
|
CID_TEMPERATURE_1,
|
||||||
|
CID_HUMIDITY_2,
|
||||||
|
CID_TEMPERATURE_2,
|
||||||
|
CID_RELAY_P1,
|
||||||
|
CID_RELAY_P2,
|
||||||
|
CID_COUNT,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DEVICE_PARAM_MAX_SIZE 24
|
||||||
|
|
||||||
|
// The structures below define the parameters that will be accessed by Modbus master device.
|
||||||
|
// These parameters reflect the parameters in the address space of external devices in Modbus network.
|
||||||
|
// They defined for each modbus register type (coils, discreet inputs, holding registers, input registers).
|
||||||
|
// These are not required but can be used by user to link characteristic to corresponded field
|
||||||
|
// See the parameter definition table for more information.
|
||||||
|
// It is just example and it is responsibility of user to define them as required.
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
// Parameter: discrete_input0
|
||||||
|
uint8_t discrete_input0:1;
|
||||||
|
// Parameter: discrete_input1
|
||||||
|
uint8_t discrete_input1:1;
|
||||||
|
// Parameter: discrete_input2
|
||||||
|
uint8_t discrete_input2:1;
|
||||||
|
// Parameter: discrete_input3
|
||||||
|
uint8_t discrete_input3:1;
|
||||||
|
// Parameter: discrete_input4
|
||||||
|
uint8_t discrete_input4:1;
|
||||||
|
// Parameter: discrete_input5
|
||||||
|
uint8_t discrete_input5:1;
|
||||||
|
// Parameter: discrete_input6
|
||||||
|
uint8_t discrete_input6:1;
|
||||||
|
// Parameter: discrete_input7
|
||||||
|
uint8_t discrete_input7:1;
|
||||||
|
uint8_t discrete_input_port1:8;
|
||||||
|
} discrete_reg_params_t;
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
// Note: For correct access the coils storage for each addressed parameter
|
||||||
|
// has to include at least 2 byte (register)!
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
typedef union
|
||||||
|
{
|
||||||
|
struct {
|
||||||
|
// Parameter: Coil 0 : Coil0
|
||||||
|
|
||||||
|
uint8_t coil0:1;
|
||||||
|
// Parameter: Coil 1 : Coil1
|
||||||
|
uint8_t coil1:1;
|
||||||
|
// Parameter: Coil 2 : Coil2
|
||||||
|
uint8_t coil2:1;
|
||||||
|
// Parameter: Coil 3 : Coil3
|
||||||
|
uint8_t coil3:1;
|
||||||
|
// Parameter: Coil 4 : Coil4
|
||||||
|
uint8_t coil4:1;
|
||||||
|
// Parameter: Coil 5 : Coil5
|
||||||
|
uint8_t coil5:1;
|
||||||
|
// Parameter: Coil 6 : Coil6
|
||||||
|
uint8_t coil6:1;
|
||||||
|
// Parameter: Coil 7 : Coil7
|
||||||
|
uint8_t coil7:1;
|
||||||
|
uint8_t coil_port2:8;
|
||||||
|
};
|
||||||
|
uint8_t coils_port1;
|
||||||
|
uint8_t coils_port2;
|
||||||
|
} coil_reg_params_t;
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
// Input register structure to keep characteristic's values
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
// Parameter: Data channel 0 : data_chan0
|
||||||
|
float data_chan0;
|
||||||
|
// Parameter: Data channel 1 : data_chan1
|
||||||
|
float data_chan1;
|
||||||
|
// Parameter: Data channel 2 : data_chan2
|
||||||
|
float data_chan2;
|
||||||
|
// Parameter: Data channel 3 : data_chan3
|
||||||
|
float data_chan3;
|
||||||
|
} input_reg_params_t;
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
// Holding register structure to keep characteristic's values
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
// Parameter: Data channel 0 : mb_device1_humidity
|
||||||
|
float mb_device1_humidity;
|
||||||
|
// Parameter: Data channel 1 : mb_device1_temperature
|
||||||
|
float mb_device1_temperature;
|
||||||
|
// Parameter: Data channel 2 : mb_device2_humidity
|
||||||
|
float mb_device2_humidity;
|
||||||
|
// Parameter: Data channel 3 : mb_device2_temperature
|
||||||
|
float mb_device2_temperature;
|
||||||
|
// Parameter: Protocol version : protocol_version
|
||||||
|
uint16_t mb_device1_protocol_version;
|
||||||
|
// Parameter: Hardware version : hardware_version
|
||||||
|
uint16_t mb_device1_hardware_version;
|
||||||
|
// Parameter: Software Version : software_version
|
||||||
|
uint16_t mb_device1_software_version;
|
||||||
|
// Parameter: Software Revision : software_revision
|
||||||
|
uint16_t mb_device1_software_revision;
|
||||||
|
// Parameter: Device Type : deviceType :
|
||||||
|
uint16_t mb_device1_device_type;
|
||||||
|
uint8_t mb_device1_string_test[PARAM_SIZE_ASCII24];
|
||||||
|
} holding_reg_params_t;
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
extern holding_reg_params_t holding_reg_params;
|
||||||
|
extern input_reg_params_t input_reg_params;
|
||||||
|
extern coil_reg_params_t coil_reg_params;
|
||||||
|
extern discrete_reg_params_t discrete_reg_params;
|
||||||
|
|
||||||
|
extern const mb_parameter_descriptor_t device_parameters[];
|
||||||
|
extern const uint16_t num_device_parameters;
|
||||||
|
|
||||||
|
#endif /* _DEVICEPARAMS_H_ */
|
60
examples/protocols/modbus_master/main/include/sense_modbus.h
Normal file
60
examples/protocols/modbus_master/main/include/sense_modbus.h
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* ESPRESSIF MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
|
||||||
|
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||||
|
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||||
|
* to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
* substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SENSE_MB_H__
|
||||||
|
#define __SENSE_MB_H__
|
||||||
|
|
||||||
|
#include "mbcontroller.h"
|
||||||
|
#include "device_params.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif /**< _cplusplus */
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint16_t cid; /*!< Characteristic cid */
|
||||||
|
const char* param_key; /*!< The key (name) of the parameter */
|
||||||
|
const char* param_units; /*!< The units of the parameter */
|
||||||
|
mb_parameter_opt_t param_opts; /*!< Parameter options */
|
||||||
|
mb_param_perms_t access; /*!< Access permissions based on mode */
|
||||||
|
void* instance_ptr; /*!< Data instance for the parameter */
|
||||||
|
mb_descr_type_t instance_type; /*!< Type of instance value */
|
||||||
|
size_t instance_size; /*!< Size of instance to save data */
|
||||||
|
uint8_t change_flag; /*!< Change value flag */
|
||||||
|
esp_err_t status; /*!< Status of the value */
|
||||||
|
uint64_t timestamp; /*!< Time stamp of last access to parameter */
|
||||||
|
} characteristic_descriptor_t;
|
||||||
|
|
||||||
|
esp_err_t sense_modbus_init();
|
||||||
|
esp_err_t sense_modbus_get_characteristics(characteristic_descriptor_t** cid_table, uint16_t* table_size);
|
||||||
|
esp_err_t sense_modbus_read_value(uint16_t cid, void* value);
|
||||||
|
esp_err_t sense_modbus_send_value(uint16_t cid, void* value);
|
||||||
|
esp_err_t sense_modbus_get_cid_data(uint16_t cid, characteristic_descriptor_t* cid_data);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif /**< _cplusplus */
|
||||||
|
|
||||||
|
#endif /**< __SENSE_MB_H__ */
|
181
examples/protocols/modbus_master/main/sense_main.c
Normal file
181
examples/protocols/modbus_master/main/sense_main.c
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
/*
|
||||||
|
* ESPRESSIF MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
|
||||||
|
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||||
|
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||||
|
* to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
* substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "string.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "sense_modbus.h"
|
||||||
|
|
||||||
|
static const char *TAG = "sense_main";
|
||||||
|
|
||||||
|
// The number of parameters that intended to be used in the particular control process
|
||||||
|
#define SENSE_MAX_CIDS 6
|
||||||
|
|
||||||
|
// Timeout to update cid over Modbus if it is not updated by set/get request from mdf
|
||||||
|
#define MODBUS_VALUE_UPDATE_TIMEOUT_US (10000000)
|
||||||
|
#define MODBUS_GET_REQUEST_TIMEOUT (1000)
|
||||||
|
|
||||||
|
#define INIT_DELAY_TICS (100 / portTICK_RATE_MS)
|
||||||
|
#define TIMEOUT_UPDATE_CIDS_MS (1000)
|
||||||
|
#define TIMEOUT_UPDATE_CIDS_TICS (TIMEOUT_UPDATE_CIDS_MS / portTICK_RATE_MS)
|
||||||
|
|
||||||
|
#define SENSE_TRIGGER_TASK_STACK_SIZE (1024 * 4)
|
||||||
|
#define SENSE_TRIGGER_TASK_PRIO (6)
|
||||||
|
|
||||||
|
#define SENSE_MAIN_TAG "SENSE_MAIN"
|
||||||
|
|
||||||
|
#define SENS_MAIN_CHECK(a, ret_val, str, ...) \
|
||||||
|
if (!(a)) { \
|
||||||
|
ESP_LOGE(SENSE_MAIN_TAG, "%s(%u): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
|
||||||
|
return (ret_val); \
|
||||||
|
}
|
||||||
|
|
||||||
|
// Characteristic information table
|
||||||
|
static characteristic_descriptor_t* cid_table[SENSE_MAX_CIDS] = { 0 };
|
||||||
|
static uint16_t cid_counter = SENSE_MAX_CIDS;
|
||||||
|
|
||||||
|
// User operational task to trigger event when cid value exceeds limit
|
||||||
|
static void trigger_operation_task(void *arg)
|
||||||
|
{
|
||||||
|
float value;
|
||||||
|
uint64_t timeout = 0;
|
||||||
|
uint16_t temp_value = 0;
|
||||||
|
bool alarm_state = false;
|
||||||
|
bool relay_state = false;
|
||||||
|
characteristic_descriptor_t cid_data = { 0 };
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
alarm_state = false;
|
||||||
|
for (int cid = 0; cid < (CID_RELAY_P2); cid++)
|
||||||
|
{
|
||||||
|
// Get cid data
|
||||||
|
ESP_ERROR_CHECK_WITHOUT_ABORT(sense_modbus_get_cid_data(cid, &cid_data));
|
||||||
|
assert(cid_data.param_key != NULL);
|
||||||
|
assert(cid_data.instance_ptr != NULL);
|
||||||
|
// If value is not updated during 10 seconds then update it
|
||||||
|
timeout = esp_timer_get_time();
|
||||||
|
value = *(float*)cid_data.instance_ptr;
|
||||||
|
// Check limits to set alarm if exceeded limit
|
||||||
|
if (((value > cid_data.param_opts.max) ||
|
||||||
|
(value < cid_data.param_opts.min)) &&
|
||||||
|
(cid != CID_RELAY_P1)) {
|
||||||
|
alarm_state = true;
|
||||||
|
}
|
||||||
|
if (timeout > (cid_data.timestamp + MODBUS_VALUE_UPDATE_TIMEOUT_US)) {
|
||||||
|
// The value is not updated during timeout then update it
|
||||||
|
// The actual value is saved in the instance storage
|
||||||
|
value = 0;
|
||||||
|
esp_err_t error = sense_modbus_read_value(cid, (void*)&value);
|
||||||
|
if (error != ESP_OK) {
|
||||||
|
ESP_LOGE(SENSE_MAIN_TAG, "Update failed for cid: %u, %s(%s) = %d, %s",
|
||||||
|
(uint16_t)cid,
|
||||||
|
(char*)cid_data.param_key,
|
||||||
|
(char*)cid_data.param_units,
|
||||||
|
*(int*)cid_data.instance_ptr,
|
||||||
|
(char*)esp_err_to_name(error));
|
||||||
|
} else {
|
||||||
|
// Update state of alarm
|
||||||
|
if (cid == CID_RELAY_P1) {
|
||||||
|
// Get actual relay state
|
||||||
|
relay_state = (*(uint16_t*)(cid_data.instance_ptr)
|
||||||
|
& (uint16_t)(cid_data.param_opts.opt1));
|
||||||
|
const char* relay_state_str = relay_state ? "ON" : "OFF";
|
||||||
|
ESP_LOGI(SENSE_MAIN_TAG, "cid: %u, (%s) = %s", (uint16_t)cid,
|
||||||
|
(char*)cid_data.param_key,
|
||||||
|
(const char*)relay_state_str);
|
||||||
|
} else {
|
||||||
|
// Update read value
|
||||||
|
ESP_LOGI(SENSE_MAIN_TAG, "cid: %u, %s(%s) = %.2f", (uint16_t)cid,
|
||||||
|
(char*)cid_data.param_key,
|
||||||
|
(char*)cid_data.param_units,
|
||||||
|
(float)value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check all characteristics if they exceed limits and set alarm accordingly
|
||||||
|
if (!alarm_state) {
|
||||||
|
if (relay_state == true) {
|
||||||
|
ESP_LOGI(SENSE_MAIN_TAG, "The values within the limit, then release relay.");
|
||||||
|
temp_value = 0;
|
||||||
|
// Release the relay in IO slave device
|
||||||
|
// Do not check an error in this example (IO slave may be not configured)
|
||||||
|
(void)sense_modbus_send_value(CID_RELAY_P1, &temp_value);
|
||||||
|
relay_state = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!relay_state) {
|
||||||
|
ESP_LOGI(SENSE_MAIN_TAG, "The value exceeds limit, then set relay.");
|
||||||
|
temp_value = 0x00FF;
|
||||||
|
(void)sense_modbus_send_value(CID_RELAY_P1, &temp_value); // Set the relay
|
||||||
|
relay_state = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vTaskDelay(TIMEOUT_UPDATE_CIDS_TICS);
|
||||||
|
}
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sense_device_init()
|
||||||
|
{
|
||||||
|
// Initialize and start Modbus controller
|
||||||
|
sense_modbus_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main()
|
||||||
|
{
|
||||||
|
esp_err_t result = ESP_OK;
|
||||||
|
|
||||||
|
// Initialization of device peripheral and objects
|
||||||
|
sense_device_init();
|
||||||
|
|
||||||
|
// Get all supported modbus characteristics from modbus controller
|
||||||
|
result = sense_modbus_get_characteristics(&cid_table[0], &cid_counter);
|
||||||
|
assert((result == ESP_OK) && (cid_counter >= 1));
|
||||||
|
ESP_LOGI(TAG, "Found (%u) characteristics in the table.", cid_counter);
|
||||||
|
characteristic_descriptor_t* sid_descr_ptr = { 0 };
|
||||||
|
float value = 0;
|
||||||
|
esp_err_t err = ESP_OK;
|
||||||
|
int cid_registered = 0;
|
||||||
|
// Read all found characteristics
|
||||||
|
for (uint16_t cid = 0; cid < cid_counter; cid++) {
|
||||||
|
sid_descr_ptr = cid_table[cid];
|
||||||
|
// Initially read of Modbus values and check status
|
||||||
|
err = sense_modbus_read_value(cid, (void*)&value);
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
ESP_LOGI(TAG, "Characteristic (%s) data = 0x%.4x read successful.",
|
||||||
|
(char*)sid_descr_ptr->param_key, (unsigned int)value);
|
||||||
|
cid_registered++;
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "Characteristic (%s) read value fail, err = %d (%s).",
|
||||||
|
(char*)sid_descr_ptr->param_key,
|
||||||
|
(int)err,
|
||||||
|
(char*)esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Starts operation task to check values and trigger an event
|
||||||
|
xTaskCreate(trigger_operation_task, "trigger_operation_task",
|
||||||
|
SENSE_TRIGGER_TASK_STACK_SIZE, NULL, SENSE_TRIGGER_TASK_PRIO, NULL);
|
||||||
|
}
|
254
examples/protocols/modbus_master/main/sense_modbus.c
Normal file
254
examples/protocols/modbus_master/main/sense_modbus.c
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
/*
|
||||||
|
* ESPRESSIF MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
|
||||||
|
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||||
|
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||||
|
* to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
* substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This module allows to hide all Modbus dependencies and work with characteristics
|
||||||
|
// instead of Modbus parameters
|
||||||
|
#include "stdlib.h"
|
||||||
|
#include "string.h" // for memset, memcpy functions
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "sense_modbus.h" // for Modbus defines
|
||||||
|
|
||||||
|
// This module provide an easy way to work with characteristics instead of
|
||||||
|
// Modbus parameters
|
||||||
|
|
||||||
|
static const char* TAG = "SENSE_MB";
|
||||||
|
|
||||||
|
#define SENSE_MB_CHECK(a, ret_val, str, ...) \
|
||||||
|
if (!(a)) { \
|
||||||
|
ESP_LOGE(TAG, "%s(%u): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
|
||||||
|
return (ret_val); \
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define port options for the master application
|
||||||
|
#define MB_BAUDRATE 115200
|
||||||
|
#define MB_PORTNUM 2
|
||||||
|
#define MB_PARITY UART_PARITY_DISABLE
|
||||||
|
|
||||||
|
// Keep the pointer to active characteristic table and its size
|
||||||
|
static characteristic_descriptor_t** active_cid_table = NULL;
|
||||||
|
static uint32_t active_cid_table_size = 0;
|
||||||
|
|
||||||
|
// The function to get pointer to parameter storage (instance) according to parameter description table
|
||||||
|
static void* sense_modbus_get_param_data(const mb_parameter_descriptor_t* param_descriptor)
|
||||||
|
{
|
||||||
|
assert(param_descriptor != NULL);
|
||||||
|
void* instance_ptr = NULL;
|
||||||
|
if (param_descriptor->param_offset != 0) {
|
||||||
|
switch(param_descriptor->mb_param_type)
|
||||||
|
{
|
||||||
|
case MB_PARAM_HOLDING:
|
||||||
|
instance_ptr = (void*)(&holding_reg_params + param_descriptor->param_offset - 1);
|
||||||
|
break;
|
||||||
|
case MB_PARAM_INPUT:
|
||||||
|
instance_ptr = (void*)(&input_reg_params + param_descriptor->param_offset - 1);
|
||||||
|
break;
|
||||||
|
case MB_PARAM_COIL:
|
||||||
|
instance_ptr = (void*)(&coil_reg_params + param_descriptor->param_offset - 1);
|
||||||
|
break;
|
||||||
|
case MB_PARAM_DISCRETE:
|
||||||
|
instance_ptr = (void*)(&discrete_reg_params + param_descriptor->param_offset - 1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
instance_ptr = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
instance_ptr = malloc((size_t)(param_descriptor->param_size));;
|
||||||
|
}
|
||||||
|
return instance_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The helper function to get time stamp in microseconds
|
||||||
|
static uint64_t sense_modbus_get_time_stamp_us()
|
||||||
|
{
|
||||||
|
uint64_t time_stamp = esp_timer_get_time();
|
||||||
|
return time_stamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialization of Modbus stack
|
||||||
|
esp_err_t sense_modbus_init()
|
||||||
|
{
|
||||||
|
mb_communication_info_t comm = {
|
||||||
|
.port = MB_PORTNUM,
|
||||||
|
.mode = MB_MODE_RTU,
|
||||||
|
.baudrate = MB_BAUDRATE,
|
||||||
|
.parity = MB_PARITY
|
||||||
|
};
|
||||||
|
void* master_handler = NULL;
|
||||||
|
|
||||||
|
esp_err_t err = mbc_master_init(MB_PORT_SERIAL_MASTER, &master_handler);
|
||||||
|
SENSE_MB_CHECK((master_handler != NULL), ESP_ERR_INVALID_STATE,
|
||||||
|
"mb controller initialization fail.");
|
||||||
|
SENSE_MB_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||||
|
"mb controller initialization fail, returns(0x%x).",
|
||||||
|
(uint32_t)err);
|
||||||
|
err = mbc_master_setup((void*)&comm);
|
||||||
|
SENSE_MB_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||||
|
"mb controller setup fail, returns(0x%x).",
|
||||||
|
(uint32_t)err);
|
||||||
|
err = mbc_master_start();
|
||||||
|
SENSE_MB_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||||
|
"mb controller start fail, returns(0x%x).",
|
||||||
|
(uint32_t)err);
|
||||||
|
// Set UART pin numbers
|
||||||
|
err = uart_set_pin(MB_PORTNUM, CONFIG_MB_UART_TXD, CONFIG_MB_UART_RXD,
|
||||||
|
CONFIG_MB_UART_RTS, UART_PIN_NO_CHANGE);
|
||||||
|
SENSE_MB_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||||
|
"mb serial set pin failure, uart_set_pin() returned (0x%x).", (uint32_t)err);
|
||||||
|
// Set driver mode to Half Duplex
|
||||||
|
err = uart_set_mode(MB_PORTNUM, UART_MODE_RS485_HALF_DUPLEX);
|
||||||
|
SENSE_MB_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||||
|
"mb serial set mode failure, uart_set_mode() returned (0x%x).", (uint32_t)err);
|
||||||
|
vTaskDelay(5);
|
||||||
|
err = mbc_master_set_descriptor(&device_parameters[0], num_device_parameters);
|
||||||
|
SENSE_MB_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
|
||||||
|
"mb controller set descriptor fail, returns(0x%x).",
|
||||||
|
(uint32_t)err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check Modbus parameters description table and create characteristic description table according to it
|
||||||
|
esp_err_t sense_modbus_get_characteristics(characteristic_descriptor_t** cid_table, uint16_t* table_size)
|
||||||
|
{
|
||||||
|
SENSE_MB_CHECK((table_size != NULL), ESP_ERR_INVALID_ARG, "incorrect cid table size.");
|
||||||
|
SENSE_MB_CHECK((*table_size >= 1), ESP_ERR_NOT_FOUND, "incorrect cid of characteristic.");
|
||||||
|
SENSE_MB_CHECK((cid_table != NULL), ESP_ERR_INVALID_ARG, "incorrect cid table pointer.");
|
||||||
|
|
||||||
|
esp_err_t error_code = ESP_OK;
|
||||||
|
const mb_parameter_descriptor_t* param_descriptor = NULL;
|
||||||
|
characteristic_descriptor_t* cid_instance_ptr = NULL;
|
||||||
|
uint16_t cid_cnt = 0;
|
||||||
|
for (cid_cnt = 0; (error_code != ESP_ERR_NOT_FOUND) && (cid_cnt < *table_size); cid_cnt++) {
|
||||||
|
// Get data from parameters description table
|
||||||
|
// and use this information to fill the characteristics description table
|
||||||
|
// and having all required fields in just one table
|
||||||
|
error_code = mbc_master_get_cid_info(cid_cnt, ¶m_descriptor);
|
||||||
|
if ((error_code != ESP_ERR_NOT_FOUND) && (param_descriptor != NULL)) {
|
||||||
|
void* temp_data_ptr = sense_modbus_get_param_data(param_descriptor);
|
||||||
|
SENSE_MB_CHECK((temp_data_ptr != NULL),
|
||||||
|
ESP_ERR_INVALID_STATE, "incorrect instance pointer for cid.");
|
||||||
|
// Allocate space for characteristic description
|
||||||
|
cid_instance_ptr = (characteristic_descriptor_t*)malloc(sizeof(characteristic_descriptor_t));
|
||||||
|
// If not enough memory, try to repair from this error
|
||||||
|
SENSE_MB_CHECK((cid_instance_ptr != NULL),
|
||||||
|
ESP_ERR_NO_MEM, "incorrect memory allocation for the cid.");
|
||||||
|
cid_table[cid_cnt] = cid_instance_ptr;
|
||||||
|
// Fill the description according to parameter description table
|
||||||
|
cid_instance_ptr->instance_ptr = temp_data_ptr;
|
||||||
|
cid_instance_ptr->param_opts = param_descriptor->param_opts;
|
||||||
|
cid_instance_ptr->cid = param_descriptor->cid;
|
||||||
|
cid_instance_ptr->access = param_descriptor->access;
|
||||||
|
cid_instance_ptr->param_key = param_descriptor->param_key;
|
||||||
|
cid_instance_ptr->param_units = param_descriptor->param_units;
|
||||||
|
cid_instance_ptr->status = ESP_FAIL;
|
||||||
|
cid_instance_ptr->instance_size = (size_t)param_descriptor->param_size;
|
||||||
|
cid_instance_ptr->instance_type = param_descriptor->param_type;
|
||||||
|
cid_instance_ptr->change_flag = 0; // clear flag
|
||||||
|
cid_instance_ptr->timestamp = 0; // Keeps timestamp of updated value
|
||||||
|
assert(cid_instance_ptr->param_key != NULL);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "%s: Found (%u) characteristics in table.", __FUNCTION__, cid_cnt);
|
||||||
|
// Keep the table pointer and size of table
|
||||||
|
active_cid_table = cid_table;
|
||||||
|
active_cid_table_size = cid_cnt;
|
||||||
|
*table_size = cid_cnt;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read characteristic value from Modbus parameter according to description table
|
||||||
|
esp_err_t sense_modbus_read_value(uint16_t cid, void *value)
|
||||||
|
{
|
||||||
|
assert(active_cid_table != NULL);
|
||||||
|
SENSE_MB_CHECK((value != NULL), ESP_ERR_INVALID_ARG, "incorrect value pointer.");
|
||||||
|
SENSE_MB_CHECK((cid < active_cid_table_size), ESP_ERR_INVALID_ARG, "incorrect cid to read.");
|
||||||
|
characteristic_descriptor_t* cid_info = active_cid_table[cid];
|
||||||
|
SENSE_MB_CHECK((cid_info->instance_ptr != NULL), ESP_ERR_INVALID_STATE, "incorrect cid table instance_pointer.");
|
||||||
|
SENSE_MB_CHECK((cid_info->param_key != NULL), ESP_ERR_INVALID_STATE, "incorrect cid table or not initialized.");
|
||||||
|
SENSE_MB_CHECK((cid_info->cid == cid), ESP_ERR_INVALID_STATE, "incorrect cid table or not initialized.");
|
||||||
|
|
||||||
|
// Set the data pointer for get request to value instance
|
||||||
|
uint8_t* value_ptr = (uint8_t*)cid_info->instance_ptr;
|
||||||
|
uint8_t type = 0;
|
||||||
|
memset((void*)value_ptr, 0, cid_info->instance_size); // Clear value instance first
|
||||||
|
|
||||||
|
// Send Modbus request to read cid correspond registers
|
||||||
|
esp_err_t error = mbc_master_get_parameter(cid, (char*)cid_info->param_key, value_ptr, &type);
|
||||||
|
SENSE_MB_CHECK((type <= PARAM_TYPE_ASCII), ESP_ERR_NOT_SUPPORTED, "returned data type is not supported (%u)", type);
|
||||||
|
cid_info->status = error; // Keep last read status of the cid in the information table
|
||||||
|
if (error == ESP_OK) {
|
||||||
|
// Copy the value to param
|
||||||
|
memcpy((void*)value, (void*)value_ptr, cid_info->instance_size);
|
||||||
|
cid_info->timestamp = sense_modbus_get_time_stamp_us(); // Set timestamp of last access to cid
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write characteristic value into associated Modbus parameter
|
||||||
|
esp_err_t sense_modbus_send_value(uint16_t cid, void* value)
|
||||||
|
{
|
||||||
|
assert(active_cid_table != NULL);
|
||||||
|
SENSE_MB_CHECK((cid < active_cid_table_size), ESP_ERR_INVALID_ARG, "incorrect cid to set.");
|
||||||
|
characteristic_descriptor_t* cid_info = active_cid_table[cid];
|
||||||
|
SENSE_MB_CHECK((cid_info->instance_ptr != NULL), ESP_ERR_INVALID_STATE, "incorrect cid table instance_ptr.");
|
||||||
|
SENSE_MB_CHECK((cid_info->param_key != NULL), ESP_ERR_INVALID_STATE, "incorrect cid table param_key or not initialized.");
|
||||||
|
SENSE_MB_CHECK((cid_info->cid == cid), ESP_ERR_INVALID_STATE, "incorrect cid in table or not initialized.");
|
||||||
|
|
||||||
|
uint8_t* value_ptr = (uint8_t*)cid_info->instance_ptr;
|
||||||
|
// Set the instance value of the characteristic to the value
|
||||||
|
memcpy((void*)value_ptr, (void*)value, cid_info->instance_size);
|
||||||
|
|
||||||
|
uint8_t type = 0;
|
||||||
|
// Set modbus parameter according to value of characteristic
|
||||||
|
esp_err_t error = mbc_master_set_parameter(cid, (char*)cid_info->param_key, value_ptr, &type);
|
||||||
|
SENSE_MB_CHECK((cid_info->instance_type == type), ESP_ERR_NOT_FOUND, "incorrect type of parameter (%u)", type);
|
||||||
|
SENSE_MB_CHECK((type <= PARAM_TYPE_ASCII), ESP_ERR_NOT_SUPPORTED, "returned data type is not supported (%u)", type);
|
||||||
|
cid_info->change_flag = 0x55; // Set value changed flag to inform higher level
|
||||||
|
cid_info->status = error; // Keep last read/write status of cid in the information table
|
||||||
|
cid_info->timestamp = sense_modbus_get_time_stamp_us(); // Set time stamp of last access to cid
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get cid data from characteristic description table
|
||||||
|
esp_err_t sense_modbus_get_cid_data(uint16_t cid, characteristic_descriptor_t* cid_data)
|
||||||
|
{
|
||||||
|
assert(active_cid_table != NULL);
|
||||||
|
SENSE_MB_CHECK((cid_data != NULL), ESP_ERR_INVALID_ARG, "incorrect data pointer.");
|
||||||
|
SENSE_MB_CHECK((cid < active_cid_table_size), ESP_ERR_INVALID_ARG, "cid is not found in the table.");
|
||||||
|
|
||||||
|
characteristic_descriptor_t* cid_info = active_cid_table[cid];
|
||||||
|
SENSE_MB_CHECK((cid_info != NULL), ESP_ERR_INVALID_STATE, "incorrect cid table or not initialized.");
|
||||||
|
SENSE_MB_CHECK((cid_info->instance_ptr != NULL), ESP_ERR_INVALID_STATE, "incorrect cid table instance_ptr.");
|
||||||
|
SENSE_MB_CHECK((cid_info->param_key != NULL), ESP_ERR_INVALID_STATE, "incorrect cid table param_key.");
|
||||||
|
SENSE_MB_CHECK((cid_info->cid == cid), ESP_ERR_INVALID_STATE, "incorrect cid in the table or not initialized.");
|
||||||
|
*cid_data = *cid_info; // Set cid data
|
||||||
|
cid_info->change_flag = 0x00; // Reset flag once we get changed value
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Todo: add modbus code here
|
11
examples/protocols/modbus_master/sdkconfig.defaults
Normal file
11
examples/protocols/modbus_master/sdkconfig.defaults
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#
|
||||||
|
# Modbus configuration
|
||||||
|
#
|
||||||
|
CONFIG_MB_TIMER_PORT_ENABLED=y
|
||||||
|
CONFIG_MB_TIMER_GROUP=0
|
||||||
|
CONFIG_MB_TIMER_INDEX=0
|
||||||
|
CONFIG_MB_MASTER_DELAY_MS_CONVERT=200
|
||||||
|
CONFIG_MB_MASTER_TIMEOUT_MS_RESPOND=150
|
||||||
|
CONFIG_MB_UART_RXD=22
|
||||||
|
CONFIG_MB_UART_TXD=23
|
||||||
|
CONFIG_MB_UART_RTS=18
|
BIN
examples/protocols/modbus_master/tool/Mbslav1.mbs
Normal file
BIN
examples/protocols/modbus_master/tool/Mbslav1.mbs
Normal file
Binary file not shown.
BIN
examples/protocols/modbus_master/tool/Mbslav2.mbs
Normal file
BIN
examples/protocols/modbus_master/tool/Mbslav2.mbs
Normal file
Binary file not shown.
BIN
examples/protocols/modbus_master/tool/Mbslav3.mbs
Normal file
BIN
examples/protocols/modbus_master/tool/Mbslav3.mbs
Normal file
Binary file not shown.
BIN
examples/protocols/modbus_master/tool/Mbslav4.mbs
Normal file
BIN
examples/protocols/modbus_master/tool/Mbslav4.mbs
Normal file
Binary file not shown.
BIN
examples/protocols/modbus_master/tool/ModbusSlave_workspace.png
Normal file
BIN
examples/protocols/modbus_master/tool/ModbusSlave_workspace.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
BIN
examples/protocols/modbus_master/tool/workspace.msw
Normal file
BIN
examples/protocols/modbus_master/tool/workspace.msw
Normal file
Binary file not shown.
@ -72,8 +72,9 @@ void app_main()
|
|||||||
|
|
||||||
// Set UART log level
|
// Set UART log level
|
||||||
esp_log_level_set(TAG, ESP_LOG_INFO);
|
esp_log_level_set(TAG, ESP_LOG_INFO);
|
||||||
|
void* mbc_slave_handler = NULL;
|
||||||
|
|
||||||
mbcontroller_init(); // Initialization of Modbus controller
|
ESP_ERROR_CHECK(mbc_slave_init(MB_PORT_SERIAL_SLAVE, &mbc_slave_handler)); // Initialization of Modbus controller
|
||||||
|
|
||||||
// Setup communication parameters and start stack
|
// Setup communication parameters and start stack
|
||||||
comm_info.mode = MB_MODE_RTU;
|
comm_info.mode = MB_MODE_RTU;
|
||||||
@ -81,45 +82,45 @@ void app_main()
|
|||||||
comm_info.port = MB_PORT_NUM;
|
comm_info.port = MB_PORT_NUM;
|
||||||
comm_info.baudrate = MB_DEV_SPEED;
|
comm_info.baudrate = MB_DEV_SPEED;
|
||||||
comm_info.parity = MB_PARITY_NONE;
|
comm_info.parity = MB_PARITY_NONE;
|
||||||
ESP_ERROR_CHECK(mbcontroller_setup(comm_info));
|
ESP_ERROR_CHECK(mbc_slave_setup((void*)&comm_info));
|
||||||
|
|
||||||
// The code below initializes Modbus register area descriptors
|
// The code below initializes Modbus register area descriptors
|
||||||
// for Modbus Holding Registers, Input Registers, Coils and Discrete Inputs
|
// for Modbus Holding Registers, Input Registers, Coils and Discrete Inputs
|
||||||
// Initialization should be done for each supported Modbus register area according to register map.
|
// Initialization should be done for each supported Modbus register area according to register map.
|
||||||
// When external master trying to access the register in the area that is not initialized
|
// When external master trying to access the register in the area that is not initialized
|
||||||
// by mbcontroller_set_descriptor() API call then Modbus stack
|
// by mbc_slave_set_descriptor() API call then Modbus stack
|
||||||
// will send exception response for this register area.
|
// will send exception response for this register area.
|
||||||
reg_area.type = MB_PARAM_HOLDING; // Set type of 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.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.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.size = sizeof(holding_reg_params); // Set the size of register storage instance
|
||||||
ESP_ERROR_CHECK(mbcontroller_set_descriptor(reg_area));
|
ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
|
||||||
|
|
||||||
// Initialization of Input Registers area
|
// Initialization of Input Registers area
|
||||||
reg_area.type = MB_PARAM_INPUT;
|
reg_area.type = MB_PARAM_INPUT;
|
||||||
reg_area.start_offset = MB_REG_INPUT_START;
|
reg_area.start_offset = MB_REG_INPUT_START;
|
||||||
reg_area.address = (void*)&input_reg_params;
|
reg_area.address = (void*)&input_reg_params;
|
||||||
reg_area.size = sizeof(input_reg_params);
|
reg_area.size = sizeof(input_reg_params);
|
||||||
ESP_ERROR_CHECK(mbcontroller_set_descriptor(reg_area));
|
ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
|
||||||
|
|
||||||
// Initialization of Coils register area
|
// Initialization of Coils register area
|
||||||
reg_area.type = MB_PARAM_COIL;
|
reg_area.type = MB_PARAM_COIL;
|
||||||
reg_area.start_offset = MB_REG_COILS_START;
|
reg_area.start_offset = MB_REG_COILS_START;
|
||||||
reg_area.address = (void*)&coil_reg_params;
|
reg_area.address = (void*)&coil_reg_params;
|
||||||
reg_area.size = sizeof(coil_reg_params);
|
reg_area.size = sizeof(coil_reg_params);
|
||||||
ESP_ERROR_CHECK(mbcontroller_set_descriptor(reg_area));
|
ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
|
||||||
|
|
||||||
// Initialization of Discrete Inputs register area
|
// Initialization of Discrete Inputs register area
|
||||||
reg_area.type = MB_PARAM_DISCRETE;
|
reg_area.type = MB_PARAM_DISCRETE;
|
||||||
reg_area.start_offset = MB_REG_DISCRETE_INPUT_START;
|
reg_area.start_offset = MB_REG_DISCRETE_INPUT_START;
|
||||||
reg_area.address = (void*)&discrete_reg_params;
|
reg_area.address = (void*)&discrete_reg_params;
|
||||||
reg_area.size = sizeof(discrete_reg_params);
|
reg_area.size = sizeof(discrete_reg_params);
|
||||||
ESP_ERROR_CHECK(mbcontroller_set_descriptor(reg_area));
|
ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
|
||||||
|
|
||||||
setup_reg_data(); // Set values into known state
|
setup_reg_data(); // Set values into known state
|
||||||
|
|
||||||
// Starts of modbus controller and stack
|
// Starts of modbus controller and stack
|
||||||
ESP_ERROR_CHECK(mbcontroller_start());
|
ESP_ERROR_CHECK(mbc_slave_start());
|
||||||
|
|
||||||
// Set UART driver mode to Half Duplex
|
// Set UART driver mode to Half Duplex
|
||||||
ESP_ERROR_CHECK(uart_set_mode(MB_PORT_NUM, UART_MODE_RS485_HALF_DUPLEX));
|
ESP_ERROR_CHECK(uart_set_mode(MB_PORT_NUM, UART_MODE_RS485_HALF_DUPLEX));
|
||||||
@ -133,12 +134,12 @@ void app_main()
|
|||||||
// incremented each access cycle reaches the CHAN_DATA_MAX_VAL value.
|
// incremented each access cycle reaches the CHAN_DATA_MAX_VAL value.
|
||||||
for(;holding_reg_params.data_chan0 < MB_CHAN_DATA_MAX_VAL;) {
|
for(;holding_reg_params.data_chan0 < MB_CHAN_DATA_MAX_VAL;) {
|
||||||
// Check for read/write events of Modbus master for certain events
|
// Check for read/write events of Modbus master for certain events
|
||||||
mb_event_group_t event = mbcontroller_check_event(MB_READ_WRITE_MASK);
|
mb_event_group_t event = mbc_slave_check_event(MB_READ_WRITE_MASK);
|
||||||
const char* rw_str = (event & MB_READ_MASK) ? "READ" : "WRITE";
|
const char* rw_str = (event & MB_READ_MASK) ? "READ" : "WRITE";
|
||||||
// Filter events and process them accordingly
|
// Filter events and process them accordingly
|
||||||
if(event & (MB_EVENT_HOLDING_REG_WR | MB_EVENT_HOLDING_REG_RD)) {
|
if(event & (MB_EVENT_HOLDING_REG_WR | MB_EVENT_HOLDING_REG_RD)) {
|
||||||
// Get parameter information from parameter queue
|
// Get parameter information from parameter queue
|
||||||
ESP_ERROR_CHECK(mbcontroller_get_param_info(®_info, MB_PAR_INFO_GET_TOUT));
|
ESP_ERROR_CHECK(mbc_slave_get_param_info(®_info, MB_PAR_INFO_GET_TOUT));
|
||||||
printf("HOLDING %s: time_stamp(us):%u, mb_addr:%u, type:%u, st_address:0x%.4x, size:%u\r\n",
|
printf("HOLDING %s: time_stamp(us):%u, mb_addr:%u, type:%u, st_address:0x%.4x, size:%u\r\n",
|
||||||
rw_str,
|
rw_str,
|
||||||
(uint32_t)reg_info.time_stamp,
|
(uint32_t)reg_info.time_stamp,
|
||||||
@ -151,7 +152,7 @@ void app_main()
|
|||||||
holding_reg_params.data_chan0 += MB_CHAN_DATA_OFFSET;
|
holding_reg_params.data_chan0 += MB_CHAN_DATA_OFFSET;
|
||||||
}
|
}
|
||||||
} else if (event & MB_EVENT_INPUT_REG_RD) {
|
} else if (event & MB_EVENT_INPUT_REG_RD) {
|
||||||
ESP_ERROR_CHECK(mbcontroller_get_param_info(®_info, MB_PAR_INFO_GET_TOUT));
|
ESP_ERROR_CHECK(mbc_slave_get_param_info(®_info, MB_PAR_INFO_GET_TOUT));
|
||||||
printf("INPUT READ: time_stamp(us):%u, mb_addr:%u, type:%u, st_address:0x%.4x, size:%u\r\n",
|
printf("INPUT READ: time_stamp(us):%u, mb_addr:%u, type:%u, st_address:0x%.4x, size:%u\r\n",
|
||||||
(uint32_t)reg_info.time_stamp,
|
(uint32_t)reg_info.time_stamp,
|
||||||
(uint32_t)reg_info.mb_offset,
|
(uint32_t)reg_info.mb_offset,
|
||||||
@ -159,7 +160,7 @@ void app_main()
|
|||||||
(uint32_t)reg_info.address,
|
(uint32_t)reg_info.address,
|
||||||
(uint32_t)reg_info.size);
|
(uint32_t)reg_info.size);
|
||||||
} else if (event & MB_EVENT_DISCRETE_RD) {
|
} else if (event & MB_EVENT_DISCRETE_RD) {
|
||||||
ESP_ERROR_CHECK(mbcontroller_get_param_info(®_info, MB_PAR_INFO_GET_TOUT));
|
ESP_ERROR_CHECK(mbc_slave_get_param_info(®_info, MB_PAR_INFO_GET_TOUT));
|
||||||
printf("DISCRETE READ: time_stamp(us):%u, mb_addr:%u, type:%u, st_address:0x%.4x, size:%u\r\n",
|
printf("DISCRETE READ: time_stamp(us):%u, mb_addr:%u, type:%u, st_address:0x%.4x, size:%u\r\n",
|
||||||
(uint32_t)reg_info.time_stamp,
|
(uint32_t)reg_info.time_stamp,
|
||||||
(uint32_t)reg_info.mb_offset,
|
(uint32_t)reg_info.mb_offset,
|
||||||
@ -167,7 +168,7 @@ void app_main()
|
|||||||
(uint32_t)reg_info.address,
|
(uint32_t)reg_info.address,
|
||||||
(uint32_t)reg_info.size);
|
(uint32_t)reg_info.size);
|
||||||
} else if (event & (MB_EVENT_COILS_RD | MB_EVENT_COILS_WR)) {
|
} else if (event & (MB_EVENT_COILS_RD | MB_EVENT_COILS_WR)) {
|
||||||
ESP_ERROR_CHECK(mbcontroller_get_param_info(®_info, MB_PAR_INFO_GET_TOUT));
|
ESP_ERROR_CHECK(mbc_slave_get_param_info(®_info, MB_PAR_INFO_GET_TOUT));
|
||||||
printf("COILS %s: time_stamp(us):%u, mb_addr:%u, type:%u, st_address:0x%.4x, size:%u\r\n",
|
printf("COILS %s: time_stamp(us):%u, mb_addr:%u, type:%u, st_address:0x%.4x, size:%u\r\n",
|
||||||
rw_str,
|
rw_str,
|
||||||
(uint32_t)reg_info.time_stamp,
|
(uint32_t)reg_info.time_stamp,
|
||||||
@ -179,5 +180,5 @@ void app_main()
|
|||||||
}
|
}
|
||||||
// Destroy of Modbus controller once get maximum value of data_chan0
|
// Destroy of Modbus controller once get maximum value of data_chan0
|
||||||
printf("Modbus controller destroyed.");
|
printf("Modbus controller destroyed.");
|
||||||
ESP_ERROR_CHECK(mbcontroller_destroy());
|
ESP_ERROR_CHECK(mbc_slave_destroy());
|
||||||
}
|
}
|
||||||
|
6
examples/protocols/modbus_slave/sdkconfig.defaults
Normal file
6
examples/protocols/modbus_slave/sdkconfig.defaults
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#
|
||||||
|
# Modbus configuration
|
||||||
|
#
|
||||||
|
CONFIG_MB_UART_RXD=22
|
||||||
|
CONFIG_MB_UART_TXD=23
|
||||||
|
CONFIG_MB_UART_RTS=18
|
Loading…
x
Reference in New Issue
Block a user