forked from espressif/esp-modbus
examples: freemodbus add tcp support for common master/slave iface
Add TCP port files to provide Modbus TCP interface for communication Add freemodbus add tcp support for common master/slave iface and tcp example based on socket API The communication between master and slave checked for each example serial_master, serial_slave (use ModbusPoll TCP) update tcp example according netif changes, fix ci issues update TCP slave implementation update example_test.py to to set IP through stdin update API documentation event bit instead of semahore to lock communication resource update default options and master/slave port files Closes https://github.com/espressif/esp-idf/issues/858 Closes IDF-452 * Original commit: espressif/esp-idf@d0b9829eef
This commit is contained in:
1 parent
837bbe01b4
commit
bf49bec977
91 files changed
+5768
-379
No files matched your search
@@ -1,6 +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
|
||||
set(srcs
|
||||
set(srcs
|
||||
"common/esp_modbus_master.c"
|
||||
"common/esp_modbus_slave.c"
|
||||
"modbus/mb.c"
|
||||
@@ -11,6 +11,7 @@ set(srcs
|
||||
"modbus/rtu/mbrtu.c"
|
||||
"modbus/rtu/mbcrc.c"
|
||||
"modbus/tcp/mbtcp.c"
|
||||
"modbus/tcp/mbtcp_m.c"
|
||||
"port/port.c"
|
||||
"port/portevent.c"
|
||||
"port/portevent_m.c"
|
||||
@@ -32,12 +33,25 @@ set(srcs
|
||||
"modbus/functions/mbfuncother.c"
|
||||
"modbus/functions/mbutils.c"
|
||||
"serial_slave/modbus_controller/mbc_serial_slave.c"
|
||||
"serial_master/modbus_controller/mbc_serial_master.c")
|
||||
"serial_master/modbus_controller/mbc_serial_master.c"
|
||||
"tcp_slave/port/port_tcp_slave.c"
|
||||
"tcp_slave/modbus_controller/mbc_tcp_slave.c"
|
||||
"tcp_master/modbus_controller/mbc_tcp_master.c"
|
||||
"tcp_master/port/port_tcp_master.c"
|
||||
"common/esp_modbus_master_tcp.c"
|
||||
"common/esp_modbus_slave_tcp.c"
|
||||
"common/esp_modbus_master_serial.c"
|
||||
"common/esp_modbus_slave_serial.c")
|
||||
|
||||
set(include_dirs common/include)
|
||||
|
||||
set(priv_include_dirs common port modbus modbus/ascii modbus/functions
|
||||
modbus/rtu modbus/tcp modbus/include)
|
||||
|
||||
list(APPEND priv_include_dirs serial_slave/port serial_slave/modbus_controller
|
||||
serial_master/port serial_master/modbus_controller)
|
||||
serial_master/port serial_master/modbus_controller
|
||||
tcp_slave/port tcp_slave/modbus_controller
|
||||
tcp_master/port tcp_master/modbus_controller)
|
||||
|
||||
idf_component_register(SRCS "${srcs}"
|
||||
INCLUDE_DIRS "${include_dirs}"
|
||||
|
||||
@@ -1,5 +1,40 @@
|
||||
menu "Modbus configuration"
|
||||
|
||||
config FMB_COMM_MODE_TCP_EN
|
||||
bool "Enable Modbus stack support for TCP communication mode"
|
||||
default y
|
||||
help
|
||||
Enable Modbus TCP option for stack.
|
||||
|
||||
config FMB_TCP_PORT_DEFAULT
|
||||
int "Modbus TCP port number"
|
||||
range 0 65535
|
||||
default 502
|
||||
depends on FMB_COMM_MODE_TCP_EN
|
||||
help
|
||||
Modbus default port number used by Modbus TCP stack
|
||||
|
||||
config FMB_TCP_PORT_MAX_CONN
|
||||
int "Maximum allowed connections for TCP stack"
|
||||
range 1 6
|
||||
default 5
|
||||
depends on FMB_COMM_MODE_TCP_EN
|
||||
help
|
||||
Maximum allowed connections number for Modbus TCP stack.
|
||||
This is used by Modbus master and slave port layer to establish connections.
|
||||
This parameter may decrease performance of Modbus stack and can cause
|
||||
increasing of processing time (increase only if absolutely necessary).
|
||||
|
||||
config FMB_TCP_CONNECTION_TOUT_SEC
|
||||
int "Modbus TCP connection timeout"
|
||||
range 1 3600
|
||||
default 20
|
||||
depends on FMB_COMM_MODE_TCP_EN
|
||||
help
|
||||
Modbus TCP connection timeout in seconds.
|
||||
Once expired the current connection with the client will be closed
|
||||
and Modbus slave will be waiting for new connection to accept.
|
||||
|
||||
config FMB_COMM_MODE_RTU_EN
|
||||
bool "Enable Modbus stack support for RTU mode"
|
||||
default y
|
||||
@@ -15,7 +50,7 @@ menu "Modbus configuration"
|
||||
config FMB_MASTER_TIMEOUT_MS_RESPOND
|
||||
int "Slave respond timeout (Milliseconds)"
|
||||
default 150
|
||||
range 50 400
|
||||
range 50 3000
|
||||
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.
|
||||
@@ -36,12 +71,12 @@ menu "Modbus configuration"
|
||||
Modbus serial driver queue length. It is used by event queue task.
|
||||
See the serial driver API for more information.
|
||||
|
||||
config FMB_SERIAL_TASK_STACK_SIZE
|
||||
int "Modbus serial task stack size"
|
||||
range 768 8192
|
||||
default 2048
|
||||
config FMB_PORT_TASK_STACK_SIZE
|
||||
int "Modbus port task stack size"
|
||||
range 2048 8192
|
||||
default 4096
|
||||
help
|
||||
Modbus serial task stack size for event queue task.
|
||||
Modbus port task stack size for rx/tx event processing.
|
||||
It may be adjusted when debugging is enabled (for example).
|
||||
|
||||
config FMB_SERIAL_BUF_SIZE
|
||||
@@ -68,19 +103,19 @@ menu "Modbus configuration"
|
||||
depends on FMB_COMM_MODE_ASCII_EN
|
||||
help
|
||||
This option defines response timeout of slave in milliseconds for ASCII communication mode.
|
||||
Thus the timeout will expire and allow the master’s program to handle the error.
|
||||
Thus the timeout will expire and allow the master program to handle the error.
|
||||
|
||||
config FMB_SERIAL_TASK_PRIO
|
||||
int "Modbus serial task priority"
|
||||
config FMB_PORT_TASK_PRIO
|
||||
int "Modbus port task priority"
|
||||
range 3 10
|
||||
default 10
|
||||
help
|
||||
Modbus UART driver event task priority.
|
||||
The priority of Modbus controller task is equal to (CONFIG_FMB_SERIAL_TASK_PRIO - 1).
|
||||
Modbus port data processing task priority.
|
||||
The priority of Modbus controller task is equal to (CONFIG_FMB_PORT_TASK_PRIO - 1).
|
||||
|
||||
config FMB_CONTROLLER_SLAVE_ID_SUPPORT
|
||||
bool "Modbus controller slave ID support"
|
||||
default n
|
||||
default y
|
||||
help
|
||||
Modbus slave ID support enable.
|
||||
When enabled the Modbus <Report Slave ID> command is supported by stack.
|
||||
|
||||
@@ -16,43 +16,15 @@
|
||||
#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 mbc_master_init_iface(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).",
|
||||
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;
|
||||
master_interface_ptr = (mb_master_interface_t*) handler;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,7 +42,7 @@ esp_err_t mbc_master_destroy(void)
|
||||
error = master_interface_ptr->destroy();
|
||||
MB_MASTER_CHECK((error == ESP_OK),
|
||||
error,
|
||||
"SERIAL master destroy failure error=(0x%x).",
|
||||
"Master destroy failure, error=(0x%x).",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
@@ -87,7 +59,7 @@ esp_err_t mbc_master_get_cid_info(uint16_t cid, const mb_parameter_descriptor_t*
|
||||
error = master_interface_ptr->get_cid_info(cid, param_info);
|
||||
MB_MASTER_CHECK((error == ESP_OK),
|
||||
error,
|
||||
"SERIAL master get cid info failure error=(0x%x).",
|
||||
"Master get cid info failure, error=(0x%x).",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
@@ -107,7 +79,7 @@ esp_err_t mbc_master_get_parameter(uint16_t cid, char* name, uint8_t* value, uin
|
||||
error = master_interface_ptr->get_parameter(cid, name, value, type);
|
||||
MB_MASTER_CHECK((error == ESP_OK),
|
||||
error,
|
||||
"SERIAL master get parameter failure error=(0x%x) (%s).",
|
||||
"Master get parameter failure, error=(0x%x) (%s).",
|
||||
error, esp_err_to_name(error));
|
||||
return error;
|
||||
}
|
||||
@@ -127,7 +99,7 @@ esp_err_t mbc_master_send_request(mb_param_request_t* request, void* data_ptr)
|
||||
error = master_interface_ptr->send_request(request, data_ptr);
|
||||
MB_MASTER_CHECK((error == ESP_OK),
|
||||
error,
|
||||
"SERIAL master send request failure error=(0x%x) (%s).",
|
||||
"Master send request failure error=(0x%x) (%s).",
|
||||
error, esp_err_to_name(error));
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -135,7 +107,8 @@ esp_err_t mbc_master_send_request(mb_param_request_t* request, void* data_ptr)
|
||||
/**
|
||||
* 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 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),
|
||||
@@ -147,7 +120,7 @@ esp_err_t mbc_master_set_descriptor(const mb_parameter_descriptor_t* descriptor,
|
||||
error = master_interface_ptr->set_descriptor(descriptor, num_elements);
|
||||
MB_MASTER_CHECK((error == ESP_OK),
|
||||
error,
|
||||
"SERIAL master set descriptor failure error=(0x%x) (%s).",
|
||||
"Master set descriptor failure, error=(0x%x) (%s).",
|
||||
error, esp_err_to_name(error));
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -167,7 +140,7 @@ esp_err_t mbc_master_set_parameter(uint16_t cid, char* name, uint8_t* value, uin
|
||||
error = master_interface_ptr->set_parameter(cid, name, value, type);
|
||||
MB_MASTER_CHECK((error == ESP_OK),
|
||||
error,
|
||||
"SERIAL master set parameter failure error=(0x%x) (%s).",
|
||||
"Master set parameter failure, error=(0x%x) (%s).",
|
||||
error, esp_err_to_name(error));
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -187,7 +160,7 @@ esp_err_t mbc_master_setup(void* comm_info)
|
||||
error = master_interface_ptr->setup(comm_info);
|
||||
MB_MASTER_CHECK((error == ESP_OK),
|
||||
error,
|
||||
"SERIAL master setup failure error=(0x%x) (%s).",
|
||||
"Master setup failure, error=(0x%x) (%s).",
|
||||
error, esp_err_to_name(error));
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -207,7 +180,7 @@ esp_err_t mbc_master_start(void)
|
||||
error = master_interface_ptr->start();
|
||||
MB_MASTER_CHECK((error == ESP_OK),
|
||||
error,
|
||||
"SERIAL master start failure error=(0x%x) (%s).",
|
||||
"Master start failure, error=(0x%x) (%s).",
|
||||
error, esp_err_to_name(error));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/* 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 slave defines
|
||||
#include "mbc_serial_master.h" // for public interface defines
|
||||
|
||||
/**
|
||||
* Initialization of Modbus master serial
|
||||
*/
|
||||
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_handler);
|
||||
break;
|
||||
default:
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
if ((port_handler != NULL) && (error == ESP_OK)) {
|
||||
mbc_master_init_iface(port_handler);
|
||||
*handler = port_handler;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/* 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 "esp_modbus_master.h" // for public interface defines
|
||||
#include "mbc_tcp_master.h" // for public interface defines
|
||||
|
||||
/**
|
||||
* Initialization of Modbus TCP Master controller interface
|
||||
*/
|
||||
esp_err_t mbc_master_init_tcp(void** handler)
|
||||
{
|
||||
void* port_handler = NULL;
|
||||
esp_err_t error = mbc_tcp_master_create(&port_handler);
|
||||
|
||||
if ((port_handler != NULL) && (error == ESP_OK)) {
|
||||
mbc_master_init_iface(port_handler);
|
||||
*handler = port_handler;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
@@ -19,7 +19,6 @@
|
||||
#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_FMB_CONTROLLER_SLAVE_ID_SUPPORT
|
||||
|
||||
@@ -29,7 +28,7 @@
|
||||
#define MB_ID_BYTE3(id) ((uint8_t)(((uint32_t)(id) >> 24) & 0xFF))
|
||||
|
||||
#define MB_CONTROLLER_SLAVE_ID (CONFIG_FMB_CONTROLLER_SLAVE_ID)
|
||||
#define MB_SLAVE_ID_SHORT (MB_ID_BYTE3(CONFIG_FMB_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),
|
||||
@@ -41,37 +40,9 @@ static uint8_t mb_slave_id[] = { MB_ID_BYTE0(MB_CONTROLLER_SLAVE_ID),
|
||||
// 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 mbc_slave_init_iface(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;
|
||||
slave_interface_ptr = (mb_slave_interface_t*) handler;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,7 +63,8 @@ esp_err_t mbc_slave_destroy(void)
|
||||
error = slave_interface_ptr->destroy();
|
||||
MB_SLAVE_CHECK((error == ESP_OK),
|
||||
ESP_ERR_INVALID_STATE,
|
||||
"SERIAL slave destroy failure error=(0x%x).", error);
|
||||
"Slave destroy failure error=(0x%x).",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -111,7 +83,8 @@ esp_err_t mbc_slave_setup(void* comm_info)
|
||||
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);
|
||||
"Slave setup failure error=(0x%x).",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -134,7 +107,9 @@ esp_err_t mbc_slave_start(void)
|
||||
#endif
|
||||
error = slave_interface_ptr->start();
|
||||
MB_SLAVE_CHECK((error == ESP_OK),
|
||||
error, "SERIAL slave start failure error=(0x%x).", error);
|
||||
ESP_ERR_INVALID_STATE,
|
||||
"Slave start failure error=(0x%x).",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -167,7 +142,9 @@ esp_err_t mbc_slave_get_param_info(mb_param_info_t* reg_info, uint32_t timeout)
|
||||
"Slave interface is not correctly initialized.");
|
||||
error = slave_interface_ptr->get_param_info(reg_info, timeout);
|
||||
MB_SLAVE_CHECK((error == ESP_OK),
|
||||
error, "SERIAL slave get parameter info failure error=(0x%x).", error);
|
||||
ESP_ERR_INVALID_STATE,
|
||||
"Slave get parameter info failure error=(0x%x).",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -185,7 +162,9 @@ esp_err_t mbc_slave_set_descriptor(mb_register_area_descriptor_t descr_data)
|
||||
"Slave interface is not correctly initialized.");
|
||||
error = slave_interface_ptr->set_descriptor(descr_data);
|
||||
MB_SLAVE_CHECK((error == ESP_OK),
|
||||
error, "SERIAL slave set descriptor failure error=(0x%x).", error);
|
||||
ESP_ERR_INVALID_STATE,
|
||||
"Slave set descriptor failure error=(0x%x).",
|
||||
(uint16_t)error);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/* 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 interface define
|
||||
#include "esp_modbus_slave.h" // for public slave defines
|
||||
#include "mbc_serial_slave.h" // for public interface defines
|
||||
|
||||
/**
|
||||
* Initialization of Modbus Serial 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_handler);
|
||||
break;
|
||||
default:
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
if ((port_handler != NULL) && (error == ESP_OK)) {
|
||||
mbc_slave_init_iface(port_handler);
|
||||
*handler = port_handler;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/* 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 "esp_modbus_slave.h" // for public slave defines
|
||||
#include "mbc_tcp_slave.h" // for public interface defines
|
||||
|
||||
/**
|
||||
* Initialization of Modbus TCP Slave controller
|
||||
*/
|
||||
esp_err_t mbc_slave_init_tcp(void** handler)
|
||||
{
|
||||
void* port_handler = NULL;
|
||||
esp_err_t error = mbc_tcp_slave_create(&port_handler);
|
||||
|
||||
if ((port_handler != NULL) && (error == ESP_OK)) {
|
||||
mbc_slave_init_iface(port_handler);
|
||||
*handler = port_handler;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
@@ -23,7 +23,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#define MB_CONTROLLER_STACK_SIZE (CONFIG_FMB_CONTROLLER_STACK_SIZE) // Stack size for Modbus controller
|
||||
#define MB_CONTROLLER_PRIORITY (CONFIG_FMB_SERIAL_TASK_PRIO - 1) // priority of MB controller task
|
||||
#define MB_CONTROLLER_PRIORITY (CONFIG_FMB_PORT_TASK_PRIO - 1) // priority of MB controller task
|
||||
|
||||
// Default port defines
|
||||
#define MB_DEVICE_ADDRESS (1) // Default slave device address in Modbus
|
||||
@@ -67,8 +67,9 @@ 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_TCP_SLAVE, /*!< Modbus port type TCP slave. */
|
||||
MB_PORT_COUNT, /*!< Modbus port count. */
|
||||
MB_PORT_INACTIVE = 0xFF
|
||||
} mb_port_type_t;
|
||||
|
||||
/**
|
||||
@@ -104,8 +105,17 @@ typedef enum {
|
||||
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
|
||||
MB_MODE_TCP, /*!< TCP communication mode. */
|
||||
MB_MODE_UDP /*!< UDP communication mode. */
|
||||
} mb_mode_type_t;
|
||||
|
||||
/*!
|
||||
* \brief Modbus TCP type of address.
|
||||
*/
|
||||
typedef enum {
|
||||
MB_IPV4 = 0, /*!< TCP IPV4 addressing */
|
||||
MB_IPV6 = 1 /*!< TCP IPV6 addressing */
|
||||
} mb_tcp_addr_type_t;
|
||||
|
||||
/**
|
||||
* @brief Device communication structure to setup Modbus controller
|
||||
@@ -120,27 +130,27 @@ typedef union {
|
||||
uart_parity_t parity; /*!< Modbus UART parity settings */
|
||||
uint16_t dummy_port; /*!< Dummy field, unused */
|
||||
};
|
||||
// Tcp communication structure
|
||||
// TCP/UDP 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_mode_type_t ip_mode; /*!< Modbus communication mode */
|
||||
uint16_t ip_port; /*!< Modbus port */
|
||||
mb_tcp_addr_type_t ip_addr_type; /*!< Modbus address type */
|
||||
void* ip_addr; /*!< Modbus address table for connection */
|
||||
void* ip_netif_ptr; /*!< Modbus network interface */
|
||||
};
|
||||
} 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 */
|
||||
typedef esp_err_t (*iface_init)(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 */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _MB_IFACE_COMMON_H
|
||||
|
||||
@@ -107,18 +107,39 @@ typedef struct {
|
||||
uint16_t reg_size; /*!< Modbus number of registers */
|
||||
} mb_param_request_t;
|
||||
|
||||
// Master interface public functions
|
||||
/**
|
||||
* @brief Initialize Modbus controller and stack
|
||||
* @brief Initialize Modbus controller and stack for TCP port
|
||||
*
|
||||
* @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_OK Success
|
||||
* - ESP_ERR_NO_MEM Parameter error
|
||||
* - ESP_ERR_NOT_SUPPORTED Port type not supported
|
||||
* - ESP_ERR_INVALID_STATE Initialization failure
|
||||
*/
|
||||
esp_err_t mbc_master_init_tcp(void** handler);
|
||||
|
||||
/**
|
||||
* @brief Initialize Modbus Master controller and stack for Serial port
|
||||
*
|
||||
* @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_NOT_SUPPORTED Port type not supported
|
||||
* - ESP_ERR_INVALID_STATE Initialization failure
|
||||
*/
|
||||
esp_err_t mbc_master_init(mb_port_type_t port_type, void** handler);
|
||||
|
||||
/**
|
||||
* @brief Initialize Modbus Master controller interface handle
|
||||
*
|
||||
* @param[in] handler - pointer to master data structure
|
||||
* @return None
|
||||
*/
|
||||
void mbc_master_init_iface(void* handler);
|
||||
|
||||
/**
|
||||
* @brief Destroy Modbus controller and stack
|
||||
*
|
||||
|
||||
@@ -50,17 +50,38 @@ typedef struct {
|
||||
} mb_register_area_descriptor_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize Modbus controller and stack
|
||||
* @brief Initialize Modbus Slave controller and stack for TCP port
|
||||
*
|
||||
* @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_OK Success
|
||||
* - ESP_ERR_NO_MEM Parameter error
|
||||
* - ESP_ERR_NOT_SUPPORTED Port type not supported
|
||||
* - ESP_ERR_INVALID_STATE Initialization failure
|
||||
*/
|
||||
esp_err_t mbc_slave_init_tcp(void** handler);
|
||||
|
||||
/**
|
||||
* @brief Initialize Modbus Slave controller and stack for Serial port
|
||||
*
|
||||
* @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_NOT_SUPPORTED Port type not supported
|
||||
* - ESP_ERR_INVALID_STATE Initialization failure
|
||||
*/
|
||||
//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 Initialize Modbus Slave controller interface handle
|
||||
*
|
||||
* @param[in] handler - pointer to slave interface data structure
|
||||
* @return None
|
||||
*/
|
||||
void mbc_slave_init_iface(void* handler);
|
||||
|
||||
/**
|
||||
* @brief Destroy Modbus controller and stack
|
||||
*
|
||||
|
||||
@@ -30,4 +30,3 @@
|
||||
#include "esp_modbus_slave.h"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
|
||||
COMPONENT_ADD_INCLUDEDIRS := common/include
|
||||
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_PRIV_INCLUDEDIRS += tcp_slave/port tcp_slave/modbus_controller
|
||||
COMPONENT_PRIV_INCLUDEDIRS += tcp_master/port tcp_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 += serial_slave/port serial_slave/modbus_controller
|
||||
COMPONENT_SRCDIRS += serial_master/port serial_master/modbus_controller
|
||||
COMPONENT_SRCDIRS += tcp_slave/port tcp_slave/modbus_controller
|
||||
COMPONENT_SRCDIRS += tcp_master/port tcp_master/modbus_controller
|
||||
COMPONENT_SRCDIRS += port
|
||||
@@ -393,7 +393,8 @@ xMBASCIITransmitFSM( void )
|
||||
return xNeedPoll;
|
||||
}
|
||||
|
||||
BOOL MB_PORT_ISR_ATTR xMBASCIITimerT1SExpired( void )
|
||||
BOOL MB_PORT_ISR_ATTR
|
||||
xMBASCIITimerT1SExpired( void )
|
||||
{
|
||||
switch ( eRcvState )
|
||||
{
|
||||
|
||||
@@ -47,7 +47,6 @@
|
||||
#if MB_MASTER_ASCII_ENABLED > 0
|
||||
|
||||
/* ----------------------- Defines ------------------------------------------*/
|
||||
#define MB_TIMER_TICS_PER_MS 20UL
|
||||
|
||||
/* ----------------------- Type definitions ---------------------------------*/
|
||||
typedef enum
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
eMBException prveMBError2Exception( eMBErrorCode eErrorCode );
|
||||
|
||||
/* ----------------------- Start implementation -----------------------------*/
|
||||
#if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED
|
||||
#if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED || MB_TCP_ENABLED
|
||||
|
||||
#if MB_FUNC_READ_COILS_ENABLED
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
eMBException prveMBError2Exception( eMBErrorCode eErrorCode );
|
||||
|
||||
/* ----------------------- Start implementation -----------------------------*/
|
||||
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED
|
||||
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
|
||||
|
||||
#if MB_FUNC_READ_COILS_ENABLED
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
eMBException prveMBError2Exception( eMBErrorCode eErrorCode );
|
||||
|
||||
/* ----------------------- Start implementation -----------------------------*/
|
||||
#if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED
|
||||
#if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED || MB_TCP_ENABLED
|
||||
|
||||
#if MB_FUNC_READ_COILS_ENABLED
|
||||
|
||||
@@ -124,4 +124,3 @@ eMBFuncReadDiscreteInputs( UCHAR * pucFrame, USHORT * usLen )
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
eMBException prveMBError2Exception( eMBErrorCode eErrorCode );
|
||||
|
||||
/* ----------------------- Start implementation -----------------------------*/
|
||||
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED
|
||||
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
|
||||
|
||||
#if MB_FUNC_READ_DISCRETE_INPUTS_ENABLED
|
||||
|
||||
@@ -159,4 +159,4 @@ eMBMasterFuncReadDiscreteInputs( UCHAR * pucFrame, USHORT * usLen )
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif // #if MB_SERIAL_MASTER_RTU_ENABLED > 0 || MB_SERIAL_MASTER_ASCII_ENABLED > 0
|
||||
#endif // #if MB_SERIAL_MASTER_RTU_ENABLED || MB_SERIAL_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
|
||||
@@ -70,7 +70,7 @@
|
||||
eMBException prveMBError2Exception( eMBErrorCode eErrorCode );
|
||||
|
||||
/* ----------------------- Start implementation -----------------------------*/
|
||||
#if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED
|
||||
#if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED || MB_TCP_ENABLED
|
||||
|
||||
#if MB_FUNC_WRITE_HOLDING_ENABLED
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
eMBException prveMBError2Exception( eMBErrorCode eErrorCode );
|
||||
|
||||
/* ----------------------- Start implementation -----------------------------*/
|
||||
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED
|
||||
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
|
||||
|
||||
#if MB_FUNC_WRITE_HOLDING_ENABLED
|
||||
|
||||
@@ -451,5 +451,5 @@ eMBMasterFuncReadWriteMultipleHoldingRegister( UCHAR * pucFrame, USHORT * usLen
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif // #if MB_MASTER_RTU_ENABLED > 0 || MB_MASTER_ASCII_ENABLED > 0
|
||||
#endif // #if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
eMBException prveMBError2Exception( eMBErrorCode eErrorCode );
|
||||
|
||||
/* ----------------------- Start implementation -----------------------------*/
|
||||
#if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED
|
||||
#if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED || MB_TCP_ENABLED
|
||||
|
||||
#if MB_FUNC_READ_INPUT_ENABLED
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
eMBException prveMBError2Exception( eMBErrorCode eErrorCode );
|
||||
|
||||
/* ----------------------- Start implementation -----------------------------*/
|
||||
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED
|
||||
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
|
||||
#if MB_FUNC_READ_INPUT_ENABLED
|
||||
|
||||
/**
|
||||
@@ -144,4 +144,4 @@ eMBMasterFuncReadInputRegister( UCHAR * pucFrame, USHORT * usLen )
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif // #if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED
|
||||
#endif // #if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
|
||||
@@ -41,7 +41,7 @@
|
||||
#include "mbproto.h"
|
||||
#include "mbconfig.h"
|
||||
|
||||
#if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED
|
||||
#if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED || MB_TCP_ENABLED
|
||||
|
||||
#if MB_FUNC_OTHER_REP_SLAVEID_ENABLED
|
||||
|
||||
|
||||
@@ -76,10 +76,6 @@ PR_BEGIN_EXTERN_C
|
||||
#define MB_FUNC_CODE_MAX 127
|
||||
|
||||
/* ----------------------- Type definitions ---------------------------------*/
|
||||
#ifndef _MB_M_H
|
||||
|
||||
#define MB_FUNC_CODE_MAX 127
|
||||
|
||||
/*! \ingroup modbus
|
||||
* \brief Modbus serial transmission modes (RTU/ASCII).
|
||||
*
|
||||
@@ -126,8 +122,6 @@ typedef enum
|
||||
MB_ETIMEDOUT /*!< timeout error occurred. */
|
||||
} eMBErrorCode;
|
||||
|
||||
#endif
|
||||
|
||||
/* ----------------------- Function prototypes ------------------------------*/
|
||||
/*! \ingroup modbus
|
||||
* \brief Initialize the Modbus protocol stack.
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
#include "mbconfig.h"
|
||||
#include "port.h"
|
||||
#include "mb.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
PR_BEGIN_EXTERN_C
|
||||
@@ -73,54 +74,6 @@ PR_BEGIN_EXTERN_C
|
||||
*/
|
||||
#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.
|
||||
*/
|
||||
@@ -167,7 +120,7 @@ typedef enum
|
||||
* is returned:
|
||||
* - eMBErrorCode::MB_EPORTERR IF the porting layer returned an error.
|
||||
*/
|
||||
eMBErrorCode eMBMasterInit( eMBMode eMode, UCHAR ucPort,
|
||||
eMBErrorCode eMBMasterSerialInit( eMBMode eMode, UCHAR ucPort,
|
||||
ULONG ulBaudRate, eMBParity eParity );
|
||||
|
||||
/*! \ingroup modbus
|
||||
|
||||
@@ -54,15 +54,16 @@ PR_BEGIN_EXTERN_C
|
||||
/*! \brief If Modbus Master RTU support is enabled. */
|
||||
#define MB_MASTER_RTU_ENABLED ( CONFIG_FMB_COMM_MODE_RTU_EN )
|
||||
/*! \brief If Modbus Master TCP support is enabled. */
|
||||
#define MB_MASTER_TCP_ENABLED ( 0 )
|
||||
#define MB_MASTER_TCP_ENABLED ( CONFIG_FMB_COMM_MODE_TCP_EN )
|
||||
/*! \brief If Modbus Slave ASCII support is enabled. */
|
||||
#define MB_SLAVE_ASCII_ENABLED ( CONFIG_FMB_COMM_MODE_ASCII_EN )
|
||||
/*! \brief If Modbus Slave RTU support is enabled. */
|
||||
#define MB_SLAVE_RTU_ENABLED ( CONFIG_FMB_COMM_MODE_RTU_EN )
|
||||
/*! \brief If Modbus Slave TCP support is enabled. */
|
||||
#define MB_TCP_ENABLED ( 1 )
|
||||
#if !CONFIG_FMB_COMM_MODE_ASCII_EN && !CONFIG_FMB_COMM_MODE_RTU_EN
|
||||
#error "None of Modbus communication mode is enabled. Please enable one of ASCII or RTU mode in Kconfig."
|
||||
#define MB_TCP_ENABLED ( CONFIG_FMB_COMM_MODE_TCP_EN )
|
||||
|
||||
#if !CONFIG_FMB_COMM_MODE_ASCII_EN && !CONFIG_FMB_COMM_MODE_RTU_EN && !MB_MASTER_TCP_ENABLED && !MB_TCP_ENABLED
|
||||
#error "None of Modbus communication mode is enabled. Please enable one of (ASCII, RTU, TCP) mode in Kconfig."
|
||||
#endif
|
||||
|
||||
/*! \brief This option defines the number of data bits per ASCII character.
|
||||
@@ -153,7 +154,7 @@ PR_BEGIN_EXTERN_C
|
||||
PR_END_EXTERN_C
|
||||
#endif
|
||||
|
||||
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED
|
||||
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_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_FMB_MASTER_DELAY_MS_CONVERT )
|
||||
|
||||
@@ -63,16 +63,24 @@ PR_BEGIN_EXTERN_C
|
||||
*/
|
||||
|
||||
/* ----------------------- Defines ------------------------------------------*/
|
||||
#define MB_PDU_SIZE_MAX ( 253 ) /*!< Maximum size of a PDU. */
|
||||
#define MB_PDU_SIZE_MIN 1 /*!< Function Code */
|
||||
#define MB_PDU_FUNC_OFF 0 /*!< Offset of function code in PDU. */
|
||||
#define MB_PDU_DATA_OFF 1 /*!< Offset for response data in PDU. */
|
||||
#define MB_PDU_SIZE_MAX 253 /*!< Maximum size of a PDU. */
|
||||
#define MB_PDU_SIZE_MIN 1 /*!< Function Code */
|
||||
#define MB_PDU_FUNC_OFF 0 /*!< Offset of function code in PDU. */
|
||||
#define MB_PDU_DATA_OFF 1 /*!< Offset for response data in PDU. */
|
||||
|
||||
#define MB_SER_PDU_SIZE_MAX ( MB_SERIAL_BUF_SIZE ) /*!< Maximum size of a Modbus frame. */
|
||||
#define MB_SER_PDU_SIZE_LRC 1 /*!< Size of LRC 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. */
|
||||
#define MB_SER_PDU_SIZE_CRC 2 /*!< Size of CRC field in PDU. */
|
||||
#define MB_SER_PDU_SIZE_MAX MB_SERIAL_BUF_SIZE /*!< Maximum size of a Modbus frame. */
|
||||
#define MB_SER_PDU_SIZE_LRC 1 /*!< Size of LRC 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. */
|
||||
#define MB_SER_PDU_SIZE_CRC 2 /*!< Size of CRC field in PDU. */
|
||||
|
||||
#define MB_TCP_TID 0
|
||||
#define MB_TCP_PID 2
|
||||
#define MB_TCP_LEN 4
|
||||
#define MB_TCP_UID 6
|
||||
#define MB_TCP_FUNC 7
|
||||
|
||||
#define MB_TCP_PSEUDO_ADDRESS 255
|
||||
|
||||
/* ----------------------- Prototypes 0-------------------------------------*/
|
||||
typedef void ( *pvMBFrameStart ) ( void );
|
||||
|
||||
@@ -62,7 +62,7 @@ typedef enum
|
||||
EV_FRAME_TRANSMIT = 0x10 /*!< Frame transmit. */
|
||||
} eMBEventType;
|
||||
|
||||
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED
|
||||
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
|
||||
typedef enum {
|
||||
EV_MASTER_NO_EVENT = 0x0000,
|
||||
EV_MASTER_READY = 0x0001, /*!< Startup finished. */
|
||||
@@ -74,14 +74,15 @@ typedef enum {
|
||||
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. */
|
||||
EV_MASTER_ERROR_EXECUTE_FUNCTION = 0x0200 /*!< Request execute function error. */
|
||||
} eMBMasterEventType;
|
||||
|
||||
typedef enum {
|
||||
EV_ERROR_INIT, /*!< No error, initial state. */
|
||||
EV_ERROR_RESPOND_TIMEOUT, /*!< Slave respond timeout. */
|
||||
EV_ERROR_RECEIVE_DATA, /*!< Receive frame data erroe. */
|
||||
EV_ERROR_RECEIVE_DATA, /*!< Receive frame data error. */
|
||||
EV_ERROR_EXECUTE_FUNCTION, /*!< Execute function error. */
|
||||
EV_ERROR_OK, /*!< Data processed. */
|
||||
EV_ERROR_OK /*!< No error, processing completed. */
|
||||
} eMBMasterErrorEventType;
|
||||
#endif
|
||||
|
||||
@@ -106,19 +107,22 @@ BOOL xMBPortEventPost( eMBEventType eEvent );
|
||||
|
||||
BOOL xMBPortEventGet( /*@out@ */ eMBEventType * eEvent );
|
||||
|
||||
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED
|
||||
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
|
||||
BOOL xMBMasterPortEventInit( void );
|
||||
|
||||
BOOL xMBMasterPortEventPost( eMBMasterEventType eEvent );
|
||||
|
||||
BOOL xMBMasterPortEventGet( /*@out@ */ eMBMasterEventType * eEvent );
|
||||
|
||||
eMBMasterEventType
|
||||
xMBMasterPortFsmWaitConfirmation( eMBMasterEventType eEventMask, ULONG ulTimeout);
|
||||
|
||||
void vMBMasterOsResInit( void );
|
||||
|
||||
BOOL xMBMasterRunResTake( LONG time );
|
||||
|
||||
void vMBMasterRunResRelease( void );
|
||||
#endif // #if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED
|
||||
#endif // MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
|
||||
/* ----------------------- Serial port functions ----------------------------*/
|
||||
|
||||
BOOL xMBPortSerialInit( UCHAR ucPort, ULONG ulBaudRate,
|
||||
@@ -160,7 +164,7 @@ void vMBPortTimersDisable( void );
|
||||
|
||||
void vMBPortTimersDelay( USHORT usTimeOutMS );
|
||||
|
||||
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED
|
||||
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
|
||||
BOOL xMBMasterPortTimersInit( USHORT usTimeOut50us );
|
||||
|
||||
void xMBMasterPortTimersClose( void );
|
||||
@@ -172,7 +176,7 @@ void vMBMasterPortTimersConvertDelayEnable( void );
|
||||
void vMBMasterPortTimersRespondTimeoutEnable( void );
|
||||
|
||||
void vMBMasterPortTimersDisable( void );
|
||||
#endif
|
||||
|
||||
|
||||
/* ----------------- Callback for the master error process ------------------*/
|
||||
void vMBMasterErrorCBRespondTimeout( UCHAR ucDestAddress, const UCHAR* pucPDUData,
|
||||
@@ -185,7 +189,7 @@ void vMBMasterErrorCBExecuteFunction( UCHAR ucDestAddress, const UCHA
|
||||
USHORT ucPDULength );
|
||||
|
||||
void vMBMasterCBRequestSuccess( void );
|
||||
|
||||
#endif
|
||||
/* ----------------------- Callback for the protocol stack ------------------*/
|
||||
/*!
|
||||
* \brief Callback function for the porting layer when a new byte is
|
||||
@@ -204,7 +208,8 @@ extern BOOL( *pxMBFrameCBByteReceived ) ( void );
|
||||
extern BOOL( *pxMBFrameCBTransmitterEmpty ) ( void );
|
||||
|
||||
extern BOOL( *pxMBPortCBTimerExpired ) ( void );
|
||||
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED
|
||||
|
||||
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
|
||||
extern BOOL( *pxMBMasterFrameCBByteReceived ) ( void );
|
||||
|
||||
extern BOOL( *pxMBMasterFrameCBTransmitterEmpty ) ( void );
|
||||
@@ -221,9 +226,21 @@ void vMBTCPPortDisable( void );
|
||||
|
||||
BOOL xMBTCPPortGetRequest( UCHAR **ppucMBTCPFrame, USHORT * usTCPLength );
|
||||
|
||||
BOOL xMBTCPPortSendResponse( const UCHAR *pucMBTCPFrame, USHORT usTCPLength );
|
||||
BOOL xMBTCPPortSendResponse( UCHAR *pucMBTCPFrame, USHORT usTCPLength );
|
||||
|
||||
#endif
|
||||
|
||||
#if MB_MASTER_TCP_ENABLED
|
||||
BOOL xMBMasterTCPPortInit( USHORT usTCPPort );
|
||||
|
||||
void vMBMasterTCPPortClose( void );
|
||||
|
||||
void vMBMasterTCPPortDisable( void );
|
||||
|
||||
BOOL xMBMasterTCPPortGetRequest( UCHAR **ppucMBTCPFrame, USHORT * usTCPLength );
|
||||
|
||||
BOOL xMBMasterTCPPortSendResponse( UCHAR *pucMBTCPFrame, USHORT usTCPLength );
|
||||
#endif
|
||||
#ifdef __cplusplus
|
||||
PR_END_EXTERN_C
|
||||
#endif
|
||||
|
||||
@@ -43,13 +43,13 @@
|
||||
#include "mbfunc.h"
|
||||
#include "mbport.h"
|
||||
|
||||
#if MB_SLAVE_RTU_ENABLED == 1
|
||||
#if MB_SLAVE_RTU_ENABLED
|
||||
#include "mbrtu.h"
|
||||
#endif
|
||||
#if MB_SLAVE_ASCII_ENABLED == 1
|
||||
#if MB_SLAVE_ASCII_ENABLED
|
||||
#include "mbascii.h"
|
||||
#endif
|
||||
#if MB_TCP_ENABLED == 1
|
||||
#if MB_TCP_ENABLED
|
||||
#include "mbtcp.h"
|
||||
#endif
|
||||
|
||||
|
||||
@@ -52,9 +52,10 @@
|
||||
#endif
|
||||
#if MB_MASTER_TCP_ENABLED
|
||||
#include "mbtcp.h"
|
||||
#include "mbtcp_m.h"
|
||||
#endif
|
||||
|
||||
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED
|
||||
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
|
||||
|
||||
#ifndef MB_PORT_HAS_CLOSE
|
||||
#define MB_PORT_HAS_CLOSE 1
|
||||
@@ -66,6 +67,7 @@ static UCHAR ucMBMasterDestAddress;
|
||||
static BOOL xMBRunInMasterMode = FALSE;
|
||||
static volatile eMBMasterErrorEventType eMBMasterCurErrorType;
|
||||
static volatile USHORT usMasterSendPDULength;
|
||||
static volatile eMBMode eMBMasterCurrentMode;
|
||||
|
||||
/*------------------------ Shared variables ---------------------------------*/
|
||||
|
||||
@@ -143,8 +145,43 @@ static xMBFunctionHandler xMasterFuncHandlers[MB_FUNC_HANDLERS_MAX] = {
|
||||
};
|
||||
|
||||
/* ----------------------- Start implementation -----------------------------*/
|
||||
#if MB_MASTER_TCP_ENABLED > 0
|
||||
eMBErrorCode
|
||||
eMBMasterInit( eMBMode eMode, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
|
||||
eMBMasterTCPInit( USHORT ucTCPPort )
|
||||
{
|
||||
eMBErrorCode eStatus = MB_ENOERR;
|
||||
|
||||
if( ( eStatus = eMBMasterTCPDoInit( ucTCPPort ) ) != MB_ENOERR ) {
|
||||
eMBState = STATE_DISABLED;
|
||||
}
|
||||
else if( !xMBMasterPortEventInit( ) ) {
|
||||
/* Port dependent event module initialization failed. */
|
||||
eStatus = MB_EPORTERR;
|
||||
} else {
|
||||
pvMBMasterFrameStartCur = eMBMasterTCPStart;
|
||||
pvMBMasterFrameStopCur = eMBMasterTCPStop;
|
||||
peMBMasterFrameReceiveCur = eMBMasterTCPReceive;
|
||||
peMBMasterFrameSendCur = eMBMasterTCPSend;
|
||||
pxMBMasterPortCBTimerExpired = xMBMasterTCPTimerExpired;
|
||||
pvMBMasterFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBMasterTCPPortClose : NULL;
|
||||
ucMBMasterDestAddress = MB_TCP_PSEUDO_ADDRESS;
|
||||
eMBMasterCurrentMode = MB_TCP;
|
||||
eMBState = STATE_DISABLED;
|
||||
|
||||
// initialize the OS resource for modbus master.
|
||||
vMBMasterOsResInit();
|
||||
if( xMBMasterPortTimersInit( MB_MASTER_TIMEOUT_MS_RESPOND * MB_TIMER_TICS_PER_MS ) != TRUE )
|
||||
{
|
||||
eStatus = MB_EPORTERR;
|
||||
}
|
||||
|
||||
}
|
||||
return eStatus;
|
||||
}
|
||||
#endif
|
||||
|
||||
eMBErrorCode
|
||||
eMBMasterSerialInit( eMBMode eMode, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
|
||||
{
|
||||
eMBErrorCode eStatus = MB_ENOERR;
|
||||
|
||||
@@ -160,6 +197,7 @@ eMBMasterInit( eMBMode eMode, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity
|
||||
pxMBMasterFrameCBByteReceived = xMBMasterRTUReceiveFSM;
|
||||
pxMBMasterFrameCBTransmitterEmpty = xMBMasterRTUTransmitFSM;
|
||||
pxMBMasterPortCBTimerExpired = xMBMasterRTUTimerExpired;
|
||||
eMBMasterCurrentMode = MB_ASCII;
|
||||
|
||||
eStatus = eMBMasterRTUInit(ucPort, ulBaudRate, eParity);
|
||||
break;
|
||||
@@ -174,6 +212,7 @@ eMBMasterInit( eMBMode eMode, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity
|
||||
pxMBMasterFrameCBByteReceived = xMBMasterASCIIReceiveFSM;
|
||||
pxMBMasterFrameCBTransmitterEmpty = xMBMasterASCIITransmitFSM;
|
||||
pxMBMasterPortCBTimerExpired = xMBMasterASCIITimerT1SExpired;
|
||||
eMBMasterCurrentMode = MB_RTU;
|
||||
|
||||
eStatus = eMBMasterASCIIInit(ucPort, ulBaudRate, eParity );
|
||||
break;
|
||||
@@ -229,7 +268,7 @@ eMBMasterEnable( void )
|
||||
/* Activate the protocol stack. */
|
||||
pvMBMasterFrameStartCur( );
|
||||
/* Release the resource, because it created in busy state */
|
||||
vMBMasterRunResRelease( );
|
||||
//vMBMasterRunResRelease( );
|
||||
eMBState = STATE_ENABLED;
|
||||
}
|
||||
else
|
||||
@@ -290,14 +329,32 @@ eMBMasterPoll( void )
|
||||
if ( MB_PORT_CHECK_EVENT( eEvent, EV_MASTER_READY ) ) {
|
||||
ESP_LOGD(MB_PORT_TAG, "%s:EV_MASTER_READY", __func__);
|
||||
MB_PORT_CLEAR_EVENT( eEvent, EV_MASTER_READY );
|
||||
} else if ( MB_PORT_CHECK_EVENT( eEvent, EV_MASTER_FRAME_TRANSMIT ) ) {
|
||||
ESP_LOGD(MB_PORT_TAG, "%s:EV_MASTER_FRAME_TRANSMIT", __func__);
|
||||
/* Master is busy now. */
|
||||
vMBMasterGetPDUSndBuf( &ucMBFrame );
|
||||
ESP_LOG_BUFFER_HEX_LEVEL("POLL transmit buffer", (void*)ucMBFrame, usMBMasterGetPDUSndLength(), ESP_LOG_DEBUG);
|
||||
eStatus = peMBMasterFrameSendCur( ucMBMasterGetDestAddress(), ucMBFrame, usMBMasterGetPDUSndLength() );
|
||||
if (eStatus != MB_ENOERR)
|
||||
{
|
||||
ESP_LOGE( MB_PORT_TAG, "%s:Frame send error. %d", __func__, eStatus );
|
||||
}
|
||||
MB_PORT_CLEAR_EVENT( eEvent, EV_MASTER_FRAME_TRANSMIT );
|
||||
} else if ( MB_PORT_CHECK_EVENT( eEvent, EV_MASTER_FRAME_SENT ) ) {
|
||||
ESP_LOGD( MB_PORT_TAG, "%s:EV_MASTER_FRAME_SENT", __func__ );
|
||||
ESP_LOG_BUFFER_HEX_LEVEL("POLL sent buffer", (void*)ucMBFrame, usMBMasterGetPDUSndLength(), ESP_LOG_DEBUG);
|
||||
MB_PORT_CLEAR_EVENT( eEvent, EV_MASTER_FRAME_SENT );
|
||||
} else if ( MB_PORT_CHECK_EVENT( eEvent, 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() ) )
|
||||
if ( ( eStatus == MB_ENOERR ) && ( ( ucRcvAddress == ucMBMasterGetDestAddress() )
|
||||
|| ( ucRcvAddress == MB_TCP_PSEUDO_ADDRESS ) ) )
|
||||
{
|
||||
( void ) xMBMasterPortEventPost( EV_MASTER_EXECUTE );
|
||||
ESP_LOGD(MB_PORT_TAG, "%s: Packet data received successfully (%u).", __func__, eStatus);
|
||||
ESP_LOG_BUFFER_HEX_LEVEL("POLL RCV buffer", (void*)ucMBFrame, (uint16_t)usLength, ESP_LOG_DEBUG);
|
||||
ESP_LOG_BUFFER_HEX_LEVEL("POLL receive buffer", (void*)ucMBFrame, (uint16_t)usLength, ESP_LOG_DEBUG);
|
||||
( void ) xMBMasterPortEventPost( EV_MASTER_EXECUTE );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -351,33 +408,21 @@ eMBMasterPoll( void )
|
||||
}
|
||||
}
|
||||
}
|
||||
/* If master has exception ,Master will send error process.Otherwise the Master is idle.*/
|
||||
if (eException != MB_EX_NONE)
|
||||
/* If master has exception, will send error process event. Otherwise the master is idle.*/
|
||||
if ( eException != MB_EX_NONE )
|
||||
{
|
||||
vMBMasterSetErrorType(EV_ERROR_EXECUTE_FUNCTION);
|
||||
vMBMasterSetErrorType( EV_ERROR_EXECUTE_FUNCTION );
|
||||
( void ) xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
|
||||
}
|
||||
else
|
||||
{
|
||||
vMBMasterSetErrorType(EV_ERROR_OK);
|
||||
( void ) xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
|
||||
if ( eMBMasterGetErrorType( ) == EV_ERROR_INIT ) {
|
||||
vMBMasterSetErrorType(EV_ERROR_OK);
|
||||
ESP_LOGD( MB_PORT_TAG, "%s: set event EV_ERROR_OK", __func__ );
|
||||
( void ) xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
|
||||
}
|
||||
}
|
||||
MB_PORT_CLEAR_EVENT( eEvent, EV_MASTER_EXECUTE );
|
||||
} else if ( MB_PORT_CHECK_EVENT( eEvent, EV_MASTER_FRAME_TRANSMIT ) ) {
|
||||
ESP_LOGD(MB_PORT_TAG, "%s:EV_MASTER_FRAME_TRANSMIT", __func__);
|
||||
/* Master is busy now. */
|
||||
vMBMasterGetPDUSndBuf( &ucMBFrame );
|
||||
eStatus = peMBMasterFrameSendCur( ucMBMasterGetDestAddress(), ucMBFrame, usMBMasterGetPDUSndLength() );
|
||||
if (eStatus != MB_ENOERR)
|
||||
{
|
||||
ESP_LOGE( MB_PORT_TAG, "%s:Frame send error. %d", __func__, eStatus );
|
||||
} else {
|
||||
ESP_LOG_BUFFER_HEX_LEVEL("Sent buffer", (void*)ucMBFrame, usMBMasterGetPDUSndLength(), ESP_LOG_DEBUG);
|
||||
}
|
||||
MB_PORT_CLEAR_EVENT( eEvent, EV_MASTER_FRAME_TRANSMIT );
|
||||
} else if ( MB_PORT_CHECK_EVENT( eEvent, EV_MASTER_FRAME_SENT ) ) {
|
||||
ESP_LOGD( MB_PORT_TAG, "%s:EV_MASTER_FRAME_SENT", __func__ );
|
||||
MB_PORT_CLEAR_EVENT( eEvent, EV_MASTER_FRAME_SENT );
|
||||
} else if ( MB_PORT_CHECK_EVENT( eEvent, EV_MASTER_ERROR_PROCESS ) ) {
|
||||
ESP_LOGD( MB_PORT_TAG, "%s:EV_MASTER_ERROR_PROCESS", __func__ );
|
||||
/* Execute specified error process callback function. */
|
||||
@@ -404,18 +449,19 @@ eMBMasterPoll( void )
|
||||
ESP_LOGE( MB_PORT_TAG, "%s: incorrect error type = %d.", __func__, errorType);
|
||||
break;
|
||||
}
|
||||
vMBMasterRunResRelease( );
|
||||
vMBMasterSetErrorType( EV_ERROR_INIT );
|
||||
MB_PORT_CLEAR_EVENT( eEvent, EV_MASTER_ERROR_PROCESS );
|
||||
vMBMasterRunResRelease( );
|
||||
}
|
||||
if ( eEvent ) {
|
||||
// Event processing is done, but some poll events still set then
|
||||
// postpone its processing for next poll cycle (rare case).
|
||||
ESP_LOGW( MB_PORT_TAG, "%s: Unprocessed event %d.", __func__, eEvent );
|
||||
ESP_LOGW( MB_PORT_TAG, "%s: Postpone event %x.", __func__, eEvent );
|
||||
( void ) xMBMasterPortEventPost( eEvent );
|
||||
}
|
||||
} else {
|
||||
// Something went wrong and task unblocked but there are no any correct events set
|
||||
ESP_LOGE( MB_PORT_TAG, "%s: Unexpected event triggered %d.", __func__, eEvent );
|
||||
ESP_LOGE( MB_PORT_TAG, "%s: Unexpected event triggered 0x%02x.", __func__, eEvent );
|
||||
eStatus = MB_EILLSTATE;
|
||||
}
|
||||
return eStatus;
|
||||
@@ -498,4 +544,4 @@ void vMBMasterRequestSetType( BOOL xIsBroadcast ){
|
||||
xFrameIsBroadcast = xIsBroadcast;
|
||||
}
|
||||
|
||||
#endif // MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED
|
||||
#endif // MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
|
||||
@@ -39,7 +39,7 @@ PR_BEGIN_EXTERN_C
|
||||
/* ----------------------- Defines ------------------------------------------*/
|
||||
#define MB_SER_PDU_SIZE_MIN 4 /*!< Minimum size of a Modbus RTU frame. */
|
||||
|
||||
#if MB_SLAVE_RTU_ENABLED > 0
|
||||
#if MB_SLAVE_RTU_ENABLED
|
||||
eMBErrorCode eMBRTUInit( UCHAR slaveAddress, UCHAR ucPort, ULONG ulBaudRate,
|
||||
eMBParity eParity );
|
||||
void eMBRTUStart( void );
|
||||
@@ -52,7 +52,7 @@ BOOL xMBRTUTimerT15Expired( void );
|
||||
BOOL xMBRTUTimerT35Expired( void );
|
||||
#endif
|
||||
|
||||
#if MB_MASTER_RTU_ENABLED > 0
|
||||
#if MB_MASTER_RTU_ENABLED
|
||||
eMBErrorCode eMBMasterRTUInit( UCHAR ucPort, ULONG ulBaudRate,eMBParity eParity );
|
||||
void eMBMasterRTUStart( void );
|
||||
void eMBMasterRTUStop( void );
|
||||
|
||||
@@ -344,7 +344,8 @@ xMBMasterRTUTransmitFSM( void )
|
||||
return xNeedPoll;
|
||||
}
|
||||
|
||||
BOOL MB_PORT_ISR_ATTR xMBMasterRTUTimerExpired(void)
|
||||
BOOL MB_PORT_ISR_ATTR
|
||||
xMBMasterRTUTimerExpired(void)
|
||||
{
|
||||
BOOL xNeedPoll = FALSE;
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
#include "mbframe.h"
|
||||
#include "mbport.h"
|
||||
|
||||
#if MB_TCP_ENABLED > 0
|
||||
#if MB_TCP_ENABLED
|
||||
|
||||
/* ----------------------- Defines ------------------------------------------*/
|
||||
|
||||
@@ -67,12 +67,6 @@
|
||||
* (1') ... Modbus Protocol Data Unit
|
||||
*/
|
||||
|
||||
#define MB_TCP_TID 0
|
||||
#define MB_TCP_PID 2
|
||||
#define MB_TCP_LEN 4
|
||||
#define MB_TCP_UID 6
|
||||
#define MB_TCP_FUNC 7
|
||||
|
||||
#define MB_TCP_PROTOCOL_ID 0 /* 0 = Modbus Protocol */
|
||||
|
||||
|
||||
|
||||
@@ -36,7 +36,8 @@ PR_BEGIN_EXTERN_C
|
||||
#endif
|
||||
|
||||
/* ----------------------- Defines ------------------------------------------*/
|
||||
#define MB_TCP_PSEUDO_ADDRESS 255
|
||||
|
||||
#if MB_TCP_ENABLED
|
||||
|
||||
/* ----------------------- Function prototypes ------------------------------*/
|
||||
eMBErrorCode eMBTCPDoInit( USHORT ucTCPPort );
|
||||
@@ -47,6 +48,8 @@ eMBErrorCode eMBTCPReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame,
|
||||
eMBErrorCode eMBTCPSend( UCHAR _unused, const UCHAR * pucFrame,
|
||||
USHORT usLength );
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
PR_END_EXTERN_C
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
|
||||
* 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: mbtcp.c,v 1.3 2006/12/07 22:10:34 wolti Exp $
|
||||
*/
|
||||
|
||||
/* ----------------------- System includes ----------------------------------*/
|
||||
#include "stdlib.h"
|
||||
#include "string.h"
|
||||
|
||||
/* ----------------------- Platform includes --------------------------------*/
|
||||
#include "port.h"
|
||||
|
||||
/* ----------------------- Modbus includes ----------------------------------*/
|
||||
#include "mb.h"
|
||||
#include "mbconfig.h"
|
||||
#include "mbtcp_m.h"
|
||||
#include "mbframe.h"
|
||||
#include "mbport.h"
|
||||
|
||||
#if MB_MASTER_TCP_ENABLED
|
||||
|
||||
/* ----------------------- Defines ------------------------------------------*/
|
||||
|
||||
/* ----------------------- MBAP Header --------------------------------------*/
|
||||
/*
|
||||
*
|
||||
* <------------------------ MODBUS TCP/IP ADU(1) ------------------------->
|
||||
* <----------- MODBUS PDU (1') ---------------->
|
||||
* +-----------+---------------+------------------------------------------+
|
||||
* | TID | PID | Length | UID |Code | Data |
|
||||
* +-----------+---------------+------------------------------------------+
|
||||
* | | | | |
|
||||
* (2) (3) (4) (5) (6)
|
||||
*
|
||||
* (2) ... MB_TCP_TID = 0 (Transaction Identifier - 2 Byte)
|
||||
* (3) ... MB_TCP_PID = 2 (Protocol Identifier - 2 Byte)
|
||||
* (4) ... MB_TCP_LEN = 4 (Number of bytes - 2 Byte)
|
||||
* (5) ... MB_TCP_UID = 6 (Unit Identifier - 1 Byte)
|
||||
* (6) ... MB_TCP_FUNC = 7 (Modbus Function Code)
|
||||
*
|
||||
* (1) ... Modbus TCP/IP Application Data Unit
|
||||
* (1') ... Modbus Protocol Data Unit
|
||||
*/
|
||||
|
||||
#define MB_TCP_PROTOCOL_ID 0 /* 0 = Modbus Protocol */
|
||||
|
||||
|
||||
/* ----------------------- Start implementation -----------------------------*/
|
||||
eMBErrorCode
|
||||
eMBMasterTCPDoInit( USHORT ucTCPPort )
|
||||
{
|
||||
eMBErrorCode eStatus = MB_ENOERR;
|
||||
|
||||
if( xMBMasterTCPPortInit( ucTCPPort ) == FALSE )
|
||||
{
|
||||
eStatus = MB_EPORTERR;
|
||||
}
|
||||
return eStatus;
|
||||
}
|
||||
|
||||
void
|
||||
eMBMasterTCPStart( void )
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
eMBMasterTCPStop( void )
|
||||
{
|
||||
/* Make sure that no more clients are connected. */
|
||||
vMBMasterTCPPortDisable( );
|
||||
}
|
||||
|
||||
eMBErrorCode
|
||||
eMBMasterTCPReceive( UCHAR * pucRcvAddress, UCHAR ** ppucFrame, USHORT * pusLength )
|
||||
{
|
||||
eMBErrorCode eStatus = MB_EIO;
|
||||
UCHAR *pucMBTCPFrame;
|
||||
USHORT usLength;
|
||||
USHORT usPID;
|
||||
|
||||
if( xMBMasterTCPPortGetRequest( &pucMBTCPFrame, &usLength ) != FALSE )
|
||||
{
|
||||
usPID = pucMBTCPFrame[MB_TCP_PID] << 8U;
|
||||
usPID |= pucMBTCPFrame[MB_TCP_PID + 1];
|
||||
|
||||
if( usPID == MB_TCP_PROTOCOL_ID )
|
||||
{
|
||||
*ppucFrame = &pucMBTCPFrame[MB_TCP_FUNC];
|
||||
*pusLength = usLength - MB_TCP_FUNC;
|
||||
eStatus = MB_ENOERR;
|
||||
|
||||
/* Modbus TCP does not use any addresses. Fake the source address such
|
||||
* that the processing part deals with this frame.
|
||||
*/
|
||||
*pucRcvAddress = MB_TCP_PSEUDO_ADDRESS;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
eStatus = MB_EIO;
|
||||
}
|
||||
return eStatus;
|
||||
}
|
||||
|
||||
eMBErrorCode
|
||||
eMBMasterTCPSend( UCHAR _unused, const UCHAR * pucFrame, USHORT usLength )
|
||||
{
|
||||
eMBErrorCode eStatus = MB_ENOERR;
|
||||
UCHAR *pucMBTCPFrame = ( UCHAR * ) pucFrame - MB_TCP_FUNC;
|
||||
USHORT usTCPLength = usLength + MB_TCP_FUNC;
|
||||
|
||||
/* The MBAP header is already initialized because the caller calls this
|
||||
* function with the buffer returned by the previous call. Therefore we
|
||||
* only have to update the length in the header. Note that the length
|
||||
* header includes the size of the Modbus PDU and the UID Byte. Therefore
|
||||
* the length is usLength plus one.
|
||||
*/
|
||||
pucMBTCPFrame[MB_TCP_LEN] = ( usLength + 1 ) >> 8U;
|
||||
pucMBTCPFrame[MB_TCP_LEN + 1] = ( usLength + 1 ) & 0xFF;
|
||||
if( xMBMasterTCPPortSendResponse( pucMBTCPFrame, usTCPLength ) == FALSE )
|
||||
{
|
||||
eStatus = MB_EIO;
|
||||
}
|
||||
return eStatus;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
|
||||
* 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: mbtcp.h,v 1.2 2006/12/07 22:10:34 wolti Exp $
|
||||
*/
|
||||
|
||||
#ifndef _MB_TCP_M_H
|
||||
#define _MB_TCP_M_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
PR_BEGIN_EXTERN_C
|
||||
#endif
|
||||
|
||||
/* ----------------------- Defines ------------------------------------------*/
|
||||
|
||||
#if MB_MASTER_TCP_ENABLED
|
||||
|
||||
/* ----------------------- Function prototypes ------------------------------*/
|
||||
eMBErrorCode eMBMasterTCPDoInit( USHORT ucTCPPort );
|
||||
void eMBMasterTCPStart( void );
|
||||
void eMBMasterTCPStop( void );
|
||||
eMBErrorCode eMBMasterTCPReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame,
|
||||
USHORT * pusLength );
|
||||
eMBErrorCode eMBMasterTCPSend( UCHAR _unused, const UCHAR * pucFrame,
|
||||
USHORT usLength );
|
||||
BOOL xMBMasterTCPTimerExpired(void);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
PR_END_EXTERN_C
|
||||
#endif
|
||||
#endif
|
||||
@@ -42,6 +42,7 @@
|
||||
|
||||
/* ----------------------- Variables ----------------------------------------*/
|
||||
static _lock_t s_port_lock;
|
||||
static UCHAR ucPortMode = 0;
|
||||
|
||||
/* ----------------------- Start implementation -----------------------------*/
|
||||
inline void
|
||||
@@ -55,3 +56,91 @@ vMBPortExitCritical(void)
|
||||
{
|
||||
_lock_release(&s_port_lock);
|
||||
}
|
||||
|
||||
UCHAR
|
||||
ucMBPortGetMode( void )
|
||||
{
|
||||
return ucPortMode;
|
||||
}
|
||||
|
||||
void
|
||||
vMBPortSetMode( UCHAR ucMode )
|
||||
{
|
||||
ENTER_CRITICAL_SECTION();
|
||||
ucPortMode = ucMode;
|
||||
EXIT_CRITICAL_SECTION();
|
||||
}
|
||||
|
||||
#if MB_TCP_DEBUG
|
||||
|
||||
// This function is kept to realize legacy freemodbus frame logging functionality
|
||||
void
|
||||
prvvMBTCPLogFrame( const CHAR * pucMsg, UCHAR * pucFrame, USHORT usFrameLen )
|
||||
{
|
||||
int i;
|
||||
int res = 0;
|
||||
int iBufPos = 0;
|
||||
size_t iBufLeft = MB_TCP_FRAME_LOG_BUFSIZE;
|
||||
static CHAR arcBuffer[MB_TCP_FRAME_LOG_BUFSIZE];
|
||||
|
||||
assert( pucFrame != NULL );
|
||||
|
||||
for ( i = 0; i < usFrameLen; i++ ) {
|
||||
// Print some additional frame information.
|
||||
switch ( i )
|
||||
{
|
||||
case 0:
|
||||
// TID = Transaction Identifier.
|
||||
res = snprintf( &arcBuffer[iBufPos], iBufLeft, "| TID = " );
|
||||
break;
|
||||
case 2:
|
||||
// PID = Protocol Identifier.
|
||||
res = snprintf( &arcBuffer[iBufPos], iBufLeft, " | PID = " );
|
||||
break;
|
||||
case 4:
|
||||
// Length
|
||||
res = snprintf( &arcBuffer[iBufPos], iBufLeft, " | LEN = " );
|
||||
break;
|
||||
case 6:
|
||||
// UID = Unit Identifier.
|
||||
res = snprintf( &arcBuffer[iBufPos], iBufLeft, " | UID = " );
|
||||
break;
|
||||
case 7:
|
||||
// MB Function Code.
|
||||
res = snprintf( &arcBuffer[iBufPos], iBufLeft, " | FUNC = " );
|
||||
break;
|
||||
case 8:
|
||||
// MB PDU rest.
|
||||
res = snprintf( &arcBuffer[iBufPos], iBufLeft, " | DATA = " );
|
||||
break;
|
||||
default:
|
||||
res = 0;
|
||||
break;
|
||||
}
|
||||
if( res == -1 ) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
iBufPos += res;
|
||||
iBufLeft -= res;
|
||||
}
|
||||
|
||||
// Print the data.
|
||||
res = snprintf( &arcBuffer[iBufPos], iBufLeft, "%02X", pucFrame[i] );
|
||||
if( res == -1 ) {
|
||||
break;
|
||||
} else {
|
||||
iBufPos += res;
|
||||
iBufLeft -= res;
|
||||
}
|
||||
}
|
||||
|
||||
if( res != -1 ) {
|
||||
// Append an end of frame string.
|
||||
res = snprintf( &arcBuffer[iBufPos], iBufLeft, " |" );
|
||||
if( res != -1 ) {
|
||||
ESP_LOGD(pucMsg, "%s", arcBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -19,31 +19,52 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/xtensa_api.h"
|
||||
#include "esp_log.h" // for ESP_LOGE macro
|
||||
#include "sdkconfig.h"
|
||||
#include "mbconfig.h"
|
||||
|
||||
#define INLINE inline
|
||||
#define PR_BEGIN_EXTERN_C extern "C" {
|
||||
#define PR_END_EXTERN_C }
|
||||
|
||||
#define MB_PORT_TAG "MB_PORT_COMMON"
|
||||
#define MB_PORT_TAG "MB_PORT_COMMON"
|
||||
|
||||
#define MB_BAUD_RATE_DEFAULT (115200)
|
||||
#define MB_QUEUE_LENGTH (CONFIG_FMB_QUEUE_LENGTH)
|
||||
#define MB_BAUD_RATE_DEFAULT (115200)
|
||||
#define MB_QUEUE_LENGTH (CONFIG_FMB_QUEUE_LENGTH)
|
||||
|
||||
#define MB_SERIAL_TASK_PRIO (CONFIG_FMB_SERIAL_TASK_PRIO)
|
||||
#define MB_SERIAL_TASK_STACK_SIZE (CONFIG_FMB_SERIAL_TASK_STACK_SIZE)
|
||||
#define MB_SERIAL_TOUT (3) // 3.5*8 = 28 ticks, TOUT=3 -> ~24..33 ticks
|
||||
#define MB_SERIAL_TASK_PRIO (CONFIG_FMB_PORT_TASK_PRIO)
|
||||
#define MB_SERIAL_TASK_STACK_SIZE (CONFIG_FMB_PORT_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_FMB_SERIAL_BUF_SIZE)
|
||||
#define MB_SERIAL_BUF_SIZE (CONFIG_FMB_SERIAL_BUF_SIZE)
|
||||
|
||||
// common definitions for serial port implementations
|
||||
#define MB_SERIAL_TX_TOUT_MS (100)
|
||||
#define MB_SERIAL_TX_TOUT_TICKS pdMS_TO_TICKS(MB_SERIAL_TX_TOUT_MS) // timeout for transmission
|
||||
#define MB_SERIAL_RX_TOUT_MS (1)
|
||||
#define MB_SERIAL_RX_TOUT_TICKS pdMS_TO_TICKS(MB_SERIAL_RX_TOUT_MS) // timeout for receive
|
||||
#define MB_SERIAL_TX_TOUT_MS (100)
|
||||
#define MB_SERIAL_TX_TOUT_TICKS (pdMS_TO_TICKS(MB_SERIAL_TX_TOUT_MS)) // timeout for transmission
|
||||
#define MB_SERIAL_RX_TOUT_MS (1)
|
||||
#define MB_SERIAL_RX_TOUT_TICKS (pdMS_TO_TICKS(MB_SERIAL_RX_TOUT_MS)) // timeout for receive
|
||||
|
||||
#define MB_SERIAL_RESP_LEN_MIN (4)
|
||||
#define MB_SERIAL_RESP_LEN_MIN (4)
|
||||
|
||||
// Common definitions for TCP port
|
||||
#define MB_TCP_BUF_SIZE (256 + 7) // Must hold a complete Modbus TCP frame.
|
||||
#define MB_TCP_DEFAULT_PORT (CONFIG_FMB_TCP_PORT_DEFAULT)
|
||||
#define MB_TCP_STACK_SIZE (CONFIG_FMB_PORT_TASK_STACK_SIZE)
|
||||
#define MB_TCP_TASK_PRIO (CONFIG_FMB_PORT_TASK_PRIO)
|
||||
|
||||
#define MB_TCP_READ_TIMEOUT_MS (100) // read timeout in mS
|
||||
#define MB_TCP_READ_TIMEOUT (pdMS_TO_TICKS(MB_TCP_READ_TIMEOUT_MS))
|
||||
#define MB_TCP_SEND_TIMEOUT_MS (500) // send event timeout in mS
|
||||
#define MB_TCP_SEND_TIMEOUT (pdMS_TO_TICKS(MB_TCP_SEND_TIMEOUT_MS))
|
||||
#define MB_TCP_PORT_MAX_CONN (CONFIG_FMB_TCP_PORT_MAX_CONN)
|
||||
|
||||
#define MB_TCP_FRAME_LOG_BUFSIZE (256)
|
||||
|
||||
// Define number of timer reloads per 1 mS
|
||||
#define MB_TIMER_TICS_PER_MS (20UL)
|
||||
|
||||
#define MB_TCP_DEBUG (LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG) // Enable legacy debug output in TCP module.
|
||||
|
||||
#define MB_TCP_GET_FIELD(buffer, field) ((USHORT)((buffer[field] << 8U) | buffer[field + 1]))
|
||||
|
||||
#define MB_PORT_CHECK(a, ret_val, str, ...) \
|
||||
if (!(a)) { \
|
||||
@@ -74,18 +95,49 @@ typedef short SHORT;
|
||||
typedef unsigned long ULONG;
|
||||
typedef long LONG;
|
||||
|
||||
#if MB_TCP_DEBUG
|
||||
typedef enum
|
||||
{
|
||||
MB_LOG_DEBUG,
|
||||
MB_LOG_INFO,
|
||||
MB_LOG_WARN,
|
||||
MB_LOG_ERROR
|
||||
} eMBPortLogLevel;
|
||||
#endif
|
||||
|
||||
typedef enum
|
||||
{
|
||||
MB_PROTO_TCP,
|
||||
MB_PROTO_UDP,
|
||||
} eMBPortProto;
|
||||
|
||||
typedef enum {
|
||||
MB_PORT_IPV4 = 0, /*!< TCP IPV4 addressing */
|
||||
MB_PORT_IPV6 = 1 /*!< TCP IPV6 addressing */
|
||||
} eMBPortIpVer;
|
||||
|
||||
void vMBPortEnterCritical(void);
|
||||
void vMBPortExitCritical(void);
|
||||
|
||||
#define ENTER_CRITICAL_SECTION( ) { ESP_LOGD(MB_PORT_TAG,"%s: Port enter critical.", __func__); \
|
||||
#define ENTER_CRITICAL_SECTION( ) { ESP_EARLY_LOGD(MB_PORT_TAG,"%s: Port enter critical.", __func__); \
|
||||
vMBPortEnterCritical(); }
|
||||
|
||||
#define EXIT_CRITICAL_SECTION( ) { vMBPortExitCritical(); \
|
||||
ESP_LOGD(MB_PORT_TAG,"%s: Port exit critical", __func__); }
|
||||
ESP_EARLY_LOGD(MB_PORT_TAG,"%s: Port exit critical", __func__); }
|
||||
|
||||
#define MB_PORT_CHECK_EVENT( event, mask ) ( event & mask )
|
||||
#define MB_PORT_CLEAR_EVENT( event, mask ) do { event &= ~mask; } while(0)
|
||||
|
||||
// Legacy Modbus logging function
|
||||
#if MB_TCP_DEBUG
|
||||
void vMBPortLog( eMBPortLogLevel eLevel, const CHAR * szModule,
|
||||
const CHAR * szFmt, ... );
|
||||
void prvvMBTCPLogFrame( const CHAR * pucMsg, UCHAR * pucFrame, USHORT usFrameLen );
|
||||
#endif
|
||||
|
||||
void vMBPortSetMode( UCHAR ucMode );
|
||||
UCHAR ucMBPortGetMode( void );
|
||||
|
||||
#ifdef __cplusplus
|
||||
PR_END_EXTERN_C
|
||||
#endif /* __cplusplus */
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
#include "freertos/semphr.h"
|
||||
#include "port_serial_master.h"
|
||||
|
||||
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED
|
||||
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
|
||||
/* ----------------------- Defines ------------------------------------------*/
|
||||
// Event bit mask for xMBMasterPortEventGet()
|
||||
#define MB_EVENT_POLL_MASK (EventBits_t)( EV_MASTER_READY | \
|
||||
@@ -62,10 +62,12 @@
|
||||
EV_MASTER_ERROR_RECEIVE_DATA | \
|
||||
EV_MASTER_ERROR_EXECUTE_FUNCTION )
|
||||
|
||||
#define MB_EVENT_RESOURCE (EventBits_t)( 0x0080 )
|
||||
|
||||
/* ----------------------- Variables ----------------------------------------*/
|
||||
static SemaphoreHandle_t xSemaphorMasterHdl;
|
||||
static EventGroupHandle_t xResourceMasterHdl;
|
||||
static EventGroupHandle_t xEventGroupMasterHdl;
|
||||
static EventGroupHandle_t xEventGroupMasterConfirmHdl;
|
||||
|
||||
/* ----------------------- Start implementation -----------------------------*/
|
||||
|
||||
@@ -73,7 +75,8 @@ BOOL
|
||||
xMBMasterPortEventInit( void )
|
||||
{
|
||||
xEventGroupMasterHdl = xEventGroupCreate();
|
||||
MB_PORT_CHECK((xEventGroupMasterHdl != NULL),
|
||||
xEventGroupMasterConfirmHdl = xEventGroupCreate();
|
||||
MB_PORT_CHECK((xEventGroupMasterHdl != NULL) && (xEventGroupMasterConfirmHdl != NULL),
|
||||
FALSE, "mb stack event group creation error.");
|
||||
return TRUE;
|
||||
}
|
||||
@@ -112,8 +115,24 @@ xMBMasterPortEventPost( eMBMasterEventType eEvent )
|
||||
return bStatus;
|
||||
}
|
||||
|
||||
eMBMasterEventType
|
||||
xMBMasterPortFsmWaitConfirmation( eMBMasterEventType eEventMask, ULONG ulTimeout)
|
||||
{
|
||||
EventBits_t uxBits;
|
||||
uxBits = xEventGroupWaitBits( xEventGroupMasterConfirmHdl, // The event group being tested.
|
||||
eEventMask, // The bits within the event group to wait for.
|
||||
pdFALSE, // Keep masked bits.
|
||||
pdFALSE, // Don't wait for both bits, either bit will do.
|
||||
ulTimeout); // Wait timeout for either bit to be set.
|
||||
if (ulTimeout && uxBits) {
|
||||
// Clear confirmation events that where set in the mask
|
||||
xEventGroupClearBits( xEventGroupMasterConfirmHdl, (uxBits & eEventMask) );
|
||||
}
|
||||
return (eMBMasterEventType)(uxBits & eEventMask);
|
||||
}
|
||||
|
||||
BOOL
|
||||
xMBMasterPortEventGet( eMBMasterEventType * eEvent)
|
||||
xMBMasterPortEventGet( eMBMasterEventType* eEvent )
|
||||
{
|
||||
EventBits_t uxBits;
|
||||
BOOL xEventHappened = FALSE;
|
||||
@@ -122,10 +141,11 @@ xMBMasterPortEventGet( eMBMasterEventType * eEvent)
|
||||
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 (MB_PORT_CHECK_EVENT(uxBits, MB_EVENT_POLL_MASK)) {
|
||||
*eEvent = (eMBMasterEventType)(uxBits & MB_EVENT_POLL_MASK);
|
||||
// Set event bits in confirmation group (for synchronization with port task)
|
||||
xEventGroupSetBits( xEventGroupMasterConfirmHdl, *eEvent );
|
||||
xEventHappened = TRUE;
|
||||
} else {
|
||||
ESP_LOGE(MB_PORT_TAG,"%s: Incorrect event triggered = %d.", __func__, uxBits);
|
||||
@@ -138,8 +158,9 @@ xMBMasterPortEventGet( eMBMasterEventType * eEvent)
|
||||
// This function is initialize the OS resource for modbus master.
|
||||
void vMBMasterOsResInit( void )
|
||||
{
|
||||
xSemaphorMasterHdl = xSemaphoreCreateBinary();
|
||||
MB_PORT_CHECK((xSemaphorMasterHdl != NULL), ; , "%s: OS semaphore create error.", __func__);
|
||||
xResourceMasterHdl = xEventGroupCreate();
|
||||
xEventGroupSetBits(xResourceMasterHdl, MB_EVENT_RESOURCE);
|
||||
MB_PORT_CHECK((xResourceMasterHdl != NULL), ; , "Resource create error.");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,24 +173,26 @@ void vMBMasterOsResInit( void )
|
||||
*/
|
||||
BOOL xMBMasterRunResTake( LONG lTimeOut )
|
||||
{
|
||||
BaseType_t xStatus = pdTRUE;
|
||||
|
||||
// If waiting time is -1. It will wait forever
|
||||
xStatus = xSemaphoreTake(xSemaphorMasterHdl, lTimeOut );
|
||||
MB_PORT_CHECK((xStatus == pdTRUE), FALSE , "%s:Take resource failure.", __func__);
|
||||
ESP_LOGV(MB_PORT_TAG,"%s:Take resource (%lu ticks).", __func__, lTimeOut);
|
||||
EventBits_t uxBits;
|
||||
uxBits = xEventGroupWaitBits( xResourceMasterHdl, // The event group being tested.
|
||||
MB_EVENT_RESOURCE, // 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.
|
||||
lTimeOut); // Resource wait timeout.
|
||||
MB_PORT_CHECK((uxBits == MB_EVENT_RESOURCE), FALSE , "Take resource failure.");
|
||||
ESP_LOGD(MB_PORT_TAG,"%s:Take resource (%x) (%lu ticks).", __func__, uxBits, lTimeOut);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is release Mobus Master running resource.
|
||||
* This function is release Modbus 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 = pdFALSE;
|
||||
xStatus = xSemaphoreGive(xSemaphorMasterHdl);
|
||||
MB_PORT_CHECK((xStatus == pdTRUE), ; , "%s: resource release failure.", __func__);
|
||||
EventBits_t uxBits = xEventGroupSetBits( xResourceMasterHdl, MB_EVENT_RESOURCE );
|
||||
MB_PORT_CHECK((uxBits == MB_EVENT_RESOURCE), ; , "Resource release failure.");
|
||||
ESP_LOGD(MB_PORT_TAG,"%s: Release resource (%x).", __func__, uxBits);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -201,6 +224,7 @@ void vMBMasterErrorCBReceiveData(UCHAR ucDestAddress, const UCHAR* pucPDUData, U
|
||||
BOOL ret = xMBMasterPortEventPost(EV_MASTER_ERROR_RECEIVE_DATA);
|
||||
MB_PORT_CHECK((ret == TRUE), ; , "%s: Post event 'EV_MASTER_ERROR_RECEIVE_DATA' failed!", __func__);
|
||||
ESP_LOGD(MB_PORT_TAG,"%s:Callback receive data timeout failure.", __func__);
|
||||
ESP_LOG_BUFFER_HEX_LEVEL("Err rcv buf", (void*)pucPDUData, (uint16_t)ucPDULength, ESP_LOG_DEBUG);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -218,6 +242,7 @@ void vMBMasterErrorCBExecuteFunction(UCHAR ucDestAddress, const UCHAR* pucPDUDat
|
||||
BOOL ret = xMBMasterPortEventPost(EV_MASTER_ERROR_EXECUTE_FUNCTION);
|
||||
MB_PORT_CHECK((ret == TRUE), ; , "%s: Post event 'EV_MASTER_ERROR_EXECUTE_FUNCTION' failed!", __func__);
|
||||
ESP_LOGD(MB_PORT_TAG,"%s:Callback execute data handler failure.", __func__);
|
||||
ESP_LOG_BUFFER_HEX_LEVEL("Exec func buf", (void*)pucPDUData, (uint16_t)ucPDULength, ESP_LOG_DEBUG);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -260,6 +285,7 @@ eMBMasterReqErrCode eMBMasterWaitRequestFinish( void ) {
|
||||
// if we wait for certain event bits but get from poll subset
|
||||
ESP_LOGE(MB_PORT_TAG,"%s: incorrect event set = 0x%x", __func__, xRecvedEvent);
|
||||
}
|
||||
xEventGroupSetBits( xEventGroupMasterConfirmHdl, (xRecvedEvent & MB_EVENT_REQ_MASK) );
|
||||
if (MB_PORT_CHECK_EVENT(xRecvedEvent, EV_MASTER_PROCESS_SUCCESS)) {
|
||||
eErrStatus = MB_MRE_NO_ERR;
|
||||
} else if (MB_PORT_CHECK_EVENT(xRecvedEvent, EV_MASTER_ERROR_RESPOND_TIMEOUT)) {
|
||||
@@ -284,7 +310,8 @@ eMBMasterReqErrCode eMBMasterWaitRequestFinish( void ) {
|
||||
void vMBMasterPortEventClose(void)
|
||||
{
|
||||
vEventGroupDelete(xEventGroupMasterHdl);
|
||||
vSemaphoreDelete(xSemaphorMasterHdl);
|
||||
vEventGroupDelete(xEventGroupMasterConfirmHdl);
|
||||
vEventGroupDelete(xResourceMasterHdl);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -50,12 +50,10 @@
|
||||
/* ----------------------- Modbus includes ----------------------------------*/
|
||||
#include "mb.h"
|
||||
#include "mbport.h"
|
||||
#include "port_serial_slave.h"
|
||||
|
||||
/* ----------------------- Modbus includes ----------------------------------*/
|
||||
|
||||
/* ----------------------- Variables ----------------------------------------*/
|
||||
static UCHAR ucPortMode = 0;
|
||||
|
||||
/* ----------------------- Start implementation -----------------------------*/
|
||||
|
||||
@@ -66,22 +64,6 @@ bMBPortIsWithinException( void )
|
||||
return bIsWithinException;
|
||||
}
|
||||
|
||||
/* ----------------------- Start implementation -----------------------------*/
|
||||
|
||||
UCHAR
|
||||
ucMBPortGetMode( void )
|
||||
{
|
||||
return ucPortMode;
|
||||
}
|
||||
|
||||
void
|
||||
vMBPortSetMode( UCHAR ucMode )
|
||||
{
|
||||
ENTER_CRITICAL_SECTION();
|
||||
ucPortMode = ucMode;
|
||||
EXIT_CRITICAL_SECTION();
|
||||
}
|
||||
|
||||
void
|
||||
vMBPortClose( void )
|
||||
{
|
||||
|
||||
@@ -55,25 +55,9 @@
|
||||
/* ----------------------- 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 )
|
||||
{
|
||||
|
||||
@@ -55,6 +55,8 @@
|
||||
#include "sdkconfig.h" // for KConfig options
|
||||
#include "port_serial_slave.h"
|
||||
|
||||
// Note: This code uses mixed coding standard from legacy IDF code and used freemodbus stack
|
||||
|
||||
// A queue to handle UART event.
|
||||
static QueueHandle_t xMbUartQueue;
|
||||
static TaskHandle_t xMbTaskHandle;
|
||||
|
||||
@@ -33,14 +33,6 @@
|
||||
* 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"
|
||||
@@ -49,8 +41,17 @@
|
||||
#include "freertos/queue.h"
|
||||
#include "esp_log.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
/* ----------------------- Modbus includes ----------------------------------*/
|
||||
#include "port.h"
|
||||
#include "mbport.h"
|
||||
#include "mb_m.h"
|
||||
#include "mbrtu.h"
|
||||
#include "mbconfig.h"
|
||||
#include "port_serial_master.h"
|
||||
|
||||
/* ----------------------- Defines ------------------------------------------*/
|
||||
|
||||
/* ----------------------- Static variables ---------------------------------*/
|
||||
static const CHAR *TAG = "MB_MASTER_SERIAL";
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
#define MB_TIMER_DIVIDER ((TIMER_BASE_CLK / 1000000UL) * MB_DISCR_TIME_US - 1) // divider for 50uS
|
||||
#define MB_TIMER_WITH_RELOAD (1)
|
||||
|
||||
static const USHORT usTimerIndex = CONFIG_FMB_TIMER_INDEX; // Modbus Timer index used by stack
|
||||
static const USHORT usTimerIndex = CONFIG_FMB_TIMER_INDEX; // Modbus Timer index used by stack
|
||||
static const USHORT usTimerGroupIndex = CONFIG_FMB_TIMER_GROUP; // Modbus Timer group index used by stack
|
||||
static timer_isr_handle_t xTimerIntHandle; // Timer interrupt handle
|
||||
|
||||
|
||||
@@ -64,7 +64,6 @@ static timer_isr_handle_t xTimerIntHandle; // Timer interrupt handl
|
||||
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
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
CONFIG_MB_MASTER_TIMEOUT_MS_RESPOND CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND
|
||||
CONFIG_MB_MASTER_DELAY_MS_CONVERT CONFIG_FMB_MASTER_DELAY_MS_CONVERT
|
||||
CONFIG_MB_QUEUE_LENGTH CONFIG_FMB_QUEUE_LENGTH
|
||||
CONFIG_MB_SERIAL_TASK_STACK_SIZE CONFIG_FMB_SERIAL_TASK_STACK_SIZE
|
||||
CONFIG_MB_SERIAL_TASK_STACK_SIZE CONFIG_FMB_PORT_TASK_STACK_SIZE
|
||||
CONFIG_MB_SERIAL_BUF_SIZE CONFIG_FMB_SERIAL_BUF_SIZE
|
||||
CONFIG_MB_SERIAL_TASK_PRIO CONFIG_FMB_SERIAL_TASK_PRIO
|
||||
CONFIG_MB_SERIAL_TASK_PRIO CONFIG_FMB_PORT_TASK_PRIO
|
||||
CONFIG_MB_CONTROLLER_SLAVE_ID_SUPPORT CONFIG_FMB_CONTROLLER_SLAVE_ID_SUPPORT
|
||||
CONFIG_MB_CONTROLLER_SLAVE_ID CONFIG_FMB_CONTROLLER_SLAVE_ID
|
||||
CONFIG_MB_CONTROLLER_NOTIFY_TIMEOUT CONFIG_FMB_CONTROLLER_NOTIFY_TIMEOUT
|
||||
|
||||
@@ -81,7 +81,7 @@ static esp_err_t mbc_serial_master_setup(void* comm_info)
|
||||
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_MAX), ESP_ERR_INVALID_ARG,
|
||||
MB_MASTER_CHECK((comm_info_ptr->port <= UART_NUM_MAX), 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);
|
||||
@@ -101,7 +101,7 @@ static esp_err_t mbc_serial_master_start(void)
|
||||
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,
|
||||
status = eMBMasterSerialInit((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);
|
||||
@@ -138,6 +138,7 @@ static esp_err_t mbc_serial_master_destroy(void)
|
||||
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
|
||||
vMBPortSetMode((UCHAR)MB_PORT_INACTIVE);
|
||||
mbm_interface_ptr = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -642,11 +643,8 @@ eMBErrorCode eMBRegDiscreteCBSerialMaster(UCHAR * pucRegBuffer, USHORT usAddress
|
||||
}
|
||||
|
||||
// Initialization of resources for Modbus serial master controller
|
||||
esp_err_t mbc_serial_master_create(mb_port_type_t port_type, void** handler)
|
||||
esp_err_t mbc_serial_master_create(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));
|
||||
@@ -657,6 +655,8 @@ esp_err_t mbc_serial_master_create(mb_port_type_t port_type, void** handler)
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
mbm_opts->port_type = MB_PORT_SERIAL_MASTER;
|
||||
|
||||
vMBPortSetMode((UCHAR)MB_PORT_SERIAL_MASTER);
|
||||
|
||||
mbm_opts->mbm_comm.mode = MB_MODE_RTU;
|
||||
mbm_opts->mbm_comm.port = MB_UART_PORT;
|
||||
mbm_opts->mbm_comm.baudrate = MB_DEVICE_SPEED;
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_NO_MEM Parameter error
|
||||
*/
|
||||
esp_err_t mbc_serial_master_create(mb_port_type_t port_type, void** handler);
|
||||
esp_err_t mbc_serial_master_create(void** handler);
|
||||
|
||||
#endif // _MODBUS_SERIAL_CONTROLLER_MASTER
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "port_serial_slave.h"
|
||||
|
||||
// Shared pointer to interface structure
|
||||
static mb_slave_interface_t* mbs_interface_ptr = NULL; // &default_interface_inst;
|
||||
static mb_slave_interface_t* mbs_interface_ptr = NULL;
|
||||
|
||||
// Modbus task function
|
||||
static void modbus_slave_task(void *pvParameters)
|
||||
@@ -36,7 +36,7 @@ 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
|
||||
for (;;) {
|
||||
@@ -133,6 +133,7 @@ static esp_err_t mbc_serial_slave_destroy(void)
|
||||
MB_SLAVE_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE,
|
||||
"mb stack close failure returned (0x%x).", (uint32_t)mb_error);
|
||||
free(mbs_interface_ptr);
|
||||
vMBPortSetMode((UCHAR)MB_PORT_INACTIVE);
|
||||
mbs_interface_ptr = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -451,17 +452,16 @@ eMBErrorCode eMBRegDiscreteCBSerialSlave(UCHAR* pucRegBuffer, USHORT usAddress,
|
||||
#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)
|
||||
esp_err_t mbc_serial_slave_create(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);
|
||||
|
||||
vMBPortSetMode((UCHAR)MB_PORT_SERIAL_SLAVE);
|
||||
|
||||
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
|
||||
mbs_opts->port_type = MB_PORT_SERIAL_SLAVE; // set interface port type
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_NO_MEM Parameter error
|
||||
*/
|
||||
esp_err_t mbc_serial_slave_create(mb_port_type_t port_type, void** handler);
|
||||
esp_err_t mbc_serial_slave_create(void** handler);
|
||||
|
||||
#endif // _MODBUS_SERIAL_CONTROLLER_SLAVE
|
||||
|
||||
@@ -0,0 +1,716 @@
|
||||
/* 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_tcp_master.c
|
||||
// TCP 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 and defines
|
||||
#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_tcp_master.h" // for tcp master create function and types
|
||||
#include "port_tcp_master.h" // for tcp master port defines and types
|
||||
|
||||
/*-----------------------Master mode use these variables----------------------*/
|
||||
|
||||
// The response time is average processing time + data transmission
|
||||
#define MB_RESPONSE_TIMEOUT pdMS_TO_TICKS(CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND)
|
||||
|
||||
static mb_master_interface_t* mbm_interface_ptr = NULL;
|
||||
|
||||
// Modbus event processing task
|
||||
static void modbus_tcp_master_task(void *pvParameters)
|
||||
{
|
||||
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
MB_MASTER_ASSERT(mbm_opts != NULL);
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setup Modbus controller parameters
|
||||
static esp_err_t mbc_tcp_master_setup(void* comm_info)
|
||||
{
|
||||
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
MB_MASTER_CHECK((mbm_opts != NULL), ESP_ERR_INVALID_ARG, "mb incorrect options pointer.");
|
||||
|
||||
const mb_communication_info_t* comm_info_ptr = (mb_communication_info_t*)comm_info;
|
||||
// Check communication options
|
||||
MB_MASTER_CHECK((comm_info_ptr->ip_mode == MB_MODE_TCP),
|
||||
ESP_ERR_INVALID_ARG, "mb incorrect mode = (0x%x).",
|
||||
(uint32_t)comm_info_ptr->ip_mode);
|
||||
MB_MASTER_CHECK((comm_info_ptr->ip_addr != NULL),
|
||||
ESP_ERR_INVALID_ARG, "mb wrong slave ip address table.");
|
||||
MB_MASTER_CHECK(((comm_info_ptr->ip_addr_type == MB_IPV4) || (comm_info_ptr->ip_addr_type == MB_IPV6)),
|
||||
ESP_ERR_INVALID_ARG, "mb incorrect addr type = (0x%x).", (uint8_t)comm_info_ptr->ip_addr_type);
|
||||
MB_MASTER_CHECK((comm_info_ptr->ip_netif_ptr != NULL),
|
||||
ESP_ERR_INVALID_ARG, "mb incorrect iface address.");
|
||||
// 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_tcp_master_start(void)
|
||||
{
|
||||
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
|
||||
eMBErrorCode status = MB_EIO;
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
MB_MASTER_CHECK((mbm_opts != NULL), ESP_ERR_INVALID_ARG, "mb incorrect options pointer.");
|
||||
const mb_communication_info_t* comm_info = (mb_communication_info_t*)&mbm_opts->mbm_comm;
|
||||
|
||||
// Initialize Modbus stack using mbcontroller parameters
|
||||
status = eMBMasterTCPInit((USHORT)comm_info->ip_port);
|
||||
MB_MASTER_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
|
||||
"mb stack initialization failure, eMBMasterInit() returns (0x%x).", status);
|
||||
|
||||
MB_MASTER_CHECK((mbm_opts->mbm_param_descriptor_size >= 1), ESP_ERR_INVALID_ARG, "mb table size is incorrect.");
|
||||
|
||||
bool result = false;
|
||||
const char** comm_ip_table = (const char**)comm_info->ip_addr;
|
||||
MB_MASTER_CHECK((comm_ip_table != NULL), ESP_ERR_INVALID_ARG, "mb ip table address is incorrect.");
|
||||
|
||||
eMBPortProto proto = (comm_info->ip_mode == MB_MODE_TCP) ? MB_PROTO_TCP : MB_PROTO_UDP;
|
||||
eMBPortIpVer ip_ver = (comm_info->ip_addr_type == MB_IPV4) ? MB_PORT_IPV4 : MB_PORT_IPV6;
|
||||
vMBTCPPortMasterSetNetOpt(comm_info->ip_netif_ptr, ip_ver, proto);
|
||||
vMBTCPPortMasterTaskStart();
|
||||
|
||||
// Add slave IP address for each slave to initialise connection
|
||||
for (int idx = 0; *comm_ip_table != NULL; idx++, comm_ip_table++)
|
||||
{
|
||||
result = (BOOL)xMBTCPPortMasterAddSlaveIp(*comm_ip_table);
|
||||
MB_MASTER_CHECK(result, ESP_ERR_INVALID_STATE, "mb stack add slave IP failed: %s.", *comm_ip_table);
|
||||
}
|
||||
// Add end of list condition
|
||||
(void)xMBTCPPortMasterAddSlaveIp(NULL);
|
||||
|
||||
status = eMBMasterEnable();
|
||||
MB_MASTER_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
|
||||
"mb stack set slave ID failure, eMBMasterEnable() returned (0x%x).", (uint32_t)status);
|
||||
|
||||
bool start = (bool)xMBTCPPortMasterWaitEvent(mbm_opts->mbm_event_group, (EventBits_t)MB_EVENT_STACK_STARTED);
|
||||
MB_MASTER_CHECK((start), ESP_ERR_INVALID_STATE, "mb stack start failed.");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Modbus controller destroy function
|
||||
static esp_err_t mbc_tcp_master_destroy(void)
|
||||
{
|
||||
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
MB_MASTER_CHECK((mbm_opts != NULL), ESP_ERR_INVALID_ARG, "mb incorrect options pointer.");
|
||||
eMBErrorCode mb_error = MB_ENOERR;
|
||||
|
||||
// Stop polling by clearing correspondent bit in the event group
|
||||
xEventGroupClearBits(mbm_opts->mbm_event_group,
|
||||
(EventBits_t)MB_EVENT_STACK_STARTED);
|
||||
// Disable 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
|
||||
vMBPortSetMode((UCHAR)MB_PORT_INACTIVE);
|
||||
mbm_interface_ptr = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Set Modbus parameter description table
|
||||
static esp_err_t mbc_tcp_master_set_descriptor(const mb_parameter_descriptor_t* descriptor, const uint16_t num_elements)
|
||||
{
|
||||
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
|
||||
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;
|
||||
MB_MASTER_CHECK((mbm_opts != NULL), ESP_ERR_INVALID_ARG, "mb options.");
|
||||
|
||||
const char** comm_ip_table = (const char**)mbm_opts->mbm_comm.ip_addr;
|
||||
MB_MASTER_CHECK((comm_ip_table != NULL), ESP_ERR_INVALID_ARG, "mb ip table address is incorrect.");
|
||||
|
||||
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++)
|
||||
{
|
||||
MB_MASTER_CHECK((comm_ip_table[reg_ptr->mb_slave_addr - 1] != NULL), ESP_ERR_INVALID_ARG, "mb ip table address is incorrect.");
|
||||
// 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_tcp_master_send_request(mb_param_request_t* request, void* data_ptr)
|
||||
{
|
||||
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
|
||||
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;
|
||||
|
||||
// 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:
|
||||
mb_error = eMBMasterReqReadCoils((UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
||||
(USHORT)mb_size , (LONG)MB_RESPONSE_TIMEOUT );
|
||||
break;
|
||||
case MB_FUNC_WRITE_SINGLE_COIL:
|
||||
mb_error = eMBMasterReqWriteCoil((UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
||||
*(USHORT*)data_ptr, (LONG)MB_RESPONSE_TIMEOUT );
|
||||
break;
|
||||
case MB_FUNC_WRITE_MULTIPLE_COILS:
|
||||
mb_error = eMBMasterReqWriteMultipleCoils((UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
||||
(USHORT)mb_size, (UCHAR*)data_ptr,
|
||||
(LONG)MB_RESPONSE_TIMEOUT);
|
||||
break;
|
||||
case MB_FUNC_READ_DISCRETE_INPUTS:
|
||||
mb_error = eMBMasterReqReadDiscreteInputs((UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
||||
(USHORT)mb_size, (LONG)MB_RESPONSE_TIMEOUT );
|
||||
break;
|
||||
case MB_FUNC_READ_HOLDING_REGISTER:
|
||||
mb_error = eMBMasterReqReadHoldingRegister((UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
||||
(USHORT)mb_size, (LONG)MB_RESPONSE_TIMEOUT );
|
||||
break;
|
||||
case MB_FUNC_WRITE_REGISTER:
|
||||
mb_error = eMBMasterReqWriteHoldingRegister( (UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
||||
*(USHORT*)data_ptr, (LONG)MB_RESPONSE_TIMEOUT );
|
||||
break;
|
||||
|
||||
case MB_FUNC_WRITE_MULTIPLE_REGISTERS:
|
||||
mb_error = eMBMasterReqWriteMultipleHoldingRegister( (UCHAR)mb_slave_addr,
|
||||
(USHORT)mb_offset, (USHORT)mb_size,
|
||||
(USHORT*)data_ptr, (LONG)MB_RESPONSE_TIMEOUT );
|
||||
break;
|
||||
case MB_FUNC_READWRITE_MULTIPLE_REGISTERS:
|
||||
mb_error = eMBMasterReqReadWriteMultipleHoldingRegister( (UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
||||
(USHORT)mb_size, (USHORT*)data_ptr,
|
||||
(USHORT)mb_offset, (USHORT)mb_size,
|
||||
(LONG)MB_RESPONSE_TIMEOUT );
|
||||
break;
|
||||
case MB_FUNC_READ_INPUT_REGISTER:
|
||||
mb_error = eMBMasterReqReadInputRegister( (UCHAR)mb_slave_addr, (USHORT)mb_offset,
|
||||
(USHORT)mb_size, (LONG) MB_RESPONSE_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; // Invalid register request
|
||||
break;
|
||||
|
||||
case MB_MRE_TIMEDOUT:
|
||||
error = ESP_ERR_TIMEOUT; // Slave did not send response
|
||||
break;
|
||||
|
||||
case MB_MRE_EXE_FUN:
|
||||
case MB_MRE_REV_DATA:
|
||||
error = ESP_ERR_INVALID_RESPONSE; // Invalid response from slave
|
||||
break;
|
||||
|
||||
case MB_MRE_MASTER_BUSY:
|
||||
error = ESP_ERR_INVALID_STATE; // Master is busy (previous request is pending)
|
||||
break;
|
||||
|
||||
default:
|
||||
ESP_LOGE(MB_MASTER_TAG, "%s: Incorrect return code (%x) ", __FUNCTION__, mb_error);
|
||||
error = ESP_FAIL;
|
||||
break;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static esp_err_t mbc_tcp_master_get_cid_info(uint16_t cid, const mb_parameter_descriptor_t** param_buffer)
|
||||
{
|
||||
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
|
||||
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_tcp_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_tcp_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_tcp_master_set_request(char* name, mb_param_mode_t mode, mb_param_request_t* request,
|
||||
mb_parameter_descriptor_t* reg_data)
|
||||
{
|
||||
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
|
||||
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_tcp_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_tcp_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_tcp_master_set_request(name, MB_PARAM_READ, &request, ®_info);
|
||||
if ((error == ESP_OK) && (cid == reg_info.cid)) {
|
||||
error = mbc_tcp_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_tcp_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_tcp_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_tcp_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_tcp_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_tcp_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 eMBRegInputCBTcpMaster(UCHAR * pucRegBuffer, USHORT usAddress,
|
||||
USHORT usNRegs)
|
||||
{
|
||||
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
MB_MASTER_ASSERT(pucRegBuffer != NULL);
|
||||
USHORT usRegInputNregs = (USHORT)mbm_opts->mbm_reg_buffer_size; // Number of input registers to be transferred
|
||||
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 eMBRegHoldingCBTcpMaster(UCHAR * pucRegBuffer, USHORT usAddress,
|
||||
USHORT usNRegs, eMBRegisterMode eMode)
|
||||
{
|
||||
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
MB_MASTER_ASSERT(pucRegBuffer != NULL);
|
||||
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 eMBRegCoilsCBTcpMaster(UCHAR* pucRegBuffer, USHORT usAddress,
|
||||
USHORT usNCoils, eMBRegisterMode eMode)
|
||||
{
|
||||
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
MB_MASTER_ASSERT(NULL != pucRegBuffer);
|
||||
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 eMBRegDiscreteCBTcpMaster(UCHAR * pucRegBuffer, USHORT usAddress,
|
||||
USHORT usNDiscrete)
|
||||
{
|
||||
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
MB_MASTER_ASSERT(pucRegBuffer != NULL);
|
||||
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 TCP master controller
|
||||
esp_err_t mbc_tcp_master_create(void** handler)
|
||||
{
|
||||
// Allocate space for master interface structure
|
||||
if (mbm_interface_ptr == NULL) {
|
||||
mbm_interface_ptr = malloc(sizeof(mb_master_interface_t));
|
||||
}
|
||||
MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
|
||||
|
||||
// Initialize interface properties
|
||||
mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
|
||||
mbm_opts->port_type = MB_PORT_TCP_MASTER;
|
||||
|
||||
vMBPortSetMode((UCHAR)MB_PORT_TCP_MASTER);
|
||||
|
||||
mbm_opts->mbm_comm.ip_mode = MB_MODE_TCP;
|
||||
mbm_opts->mbm_comm.ip_port = MB_TCP_DEFAULT_PORT;
|
||||
|
||||
// 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_tcp_master_task,
|
||||
"modbus_tcp_master_task",
|
||||
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_tcp_master_create;
|
||||
mbm_interface_ptr->destroy = mbc_tcp_master_destroy;
|
||||
mbm_interface_ptr->setup = mbc_tcp_master_setup;
|
||||
mbm_interface_ptr->start = mbc_tcp_master_start;
|
||||
mbm_interface_ptr->get_cid_info = mbc_tcp_master_get_cid_info;
|
||||
mbm_interface_ptr->get_parameter = mbc_tcp_master_get_parameter;
|
||||
mbm_interface_ptr->send_request = mbc_tcp_master_send_request;
|
||||
mbm_interface_ptr->set_descriptor = mbc_tcp_master_set_descriptor;
|
||||
mbm_interface_ptr->set_parameter = mbc_tcp_master_set_parameter;
|
||||
|
||||
mbm_interface_ptr->master_reg_cb_discrete = eMBRegDiscreteCBTcpMaster;
|
||||
mbm_interface_ptr->master_reg_cb_input = eMBRegInputCBTcpMaster;
|
||||
mbm_interface_ptr->master_reg_cb_holding = eMBRegHoldingCBTcpMaster;
|
||||
mbm_interface_ptr->master_reg_cb_coils = eMBRegCoilsCBTcpMaster;
|
||||
|
||||
*handler = mbm_interface_ptr;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/* 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_tcp_master.h Modbus controller TCP master implementation header file
|
||||
|
||||
#ifndef _MODBUS_TCP_CONTROLLER_MASTER
|
||||
#define _MODBUS_TCP_CONTROLLER_MASTER
|
||||
|
||||
#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_INST_MIN_SIZE (2) // The minimal size of Modbus registers area in bytes
|
||||
#define MB_INST_MAX_SIZE (CONFIG_MB_INST_MAX_SIZE) // 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
|
||||
|
||||
/**
|
||||
* @brief Create Modbus Master controller and stack for TCP port
|
||||
*
|
||||
* @param[out] handler handler(pointer) to master data structure
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_NO_MEM Parameter error
|
||||
*/
|
||||
esp_err_t mbc_tcp_master_create(void** handler);
|
||||
|
||||
#endif // _MODBUS_TCP_CONTROLLER_SLAVE
|
||||
|
||||
@@ -0,0 +1,941 @@
|
||||
/* 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 TCP Port
|
||||
* Copyright (C) 2006 Christian Walter <wolti@sil.at>
|
||||
* Parts of crt0.S Copyright (c) 1995, 1996, 1998 Cygnus Support
|
||||
*
|
||||
* 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.h,v 1.2 2006/09/04 14:39:20 wolti Exp $
|
||||
*/
|
||||
|
||||
/* ----------------------- System includes ----------------------------------*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "esp_err.h"
|
||||
|
||||
/* ----------------------- lwIP includes ------------------------------------*/
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/sockets.h"
|
||||
#include "lwip/netdb.h"
|
||||
#include "esp_netif.h"
|
||||
|
||||
/* ----------------------- Modbus includes ----------------------------------*/
|
||||
#include "mb_m.h"
|
||||
#include "port.h"
|
||||
#include "mbport.h"
|
||||
#include "mbframe.h"
|
||||
#include "port_tcp_master.h"
|
||||
|
||||
/* ----------------------- Defines -----------------------------------------*/
|
||||
#define MB_TCP_CONNECTION_TIMEOUT_MS ( 20 ) // Connection timeout in mS
|
||||
#define MB_TCP_RECONNECT_TIMEOUT ( 5000000 ) // Connection timeout in uS
|
||||
|
||||
#define MB_TCP_MASTER_PORT_TAG "MB_TCP_MASTER_PORT"
|
||||
#define MB_EVENT_REQ_DONE_MASK ( EV_MASTER_PROCESS_SUCCESS | \
|
||||
EV_MASTER_ERROR_RESPOND_TIMEOUT | \
|
||||
EV_MASTER_ERROR_RECEIVE_DATA | \
|
||||
EV_MASTER_ERROR_EXECUTE_FUNCTION )
|
||||
|
||||
#define MB_EVENT_REQ_ERR_MASK ( EV_MASTER_PROCESS_SUCCESS )
|
||||
|
||||
#define MB_EVENT_WAIT_TOUT_MS ( 2000 )
|
||||
|
||||
#define MB_TCP_READ_TICK_MS ( 1 )
|
||||
#define MB_TCP_READ_BUF_RETRY_CNT ( 4 )
|
||||
#define MB_SLAVE_FMT(fmt) "Slave #%d, Socket(#%d)(%s)"fmt
|
||||
|
||||
/* ----------------------- Types & Prototypes --------------------------------*/
|
||||
void vMBPortEventClose( void );
|
||||
|
||||
/* ----------------------- Static variables ---------------------------------*/
|
||||
static MbPortConfig_t xMbPortConfig;
|
||||
static EventGroupHandle_t xMasterEventHandle = NULL;
|
||||
static EventBits_t xMasterEvent = 0;
|
||||
|
||||
/* ----------------------- Static functions ---------------------------------*/
|
||||
static void vMBTCPPortMasterTask(void *pvParameters);
|
||||
|
||||
/* ----------------------- Begin implementation -----------------------------*/
|
||||
|
||||
// Waits for stack start event to start Modbus event processing
|
||||
BOOL xMBTCPPortMasterWaitEvent(EventGroupHandle_t xEventHandle, EventBits_t xEvent)
|
||||
{
|
||||
xMasterEventHandle = xEventHandle;
|
||||
xMasterEvent = xEvent;
|
||||
BaseType_t status = xEventGroupWaitBits(xMasterEventHandle,
|
||||
(BaseType_t)(xEvent),
|
||||
pdFALSE, // do not clear start bit
|
||||
pdFALSE,
|
||||
portMAX_DELAY);
|
||||
return (BOOL)(status & xEvent);
|
||||
}
|
||||
|
||||
BOOL
|
||||
xMBMasterTCPPortInit( USHORT usTCPPort )
|
||||
{
|
||||
BOOL bOkay = FALSE;
|
||||
|
||||
xMbPortConfig.pxMbSlaveInfo = calloc(MB_TCP_PORT_MAX_CONN, sizeof(MbSlaveInfo_t*));
|
||||
if (!xMbPortConfig.pxMbSlaveInfo) {
|
||||
ESP_LOGE(MB_TCP_MASTER_PORT_TAG, "TCP slave info alloc failure.");
|
||||
return FALSE;
|
||||
}
|
||||
for(int idx = 0; idx < MB_TCP_PORT_MAX_CONN; xMbPortConfig.pxMbSlaveInfo[idx] = NULL, idx++);
|
||||
|
||||
xMbPortConfig.xConnectQueue = NULL;
|
||||
xMbPortConfig.usPort = usTCPPort;
|
||||
xMbPortConfig.usMbSlaveInfoCount = 0;
|
||||
xMbPortConfig.ucCurSlaveIndex = 1;
|
||||
|
||||
xMbPortConfig.xConnectQueue = xQueueCreate(2, sizeof(CHAR*));
|
||||
if (xMbPortConfig.xConnectQueue == 0)
|
||||
{
|
||||
// Queue was not created and must not be used.
|
||||
ESP_LOGE(MB_TCP_MASTER_PORT_TAG, "TCP master queue creation failure.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Create task for packet processing
|
||||
BaseType_t xErr = xTaskCreate(vMBTCPPortMasterTask,
|
||||
"tcp_master_task",
|
||||
MB_TCP_STACK_SIZE,
|
||||
NULL,
|
||||
MB_TCP_TASK_PRIO,
|
||||
&xMbPortConfig.xMbTcpTaskHandle);
|
||||
if (xErr != pdTRUE)
|
||||
{
|
||||
ESP_LOGE(MB_TCP_MASTER_PORT_TAG, "TCP master task creation failure.");
|
||||
(void)vTaskDelete(xMbPortConfig.xMbTcpTaskHandle);
|
||||
} else {
|
||||
ESP_LOGI(MB_TCP_MASTER_PORT_TAG, "TCP master stack initialized.");
|
||||
bOkay = TRUE;
|
||||
}
|
||||
|
||||
vTaskSuspend(xMbPortConfig.xMbTcpTaskHandle);
|
||||
return bOkay;
|
||||
}
|
||||
|
||||
static MbSlaveInfo_t* vMBTCPPortMasterGetCurrInfo(void)
|
||||
{
|
||||
return xMbPortConfig.pxMbSlaveInfo[xMbPortConfig.ucCurSlaveIndex - 1];
|
||||
}
|
||||
|
||||
// Start Modbus event state machine
|
||||
static void vMBTCPPortMasterStartPoll(void)
|
||||
{
|
||||
if (xMasterEventHandle) {
|
||||
// Set the mbcontroller start flag
|
||||
EventBits_t xFlags = xEventGroupSetBits(xMasterEventHandle,
|
||||
(EventBits_t)xMasterEvent);
|
||||
if (!(xFlags & xMasterEvent)) {
|
||||
ESP_LOGE(MB_TCP_MASTER_PORT_TAG, "Fail to start TCP stack.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stop Modbus event state machine
|
||||
static void vMBTCPPortMasterStopPoll(void)
|
||||
{
|
||||
if (xMasterEventHandle) {
|
||||
// Set the mbcontroller start flag
|
||||
EventBits_t xFlags = xEventGroupClearBits(xMasterEventHandle,
|
||||
(EventBits_t)xMasterEvent);
|
||||
if (!(xFlags & xMasterEvent)) {
|
||||
ESP_LOGE(MB_TCP_MASTER_PORT_TAG, "Fail to stop polling.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The helper function to get time stamp in microseconds
|
||||
static int64_t xMBTCPGetTimeStamp(void)
|
||||
{
|
||||
int64_t xTimeStamp = esp_timer_get_time();
|
||||
return xTimeStamp;
|
||||
}
|
||||
|
||||
static void vMBTCPPortMasterMStoTimeVal(USHORT usTimeoutMs, struct timeval *tv)
|
||||
{
|
||||
tv->tv_sec = usTimeoutMs / 1000;
|
||||
tv->tv_usec = (usTimeoutMs - (tv->tv_sec * 1000)) * 1000;
|
||||
}
|
||||
|
||||
static BOOL xMBTCPPortMasterCloseConnection(MbSlaveInfo_t* pxInfo)
|
||||
{
|
||||
if (!pxInfo) {
|
||||
return FALSE;
|
||||
}
|
||||
if (pxInfo->xSockId == -1) {
|
||||
ESP_LOGE(MB_TCP_MASTER_PORT_TAG, "Wrong socket info or disconnected socket: %d, skip.", pxInfo->xSockId);
|
||||
return FALSE;
|
||||
}
|
||||
if (shutdown(pxInfo->xSockId, SHUT_RDWR) == -1) {
|
||||
ESP_LOGV(MB_TCP_MASTER_PORT_TAG, "Shutdown failed sock %d, errno=%d", pxInfo->xSockId, errno);
|
||||
}
|
||||
close(pxInfo->xSockId);
|
||||
pxInfo->xSockId = -1;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void vMBTCPPortMasterSetNetOpt(void* pvNetIf, eMBPortIpVer xIpVersion, eMBPortProto xProto)
|
||||
{
|
||||
xMbPortConfig.pvNetIface = pvNetIf;
|
||||
xMbPortConfig.eMbProto = xProto;
|
||||
xMbPortConfig.eMbIpVer = xIpVersion;
|
||||
}
|
||||
|
||||
void vMBTCPPortMasterTaskStart(void)
|
||||
{
|
||||
vTaskResume(xMbPortConfig.xMbTcpTaskHandle);
|
||||
}
|
||||
|
||||
// Function returns time left for response processing according to response timeout
|
||||
static int64_t xMBTCPPortMasterGetRespTimeLeft(MbSlaveInfo_t* pxInfo)
|
||||
{
|
||||
if (!pxInfo) {
|
||||
return 0;
|
||||
}
|
||||
int64_t xTimeStamp = xMBTCPGetTimeStamp() - pxInfo->xSendTimeStamp;
|
||||
return (xTimeStamp > (1000 * MB_MASTER_TIMEOUT_MS_RESPOND)) ? 0 :
|
||||
(MB_MASTER_TIMEOUT_MS_RESPOND - (xTimeStamp / 1000) - 1);
|
||||
}
|
||||
|
||||
// Wait socket ready to read state
|
||||
static int vMBTCPPortMasterRxCheck(int xSd, fd_set* pxFdSet, int xTimeMs)
|
||||
{
|
||||
fd_set xReadSet = *pxFdSet;
|
||||
fd_set xErrorSet = *pxFdSet;
|
||||
int xRes = 0;
|
||||
struct timeval xTimeout;
|
||||
|
||||
vMBTCPPortMasterMStoTimeVal(xTimeMs, &xTimeout);
|
||||
xRes = select(xSd + 1, &xReadSet, NULL, &xErrorSet, &xTimeout);
|
||||
if (xRes == 0) {
|
||||
// No respond from slave during timeout
|
||||
xRes = ERR_TIMEOUT;
|
||||
} else if ((xRes < 0) || FD_ISSET(xSd, &xErrorSet)) {
|
||||
xRes = -1;
|
||||
}
|
||||
|
||||
*pxFdSet = xReadSet;
|
||||
return xRes;
|
||||
}
|
||||
|
||||
static int xMBTCPPortMasterGetBuf(MbSlaveInfo_t* pxInfo, UCHAR* pucDstBuf, USHORT usLength)
|
||||
{
|
||||
int xLength = 0;
|
||||
UCHAR* pucBuf = pucDstBuf;
|
||||
USHORT usBytesLeft = usLength;
|
||||
|
||||
MB_PORT_CHECK((pxInfo && pxInfo->xSockId > -1), -1, "Try to read incorrect socket = #%d.", pxInfo->xSockId);
|
||||
|
||||
// Receive data from connected client
|
||||
while (usBytesLeft > 0) {
|
||||
// none blocking read from socket with timeout
|
||||
xLength = recv(pxInfo->xSockId, pucBuf, usBytesLeft, MSG_DONTWAIT);
|
||||
if (xLength < 0) {
|
||||
if (errno == EAGAIN) {
|
||||
// Read timeout occurred, continue reading
|
||||
continue;
|
||||
} else if (errno == ENOTCONN) {
|
||||
// Socket connection closed
|
||||
ESP_LOGE(MB_TCP_MASTER_PORT_TAG, "Socket(#%d)(%s) connection closed.",
|
||||
pxInfo->xSockId, pxInfo->pcIpAddr);
|
||||
return ERR_CONN;
|
||||
} else {
|
||||
// Other error occurred during receiving
|
||||
ESP_LOGE(MB_TCP_MASTER_PORT_TAG, "Socket(#%d)(%s) receive error, length=%d, errno=%d",
|
||||
pxInfo->xSockId, pxInfo->pcIpAddr, xLength, errno);
|
||||
return -1;
|
||||
}
|
||||
} else if (xLength) {
|
||||
pucBuf += xLength;
|
||||
usBytesLeft -= xLength;
|
||||
}
|
||||
if (xMBTCPPortMasterGetRespTimeLeft(pxInfo) == 0) {
|
||||
return ERR_TIMEOUT;
|
||||
}
|
||||
vTaskDelay(1);
|
||||
}
|
||||
return usLength;
|
||||
}
|
||||
|
||||
static int vMBTCPPortMasterReadPacket(MbSlaveInfo_t* pxInfo)
|
||||
{
|
||||
int xLength = 0;
|
||||
int xRet = 0;
|
||||
USHORT usTidRcv = 0;
|
||||
|
||||
// Receive data from connected client
|
||||
if (pxInfo) {
|
||||
MB_PORT_CHECK((pxInfo->xSockId > 0), -1, "Try to read incorrect socket = #%d.", pxInfo->xSockId);
|
||||
// Read packet header
|
||||
xRet = xMBTCPPortMasterGetBuf(pxInfo, &pxInfo->pucRcvBuf[0], MB_TCP_UID);
|
||||
if (xRet < 0) {
|
||||
pxInfo->xRcvErr = xRet;
|
||||
return xRet;
|
||||
} else if (xRet != MB_TCP_UID) {
|
||||
ESP_LOGD(MB_TCP_MASTER_PORT_TAG, "Socket (#%d)(%s), Fail to read modbus header. ret=%d",
|
||||
pxInfo->xSockId, pxInfo->pcIpAddr, xRet);
|
||||
pxInfo->xRcvErr = ERR_VAL;
|
||||
return ERR_VAL;
|
||||
}
|
||||
// If we have received the MBAP header we can analyze it and calculate
|
||||
// the number of bytes left to complete the current request.
|
||||
xLength = (int)MB_TCP_GET_FIELD(pxInfo->pucRcvBuf, MB_TCP_LEN);
|
||||
xRet = xMBTCPPortMasterGetBuf(pxInfo, &pxInfo->pucRcvBuf[MB_TCP_UID], xLength);
|
||||
if (xRet < 0) {
|
||||
pxInfo->xRcvErr = xRet;
|
||||
return xRet;
|
||||
} else if (xRet != xLength) {
|
||||
// Received incorrect or fragmented packet.
|
||||
ESP_LOGD(MB_TCP_MASTER_PORT_TAG, "Socket(#%d)(%s) incorrect packet, length=%d, TID=0x%02x, errno=%d(%s)",
|
||||
pxInfo->xSockId, pxInfo->pcIpAddr, pxInfo->usRcvPos,
|
||||
usTidRcv, errno, strerror(errno));
|
||||
pxInfo->xRcvErr = ERR_VAL;
|
||||
return ERR_VAL;
|
||||
}
|
||||
usTidRcv = MB_TCP_GET_FIELD(pxInfo->pucRcvBuf, MB_TCP_TID);
|
||||
|
||||
// Check transaction identifier field in the incoming packet.
|
||||
if ((pxInfo->usTidCnt - 1) != usTidRcv) {
|
||||
ESP_LOGD(MB_TCP_MASTER_PORT_TAG, "Socket (#%d)(%s), incorrect TID(0x%02x)!=(0x%02x) received, discard data.",
|
||||
pxInfo->xSockId, pxInfo->pcIpAddr, usTidRcv, (pxInfo->usTidCnt - 1));
|
||||
pxInfo->xRcvErr = ERR_BUF;
|
||||
return ERR_BUF;
|
||||
}
|
||||
pxInfo->usRcvPos += xRet + MB_TCP_UID;
|
||||
ESP_LOGD(MB_TCP_MASTER_PORT_TAG, "Socket(#%d)(%s) get data, length=%d, TID=0x%02x, errno=%d(%s)",
|
||||
pxInfo->xSockId, pxInfo->pcIpAddr, pxInfo->usRcvPos,
|
||||
usTidRcv, errno, strerror(errno));
|
||||
pxInfo->xRcvErr = ERR_OK;
|
||||
return pxInfo->usRcvPos;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static err_t xMBTCPPortMasterSetNonBlocking(MbSlaveInfo_t* pxInfo)
|
||||
{
|
||||
// Set non blocking attribute for socket
|
||||
ULONG ulFlags = fcntl(pxInfo->xSockId, F_GETFL);
|
||||
if (fcntl(pxInfo->xSockId, F_SETFL, ulFlags | O_NONBLOCK) == -1) {
|
||||
ESP_LOGE(MB_TCP_MASTER_PORT_TAG, "Socket(#%d)(%s), fcntl() call error=%d",
|
||||
pxInfo->xSockId, pxInfo->pcIpAddr, errno);
|
||||
return ERR_WOULDBLOCK;
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static void vMBTCPPortSetKeepAlive(MbSlaveInfo_t* pxInfo)
|
||||
{
|
||||
int optval = 1;
|
||||
setsockopt(pxInfo->xSockId, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
|
||||
}
|
||||
|
||||
// Check connection for timeout helper
|
||||
static err_t xMBTCPPortMasterCheckAlive(MbSlaveInfo_t* pxInfo, ULONG xTimeoutMs)
|
||||
{
|
||||
fd_set xWriteSet;
|
||||
fd_set xErrorSet;
|
||||
err_t xErr = -1;
|
||||
struct timeval xTimeVal;
|
||||
|
||||
if (pxInfo && pxInfo->xSockId != -1) {
|
||||
FD_ZERO(&xWriteSet);
|
||||
FD_ZERO(&xErrorSet);
|
||||
FD_SET(pxInfo->xSockId, &xWriteSet);
|
||||
FD_SET(pxInfo->xSockId, &xErrorSet);
|
||||
vMBTCPPortMasterMStoTimeVal(xTimeoutMs, &xTimeVal);
|
||||
// Check if the socket is writable
|
||||
xErr = select(pxInfo->xSockId + 1, NULL, &xWriteSet, &xErrorSet, &xTimeVal);
|
||||
if ((xErr < 0) || FD_ISSET(pxInfo->xSockId, &xErrorSet)) {
|
||||
if (errno == EINPROGRESS) {
|
||||
xErr = ERR_INPROGRESS;
|
||||
} else {
|
||||
ESP_LOGV(MB_TCP_MASTER_PORT_TAG, MB_SLAVE_FMT(" connection, select write err(errno) = %d(%d)."),
|
||||
pxInfo->xIndex, pxInfo->xSockId, pxInfo->pcIpAddr, xErr, errno);
|
||||
xErr = ERR_CONN;
|
||||
}
|
||||
} else if (xErr == 0) {
|
||||
ESP_LOGV(MB_TCP_MASTER_PORT_TAG, "Socket(#%d)(%s), connection timeout occurred, err(errno) = %d(%d).",
|
||||
pxInfo->xSockId, pxInfo->pcIpAddr, xErr, errno);
|
||||
return ERR_INPROGRESS;
|
||||
} else {
|
||||
int xOptErr = 0;
|
||||
ULONG ulOptLen = sizeof(xOptErr);
|
||||
// Check socket error
|
||||
xErr = getsockopt(pxInfo->xSockId, SOL_SOCKET, SO_ERROR, (void*)&xOptErr, (socklen_t*)&ulOptLen);
|
||||
if (xOptErr != 0) {
|
||||
ESP_LOGD(MB_TCP_MASTER_PORT_TAG, "Socket(#%d)(%s), sock error occurred (%d).",
|
||||
pxInfo->xSockId, pxInfo->pcIpAddr, xOptErr);
|
||||
return ERR_CONN;
|
||||
}
|
||||
ESP_LOGV(MB_TCP_MASTER_PORT_TAG, "Socket(#%d)(%s), is alive.",
|
||||
pxInfo->xSockId, pxInfo->pcIpAddr);
|
||||
return ERR_OK;
|
||||
}
|
||||
} else {
|
||||
xErr = ERR_CONN;
|
||||
}
|
||||
return xErr;
|
||||
}
|
||||
|
||||
// Resolve host name and/or fill the IP address structure
|
||||
static BOOL xMBTCPPortMasterCheckHost(const CHAR* pcHostStr, ip_addr_t* pxHostAddr)
|
||||
{
|
||||
MB_PORT_CHECK((pcHostStr), FALSE, "Wrong host name or IP.");
|
||||
CHAR cStr[45];
|
||||
CHAR* pcStr = &cStr[0];
|
||||
ip_addr_t xTargetAddr;
|
||||
struct addrinfo xHint;
|
||||
struct addrinfo* pxAddrList;
|
||||
memset(&xHint, 0, sizeof(xHint));
|
||||
// Do name resolution for both protocols
|
||||
xHint.ai_family = AF_UNSPEC;
|
||||
xHint.ai_flags = AI_ADDRCONFIG; // get IPV6 address if supported, otherwise IPV4
|
||||
memset(&xTargetAddr, 0, sizeof(xTargetAddr));
|
||||
|
||||
// convert domain name to IP address
|
||||
// Todo: check EAI_FAIL error when resolve host name
|
||||
int xRet = getaddrinfo(pcHostStr, NULL, &xHint, &pxAddrList);
|
||||
|
||||
if (xRet != 0) {
|
||||
ESP_LOGE(MB_TCP_MASTER_PORT_TAG, "Incorrect host name or IP: %s", pcHostStr);
|
||||
return FALSE;
|
||||
}
|
||||
if (pxAddrList->ai_family == AF_INET) {
|
||||
struct in_addr addr4 = ((struct sockaddr_in *) (pxAddrList->ai_addr))->sin_addr;
|
||||
inet_addr_to_ip4addr(ip_2_ip4(&xTargetAddr), &addr4);
|
||||
pcStr = ip4addr_ntoa_r(ip_2_ip4(&xTargetAddr), cStr, sizeof(cStr));
|
||||
} else {
|
||||
struct in6_addr addr6 = ((struct sockaddr_in6 *) (pxAddrList->ai_addr))->sin6_addr;
|
||||
inet6_addr_to_ip6addr(ip_2_ip6(&xTargetAddr), &addr6);
|
||||
pcStr = ip6addr_ntoa_r(ip_2_ip6(&xTargetAddr), cStr, sizeof(cStr));
|
||||
}
|
||||
if (pxHostAddr) {
|
||||
*pxHostAddr = xTargetAddr;
|
||||
}
|
||||
ESP_LOGI(MB_TCP_MASTER_PORT_TAG, "Host[IP]: \"%s\"[%s]", pxAddrList->ai_canonname, pcStr);
|
||||
freeaddrinfo(pxAddrList);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL xMBTCPPortMasterAddSlaveIp(const CHAR* pcIpStr)
|
||||
{
|
||||
BOOL xRes = FALSE;
|
||||
MB_PORT_CHECK(xMbPortConfig.xConnectQueue != NULL, FALSE, "Wrong slave IP address to add.");
|
||||
if (pcIpStr) {
|
||||
xRes = xMBTCPPortMasterCheckHost(pcIpStr, NULL);
|
||||
}
|
||||
if (xRes || !pcIpStr) {
|
||||
BaseType_t xStatus = xQueueSend(xMbPortConfig.xConnectQueue, (const void*)&pcIpStr, 100);
|
||||
MB_PORT_CHECK((xStatus == pdTRUE), FALSE, "FAIL to add slave IP address: [%s].", pcIpStr);
|
||||
}
|
||||
return xRes;
|
||||
}
|
||||
|
||||
// Unblocking connect function
|
||||
static err_t xMBTCPPortMasterConnect(MbSlaveInfo_t* pxInfo)
|
||||
{
|
||||
err_t xErr = ERR_OK;
|
||||
CHAR cStr[128];
|
||||
CHAR* pcStr = NULL;
|
||||
ip_addr_t xTargetAddr;
|
||||
struct addrinfo xHint;
|
||||
struct addrinfo* pxAddrList;
|
||||
struct addrinfo* pxCurAddr;
|
||||
|
||||
memset(&xHint, 0, sizeof(xHint));
|
||||
// Do name resolution for both protocols
|
||||
//xHint.ai_family = AF_UNSPEC; Todo: Find a reason why AF_UNSPEC does not work
|
||||
xHint.ai_flags = AI_ADDRCONFIG; // get IPV6 address if supported, otherwise IPV4
|
||||
xHint.ai_family = (xMbPortConfig.eMbIpVer == MB_PORT_IPV4) ? AF_INET : AF_INET6;
|
||||
xHint.ai_socktype = (pxInfo->xMbProto == MB_PROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM;
|
||||
xHint.ai_protocol = (pxInfo->xMbProto == MB_PROTO_UDP) ? IPPROTO_UDP : IPPROTO_TCP;
|
||||
memset(&xTargetAddr, 0, sizeof(xTargetAddr));
|
||||
|
||||
if (asprintf(&pcStr, "%u", xMbPortConfig.usPort) == -1) {
|
||||
abort();
|
||||
}
|
||||
|
||||
// convert domain name to IP address
|
||||
int xRet = getaddrinfo(pxInfo->pcIpAddr, pcStr, &xHint, &pxAddrList);
|
||||
free(pcStr);
|
||||
if (xRet != 0) {
|
||||
ESP_LOGE(MB_TCP_MASTER_PORT_TAG, "Cannot resolve host: %s", pxInfo->pcIpAddr);
|
||||
return ERR_CONN;
|
||||
}
|
||||
|
||||
for (pxCurAddr = pxAddrList; pxCurAddr != NULL; pxCurAddr = pxCurAddr->ai_next) {
|
||||
if (pxCurAddr->ai_family == AF_INET) {
|
||||
struct in_addr addr4 = ((struct sockaddr_in *) (pxCurAddr->ai_addr))->sin_addr;
|
||||
inet_addr_to_ip4addr(ip_2_ip4(&xTargetAddr), &addr4);
|
||||
pcStr = ip4addr_ntoa_r(ip_2_ip4(&xTargetAddr), cStr, sizeof(cStr));
|
||||
} else if (pxCurAddr->ai_family == AF_INET6) {
|
||||
struct in6_addr addr6 = ((struct sockaddr_in6 *) (pxCurAddr->ai_addr))->sin6_addr;
|
||||
inet6_addr_to_ip6addr(ip_2_ip6(&xTargetAddr), &addr6);
|
||||
pcStr = ip6addr_ntoa_r(ip_2_ip6(&xTargetAddr), cStr, sizeof(cStr));
|
||||
// Set scope id to fix routing issues with local address
|
||||
((struct sockaddr_in6 *) (pxCurAddr->ai_addr))->sin6_scope_id =
|
||||
esp_netif_get_netif_impl_index(xMbPortConfig.pvNetIface);
|
||||
}
|
||||
|
||||
if (pxInfo->xSockId <= 0) {
|
||||
pxInfo->xSockId = socket(pxCurAddr->ai_family, pxCurAddr->ai_socktype, pxCurAddr->ai_protocol);
|
||||
if (pxInfo->xSockId < 0) {
|
||||
ESP_LOGE(MB_TCP_MASTER_PORT_TAG, "Unable to create socket: #%d, errno %d", pxInfo->xSockId, errno);
|
||||
xErr = ERR_IF;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
ESP_LOGV(MB_TCP_MASTER_PORT_TAG, "Socket (#%d)(%s) created.", pxInfo->xSockId, cStr);
|
||||
}
|
||||
|
||||
// Set non blocking attribute for socket
|
||||
xMBTCPPortMasterSetNonBlocking(pxInfo);
|
||||
|
||||
// Can return EINPROGRESS as an error which means
|
||||
// that connection is in progress and should be checked later
|
||||
xErr = connect(pxInfo->xSockId, (struct sockaddr*)pxCurAddr->ai_addr, pxCurAddr->ai_addrlen);
|
||||
if ((xErr < 0) && (errno == EINPROGRESS || errno == EALREADY)) {
|
||||
// The unblocking connect is pending (check status later) or already connected
|
||||
ESP_LOGV(MB_TCP_MASTER_PORT_TAG, "Socket(#%d)(%s) connection is pending, errno %d (%s).",
|
||||
pxInfo->xSockId, cStr, errno, strerror(errno));
|
||||
|
||||
// Set keep alive flag in socket options
|
||||
vMBTCPPortSetKeepAlive(pxInfo);
|
||||
xErr = xMBTCPPortMasterCheckAlive(pxInfo, MB_TCP_CONNECTION_TIMEOUT_MS);
|
||||
continue;
|
||||
} else if ((xErr < 0) && (errno == EISCONN)) {
|
||||
// Socket already connected
|
||||
xErr = ERR_OK;
|
||||
continue;
|
||||
} else if (xErr != ERR_OK) {
|
||||
// Other error occurred during connection
|
||||
ESP_LOGV(MB_TCP_MASTER_PORT_TAG, MB_SLAVE_FMT(" unable to connect, error=%d, errno %d (%s)"),
|
||||
pxInfo->xIndex, pxInfo->xSockId, cStr, xErr, errno, strerror(errno));
|
||||
xMBTCPPortMasterCloseConnection(pxInfo);
|
||||
xErr = ERR_CONN;
|
||||
} else {
|
||||
ESP_LOGI(MB_TCP_MASTER_PORT_TAG, MB_SLAVE_FMT(", successfully connected."),
|
||||
pxInfo->xIndex, pxInfo->xSockId, cStr);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
freeaddrinfo(pxAddrList);
|
||||
return xErr;
|
||||
}
|
||||
|
||||
// Find the first slave info whose descriptor is set in xFdSet
|
||||
static MbSlaveInfo_t* xMBTCPPortMasterGetSlaveReady(fd_set* pxFdSet)
|
||||
{
|
||||
MbSlaveInfo_t* pxInfo = NULL;
|
||||
|
||||
// Slave connection loop
|
||||
for (int xIndex = 0; (xIndex < MB_TCP_PORT_MAX_CONN); xIndex++) {
|
||||
pxInfo = xMbPortConfig.pxMbSlaveInfo[xIndex];
|
||||
if (pxInfo) {
|
||||
// Is this response for current processing slave
|
||||
if (FD_ISSET(pxInfo->xSockId, pxFdSet)) {
|
||||
FD_CLR(pxInfo->xSockId, pxFdSet);
|
||||
return pxInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (MbSlaveInfo_t*)NULL;
|
||||
}
|
||||
|
||||
static int xMBTCPPortMasterCheckConnState(fd_set* pxFdSet)
|
||||
{
|
||||
fd_set xConnSetCheck = *pxFdSet;
|
||||
MbSlaveInfo_t* pxInfo = NULL;
|
||||
int64_t xTime = 0;
|
||||
int xErr = 0;
|
||||
int xCount = 0;
|
||||
do {
|
||||
xTime = xMBTCPGetTimeStamp();
|
||||
pxInfo = xMBTCPPortMasterGetSlaveReady(&xConnSetCheck);
|
||||
if (pxInfo) {
|
||||
xErr = xMBTCPPortMasterCheckAlive(pxInfo, 0);
|
||||
if ((xErr < 0) && (((xTime - pxInfo->xRecvTimeStamp) > MB_TCP_RECONNECT_TIMEOUT) ||
|
||||
((xTime - pxInfo->xSendTimeStamp) > MB_TCP_RECONNECT_TIMEOUT))) {
|
||||
ESP_LOGI(MB_TCP_MASTER_PORT_TAG, MB_SLAVE_FMT(", slave is down, off_time[r][w](us) = [%ju][%ju]."),
|
||||
pxInfo->xIndex,
|
||||
pxInfo->xSockId,
|
||||
pxInfo->pcIpAddr,
|
||||
(int64_t)(xTime - pxInfo->xRecvTimeStamp),
|
||||
(int64_t)(xTime - pxInfo->xSendTimeStamp));
|
||||
xCount++;
|
||||
}
|
||||
}
|
||||
} while (pxInfo && (xCount < MB_TCP_PORT_MAX_CONN));
|
||||
return xCount;
|
||||
}
|
||||
|
||||
static void xMBTCPPortMasterFsmSetError(eMBMasterErrorEventType xErrType, eMBMasterEventType xPostEvent)
|
||||
{
|
||||
vMBMasterPortTimersDisable();
|
||||
vMBMasterSetErrorType(xErrType);
|
||||
xMBMasterPortEventPost(xPostEvent);
|
||||
}
|
||||
|
||||
static void vMBTCPPortMasterTask(void *pvParameters)
|
||||
{
|
||||
CHAR* pcAddrStr = NULL;
|
||||
MbSlaveInfo_t* pxInfo;
|
||||
MbSlaveInfo_t* pxCurrInfo;
|
||||
fd_set xConnSet;
|
||||
fd_set xReadSet;
|
||||
int xMaxSd = 0;
|
||||
err_t xErr = ERR_ABRT;
|
||||
USHORT usSlaveConnCnt = 0;
|
||||
int64_t xTime = 0;
|
||||
|
||||
// Register each slave in the connection info structure
|
||||
while (1) {
|
||||
BaseType_t xStatus = xQueueReceive(xMbPortConfig.xConnectQueue, (void*)&pcAddrStr, portMAX_DELAY);
|
||||
if (xStatus != pdTRUE) {
|
||||
ESP_LOGE(MB_TCP_MASTER_PORT_TAG, "Fail to register slave IP.");
|
||||
} else {
|
||||
if (pcAddrStr == NULL && xMbPortConfig.usMbSlaveInfoCount) {
|
||||
break;
|
||||
}
|
||||
if (xMbPortConfig.usMbSlaveInfoCount > MB_TCP_PORT_MAX_CONN) {
|
||||
ESP_LOGE(MB_TCP_MASTER_PORT_TAG, "Exceeds maximum connections limit=%d.", MB_TCP_PORT_MAX_CONN);
|
||||
break;
|
||||
}
|
||||
pxInfo = calloc(1, sizeof(MbSlaveInfo_t));
|
||||
if (!pxInfo) {
|
||||
ESP_LOGE(MB_TCP_MASTER_PORT_TAG, "Slave(#%d), info structure allocation fail.",
|
||||
xMbPortConfig.usMbSlaveInfoCount);
|
||||
free(pxInfo);
|
||||
break;
|
||||
}
|
||||
pxInfo->pucRcvBuf = calloc(MB_TCP_BUF_SIZE, sizeof(UCHAR));
|
||||
if (!pxInfo->pucRcvBuf) {
|
||||
ESP_LOGE(MB_TCP_MASTER_PORT_TAG, "Slave(#%d), receive buffer allocation fail.",
|
||||
xMbPortConfig.usMbSlaveInfoCount);
|
||||
free(pxInfo->pucRcvBuf);
|
||||
break;
|
||||
}
|
||||
pxInfo->usRcvPos = 0;
|
||||
pxInfo->pcIpAddr = pcAddrStr;
|
||||
pxInfo->xSockId = -1;
|
||||
pxInfo->xError = -1;
|
||||
pxInfo->xRecvTimeStamp = xMBTCPGetTimeStamp();
|
||||
pxInfo->xSendTimeStamp = xMBTCPGetTimeStamp();
|
||||
pxInfo->xMbProto = MB_PROTO_TCP;
|
||||
pxInfo->xIndex = xMbPortConfig.usMbSlaveInfoCount;
|
||||
pxInfo->usTidCnt = (USHORT)(xMbPortConfig.usMbSlaveInfoCount << 8U);
|
||||
// Register slave
|
||||
xMbPortConfig.pxMbSlaveInfo[xMbPortConfig.usMbSlaveInfoCount++] = pxInfo;
|
||||
ESP_LOGI(MB_TCP_MASTER_PORT_TAG, "Add slave IP: %s", pcAddrStr);
|
||||
}
|
||||
}
|
||||
|
||||
// Main connection cycle
|
||||
while (1)
|
||||
{
|
||||
ESP_LOGI(MB_TCP_MASTER_PORT_TAG, "Connecting to slaves...");
|
||||
xTime = xMBTCPGetTimeStamp();
|
||||
usSlaveConnCnt = 0;
|
||||
CHAR ucDot = '.';
|
||||
while(usSlaveConnCnt < xMbPortConfig.usMbSlaveInfoCount) {
|
||||
usSlaveConnCnt = 0;
|
||||
FD_ZERO(&xConnSet);
|
||||
ucDot ^= 0x03;
|
||||
// Slave connection loop
|
||||
for (UCHAR ucCnt = 0; (ucCnt < MB_TCP_PORT_MAX_CONN); ucCnt++) {
|
||||
pxInfo = xMbPortConfig.pxMbSlaveInfo[ucCnt];
|
||||
// if slave descriptor is NULL then it is end of list or connection closed.
|
||||
if (!pxInfo) {
|
||||
ESP_LOGV(MB_TCP_MASTER_PORT_TAG, "Index: %d is not initialized, skip.", ucCnt);
|
||||
if (xMbPortConfig.usMbSlaveInfoCount) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
putchar(ucDot);
|
||||
xErr = xMBTCPPortMasterConnect(pxInfo);
|
||||
switch(xErr)
|
||||
{
|
||||
case ERR_CONN:
|
||||
case ERR_INPROGRESS:
|
||||
// In case of connection errors remove the socket from set
|
||||
if (FD_ISSET(pxInfo->xSockId, &xConnSet)) {
|
||||
FD_CLR(pxInfo->xSockId, &xConnSet);
|
||||
ESP_LOGE(MB_TCP_MASTER_PORT_TAG, MB_SLAVE_FMT(" connect failed, error = %d."),
|
||||
pxInfo->xIndex, pxInfo->xSockId,
|
||||
(char*)pxInfo->pcIpAddr, xErr);
|
||||
if (usSlaveConnCnt) {
|
||||
usSlaveConnCnt--;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ERR_OK:
|
||||
// if connection is successful, add the descriptor into set
|
||||
if (!FD_ISSET(pxInfo->xSockId, &xConnSet)) {
|
||||
FD_SET(pxInfo->xSockId, &xConnSet);
|
||||
usSlaveConnCnt++;
|
||||
xMaxSd = (pxInfo->xSockId > xMaxSd) ? pxInfo->xSockId : xMaxSd;
|
||||
ESP_LOGD(MB_TCP_MASTER_PORT_TAG, MB_SLAVE_FMT(", connected %d slave(s), error = %d."),
|
||||
pxInfo->xIndex, pxInfo->xSockId,
|
||||
pxInfo->pcIpAddr,
|
||||
usSlaveConnCnt, xErr);
|
||||
// Update time stamp for connected slaves
|
||||
pxInfo->xRecvTimeStamp = xMBTCPGetTimeStamp();
|
||||
pxInfo->xSendTimeStamp = xMBTCPGetTimeStamp();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(MB_TCP_MASTER_PORT_TAG, MB_SLAVE_FMT(", unexpected error = %d."),
|
||||
pxInfo->xIndex,
|
||||
pxInfo->xSockId,
|
||||
pxInfo->pcIpAddr, xErr);
|
||||
break;
|
||||
}
|
||||
pxInfo->xError = xErr;
|
||||
}
|
||||
}
|
||||
ESP_LOGI(MB_TCP_MASTER_PORT_TAG, "Connected %d slaves, start polling...", usSlaveConnCnt);
|
||||
vMBTCPPortMasterStartPoll(); // Send event to start stack
|
||||
|
||||
// Slave receive data loop
|
||||
while(usSlaveConnCnt) {
|
||||
xReadSet = xConnSet;
|
||||
// Check transmission event to clear appropriate bit.
|
||||
xMBMasterPortFsmWaitConfirmation(EV_MASTER_FRAME_TRANSMIT, pdMS_TO_TICKS(MB_EVENT_WAIT_TOUT_MS));
|
||||
// Synchronize state machine with send packet event
|
||||
if (xMBMasterPortFsmWaitConfirmation(EV_MASTER_FRAME_SENT, pdMS_TO_TICKS(MB_EVENT_WAIT_TOUT_MS))) {
|
||||
ESP_LOGD(MB_TCP_MASTER_PORT_TAG, "FSM Synchronized with sent event.");
|
||||
}
|
||||
// Get slave info for the current slave.
|
||||
pxCurrInfo = vMBTCPPortMasterGetCurrInfo();
|
||||
if (!pxCurrInfo) {
|
||||
ESP_LOGE(MB_TCP_MASTER_PORT_TAG, "Incorrect connection options for slave index: %d.",
|
||||
xMbPortConfig.ucCurSlaveIndex);
|
||||
vMBTCPPortMasterStopPoll();
|
||||
break; // incorrect slave descriptor, reconnect.
|
||||
}
|
||||
xTime = xMBTCPPortMasterGetRespTimeLeft(pxCurrInfo);
|
||||
ESP_LOGD(MB_TCP_MASTER_PORT_TAG, "Set select timeout, left time: %ju ms.",
|
||||
xMBTCPPortMasterGetRespTimeLeft(pxCurrInfo));
|
||||
// Wait respond from current slave during respond timeout
|
||||
int xRes = vMBTCPPortMasterRxCheck(pxCurrInfo->xSockId, &xReadSet, xTime);
|
||||
if (xRes == ERR_TIMEOUT) {
|
||||
// No respond from current slave, process timeout.
|
||||
// Need to drop response later if it is received after timeout.
|
||||
ESP_LOGD(MB_TCP_MASTER_PORT_TAG, "Select timeout, left time: %ju ms.",
|
||||
xMBTCPPortMasterGetRespTimeLeft(pxCurrInfo));
|
||||
xTime = xMBTCPPortMasterGetRespTimeLeft(pxCurrInfo);
|
||||
// Wait completion of last transaction
|
||||
xMBMasterPortFsmWaitConfirmation(MB_EVENT_REQ_DONE_MASK, pdMS_TO_TICKS(xTime + 1));
|
||||
continue;
|
||||
} else if (xRes < 0) {
|
||||
// Select error (slave connection or r/w failure).
|
||||
ESP_LOGD(MB_TCP_MASTER_PORT_TAG, MB_SLAVE_FMT(", socket select error. Slave disconnected?"),
|
||||
pxCurrInfo->xIndex, pxCurrInfo->xSockId, pxCurrInfo->pcIpAddr);
|
||||
xTime = xMBTCPPortMasterGetRespTimeLeft(pxCurrInfo);
|
||||
// Wait completion of last transaction
|
||||
xMBMasterPortFsmWaitConfirmation(MB_EVENT_REQ_DONE_MASK, pdMS_TO_TICKS(xTime));
|
||||
// Stop polling process
|
||||
vMBTCPPortMasterStopPoll();
|
||||
// Check disconnected slaves, do not need a result just to print information.
|
||||
xMBTCPPortMasterCheckConnState(&xConnSet);
|
||||
break;
|
||||
} else {
|
||||
// Check to make sure that active slave data is ready
|
||||
if (FD_ISSET(pxCurrInfo->xSockId, &xReadSet)) {
|
||||
xErr = ERR_BUF;
|
||||
for (int retry = 0; (xErr == ERR_BUF) && (retry < MB_TCP_READ_BUF_RETRY_CNT); retry++) {
|
||||
xErr = vMBTCPPortMasterReadPacket(pxCurrInfo);
|
||||
// The error ERR_BUF means received response to previous request
|
||||
// (due to timeout) with the same socket ID and incorrect TID,
|
||||
// then ignore it and try to get next response buffer.
|
||||
}
|
||||
if (xErr > 0) {
|
||||
// Response received correctly, send an event to stack
|
||||
xMBTCPPortMasterFsmSetError(EV_ERROR_INIT, EV_MASTER_FRAME_RECEIVED);
|
||||
ESP_LOGD(MB_TCP_MASTER_PORT_TAG, MB_SLAVE_FMT(", frame received."),
|
||||
pxCurrInfo->xIndex, pxCurrInfo->xSockId, pxCurrInfo->pcIpAddr);
|
||||
} else if ((xErr == ERR_TIMEOUT) || (xMBTCPPortMasterGetRespTimeLeft(pxCurrInfo) == 0)) {
|
||||
// Timeout occurred when receiving frame, process respond timeout
|
||||
ESP_LOGD(MB_TCP_MASTER_PORT_TAG, MB_SLAVE_FMT(", frame read timeout."),
|
||||
pxCurrInfo->xIndex, pxCurrInfo->xSockId, pxCurrInfo->pcIpAddr);
|
||||
} else if (xErr == ERR_BUF) {
|
||||
// After retries a response with incorrect TID received, process failure.
|
||||
xMBTCPPortMasterFsmSetError(EV_ERROR_RECEIVE_DATA, EV_MASTER_ERROR_PROCESS);
|
||||
ESP_LOGD(MB_TCP_MASTER_PORT_TAG, MB_SLAVE_FMT(", frame error."),
|
||||
pxCurrInfo->xIndex, pxCurrInfo->xSockId, pxCurrInfo->pcIpAddr);
|
||||
} else {
|
||||
ESP_LOGE(MB_TCP_MASTER_PORT_TAG, MB_SLAVE_FMT(", critical error=%d."),
|
||||
pxCurrInfo->xIndex, pxCurrInfo->xSockId, pxCurrInfo->pcIpAddr, xErr);
|
||||
// Stop polling process
|
||||
vMBTCPPortMasterStopPoll();
|
||||
// Check disconnected slaves, do not need a result just to print information.
|
||||
xMBTCPPortMasterCheckConnState(&xConnSet);
|
||||
break;
|
||||
}
|
||||
xTime = xMBTCPPortMasterGetRespTimeLeft(pxCurrInfo);
|
||||
ESP_LOGD(MB_TCP_MASTER_PORT_TAG, "Slave #%d, data processing left time %ju [ms].", pxCurrInfo->xIndex, xTime);
|
||||
// Wait completion of Modbus frame processing before start of new transaction.
|
||||
if (xMBMasterPortFsmWaitConfirmation(MB_EVENT_REQ_DONE_MASK, pdMS_TO_TICKS(xTime))) {
|
||||
ESP_LOGD(MB_TCP_MASTER_PORT_TAG, MB_SLAVE_FMT(", data processing completed."),
|
||||
pxCurrInfo->xIndex, pxCurrInfo->xSockId, pxCurrInfo->pcIpAddr);
|
||||
}
|
||||
xTime = xMBTCPGetTimeStamp() - pxCurrInfo->xSendTimeStamp;
|
||||
ESP_LOGD(MB_TCP_MASTER_PORT_TAG, MB_SLAVE_FMT(", processing time[us] = %ju."),
|
||||
pxCurrInfo->xIndex, pxCurrInfo->xSockId, pxCurrInfo->pcIpAddr, xTime);
|
||||
}
|
||||
}
|
||||
} // while(usMbSlaveInfoCount)
|
||||
} // while (1)
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
extern void vMBMasterPortEventClose(void);
|
||||
extern void vMBMasterPortTimerClose(void);
|
||||
|
||||
void
|
||||
vMBMasterTCPPortClose(void)
|
||||
{
|
||||
(void)vTaskDelete(xMbPortConfig.xMbTcpTaskHandle);
|
||||
(void)vMBMasterTCPPortDisable();
|
||||
free(xMbPortConfig.pxMbSlaveInfo);
|
||||
vQueueDelete(xMbPortConfig.xConnectQueue);
|
||||
vMBMasterPortTimerClose();
|
||||
// Release resources for the event queue.
|
||||
vMBMasterPortEventClose();
|
||||
}
|
||||
|
||||
void
|
||||
vMBMasterTCPPortDisable(void)
|
||||
{
|
||||
for (USHORT ucCnt = 0; ucCnt < MB_TCP_PORT_MAX_CONN; ucCnt++) {
|
||||
MbSlaveInfo_t* pxInfo = xMbPortConfig.pxMbSlaveInfo[ucCnt];
|
||||
if (pxInfo) {
|
||||
xMBTCPPortMasterCloseConnection(pxInfo);
|
||||
if (pxInfo->pucRcvBuf) {
|
||||
free(pxInfo->pucRcvBuf);
|
||||
}
|
||||
free(pxInfo);
|
||||
xMbPortConfig.pxMbSlaveInfo[ucCnt] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOL
|
||||
xMBMasterTCPPortGetRequest( UCHAR ** ppucMBTCPFrame, USHORT * usTCPLength )
|
||||
{
|
||||
MbSlaveInfo_t* pxInfo = vMBTCPPortMasterGetCurrInfo();
|
||||
*ppucMBTCPFrame = pxInfo->pucRcvBuf;
|
||||
*usTCPLength = pxInfo->usRcvPos;
|
||||
|
||||
// Reset the buffer.
|
||||
pxInfo->usRcvPos = 0;
|
||||
// Save slave receive timestamp
|
||||
if (pxInfo->xRcvErr == ERR_OK && *usTCPLength > 0) {
|
||||
pxInfo->xRecvTimeStamp = xMBTCPGetTimeStamp();
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int xMBMasterTCPPortWritePoll(MbSlaveInfo_t* pxInfo, const UCHAR * pucMBTCPFrame, USHORT usTCPLength, ULONG xTimeout)
|
||||
{
|
||||
// Check if the socket is alive (writable and SO_ERROR == 0)
|
||||
int xRes = (int)xMBTCPPortMasterCheckAlive(pxInfo, xTimeout);
|
||||
if ((xRes < 0) && (xRes != ERR_INPROGRESS))
|
||||
{
|
||||
ESP_LOGE(MB_TCP_MASTER_PORT_TAG, MB_SLAVE_FMT(", is not writable, error: %d, errno %d"),
|
||||
pxInfo->xIndex, pxInfo->xSockId, pxInfo->pcIpAddr, xRes, errno);
|
||||
return xRes;
|
||||
}
|
||||
xRes = send(pxInfo->xSockId, pucMBTCPFrame, usTCPLength, TCP_NODELAY);
|
||||
if (xRes < 0) {
|
||||
ESP_LOGE(MB_TCP_MASTER_PORT_TAG, MB_SLAVE_FMT(", send data error: %d, errno %d"),
|
||||
pxInfo->xIndex, pxInfo->xSockId, pxInfo->pcIpAddr, xRes, errno);
|
||||
}
|
||||
return xRes;
|
||||
}
|
||||
|
||||
BOOL
|
||||
xMBMasterTCPPortSendResponse( UCHAR * pucMBTCPFrame, USHORT usTCPLength )
|
||||
{
|
||||
BOOL bFrameSent = FALSE;
|
||||
xMbPortConfig.ucCurSlaveIndex = ucMBMasterGetDestAddress();
|
||||
MbSlaveInfo_t* pxInfo = vMBTCPPortMasterGetCurrInfo();
|
||||
|
||||
// If socket active then send data
|
||||
if (pxInfo->xSockId > -1) {
|
||||
// Apply TID field to the frame before send
|
||||
pucMBTCPFrame[MB_TCP_TID] = (UCHAR)(pxInfo->usTidCnt >> 8U);
|
||||
pucMBTCPFrame[MB_TCP_TID + 1] = (UCHAR)(pxInfo->usTidCnt & 0xFF);
|
||||
int xRes = xMBMasterTCPPortWritePoll(pxInfo, pucMBTCPFrame, usTCPLength, MB_TCP_SEND_TIMEOUT_MS);
|
||||
if (xRes < 0) {
|
||||
ESP_LOGE(MB_TCP_MASTER_PORT_TAG, MB_SLAVE_FMT(", send data failure, err(errno) = %d(%d)."),
|
||||
pxInfo->xIndex, pxInfo->xSockId, pxInfo->pcIpAddr, xRes, errno);
|
||||
bFrameSent = FALSE;
|
||||
pxInfo->xError = xRes;
|
||||
} else {
|
||||
bFrameSent = TRUE;
|
||||
ESP_LOGD(MB_TCP_MASTER_PORT_TAG, MB_SLAVE_FMT(", send data successful: TID=0x%02x, %d (bytes), errno %d"),
|
||||
pxInfo->xIndex, pxInfo->xSockId, pxInfo->pcIpAddr, pxInfo->usTidCnt, xRes, errno);
|
||||
pxInfo->xError = 0;
|
||||
pxInfo->usRcvPos = 0;
|
||||
if (pxInfo->usTidCnt < (USHRT_MAX - 1)) {
|
||||
pxInfo->usTidCnt++;
|
||||
} else {
|
||||
pxInfo->usTidCnt = (USHORT)(pxInfo->xIndex << 8U);
|
||||
}
|
||||
}
|
||||
pxInfo->xSendTimeStamp = xMBTCPGetTimeStamp();
|
||||
} else {
|
||||
ESP_LOGD(MB_TCP_MASTER_PORT_TAG, MB_SLAVE_FMT(", send to died slave, error = %d"),
|
||||
pxInfo->xIndex, pxInfo->xSockId, pxInfo->pcIpAddr, pxInfo->xError);
|
||||
}
|
||||
vMBMasterPortTimersRespondTimeoutEnable();
|
||||
xMBMasterPortEventPost(EV_MASTER_FRAME_SENT);
|
||||
return bFrameSent;
|
||||
}
|
||||
|
||||
// Timer handler to check timeout of socket response
|
||||
BOOL MB_PORT_ISR_ATTR
|
||||
xMBMasterTCPTimerExpired(void)
|
||||
{
|
||||
BOOL xNeedPoll = FALSE;
|
||||
|
||||
vMBMasterPortTimersDisable();
|
||||
// If timer mode is respond timeout, the master event then turns EV_MASTER_EXECUTE status.
|
||||
if (xMBMasterGetCurTimerMode() == MB_TMODE_RESPOND_TIMEOUT) {
|
||||
vMBMasterSetErrorType(EV_ERROR_RESPOND_TIMEOUT);
|
||||
xNeedPoll = xMBMasterPortEventPost(EV_MASTER_ERROR_PROCESS);
|
||||
}
|
||||
|
||||
return xNeedPoll;
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/* 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 TCP Port
|
||||
* Copyright (C) 2006 Christian Walter <wolti@sil.at>
|
||||
* Parts of crt0.S Copyright (c) 1995, 1996, 1998 Cygnus Support
|
||||
*
|
||||
* 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.h,v 1.2 2006/09/04 14:39:20 wolti Exp $
|
||||
*/
|
||||
|
||||
#ifndef _PORT_TCP_SLAVE_H
|
||||
#define _PORT_TCP_SLAVE_H
|
||||
|
||||
/* ----------------------- Platform includes --------------------------------*/
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "lwip/sys.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "port.h"
|
||||
|
||||
/* ----------------------- Defines ------------------------------------------*/
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
PR_BEGIN_EXTERN_C
|
||||
#endif
|
||||
|
||||
/* ----------------------- Type definitions ---------------------------------*/
|
||||
|
||||
typedef struct {
|
||||
int xIndex; /*!< Slave information index */
|
||||
int xSockId; /*!< Socket ID of slave */
|
||||
int xError; /*!< Socket error */
|
||||
int xRcvErr; /*!< Socket receive error */
|
||||
const char* pcIpAddr; /*!< TCP/UDP IP address */
|
||||
UCHAR* pucRcvBuf; /*!< Receive buffer pointer */
|
||||
USHORT usRcvPos; /*!< Receive buffer position */
|
||||
int pcPort; /*!< TCP/UDP port number */
|
||||
eMBPortProto xMbProto; /*!< Protocol type */
|
||||
int64_t xSendTimeStamp; /*!< Send request time stamp */
|
||||
int64_t xRecvTimeStamp; /*!< Receive response time stamp */
|
||||
uint16_t usTidCnt; /*!< Transaction identifier (TID) for slave */
|
||||
} MbSlaveInfo_t;
|
||||
|
||||
typedef struct {
|
||||
TaskHandle_t xMbTcpTaskHandle; /*!< Master TCP/UDP handling task handle */
|
||||
QueueHandle_t xConnectQueue; /*!< Master connection queue */
|
||||
USHORT usPort; /*!< Master TCP/UDP port number */
|
||||
USHORT usMbSlaveInfoCount; /*!< Master count of connected slaves */
|
||||
USHORT ucCurSlaveIndex; /*!< Master current processing slave index */
|
||||
eMBPortIpVer eMbIpVer; /*!< Master IP version */
|
||||
eMBPortProto eMbProto; /*!< Master protocol type */
|
||||
void* pvNetIface; /*!< Master netif interface pointer */
|
||||
MbSlaveInfo_t** pxMbSlaveInfo; /*!< Master information structure for each connected slave */
|
||||
} MbPortConfig_t;
|
||||
|
||||
/* ----------------------- Function prototypes ------------------------------*/
|
||||
|
||||
// The functions below are used by Modbus controller interface to configure Modbus port.
|
||||
/**
|
||||
* Registers slave IP address
|
||||
*
|
||||
* @param pcIpStr IP address to register
|
||||
*
|
||||
* @return TRUE if address registered successfully, else FALSE
|
||||
*/
|
||||
BOOL xMBTCPPortMasterAddSlaveIp(const CHAR* pcIpStr);
|
||||
|
||||
/**
|
||||
* Keeps FSM event handle and mask then wait for Master stack to start
|
||||
*
|
||||
* @param xEventHandle Master event handle
|
||||
* @param xEvent event mask to start Modbus stack FSM
|
||||
*
|
||||
* @return TRUE if stack started, else FALSE
|
||||
*/
|
||||
BOOL xMBTCPPortMasterWaitEvent(EventGroupHandle_t xEventHandle, EventBits_t xEvent);
|
||||
|
||||
/**
|
||||
* Set network options for Master port
|
||||
*
|
||||
* @param pvNetIf netif interface pointer
|
||||
* @param xIpVersion IP version option for the Master port
|
||||
* @param xProto Protocol version option for the Master port
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
void vMBTCPPortMasterSetNetOpt(void* pvNetIf, eMBPortIpVer xIpVersion, eMBPortProto xProto);
|
||||
|
||||
/**
|
||||
* Resume TCP/UDP Master processing task
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
void vMBTCPPortMasterTaskStart(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
PR_END_EXTERN_C
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,484 @@
|
||||
/* 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_tcp_slave.c
|
||||
// Implementation of the Modbus controller TCP slave
|
||||
|
||||
#include <sys/time.h> // for calculation of time stamp in milliseconds
|
||||
#include "esp_log.h" // for log_write
|
||||
#include "mb.h" // for mb types definition
|
||||
#include "mbutils.h" // for mbutils functions definition for stack callback
|
||||
#include "port.h" // for port callback functions and defines
|
||||
#include "sdkconfig.h" // for KConfig values
|
||||
#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_tcp_slave.h" // for tcp slave mb controller defines
|
||||
#include "port_tcp_slave.h" // for tcp slave port defines
|
||||
|
||||
// Shared pointer to interface structure
|
||||
static mb_slave_interface_t* mbs_interface_ptr = NULL;
|
||||
|
||||
// The helper function to get time stamp in microseconds
|
||||
static uint64_t get_time_stamp(void)
|
||||
{
|
||||
uint64_t time_stamp = esp_timer_get_time();
|
||||
return time_stamp;
|
||||
}
|
||||
|
||||
// Modbus task function
|
||||
static void modbus_tcp_slave_task(void *pvParameters)
|
||||
{
|
||||
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
|
||||
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
|
||||
|
||||
// Main Modbus stack processing cycle
|
||||
for (;;) {
|
||||
BaseType_t status = xEventGroupWaitBits(mbs_opts->mbs_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)eMBPoll(); // allow stack to process data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setup Modbus controller parameters
|
||||
static esp_err_t mbc_tcp_slave_setup(void* comm_info)
|
||||
{
|
||||
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
|
||||
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
|
||||
MB_SLAVE_CHECK((comm_info != NULL), ESP_ERR_INVALID_ARG,
|
||||
"mb wrong communication settings.");
|
||||
mb_communication_info_t* comm_settings = (mb_communication_info_t*)comm_info;
|
||||
MB_SLAVE_CHECK((comm_settings->ip_mode == MB_MODE_TCP),
|
||||
ESP_ERR_INVALID_ARG, "mb incorrect mode = (0x%x).", (uint8_t)comm_settings->ip_mode);
|
||||
MB_SLAVE_CHECK(((comm_settings->ip_addr_type == MB_IPV4) || (comm_settings->ip_addr_type == MB_IPV6)),
|
||||
ESP_ERR_INVALID_ARG, "mb incorrect addr type = (0x%x).", (uint8_t)comm_settings->ip_addr_type);
|
||||
MB_SLAVE_CHECK((comm_settings->ip_netif_ptr != NULL),
|
||||
ESP_ERR_INVALID_ARG, "mb incorrect iface address.");
|
||||
// Set communication options of the controller
|
||||
mbs_opts->mbs_comm = *comm_settings;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Start Modbus controller start function
|
||||
static esp_err_t mbc_tcp_slave_start(void)
|
||||
{
|
||||
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
|
||||
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
|
||||
eMBErrorCode status = MB_EIO;
|
||||
|
||||
// Initialize Modbus stack using mbcontroller parameters
|
||||
status = eMBTCPInit((USHORT)mbs_opts->mbs_comm.ip_port);
|
||||
MB_SLAVE_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
|
||||
"mb stack initialization failure, eMBInit() returns (0x%x).", status);
|
||||
|
||||
eMBPortProto proto = (mbs_opts->mbs_comm.ip_mode == MB_MODE_TCP) ? MB_PROTO_TCP : MB_PROTO_UDP;
|
||||
eMBPortIpVer ip_ver = (mbs_opts->mbs_comm.ip_addr_type == MB_IPV4) ? MB_PORT_IPV4 : MB_PORT_IPV6;
|
||||
vMBTCPPortSlaveSetNetOpt(mbs_opts->mbs_comm.ip_netif_ptr, ip_ver, proto, (char*)mbs_opts->mbs_comm.ip_addr);
|
||||
vMBTCPPortSlaveStartServerTask();
|
||||
|
||||
status = eMBEnable();
|
||||
MB_SLAVE_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
|
||||
"mb TCP stack start failure, eMBEnable() returned (0x%x).", (uint32_t)status);
|
||||
// Set the mbcontroller start flag
|
||||
EventBits_t flag = xEventGroupSetBits(mbs_opts->mbs_event_group,
|
||||
(EventBits_t)MB_EVENT_STACK_STARTED);
|
||||
MB_SLAVE_CHECK((flag & MB_EVENT_STACK_STARTED),
|
||||
ESP_ERR_INVALID_STATE, "mb stack start event set error.");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Modbus controller destroy function
|
||||
static esp_err_t mbc_tcp_slave_destroy(void)
|
||||
{
|
||||
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
|
||||
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
|
||||
eMBErrorCode mb_error = MB_ENOERR;
|
||||
// Stop polling by clearing correspondent bit in the event group
|
||||
EventBits_t flag = xEventGroupClearBits(mbs_opts->mbs_event_group,
|
||||
(EventBits_t)MB_EVENT_STACK_STARTED);
|
||||
MB_SLAVE_CHECK((flag & MB_EVENT_STACK_STARTED),
|
||||
ESP_ERR_INVALID_STATE, "mb stack stop event failure.");
|
||||
// Disable and then destroy the Modbus stack
|
||||
mb_error = eMBDisable();
|
||||
MB_SLAVE_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE, "mb stack disable failure.");
|
||||
(void)vTaskDelete(mbs_opts->mbs_task_handle);
|
||||
(void)vQueueDelete(mbs_opts->mbs_notification_queue_handle);
|
||||
(void)vEventGroupDelete(mbs_opts->mbs_event_group);
|
||||
(void)vMBTCPPortClose();
|
||||
free(mbs_interface_ptr);
|
||||
vMBPortSetMode((UCHAR)MB_PORT_INACTIVE);
|
||||
mbs_interface_ptr = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mbc_tcp_slave_set_descriptor(const mb_register_area_descriptor_t descr_info)
|
||||
{
|
||||
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
|
||||
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
|
||||
MB_SLAVE_CHECK(((descr_info.type < MB_PARAM_COUNT) && (descr_info.type >= MB_PARAM_HOLDING)),
|
||||
ESP_ERR_INVALID_ARG, "mb incorrect modbus instance type = (0x%x).",
|
||||
(uint32_t)descr_info.type);
|
||||
MB_SLAVE_CHECK((descr_info.address != NULL),
|
||||
ESP_ERR_INVALID_ARG, "mb instance pointer is NULL.");
|
||||
MB_SLAVE_CHECK((descr_info.size >= MB_INST_MIN_SIZE) && (descr_info.size < (MB_INST_MAX_SIZE)),
|
||||
ESP_ERR_INVALID_ARG, "mb instance size is incorrect = (0x%x).",
|
||||
(uint32_t)descr_info.size);
|
||||
mbs_opts->mbs_area_descriptors[descr_info.type] = descr_info;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Helper function to send parameter information to application task
|
||||
static esp_err_t send_param_info(mb_event_group_t par_type, uint16_t mb_offset,
|
||||
uint8_t* par_address, uint16_t par_size)
|
||||
{
|
||||
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
|
||||
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
|
||||
esp_err_t error = ESP_FAIL;
|
||||
mb_param_info_t par_info;
|
||||
// Check if queue is not full the send parameter information
|
||||
par_info.type = par_type;
|
||||
par_info.size = par_size;
|
||||
par_info.address = par_address;
|
||||
par_info.time_stamp = get_time_stamp();
|
||||
par_info.mb_offset = mb_offset;
|
||||
BaseType_t status = xQueueSend(mbs_opts->mbs_notification_queue_handle,
|
||||
&par_info, MB_PAR_INFO_TOUT);
|
||||
if (pdTRUE == status) {
|
||||
ESP_LOGD(MB_SLAVE_TAG, "Queue send parameter info (type, address, size): %d, 0x%.4x, %d",
|
||||
par_type, (uint32_t)par_address, par_size);
|
||||
error = ESP_OK;
|
||||
} else if (errQUEUE_FULL == status) {
|
||||
ESP_LOGD(MB_SLAVE_TAG, "Parameter queue is overflowed.");
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// Helper function to send notification
|
||||
static esp_err_t send_param_access_notification(mb_event_group_t event)
|
||||
{
|
||||
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
|
||||
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
|
||||
esp_err_t err = ESP_FAIL;
|
||||
mb_event_group_t bits = (mb_event_group_t)xEventGroupSetBits(mbs_opts->mbs_event_group,
|
||||
(EventBits_t)event);
|
||||
if (bits & event) {
|
||||
ESP_LOGD(MB_SLAVE_TAG, "The MB_REG_CHANGE_EVENT = 0x%.2x is set.", (uint8_t)event);
|
||||
err = ESP_OK;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
// Blocking function to get event on parameter group change for application task
|
||||
static mb_event_group_t mbc_tcp_slave_check_event(mb_event_group_t group)
|
||||
{
|
||||
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
|
||||
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_tcp_slave_get_param_info(mb_param_info_t* reg_info, uint32_t timeout)
|
||||
{
|
||||
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
|
||||
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 ---------------------------------*/
|
||||
// These are executed by modbus stack to read appropriate type of registers.
|
||||
|
||||
// This is required to suppress warning when register start address is zero
|
||||
#pragma GCC diagnostic ignored "-Wtype-limits"
|
||||
|
||||
// Callback function for reading of MB Input Registers
|
||||
eMBErrorCode eMBRegInputCBTcpSlave(UCHAR * pucRegBuffer, USHORT usAddress,
|
||||
USHORT usNRegs)
|
||||
{
|
||||
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
|
||||
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
|
||||
MB_SLAVE_ASSERT(pucRegBuffer != NULL);
|
||||
USHORT usRegInputNregs = (USHORT)(mbs_opts->mbs_area_descriptors[MB_PARAM_INPUT].size >> 1); // Number of input registers
|
||||
USHORT usInputRegStart = (USHORT)mbs_opts->mbs_area_descriptors[MB_PARAM_INPUT].start_offset; // Get Modbus start address
|
||||
UCHAR* pucInputBuffer = (UCHAR*)mbs_opts->mbs_area_descriptors[MB_PARAM_INPUT].address; // Get instance address
|
||||
USHORT usRegs = usNRegs;
|
||||
eMBErrorCode eStatus = MB_ENOERR;
|
||||
USHORT iRegIndex;
|
||||
// If input or configuration parameters are incorrect then return an error to stack layer
|
||||
if ((usAddress >= usInputRegStart)
|
||||
&& (pucInputBuffer != NULL)
|
||||
&& (usNRegs >= 1)
|
||||
&& ((usAddress + usRegs) <= (usInputRegStart + usRegInputNregs + 1))
|
||||
&& (usRegInputNregs >= 1)) {
|
||||
iRegIndex = (USHORT)(usAddress - usInputRegStart - 1);
|
||||
iRegIndex <<= 1; // register Address to byte address
|
||||
pucInputBuffer += iRegIndex;
|
||||
UCHAR* pucBufferStart = pucInputBuffer;
|
||||
while (usRegs > 0) {
|
||||
_XFER_2_RD(pucRegBuffer, pucInputBuffer);
|
||||
iRegIndex += 2;
|
||||
usRegs -= 1;
|
||||
}
|
||||
// Send access notification
|
||||
(void)send_param_access_notification(MB_EVENT_INPUT_REG_RD);
|
||||
// Send parameter info to application task
|
||||
(void)send_param_info(MB_EVENT_INPUT_REG_RD, (uint16_t)usAddress,
|
||||
(uint8_t*)pucBufferStart, (uint16_t)usNRegs);
|
||||
} else {
|
||||
eStatus = MB_ENOREG;
|
||||
}
|
||||
return eStatus;
|
||||
}
|
||||
|
||||
// Callback function for reading of MB Holding Registers
|
||||
// Executed by stack when request to read/write holding registers is received
|
||||
eMBErrorCode eMBRegHoldingCBTcpSlave(UCHAR * pucRegBuffer, USHORT usAddress,
|
||||
USHORT usNRegs, eMBRegisterMode eMode)
|
||||
{
|
||||
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
|
||||
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
|
||||
MB_SLAVE_ASSERT(pucRegBuffer != NULL);
|
||||
USHORT usRegHoldingNregs = (USHORT)(mbs_opts->mbs_area_descriptors[MB_PARAM_HOLDING].size >> 1);
|
||||
USHORT usRegHoldingStart = (USHORT)mbs_opts->mbs_area_descriptors[MB_PARAM_HOLDING].start_offset;
|
||||
UCHAR* pucHoldingBuffer = (UCHAR*)mbs_opts->mbs_area_descriptors[MB_PARAM_HOLDING].address;
|
||||
eMBErrorCode eStatus = MB_ENOERR;
|
||||
USHORT iRegIndex;
|
||||
USHORT usRegs = usNRegs;
|
||||
// Check input and configuration parameters for correctness
|
||||
if ((usAddress >= usRegHoldingStart)
|
||||
&& (pucHoldingBuffer != NULL)
|
||||
&& ((usAddress + usRegs) <= (usRegHoldingStart + usRegHoldingNregs + 1))
|
||||
&& (usRegHoldingNregs >= 1)
|
||||
&& (usNRegs >= 1)) {
|
||||
iRegIndex = (USHORT) (usAddress - usRegHoldingStart - 1);
|
||||
iRegIndex <<= 1; // register Address to byte address
|
||||
pucHoldingBuffer += iRegIndex;
|
||||
UCHAR* pucBufferStart = pucHoldingBuffer;
|
||||
switch (eMode) {
|
||||
case MB_REG_READ:
|
||||
while (usRegs > 0) {
|
||||
_XFER_2_RD(pucRegBuffer, pucHoldingBuffer);
|
||||
iRegIndex += 2;
|
||||
usRegs -= 1;
|
||||
};
|
||||
// Send access notification
|
||||
(void)send_param_access_notification(MB_EVENT_HOLDING_REG_RD);
|
||||
// Send parameter info
|
||||
(void)send_param_info(MB_EVENT_HOLDING_REG_RD, (uint16_t)usAddress,
|
||||
(uint8_t*)pucBufferStart, (uint16_t)usNRegs);
|
||||
break;
|
||||
case MB_REG_WRITE:
|
||||
while (usRegs > 0) {
|
||||
_XFER_2_WR(pucHoldingBuffer, pucRegBuffer);
|
||||
pucHoldingBuffer += 2;
|
||||
iRegIndex += 2;
|
||||
usRegs -= 1;
|
||||
};
|
||||
// Send access notification
|
||||
(void)send_param_access_notification(MB_EVENT_HOLDING_REG_WR);
|
||||
// Send parameter info
|
||||
(void)send_param_info(MB_EVENT_HOLDING_REG_WR, (uint16_t)usAddress,
|
||||
(uint8_t*)pucBufferStart, (uint16_t)usNRegs);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
eStatus = MB_ENOREG;
|
||||
}
|
||||
return eStatus;
|
||||
}
|
||||
|
||||
// Callback function for reading of MB Coils Registers
|
||||
eMBErrorCode eMBRegCoilsCBTcpSlave(UCHAR* pucRegBuffer, USHORT usAddress,
|
||||
USHORT usNCoils, eMBRegisterMode eMode)
|
||||
{
|
||||
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
|
||||
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
|
||||
MB_SLAVE_ASSERT(NULL != pucRegBuffer);
|
||||
USHORT usRegCoilNregs = (USHORT)(mbs_opts->mbs_area_descriptors[MB_PARAM_COIL].size >> 1); // number of registers in storage area
|
||||
USHORT usRegCoilsStart = (USHORT)mbs_opts->mbs_area_descriptors[MB_PARAM_COIL].start_offset; // MB offset of coils registers
|
||||
UCHAR* pucRegCoilsBuf = (UCHAR*)mbs_opts->mbs_area_descriptors[MB_PARAM_COIL].address;
|
||||
eMBErrorCode eStatus = MB_ENOERR;
|
||||
USHORT iRegIndex;
|
||||
USHORT usCoils = usNCoils;
|
||||
usAddress--; // The address is already +1
|
||||
if ((usAddress >= usRegCoilsStart)
|
||||
&& (usRegCoilNregs >= 1)
|
||||
&& ((usAddress + usCoils) <= (usRegCoilsStart + (usRegCoilNregs << 4) + 1))
|
||||
&& (pucRegCoilsBuf != NULL)
|
||||
&& (usNCoils >= 1)) {
|
||||
iRegIndex = (USHORT) (usAddress - usRegCoilsStart);
|
||||
CHAR* pucCoilsDataBuf = (CHAR*)(pucRegCoilsBuf + (iRegIndex >> 3));
|
||||
switch (eMode) {
|
||||
case MB_REG_READ:
|
||||
while (usCoils > 0) {
|
||||
UCHAR ucResult = xMBUtilGetBits((UCHAR*)pucRegCoilsBuf, iRegIndex, 1);
|
||||
xMBUtilSetBits(pucRegBuffer, iRegIndex - (usAddress - usRegCoilsStart), 1, ucResult);
|
||||
iRegIndex++;
|
||||
usCoils--;
|
||||
}
|
||||
// Send an event to notify application task about event
|
||||
(void)send_param_access_notification(MB_EVENT_COILS_RD);
|
||||
(void)send_param_info(MB_EVENT_COILS_RD, (uint16_t)usAddress,
|
||||
(uint8_t*)(pucCoilsDataBuf), (uint16_t)usNCoils);
|
||||
break;
|
||||
case MB_REG_WRITE:
|
||||
while (usCoils > 0) {
|
||||
UCHAR ucResult = xMBUtilGetBits(pucRegBuffer,
|
||||
iRegIndex - (usAddress - usRegCoilsStart), 1);
|
||||
xMBUtilSetBits((uint8_t*)pucRegCoilsBuf, iRegIndex, 1, ucResult);
|
||||
iRegIndex++;
|
||||
usCoils--;
|
||||
}
|
||||
// Send an event to notify application task about event
|
||||
(void)send_param_access_notification(MB_EVENT_COILS_WR);
|
||||
(void)send_param_info(MB_EVENT_COILS_WR, (uint16_t)usAddress,
|
||||
(uint8_t*)pucCoilsDataBuf, (uint16_t)usNCoils);
|
||||
break;
|
||||
} // switch ( eMode )
|
||||
} else {
|
||||
// If the configuration or input parameters are incorrect then return error to stack
|
||||
eStatus = MB_ENOREG;
|
||||
}
|
||||
return eStatus;
|
||||
}
|
||||
|
||||
// Callback function for reading of MB Discrete Input Registers
|
||||
eMBErrorCode eMBRegDiscreteCBTcpSlave(UCHAR* pucRegBuffer, USHORT usAddress,
|
||||
USHORT usNDiscrete)
|
||||
{
|
||||
MB_SLAVE_ASSERT(mbs_interface_ptr != NULL);
|
||||
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
|
||||
MB_SLAVE_ASSERT(pucRegBuffer != NULL);
|
||||
USHORT usRegDiscreteNregs = (USHORT)(mbs_opts->mbs_area_descriptors[MB_PARAM_DISCRETE].size >> 1); // number of registers in storage area
|
||||
USHORT usRegDiscreteStart = (USHORT)mbs_opts->mbs_area_descriptors[MB_PARAM_DISCRETE].start_offset; // MB offset of registers
|
||||
UCHAR* pucRegDiscreteBuf = (UCHAR*)mbs_opts->mbs_area_descriptors[MB_PARAM_DISCRETE].address; // the storage address
|
||||
eMBErrorCode eStatus = MB_ENOERR;
|
||||
USHORT iRegIndex, iRegBitIndex, iNReg;
|
||||
UCHAR* pucDiscreteInputBuf;
|
||||
iNReg = usNDiscrete / 8 + 1;
|
||||
pucDiscreteInputBuf = (UCHAR*) pucRegDiscreteBuf;
|
||||
// It already plus one in modbus function method.
|
||||
usAddress--;
|
||||
if ((usAddress >= usRegDiscreteStart)
|
||||
&& (usRegDiscreteNregs >= 1)
|
||||
&& (pucRegDiscreteBuf != NULL)
|
||||
&& ((usAddress + usNDiscrete) <= (usRegDiscreteStart + (usRegDiscreteNregs * 16)))
|
||||
&& (usNDiscrete >= 1)) {
|
||||
iRegIndex = (USHORT) (usAddress - usRegDiscreteStart) / 8; // Get register index in the buffer for bit number
|
||||
iRegBitIndex = (USHORT)(usAddress - usRegDiscreteStart) % 8; // Get bit index
|
||||
UCHAR* pucTempBuf = &pucDiscreteInputBuf[iRegIndex];
|
||||
while (iNReg > 0) {
|
||||
*pucRegBuffer++ = xMBUtilGetBits(&pucDiscreteInputBuf[iRegIndex++], iRegBitIndex, 8);
|
||||
iNReg--;
|
||||
}
|
||||
pucRegBuffer--;
|
||||
// Last discrete
|
||||
usNDiscrete = usNDiscrete % 8;
|
||||
// Filling zero to high bit
|
||||
*pucRegBuffer = *pucRegBuffer << (8 - usNDiscrete);
|
||||
*pucRegBuffer = *pucRegBuffer >> (8 - usNDiscrete);
|
||||
// Send an event to notify application task about event
|
||||
(void)send_param_access_notification(MB_EVENT_DISCRETE_RD);
|
||||
(void)send_param_info(MB_EVENT_DISCRETE_RD, (uint16_t)usAddress,
|
||||
(uint8_t*)pucTempBuf, (uint16_t)usNDiscrete);
|
||||
} else {
|
||||
eStatus = MB_ENOREG;
|
||||
}
|
||||
return eStatus;
|
||||
}
|
||||
#pragma GCC diagnostic pop // require GCC
|
||||
|
||||
// Initialization of Modbus controller
|
||||
esp_err_t mbc_tcp_slave_create(void** handler)
|
||||
{
|
||||
// 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);
|
||||
mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts;
|
||||
mbs_opts->port_type = MB_PORT_TCP_SLAVE; // set interface port type
|
||||
|
||||
vMBPortSetMode((UCHAR)MB_PORT_TCP_SLAVE);
|
||||
|
||||
// Set default values of communication options
|
||||
mbs_opts->mbs_comm.ip_port = MB_TCP_DEFAULT_PORT;
|
||||
|
||||
// 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_tcp_slave_task,
|
||||
"modbus_tcp_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);
|
||||
}
|
||||
|
||||
// The task is created but handle is incorrect
|
||||
MB_SLAVE_ASSERT(mbs_opts->mbs_task_handle != NULL);
|
||||
|
||||
// Initialization of interface pointers
|
||||
mbs_interface_ptr->init = mbc_tcp_slave_create;
|
||||
mbs_interface_ptr->destroy = mbc_tcp_slave_destroy;
|
||||
mbs_interface_ptr->setup = mbc_tcp_slave_setup;
|
||||
mbs_interface_ptr->start = mbc_tcp_slave_start;
|
||||
mbs_interface_ptr->check_event = mbc_tcp_slave_check_event;
|
||||
mbs_interface_ptr->get_param_info = mbc_tcp_slave_get_param_info;
|
||||
mbs_interface_ptr->set_descriptor = mbc_tcp_slave_set_descriptor;
|
||||
|
||||
// Initialize stack callback function pointers
|
||||
mbs_interface_ptr->slave_reg_cb_discrete = eMBRegDiscreteCBTcpSlave;
|
||||
mbs_interface_ptr->slave_reg_cb_input = eMBRegInputCBTcpSlave;
|
||||
mbs_interface_ptr->slave_reg_cb_holding = eMBRegHoldingCBTcpSlave;
|
||||
mbs_interface_ptr->slave_reg_cb_coils = eMBRegCoilsCBTcpSlave;
|
||||
|
||||
*handler = (void*)mbs_interface_ptr;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/* 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_tcp_slave.h Modbus controller TCP slave implementation header file
|
||||
|
||||
#ifndef _MODBUS_TCP_CONTROLLER_SLAVE
|
||||
#define _MODBUS_TCP_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_FMB_CONTROLLER_NOTIFY_QUEUE_SIZE) // Number of messages in parameter notification queue
|
||||
#define MB_CONTROLLER_NOTIFY_TIMEOUT (pdMS_TO_TICKS(CONFIG_FMB_CONTROLLER_NOTIFY_TIMEOUT)) // notification timeout
|
||||
|
||||
/**
|
||||
* @brief Initialize Modbus controller and stack for TCP slave
|
||||
*
|
||||
* @param[out] handler handler(pointer) to slave data structure
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_NO_MEM Parameter error
|
||||
*/
|
||||
esp_err_t mbc_tcp_slave_create(void** handler);
|
||||
|
||||
#endif // _MODBUS_TCP_CONTROLLER_SLAVE
|
||||
|
||||
@@ -0,0 +1,709 @@
|
||||
/* 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 TCP Port
|
||||
* Copyright (C) 2006 Christian Walter <wolti@sil.at>
|
||||
* Parts of crt0.S Copyright (c) 1995, 1996, 1998 Cygnus Support
|
||||
*
|
||||
* 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.h,v 1.2 2006/09/04 14:39:20 wolti Exp $
|
||||
*/
|
||||
|
||||
/* ----------------------- System includes ----------------------------------*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "esp_err.h"
|
||||
#include "sys/time.h"
|
||||
#include "esp_netif.h"
|
||||
|
||||
/* ----------------------- lwIP includes ------------------------------------*/
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/sockets.h"
|
||||
#include "lwip/netdb.h"
|
||||
#include "net/if.h"
|
||||
|
||||
/* ----------------------- Modbus includes ----------------------------------*/
|
||||
#include "mb.h"
|
||||
#include "mbport.h"
|
||||
#include "port.h"
|
||||
#include "mbframe.h"
|
||||
#include "port_tcp_slave.h"
|
||||
#include "esp_modbus_common.h" // for common types for network options
|
||||
|
||||
/* ----------------------- Defines -----------------------------------------*/
|
||||
#define MB_TCP_DISCONNECT_TIMEOUT ( CONFIG_FMB_TCP_CONNECTION_TOUT_SEC * 1000000 ) // disconnect timeout in uS
|
||||
#define MB_TCP_RESP_TIMEOUT_MS ( MB_MASTER_TIMEOUT_MS_RESPOND - 2 ) // slave response time limit
|
||||
#define MB_TCP_SLAVE_PORT_TAG "MB_TCP_SLAVE_PORT"
|
||||
#define MB_TCP_NET_LISTEN_BACKLOG ( SOMAXCONN )
|
||||
|
||||
/* ----------------------- Prototypes ---------------------------------------*/
|
||||
void vMBPortEventClose( void );
|
||||
|
||||
/* ----------------------- Static variables ---------------------------------*/
|
||||
static int xListenSock = -1;
|
||||
static MbSlavePortConfig_t xConfig = { 0 };
|
||||
|
||||
/* ----------------------- Static functions ---------------------------------*/
|
||||
// The helper function to get time stamp in microseconds
|
||||
static int64_t xMBTCPGetTimeStamp(void)
|
||||
{
|
||||
int64_t xTimeStamp = esp_timer_get_time();
|
||||
return xTimeStamp;
|
||||
}
|
||||
|
||||
static void vxMBTCPPortMStoTimeVal(USHORT usTimeoutMs, struct timeval *pxTimeout)
|
||||
{
|
||||
pxTimeout->tv_sec = usTimeoutMs / 1000;
|
||||
pxTimeout->tv_usec = (usTimeoutMs - (pxTimeout->tv_sec * 1000)) * 1000;
|
||||
}
|
||||
|
||||
static xQueueHandle xMBTCPPortRespQueueCreate(void)
|
||||
{
|
||||
xQueueHandle xRespQueueHandle = xQueueCreate(2, sizeof(void*));
|
||||
MB_PORT_CHECK((xRespQueueHandle != NULL), NULL, "TCP respond queue creation failure.");
|
||||
return xRespQueueHandle;
|
||||
}
|
||||
|
||||
static void vMBTCPPortRespQueueDelete(xQueueHandle xRespQueueHandle)
|
||||
{
|
||||
vQueueDelete(xRespQueueHandle);
|
||||
}
|
||||
|
||||
static void* vxMBTCPPortRespQueueRecv(xQueueHandle xRespQueueHandle)
|
||||
{
|
||||
void* pvResp = NULL;
|
||||
MB_PORT_CHECK(xRespQueueHandle != NULL, NULL, "Response queue is not initialized.");
|
||||
BaseType_t xStatus = xQueueReceive(xRespQueueHandle,
|
||||
(void*)&pvResp,
|
||||
pdMS_TO_TICKS(MB_TCP_RESP_TIMEOUT_MS));
|
||||
MB_PORT_CHECK((xStatus == pdTRUE), NULL, "Could not get respond confirmation.");
|
||||
MB_PORT_CHECK((pvResp), NULL, "Incorrect response processing.");
|
||||
return pvResp;
|
||||
}
|
||||
|
||||
static BOOL vxMBTCPPortRespQueueSend(xQueueHandle xRespQueueHandle, void* pvResp)
|
||||
{
|
||||
MB_PORT_CHECK(xRespQueueHandle != NULL, FALSE, "Response queue is not initialized.");
|
||||
BaseType_t xStatus = xQueueSend(xConfig.xRespQueueHandle,
|
||||
(const void*)&pvResp,
|
||||
pdMS_TO_TICKS(MB_TCP_RESP_TIMEOUT_MS));
|
||||
MB_PORT_CHECK((xStatus == pdTRUE), FALSE, "FAIL to send to response queue.");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void vMBTCPPortServerTask(void *pvParameters);
|
||||
|
||||
/* ----------------------- Begin implementation -----------------------------*/
|
||||
BOOL
|
||||
xMBTCPPortInit( USHORT usTCPPort )
|
||||
{
|
||||
BOOL bOkay = FALSE;
|
||||
|
||||
xConfig.pxMbClientInfo = calloc(MB_TCP_PORT_MAX_CONN + 1, sizeof(MbClientInfo_t*));
|
||||
if (!xConfig.pxMbClientInfo) {
|
||||
ESP_LOGE(MB_TCP_SLAVE_PORT_TAG, "TCP client info allocation failure.");
|
||||
return FALSE;
|
||||
}
|
||||
for(int idx = 0; idx < MB_TCP_PORT_MAX_CONN; xConfig.pxMbClientInfo[idx] = NULL, idx++);
|
||||
|
||||
xConfig.xRespQueueHandle = xMBTCPPortRespQueueCreate();
|
||||
if (!xConfig.xRespQueueHandle) {
|
||||
ESP_LOGE(MB_TCP_SLAVE_PORT_TAG, "Response queue allocation failure.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
xConfig.usPort = usTCPPort;
|
||||
xConfig.eMbProto = MB_PROTO_TCP;
|
||||
xConfig.usClientCount = 0;
|
||||
xConfig.pvNetIface = NULL;
|
||||
xConfig.xIpVer = MB_PORT_IPV4;
|
||||
xConfig.pcBindAddr = NULL;
|
||||
|
||||
// Create task for packet processing
|
||||
BaseType_t xErr = xTaskCreate(vMBTCPPortServerTask,
|
||||
"tcp_server_task",
|
||||
MB_TCP_STACK_SIZE,
|
||||
NULL,
|
||||
MB_TCP_TASK_PRIO,
|
||||
&xConfig.xMbTcpTaskHandle);
|
||||
vTaskSuspend(xConfig.xMbTcpTaskHandle);
|
||||
if (xErr != pdTRUE)
|
||||
{
|
||||
ESP_LOGE(MB_TCP_SLAVE_PORT_TAG, "Server task creation failure.");
|
||||
vTaskDelete(xConfig.xMbTcpTaskHandle);
|
||||
} else {
|
||||
ESP_LOGI(MB_TCP_SLAVE_PORT_TAG, "Protocol stack initialized.");
|
||||
bOkay = TRUE;
|
||||
}
|
||||
return bOkay;
|
||||
}
|
||||
|
||||
void vMBTCPPortSlaveSetNetOpt(void* pvNetIf, eMBPortIpVer xIpVersion, eMBPortProto xProto, CHAR* pcBindAddrStr)
|
||||
{
|
||||
// Set network options
|
||||
xConfig.pvNetIface = pvNetIf;
|
||||
xConfig.eMbProto = xProto;
|
||||
xConfig.xIpVer = xIpVersion;
|
||||
xConfig.pcBindAddr = pcBindAddrStr;
|
||||
}
|
||||
|
||||
void vMBTCPPortSlaveStartServerTask(void)
|
||||
{
|
||||
vTaskResume(xConfig.xMbTcpTaskHandle);
|
||||
}
|
||||
|
||||
static int xMBTCPPortAcceptConnection(int xListenSockId, char** pcIPAddr)
|
||||
{
|
||||
MB_PORT_CHECK(pcIPAddr, -1, "Wrong IP address pointer.");
|
||||
MB_PORT_CHECK((xListenSockId > 0), -1, "Incorrect listen socket ID.");
|
||||
|
||||
// Address structure large enough for both IPv4 or IPv6 address
|
||||
struct sockaddr_in6 xSrcAddr;
|
||||
|
||||
CHAR cAddrStr[128];
|
||||
int xSockId = -1;
|
||||
CHAR* pcStr = NULL;
|
||||
socklen_t xSize = sizeof(struct sockaddr_in6);
|
||||
|
||||
// Accept new socket connection if not active
|
||||
xSockId = accept(xListenSockId, (struct sockaddr *)&xSrcAddr, &xSize);
|
||||
if (xSockId < 0) {
|
||||
ESP_LOGE(MB_TCP_SLAVE_PORT_TAG, "Unable to accept connection: errno=%d", errno);
|
||||
close(xSockId);
|
||||
} else {
|
||||
// Get the sender's ip address as string
|
||||
if (xSrcAddr.sin6_family == PF_INET) {
|
||||
inet_ntoa_r(((struct sockaddr_in *)&xSrcAddr)->sin_addr.s_addr, cAddrStr, sizeof(cAddrStr) - 1);
|
||||
} else if (xSrcAddr.sin6_family == PF_INET6) {
|
||||
inet6_ntoa_r(xSrcAddr.sin6_addr, cAddrStr, sizeof(cAddrStr) - 1);
|
||||
}
|
||||
ESP_LOGI(MB_TCP_SLAVE_PORT_TAG, "Socket (#%d), accept client connection from address: %s", xSockId, cAddrStr);
|
||||
pcStr = calloc(1, strlen(cAddrStr) + 1);
|
||||
if (pcStr && pcIPAddr) {
|
||||
memcpy(pcStr, cAddrStr, strlen(cAddrStr));
|
||||
pcStr[strlen(cAddrStr)] = '\0';
|
||||
*pcIPAddr = pcStr; // Set IP address of connected client
|
||||
}
|
||||
}
|
||||
return xSockId;
|
||||
}
|
||||
|
||||
static BOOL xMBTCPPortCloseConnection(MbClientInfo_t* pxInfo)
|
||||
{
|
||||
MB_PORT_CHECK(pxInfo, FALSE, "Client info is NULL.");
|
||||
|
||||
if (pxInfo->xSockId == -1) {
|
||||
ESP_LOGE(MB_TCP_SLAVE_PORT_TAG, "Wrong socket info or disconnected socket: %d.", pxInfo->xSockId);
|
||||
return FALSE;
|
||||
}
|
||||
if (shutdown(pxInfo->xSockId, SHUT_RDWR) == -1) {
|
||||
ESP_LOGE(MB_TCP_SLAVE_PORT_TAG, "Socket (#%d), shutdown failed: errno %d", pxInfo->xSockId, errno);
|
||||
}
|
||||
close(pxInfo->xSockId);
|
||||
pxInfo->xSockId = -1;
|
||||
if (xConfig.usClientCount) {
|
||||
xConfig.usClientCount--; // decrement counter of client connections
|
||||
} else {
|
||||
xConfig.pxCurClientInfo = NULL;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int xMBTCPPortRxPoll(MbClientInfo_t* pxClientInfo, ULONG xTimeoutMs)
|
||||
{
|
||||
int xRet = ERR_CLSD;
|
||||
struct timeval xTimeVal;
|
||||
fd_set xReadSet;
|
||||
int64_t xStartTimeStamp = 0;
|
||||
|
||||
// Receive data from connected client
|
||||
if (pxClientInfo && pxClientInfo->xSockId > -1) {
|
||||
// Set receive timeout
|
||||
vxMBTCPPortMStoTimeVal(xTimeoutMs, &xTimeVal);
|
||||
xStartTimeStamp = xMBTCPGetTimeStamp();
|
||||
while (1)
|
||||
{
|
||||
FD_ZERO(&xReadSet);
|
||||
FD_SET(pxClientInfo->xSockId, &xReadSet);
|
||||
xRet = select(pxClientInfo->xSockId + 1, &xReadSet, NULL, NULL, &xTimeVal);
|
||||
if (xRet == -1)
|
||||
{
|
||||
// If select an error occurred
|
||||
xRet = ERR_CLSD;
|
||||
break;
|
||||
} else if (xRet == 0) {
|
||||
// timeout occurred
|
||||
if ((xStartTimeStamp + xTimeoutMs * 1000) > xMBTCPGetTimeStamp()) {
|
||||
ESP_LOGD(MB_TCP_SLAVE_PORT_TAG, "Socket (#%d) Read timeout.", pxClientInfo->xSockId);
|
||||
xRet = ERR_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (FD_ISSET(pxClientInfo->xSockId, &xReadSet)) {
|
||||
// If new buffer received then read Modbus packet into buffer
|
||||
MB_PORT_CHECK((pxClientInfo->usTCPBufPos + pxClientInfo->usTCPFrameBytesLeft < MB_TCP_BUF_SIZE),
|
||||
ERR_BUF, "Socket (#%d), incorrect request buffer size = %d, ignore.",
|
||||
pxClientInfo->xSockId,
|
||||
(pxClientInfo->usTCPBufPos + pxClientInfo->usTCPFrameBytesLeft));
|
||||
int xLength = recv(pxClientInfo->xSockId, &pxClientInfo->pucTCPBuf[pxClientInfo->usTCPBufPos],
|
||||
pxClientInfo->usTCPFrameBytesLeft, MSG_DONTWAIT);
|
||||
if (xLength < 0) {
|
||||
// If an error occurred during receiving
|
||||
ESP_LOGE(MB_TCP_SLAVE_PORT_TAG, "Receive failed: length=%d, errno=%d", xLength, errno);
|
||||
xRet = (err_t)xLength;
|
||||
break;
|
||||
} else if (xLength == 0) {
|
||||
// Socket connection closed
|
||||
ESP_LOGD(MB_TCP_SLAVE_PORT_TAG, "Socket (#%d)(%s), connection closed.",
|
||||
pxClientInfo->xSockId, pxClientInfo->pcIpAddr);
|
||||
xRet = ERR_CLSD;
|
||||
break;
|
||||
} else {
|
||||
// New data received
|
||||
pxClientInfo->usTCPBufPos += xLength;
|
||||
pxClientInfo->usTCPFrameBytesLeft -= xLength;
|
||||
if (pxClientInfo->usTCPBufPos >= MB_TCP_FUNC) {
|
||||
// Length is a byte count of Modbus PDU (function code + data) and the
|
||||
// unit identifier.
|
||||
xLength = (int)MB_TCP_GET_FIELD(pxClientInfo->pucTCPBuf, MB_TCP_LEN);
|
||||
// Is the frame already complete.
|
||||
if (pxClientInfo->usTCPBufPos < (MB_TCP_UID + xLength)) {
|
||||
// The incomplete frame is received
|
||||
pxClientInfo->usTCPFrameBytesLeft = xLength + MB_TCP_UID - pxClientInfo->usTCPBufPos;
|
||||
} else if (pxClientInfo->usTCPBufPos == (MB_TCP_UID + xLength)) {
|
||||
#if MB_TCP_DEBUG
|
||||
prvvMBTCPLogFrame(MB_TCP_SLAVE_PORT_TAG, (UCHAR*)&pxClientInfo->pucTCPBuf[0], pxClientInfo->usTCPBufPos);
|
||||
#endif
|
||||
// Copy TID field from incoming packet
|
||||
pxClientInfo->usTidCnt = MB_TCP_GET_FIELD(pxClientInfo->pucTCPBuf, MB_TCP_TID);
|
||||
xRet = pxClientInfo->usTCPBufPos;
|
||||
break;
|
||||
} else if ((pxClientInfo->usTCPBufPos + xLength) >= MB_TCP_BUF_SIZE) {
|
||||
ESP_LOGE(MB_TCP_SLAVE_PORT_TAG, "Incorrect buffer received (%u) bytes.", xLength);
|
||||
// This should not happen. We can't deal with such a client and
|
||||
// drop the connection for security reasons.
|
||||
xRet = ERR_BUF;
|
||||
break;
|
||||
}
|
||||
} // if ( pxClientInfo->usTCPBufPos >= MB_TCP_FUNC )
|
||||
} // if data received
|
||||
} // if (FD_ISSET(pxClientInfo->xSockId, &xReadSet))
|
||||
} // while (1)
|
||||
}
|
||||
return (xRet);
|
||||
}
|
||||
|
||||
// Create a listening socket on pcBindIp: Port
|
||||
static int
|
||||
vMBTCPPortBindAddr(const CHAR* pcBindIp)
|
||||
{
|
||||
int xPar, xRet;
|
||||
int xListenSockFd = -1;
|
||||
struct addrinfo xHint;
|
||||
struct addrinfo* pxAddrList;
|
||||
struct addrinfo* pxCurAddr;
|
||||
CHAR* pcStr = NULL;
|
||||
|
||||
memset( &xHint, 0, sizeof( xHint ) );
|
||||
|
||||
// Bind to IPv6 and/or IPv4, but only in the desired protocol
|
||||
// Todo: Find a reason why AF_UNSPEC does not work for IPv6
|
||||
xHint.ai_family = (xConfig.xIpVer == MB_PORT_IPV4) ? AF_INET : AF_INET6;
|
||||
xHint.ai_socktype = (xConfig.eMbProto == MB_PROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM;
|
||||
// The LWIP has an issue when connection to IPv6 socket
|
||||
xHint.ai_protocol = (xConfig.eMbProto == MB_PROTO_UDP) ? IPPROTO_UDP : IPPROTO_TCP;
|
||||
xHint.ai_flags = AI_NUMERICSERV;
|
||||
|
||||
if (pcBindIp == NULL) {
|
||||
xHint.ai_flags |= AI_PASSIVE;
|
||||
} else {
|
||||
xHint.ai_flags |= AI_CANONNAME;
|
||||
}
|
||||
|
||||
if (asprintf(&pcStr, "%u", xConfig.usPort) == -1) {
|
||||
abort();
|
||||
}
|
||||
|
||||
xRet = getaddrinfo(pcBindIp, pcStr, &xHint, &pxAddrList);
|
||||
free(pcStr);
|
||||
|
||||
if (xRet != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Try the sockaddr until a binding succeeds
|
||||
for (pxCurAddr = pxAddrList; pxCurAddr != NULL; pxCurAddr = pxCurAddr->ai_next)
|
||||
{
|
||||
xListenSockFd = (int)socket(pxCurAddr->ai_family, pxCurAddr->ai_socktype,
|
||||
pxCurAddr->ai_protocol);
|
||||
if (xListenSockFd < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
xPar = 1;
|
||||
// Allow multi client connections
|
||||
if (setsockopt(xListenSockFd, SOL_SOCKET, SO_REUSEADDR,
|
||||
(const char*)&xPar, sizeof(xPar)) != 0)
|
||||
{
|
||||
close(xListenSockFd);
|
||||
xListenSockFd = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bind(xListenSockFd, (struct sockaddr *)pxCurAddr->ai_addr,
|
||||
(socklen_t)pxCurAddr->ai_addrlen) != 0 )
|
||||
{
|
||||
close(xListenSockFd);
|
||||
xListenSockFd = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Listen only makes sense for TCP
|
||||
if (xConfig.eMbProto == MB_PROTO_TCP)
|
||||
{
|
||||
if (listen(xListenSockFd, MB_TCP_NET_LISTEN_BACKLOG) != 0)
|
||||
{
|
||||
ESP_LOGE(MB_TCP_SLAVE_PORT_TAG, "Error occurred during listen: errno=%d", errno);
|
||||
close(xListenSockFd);
|
||||
xListenSockFd = -1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Bind was successful
|
||||
pcStr = (pxCurAddr->ai_canonname == NULL) ? (CHAR*)"\0" : pxCurAddr->ai_canonname;
|
||||
ESP_LOGI(MB_TCP_SLAVE_PORT_TAG, "Socket (#%d), listener %s on port: %d, errno=%d",
|
||||
xListenSockFd, pcStr, xConfig.usPort, errno);
|
||||
break;
|
||||
}
|
||||
|
||||
freeaddrinfo(pxAddrList);
|
||||
return(xListenSockFd);
|
||||
}
|
||||
|
||||
static void
|
||||
vMBTCPPortFreeClientInfo(MbClientInfo_t* pxClientInfo)
|
||||
{
|
||||
if (pxClientInfo) {
|
||||
if (pxClientInfo->pucTCPBuf) {
|
||||
free((void*)pxClientInfo->pucTCPBuf);
|
||||
}
|
||||
if (pxClientInfo->pcIpAddr) {
|
||||
free((void*)pxClientInfo->pcIpAddr);
|
||||
}
|
||||
free((void*)pxClientInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void vMBTCPPortServerTask(void *pvParameters)
|
||||
{
|
||||
int xErr = 0;
|
||||
fd_set xReadSet;
|
||||
int i;
|
||||
CHAR* pcClientIp = NULL;
|
||||
struct timeval xTimeVal;
|
||||
|
||||
// Main connection cycle
|
||||
while (1) {
|
||||
// Create listen socket
|
||||
xListenSock = vMBTCPPortBindAddr(xConfig.pcBindAddr);
|
||||
if (xListenSock < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Connections handling cycle
|
||||
while (1) {
|
||||
//clear the socket set
|
||||
FD_ZERO(&xReadSet);
|
||||
//add master socket to set
|
||||
FD_SET(xListenSock, &xReadSet);
|
||||
int xMaxSd = xListenSock;
|
||||
xConfig.usClientCount = 0;
|
||||
|
||||
vxMBTCPPortMStoTimeVal(1, &xTimeVal);
|
||||
// Initialize read set and file descriptor according to
|
||||
// all registered connected clients
|
||||
for (i = 0; i < MB_TCP_PORT_MAX_CONN; i++) {
|
||||
if ((xConfig.pxMbClientInfo[i] != NULL) && (xConfig.pxMbClientInfo[i]->xSockId > 0)) {
|
||||
// calculate max file descriptor for select
|
||||
xMaxSd = (xConfig.pxMbClientInfo[i]->xSockId > xMaxSd) ?
|
||||
xConfig.pxMbClientInfo[i]->xSockId : xMaxSd;
|
||||
FD_SET(xConfig.pxMbClientInfo[i]->xSockId, &xReadSet);
|
||||
xConfig.usClientCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for an activity on one of the sockets, timeout is NULL, so wait indefinitely
|
||||
xErr = select(xMaxSd + 1 , &xReadSet , NULL , NULL , NULL);
|
||||
if ((xErr < 0) && (errno != EINTR)) {
|
||||
// error occurred during wait for read
|
||||
ESP_LOGE(MB_TCP_SLAVE_PORT_TAG, "select() errno = %d.", errno);
|
||||
continue;
|
||||
} else if (xErr == 0) {
|
||||
// If timeout happened, something is wrong
|
||||
ESP_LOGE(MB_TCP_SLAVE_PORT_TAG, "select() timeout, errno = %d.", errno);
|
||||
}
|
||||
|
||||
// If something happened on the master socket, then its an incoming connection.
|
||||
if (FD_ISSET(xListenSock, &xReadSet) && xConfig.usClientCount < MB_TCP_PORT_MAX_CONN) {
|
||||
MbClientInfo_t* pxClientInfo = NULL;
|
||||
// find first empty place to insert connection info
|
||||
for (i = 0; i < MB_TCP_PORT_MAX_CONN; i++) {
|
||||
pxClientInfo = xConfig.pxMbClientInfo[i];
|
||||
if (pxClientInfo == NULL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// if request for new connection but no space left
|
||||
if (pxClientInfo != NULL) {
|
||||
if (xConfig.pxMbClientInfo[MB_TCP_PORT_MAX_CONN] == NULL) {
|
||||
ESP_LOGE(MB_TCP_SLAVE_PORT_TAG, "Fail to accept connection %d, only %d connections supported.", i + 1, MB_TCP_PORT_MAX_CONN);
|
||||
}
|
||||
xConfig.pxMbClientInfo[MB_TCP_PORT_MAX_CONN] = pxClientInfo; // set last connection info
|
||||
} else {
|
||||
// allocate memory for new client info
|
||||
pxClientInfo = calloc(1, sizeof(MbClientInfo_t));
|
||||
if (!pxClientInfo) {
|
||||
ESP_LOGE(MB_TCP_SLAVE_PORT_TAG, "Client info allocation fail.");
|
||||
vMBTCPPortFreeClientInfo(pxClientInfo);
|
||||
pxClientInfo = NULL;
|
||||
} else {
|
||||
// Accept new client connection
|
||||
pxClientInfo->xSockId = xMBTCPPortAcceptConnection(xListenSock, &pcClientIp);
|
||||
if (pxClientInfo->xSockId < 0) {
|
||||
ESP_LOGE(MB_TCP_SLAVE_PORT_TAG, "Fail to accept connection for client %d.", (xConfig.usClientCount - 1));
|
||||
// Accept connection fail, then free client info and continue polling.
|
||||
vMBTCPPortFreeClientInfo(pxClientInfo);
|
||||
pxClientInfo = NULL;
|
||||
continue;
|
||||
}
|
||||
pxClientInfo->pucTCPBuf = calloc(MB_TCP_BUF_SIZE, sizeof(UCHAR));
|
||||
if (!pxClientInfo->pucTCPBuf) {
|
||||
ESP_LOGE(MB_TCP_SLAVE_PORT_TAG, "Fail to allocate buffer for client %d.", (xConfig.usClientCount - 1));
|
||||
vMBTCPPortFreeClientInfo(pxClientInfo);
|
||||
pxClientInfo = NULL;
|
||||
continue;
|
||||
}
|
||||
// Fill the connection info structure
|
||||
xConfig.pxMbClientInfo[i] = pxClientInfo;
|
||||
pxClientInfo->xIndex = i;
|
||||
xConfig.usClientCount++;
|
||||
pxClientInfo->pcIpAddr = pcClientIp;
|
||||
pxClientInfo->xRecvTimeStamp = xMBTCPGetTimeStamp();
|
||||
xConfig.pxMbClientInfo[MB_TCP_PORT_MAX_CONN] = NULL;
|
||||
pxClientInfo->usTCPFrameBytesLeft = MB_TCP_FUNC;
|
||||
pxClientInfo->usTCPBufPos = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Handle data request from client
|
||||
if (xErr > 0) {
|
||||
// Handling client connection requests
|
||||
for (i = 0; i < MB_TCP_PORT_MAX_CONN; i++) {
|
||||
MbClientInfo_t* pxClientInfo = xConfig.pxMbClientInfo[i];
|
||||
if ((pxClientInfo != NULL) && (pxClientInfo->xSockId > 0)) {
|
||||
if (FD_ISSET(pxClientInfo->xSockId, &xReadSet)) {
|
||||
// Other sockets are ready to be read
|
||||
xErr = xMBTCPPortRxPoll(pxClientInfo, MB_TCP_READ_TIMEOUT_MS);
|
||||
// If an invalid data received from socket or connection fail
|
||||
// or if timeout then drop connection and restart
|
||||
if (xErr < 0) {
|
||||
uint64_t xTimeStamp = xMBTCPGetTimeStamp();
|
||||
// If data update is timed out
|
||||
switch(xErr)
|
||||
{
|
||||
case ERR_TIMEOUT:
|
||||
ESP_LOGE(MB_TCP_SLAVE_PORT_TAG, "Socket (#%d)(%s), data receive timeout, time[us]: %d, close active connection.",
|
||||
pxClientInfo->xSockId, pxClientInfo->pcIpAddr,
|
||||
(int)(xTimeStamp - pxClientInfo->xRecvTimeStamp));
|
||||
break;
|
||||
case ERR_CLSD:
|
||||
ESP_LOGE(MB_TCP_SLAVE_PORT_TAG, "Socket (#%d)(%s), connection closed by peer.",
|
||||
pxClientInfo->xSockId, pxClientInfo->pcIpAddr);
|
||||
break;
|
||||
case ERR_BUF:
|
||||
default:
|
||||
ESP_LOGE(MB_TCP_SLAVE_PORT_TAG, "Socket (#%d)(%s), read data error: %d",
|
||||
pxClientInfo->xSockId, pxClientInfo->pcIpAddr, xErr);
|
||||
break;
|
||||
}
|
||||
// Close client connection
|
||||
xMBTCPPortCloseConnection(pxClientInfo);
|
||||
|
||||
// This client does not respond, then unregister it
|
||||
vMBTCPPortFreeClientInfo(pxClientInfo);
|
||||
xConfig.pxMbClientInfo[i] = NULL;
|
||||
xConfig.pxMbClientInfo[MB_TCP_PORT_MAX_CONN] = NULL;
|
||||
// If no any active connections, break
|
||||
if (!xConfig.usClientCount) {
|
||||
xConfig.pxCurClientInfo = NULL;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
pxClientInfo->xRecvTimeStamp = xMBTCPGetTimeStamp();
|
||||
|
||||
// set current client info to active client from which we received request
|
||||
xConfig.pxCurClientInfo = pxClientInfo;
|
||||
|
||||
// Complete frame received, inform state machine to process frame
|
||||
xMBPortEventPost(EV_FRAME_RECEIVED);
|
||||
|
||||
ESP_LOGD(MB_TCP_SLAVE_PORT_TAG, "Socket (#%d)(%s), get packet TID=0x%X, %d bytes.",
|
||||
pxClientInfo->xSockId, pxClientInfo->pcIpAddr,
|
||||
pxClientInfo->usTidCnt, xErr);
|
||||
|
||||
// Wait while response is not processed by stack by timeout
|
||||
UCHAR* pucSentBuffer = vxMBTCPPortRespQueueRecv(xConfig.xRespQueueHandle);
|
||||
if (pucSentBuffer == NULL) {
|
||||
ESP_LOGE(MB_TCP_SLAVE_PORT_TAG, "Response time exceeds configured %d [ms], ignore packet.",
|
||||
MB_TCP_RESP_TIMEOUT_MS);
|
||||
} else {
|
||||
USHORT usSentTid = MB_TCP_GET_FIELD(pucSentBuffer, MB_TCP_TID);
|
||||
if (usSentTid != pxClientInfo->usTidCnt) {
|
||||
ESP_LOGE(MB_TCP_SLAVE_PORT_TAG, "Sent TID(%x) != Recv TID(%x), ignore packet.",
|
||||
usSentTid, pxClientInfo->usTidCnt);
|
||||
}
|
||||
}
|
||||
|
||||
// Get time stamp of last data update
|
||||
pxClientInfo->xSendTimeStamp = xMBTCPGetTimeStamp();
|
||||
ESP_LOGD(MB_TCP_SLAVE_PORT_TAG, "Client %d, Socket(#%d), processing time = %d (us).",
|
||||
pxClientInfo->xIndex, pxClientInfo->xSockId,
|
||||
(int)(pxClientInfo->xSendTimeStamp - pxClientInfo->xRecvTimeStamp));
|
||||
}
|
||||
} else {
|
||||
if (pxClientInfo) {
|
||||
// client is not ready to be read
|
||||
int64_t xTime = xMBTCPGetTimeStamp() - pxClientInfo->xRecvTimeStamp;
|
||||
if (xTime > MB_TCP_DISCONNECT_TIMEOUT) {
|
||||
ESP_LOGE(MB_TCP_SLAVE_PORT_TAG, "Client %d, Socket(#%d) do not answer for %d (us). Drop connection...",
|
||||
pxClientInfo->xIndex, pxClientInfo->xSockId, (int)(xTime));
|
||||
xMBTCPPortCloseConnection(pxClientInfo);
|
||||
|
||||
// This client does not respond, then delete registered data
|
||||
vMBTCPPortFreeClientInfo(pxClientInfo);
|
||||
xConfig.pxMbClientInfo[i] = NULL;
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(MB_TCP_SLAVE_PORT_TAG, "Client %d is disconnected.", i);
|
||||
}
|
||||
}
|
||||
} // if ((pxClientInfo != NULL)
|
||||
} // Handling client connection requests
|
||||
}
|
||||
} // while(1) // Handle connection cycle
|
||||
} // Main connection cycle
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void
|
||||
vMBTCPPortClose( )
|
||||
{
|
||||
// Release resources for the event queue.
|
||||
vMBPortEventClose( );
|
||||
vTaskDelete(xConfig.xMbTcpTaskHandle);
|
||||
}
|
||||
|
||||
void
|
||||
vMBTCPPortDisable( void )
|
||||
{
|
||||
vTaskSuspend(xConfig.xMbTcpTaskHandle);
|
||||
for (int i = 0; i < MB_TCP_PORT_MAX_CONN; i++) {
|
||||
MbClientInfo_t* pxClientInfo = xConfig.pxMbClientInfo[i];
|
||||
if ((pxClientInfo != NULL) && (pxClientInfo->xSockId > 0)) {
|
||||
xMBTCPPortCloseConnection(pxClientInfo);
|
||||
vMBTCPPortFreeClientInfo(pxClientInfo);
|
||||
xConfig.pxMbClientInfo[i] = NULL;
|
||||
}
|
||||
}
|
||||
close(xListenSock);
|
||||
xListenSock = -1;
|
||||
vMBTCPPortRespQueueDelete(xConfig.xRespQueueHandle);
|
||||
}
|
||||
|
||||
BOOL
|
||||
xMBTCPPortGetRequest( UCHAR ** ppucMBTCPFrame, USHORT * usTCPLength )
|
||||
{
|
||||
BOOL xRet = FALSE;
|
||||
if (xConfig.pxCurClientInfo) {
|
||||
*ppucMBTCPFrame = &xConfig.pxCurClientInfo->pucTCPBuf[0];
|
||||
*usTCPLength = xConfig.pxCurClientInfo->usTCPBufPos;
|
||||
|
||||
// Reset the buffer.
|
||||
xConfig.pxCurClientInfo->usTCPBufPos = 0;
|
||||
xConfig.pxCurClientInfo->usTCPFrameBytesLeft = MB_TCP_FUNC;
|
||||
xRet = TRUE;
|
||||
}
|
||||
return xRet;
|
||||
}
|
||||
|
||||
BOOL
|
||||
xMBTCPPortSendResponse( UCHAR * pucMBTCPFrame, USHORT usTCPLength )
|
||||
{
|
||||
BOOL bFrameSent = FALSE;
|
||||
fd_set xWriteSet;
|
||||
fd_set xErrorSet;
|
||||
int xErr = -1;
|
||||
struct timeval xTimeVal;
|
||||
|
||||
if (xConfig.pxCurClientInfo) {
|
||||
FD_ZERO(&xWriteSet);
|
||||
FD_ZERO(&xErrorSet);
|
||||
FD_SET(xConfig.pxCurClientInfo->xSockId, &xWriteSet);
|
||||
FD_SET(xConfig.pxCurClientInfo->xSockId, &xErrorSet);
|
||||
vxMBTCPPortMStoTimeVal(MB_TCP_SEND_TIMEOUT_MS, &xTimeVal);
|
||||
// Check if socket writable
|
||||
xErr = select(xConfig.pxCurClientInfo->xSockId + 1, NULL, &xWriteSet, &xErrorSet, &xTimeVal);
|
||||
if ((xErr == -1) || FD_ISSET(xConfig.pxCurClientInfo->xSockId, &xErrorSet)) {
|
||||
ESP_LOGE(MB_TCP_SLAVE_PORT_TAG, "Socket(#%d) , send select() error = %d.",
|
||||
xConfig.pxCurClientInfo->xSockId, errno);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Apply TID field from request to the frame before send response
|
||||
pucMBTCPFrame[MB_TCP_TID] = (UCHAR)(xConfig.pxCurClientInfo->usTidCnt >> 8U);
|
||||
pucMBTCPFrame[MB_TCP_TID + 1] = (UCHAR)(xConfig.pxCurClientInfo->usTidCnt & 0xFF);
|
||||
|
||||
// Write message into socket and disable Nagle's algorithm
|
||||
xErr = send(xConfig.pxCurClientInfo->xSockId, pucMBTCPFrame, usTCPLength, TCP_NODELAY);
|
||||
if (xErr < 0) {
|
||||
ESP_LOGE(MB_TCP_SLAVE_PORT_TAG, "Socket(#%d), fail to send data, errno = %d",
|
||||
xConfig.pxCurClientInfo->xSockId, errno);
|
||||
xConfig.pxCurClientInfo->xError = xErr;
|
||||
} else {
|
||||
bFrameSent = TRUE;
|
||||
vxMBTCPPortRespQueueSend(xConfig.xRespQueueHandle, (void*)pucMBTCPFrame);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGD(MB_TCP_SLAVE_PORT_TAG, "Port is not active. Release lock.");
|
||||
vxMBTCPPortRespQueueSend(xConfig.xRespQueueHandle, (void*)pucMBTCPFrame);
|
||||
}
|
||||
return bFrameSent;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
/* 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 TCP Port
|
||||
* Copyright (C) 2006 Christian Walter <wolti@sil.at>
|
||||
* Parts of crt0.S Copyright (c) 1995, 1996, 1998 Cygnus Support
|
||||
*
|
||||
* 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.h,v 1.2 2006/09/04 14:39:20 wolti Exp $
|
||||
*/
|
||||
|
||||
#ifndef _PORT_TCP_SLAVE_H
|
||||
#define _PORT_TCP_SLAVE_H
|
||||
|
||||
/* ----------------------- Platform includes --------------------------------*/
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "lwip/opt.h"
|
||||
#include "lwip/sys.h"
|
||||
#include "port.h"
|
||||
#include "esp_modbus_common.h" // for common types for network options
|
||||
|
||||
/* ----------------------- Defines ------------------------------------------*/
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
PR_BEGIN_EXTERN_C
|
||||
#endif
|
||||
|
||||
/* ----------------------- Type definitions ---------------------------------*/
|
||||
typedef struct {
|
||||
int xIndex; /*!< Modbus info index */
|
||||
int xSockId; /*!< Socket id */
|
||||
int xError; /*!< TCP/UDP sock error */
|
||||
const char* pcIpAddr; /*!< TCP/UDP IP address (string) */
|
||||
UCHAR* pucTCPBuf; /*!< buffer pointer */
|
||||
USHORT usTCPBufPos; /*!< buffer active position */
|
||||
USHORT usTCPFrameBytesLeft; /*!< buffer left bytes to receive transaction */
|
||||
int64_t xSendTimeStamp; /*!< send request timestamp */
|
||||
int64_t xRecvTimeStamp; /*!< receive response timestamp */
|
||||
USHORT usTidCnt; /*!< last TID counter from packet */
|
||||
} MbClientInfo_t;
|
||||
|
||||
typedef struct {
|
||||
TaskHandle_t xMbTcpTaskHandle; /*!< Server task handle */
|
||||
xQueueHandle xRespQueueHandle; /*!< Response queue handle */
|
||||
MbClientInfo_t* pxCurClientInfo; /*!< Current client info */
|
||||
MbClientInfo_t** pxMbClientInfo; /*!< Pointers to information about connected clients */
|
||||
USHORT usPort; /*!< TCP/UDP port number */
|
||||
CHAR* pcBindAddr; /*!< IP address to bind */
|
||||
eMBPortProto eMbProto; /*!< Protocol type used by port */
|
||||
USHORT usClientCount; /*!< Client connection count */
|
||||
void* pvNetIface; /*!< Network netif interface pointer for port */
|
||||
eMBPortIpVer xIpVer; /*!< IP protocol version */
|
||||
} MbSlavePortConfig_t;
|
||||
|
||||
/* ----------------------- Function prototypes ------------------------------*/
|
||||
|
||||
/**
|
||||
* Function to setup communication options for TCP/UDP Modbus port
|
||||
*
|
||||
* @param pvNetIf netif interface pointer
|
||||
* @param xIpVersion IP version
|
||||
* @param xProto protocol type option
|
||||
* @param pcBindAddr IP bind address
|
||||
*
|
||||
* @return error code
|
||||
*/
|
||||
void vMBTCPPortSlaveSetNetOpt(void* pvNetIf, eMBPortIpVer xIpVersion, eMBPortProto xProto, CHAR* pcBindAddr);
|
||||
|
||||
/**
|
||||
* Resume TCP Slave processing task
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
void vMBTCPPortSlaveStartServerTask(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
PR_END_EXTERN_C
|
||||
#endif
|
||||
#endif
|
||||
Reference in new issue
Block a user