Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,12 @@ if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER "5.2")
set(requires esp_driver_uart)
set(priv_requires
esp_timer # mb timer implementation
esp_event # mb tcp event loops
esp_netif # mb tcp workaround for ipv6
vfs # mb tcp uses event_fd
)
else()
set(requires driver)
set(priv_requires esp_timer esp_event esp_netif vfs)
set(priv_requires esp_timer esp_netif vfs)
endif()


Expand Down
16 changes: 3 additions & 13 deletions Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -77,21 +77,11 @@ menu "Modbus configuration"
default 100
depends on FMB_COMM_MODE_TCP_EN
help
This is a timeout for the socket writability check and send operation.
Before each send, the driver verifies the socket is writable via select function.
This is the total timeout for sending a complete Modbus TCP frame.
The driver sends without blocking first and waits for socket writability only
when the TCP send buffer cannot accept more data immediately.
Reduce for faster failure detection; increase on high-latency links.

config FMB_TCP_EVENT_LOOP_TICK_MS
int "Modbus TCP event loop processing budget (ms)"
range 10 300
default 50
depends on FMB_COMM_MODE_TCP_EN
help
This option represents the maximum time allocated to event loop run function per driver loop iteration.
The value controls how many queued events (connect, send, error, etc.) are dispatched
before returning to check sockets. Lower values reduce latency for socket
I/O at the cost of processing fewer events per iteration.

config FMB_TCP_UID_ENABLED
bool "Modbus TCP enable UID (Unit Identifier) support"
default n
Expand Down
214 changes: 79 additions & 135 deletions modbus/mb_ports/tcp/port_tcp_driver.c

Large diffs are not rendered by default.

51 changes: 15 additions & 36 deletions modbus/mb_ports/tcp/port_tcp_driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,11 @@
*/
#pragma once

#include <stdatomic.h>

#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#include "esp_event.h" // for esp event loop

#if __has_include("mdns.h")
#include "mdns.h"
Expand All @@ -33,10 +30,9 @@ extern "C" {
#define MB_PORT_DEFAULT (CONFIG_FMB_TCP_PORT_DEFAULT)
#define UNDEF_FD (-1)
#define MB_EVENT_TOUT (300 / portTICK_PERIOD_MS)
#define MB_CONN_TICK_TIMEOUT (10 / portTICK_PERIOD_MS)

typedef void (*mb_event_handler_fp)(void *ctx, esp_event_base_t base, int32_t id, void *data);
#define MB_EVENT_HANDLER(handler_name) void (handler_name)(void *ctx, esp_event_base_t base, int32_t id, void *data)
typedef void (*mb_event_handler_fp)(void *ctx, void *data);
#define MB_EVENT_HANDLER(handler_name) void (handler_name)(void *ctx, void *data)

#define MB_TASK_STACK_SZ (CONFIG_FMB_PORT_TASK_STACK_SIZE)
#define MB_TASK_PRIO (CONFIG_FMB_PORT_TASK_PRIO)
Expand All @@ -47,13 +43,13 @@ typedef void (*mb_event_handler_fp)(void *ctx, esp_event_base_t base, int32_t id
#define MB_RX_QUEUE_MAX_SIZE (CONFIG_FMB_QUEUE_LENGTH)
#define MB_TX_QUEUE_MAX_SIZE (CONFIG_FMB_QUEUE_LENGTH)
#define MB_EVENT_QUEUE_SZ (CONFIG_FMB_QUEUE_LENGTH * MB_TCP_PORT_MAX_CONN)
#define MB_EVENT_DISPATCH_MAX (16)

#define MB_DROP_TRANSACTION_TIME_US (1000UL * (CONFIG_FMB_TCP_KEEP_ALIVE_TOUT_SEC * 2000UL)) // drop after twice keep alive timeout is reasonable

#define MB_WAIT_DONE_MS (5000)
#define MB_SELECT_WAIT_MS (CONFIG_FMB_TCP_EVENT_WAIT_MS)
#define MB_TCP_SEND_TIMEOUT_MS (CONFIG_FMB_TCP_SEND_TIMEOUT_MS)
#define MB_TCP_EVENT_LOOP_TICK_MS (CONFIG_FMB_TCP_EVENT_LOOP_TICK_MS)

#define MB_DRIVER_CONFIG_DEFAULT { \
.spin_lock = portMUX_INITIALIZER_UNLOCKED, \
Expand All @@ -70,6 +66,7 @@ typedef void (*mb_event_handler_fp)(void *ctx, esp_event_base_t base, int32_t id
.close_done_sema = NULL, \
.node_conn_count = 0, \
.event_fd = UNDEF_FD, \
.event_queue = NULL, \
}

#define MB_EVENTFD_CONFIG() (esp_vfs_eventfd_config_t) { \
Expand All @@ -87,17 +84,8 @@ typedef struct _port_driver port_driver_t;
} \
))

#define MB_EVENT_TBL_IT(event) {event, #event}

#define MB_EVENT_BASE(context) (__extension__( \
{ \
port_driver_t *drv_obj = MB_GET_DRV_PTR(context); \
(drv_obj->loop_name) ? (esp_event_base_t)(drv_obj->loop_name) : "UNK_BASE"; \
} \
))

#define MB_ADD_FD(fd, max_fd, fdset) do { \
if (fd) { \
if ((fd) >= 0) { \
(max_fd = (fd > max_fd) ? fd : max_fd); \
FD_SET(fd, fdset); \
} \
Expand All @@ -119,8 +107,7 @@ typedef struct _port_driver port_driver_t;
} \
))

// Post event to event loop and unblocks the select through the eventfd to handle the event loop run,
// So, the eventfd value keeps last event and its fd.
// Queue an event and unblock select through eventfd.
#define DRIVER_SEND_EVENT_MACRO(ctx, event, fd, value) (__extension__( \
{ \
port_driver_t *drv_obj = MB_GET_DRV_PTR(ctx); \
Expand Down Expand Up @@ -185,17 +172,9 @@ typedef enum _mb_driver_event {
} mb_driver_event_t;

typedef struct {
mb_driver_event_t event;
const char *msg;
} event_msg_t;

typedef union {
struct {
int32_t event_id; /*!< an event */
int16_t opt_fd; /*!< fd option for an event */
int16_t opt_val; /*!< value option for an event */
};
uint64_t val;
int32_t event_id; /*!< an event */
int16_t opt_fd; /*!< fd option for an event */
int16_t opt_val; /*!< value option for an event */
} mb_event_info_t;

typedef struct mb_node_info_s {
Expand All @@ -207,6 +186,9 @@ typedef struct mb_node_info_s {
int recv_err; /*!< socket receive error */
QueueHandle_t rx_queue; /*!< receive response queue */
QueueHandle_t tx_queue; /*!< send request queue */
uint8_t rx_buffer[MB_TCP_BUFF_MAX_SIZE]; /*!< partially received TCP frame */
uint16_t rx_length; /*!< number of bytes accumulated in rx_buffer */
uint16_t rx_expected_length; /*!< complete frame length, zero until MBAP header is received */
int64_t send_time; /*!< send request time stamp */
int64_t recv_time; /*!< receive response time stamp */
uint16_t tid_counter; /*!< transaction identifier (TID) for slave */
Expand Down Expand Up @@ -264,13 +246,12 @@ typedef struct _port_driver {
uint16_t curr_node_index; /*!< current processing slave index */
fd_set open_set; /*!< file descriptor set for opened nodes */
fd_set conn_set; /*!< file descriptor set for associated nodes */
int event_fd; /*!< eventfd descriptor for modbus event tracking */
int event_fd; /*!< eventfd used to wake select */
QueueHandle_t event_queue; /*!< queued driver events */
SemaphoreHandle_t close_done_sema; /*!< close and done semaphore */
EventGroupHandle_t status_flags_hdl; /*!< status bits to control nodes states */
TaskHandle_t mb_tcp_task_handle; /*!< TCP/UDP handling task handle */
esp_event_loop_handle_t event_loop_hdl; /*!< event loop handle */
esp_event_handler_instance_t event_handler[MB_EVENT_COUNT]; /*!< event handler instance */
char *loop_name; /*!< name for event loop used as base */
mb_event_handler_fp event_handler[MB_EVENT_COUNT]; /*!< driver event handlers */
mb_driver_event_cb_t event_cbs;
//LIST_HEAD(mb_uid_info_, mb_uid_entry_s) node_list; /*!< node address information list */
//uint16_t node_list_count;
Expand Down Expand Up @@ -342,8 +323,6 @@ int mb_drv_close(void *ctx, int fd);

int32_t write_event(void *ctx, mb_event_info_t *event);

const char *driver_event_to_name_r(mb_driver_event_t event);

void mb_drv_set_cb(void *ctx, void *conn_cb, void *arg);

mb_status_flags_t mb_drv_wait_status_flag(void *ctx, mb_status_flags_t mask, TickType_t ticks);
Expand Down
37 changes: 10 additions & 27 deletions modbus/mb_ports/tcp/port_tcp_master.c
Original file line number Diff line number Diff line change
Expand Up @@ -295,25 +295,12 @@ void mbm_port_tcp_set_conn_cb(mb_port_base_t *inst, void *conn_fp, void *arg)
// Timer handler to check timeout of socket response
bool mbm_port_timer_expired(void *inst)
{
mbm_tcp_port_t *port_obj = __containerof(inst, mbm_tcp_port_t, base);
bool need_poll = false;
BaseType_t task_unblocked;
mb_event_info_t mb_event;
esp_err_t err = ESP_FAIL;

ESP_EARLY_LOGD(TAG, "Timer timeout event: %p", inst);
mb_port_timer_disable(inst);
// If timer mode is respond timeout, the master event then turns EV_MASTER_EXECUTE status.
if (mb_port_get_cur_timer_mode(inst) == MB_TMODE_RESPOND_TIMEOUT) {
// It is now to check solution.
mb_event.event_id = MB_EVENT_TIMEOUT;
mb_event.opt_fd = port_obj->drv_obj->curr_node_index;
err = esp_event_isr_post_to(port_obj->drv_obj->event_loop_hdl, MB_EVENT_BASE(port_obj->drv_obj),
(int32_t)MB_EVENT_TIMEOUT, (void *)&mb_event, sizeof(mb_event_info_t *), &task_unblocked);
if (err != ESP_OK) {
ESP_EARLY_LOGE(TAG, "Timeout event send error: %d", err);
}
need_poll = task_unblocked;
mb_port_event_set_err_type(inst, EV_ERROR_RESPOND_TIMEOUT);
need_poll = mb_port_event_post(inst, EVENT(EV_ERROR_PROCESS));
}
Expand Down Expand Up @@ -367,22 +354,21 @@ static uint64_t mbm_port_tcp_sync_event(void *inst, mb_sync_event_t sync_event)

MB_EVENT_HANDLER(mbm_on_ready)
{
// The driver is registered
mb_event_info_t *event_info = (mb_event_info_t *)data;
ESP_LOGD(TAG, "%s %s: fd: %d", (char *)base, __func__, (int)event_info->opt_fd);
ESP_LOGD(TAG, "%s: fd: %d", __func__, (int)event_info->opt_fd);
}

MB_EVENT_HANDLER(mbm_on_open)
{
mb_event_info_t *event_info = (mb_event_info_t *)data;
ESP_LOGD(TAG, "%s %s: fd: %d", (char *)base, __func__, (int)event_info->opt_fd);
ESP_LOGD(TAG, "%s: fd: %d", __func__, (int)event_info->opt_fd);
}

MB_EVENT_HANDLER(mbm_on_resolve)
{
port_driver_t *drv_obj = MB_GET_DRV_PTR(ctx);
mb_event_info_t *event_info = (mb_event_info_t *)data;
ESP_LOGD(TAG, "%s %s: fd: %d", (char *)base, __func__, (int)event_info->opt_fd);
ESP_LOGD(TAG, "%s: fd: %d", __func__, (int)event_info->opt_fd);

if (MB_CHECK_FD_RANGE(event_info->opt_fd)) {
ESP_LOGD(TAG, "%p, Node: %d, resolve.", ctx, (int)event_info->opt_fd);
Expand Down Expand Up @@ -444,7 +430,7 @@ MB_EVENT_HANDLER(mbm_on_connect)
port_driver_t *drv_obj = MB_GET_DRV_PTR(ctx);
mb_node_info_t *node_ptr = NULL;
mb_event_info_t *event_info = (mb_event_info_t *)data;
ESP_LOGD(TAG, "%s %s: fd: %d", (char *)base, __func__, (int)event_info->opt_fd);
ESP_LOGD(TAG, "%s: fd: %d", __func__, (int)event_info->opt_fd);
err_t err = ERR_CONN;
if (MB_CHECK_FD_RANGE(event_info->opt_fd)) {
node_ptr = mb_drv_get_node(drv_obj, event_info->opt_fd);
Expand All @@ -468,6 +454,7 @@ MB_EVENT_HANDLER(mbm_on_connect)
node_ptr->addr_info.ip_addr_str);
MB_SET_NODE_STATE(node_ptr, MB_SOCK_STATE_CONNECTED);
(void)port_keep_alive_enable(node_ptr->sock_id, CONFIG_FMB_TCP_KEEP_ALIVE_TOUT_SEC);
(void)port_tcp_set_no_delay(node_ptr->sock_id);
ESP_LOGD(TAG, "Opened/connected: %u, %u.",
(unsigned)drv_obj->mb_node_open_count, (unsigned)drv_obj->node_conn_count);
if (drv_obj->mb_node_open_count == drv_obj->node_conn_count) {
Expand Down Expand Up @@ -579,7 +566,7 @@ MB_EVENT_HANDLER(mbm_on_send_data)
{
port_driver_t *drv_obj = MB_GET_DRV_PTR(ctx);
mb_event_info_t *event_info = (mb_event_info_t *)data;
ESP_LOGD(TAG, "%s %s: fd: %d", (char *)base, __func__, (int)event_info->opt_fd);
ESP_LOGD(TAG, "%s: fd: %d", __func__, (int)event_info->opt_fd);
mb_drv_check_suspend_shutdown(ctx);
mb_node_info_t *info_ptr = mb_drv_get_node(drv_obj, event_info->opt_fd);
if (info_ptr && !queue_is_empty(info_ptr->tx_queue)) {
Expand Down Expand Up @@ -628,8 +615,6 @@ MB_EVENT_HANDLER(mbm_on_send_data)
mb_drv_lock(ctx);
drv_obj->mb_node_curr = info_ptr;
drv_obj->curr_node_index = info_ptr->index;
info_ptr->send_time = esp_timer_get_time();
info_ptr->send_counter = (info_ptr->send_counter < (USHRT_MAX - 1)) ? (info_ptr->send_counter + 1) : 0;
mb_drv_unlock(ctx);
// Get send buffer from stack
ESP_LOG_BUFFER_HEX_LEVEL("SENT", tx_buffer, sz, ESP_LOG_DEBUG);
Expand All @@ -640,7 +625,7 @@ MB_EVENT_HANDLER(mbm_on_recv_data)
{
port_driver_t *drv_obj = MB_GET_DRV_PTR(ctx);
mb_event_info_t *event_info = (mb_event_info_t *)data;
ESP_LOGD(TAG, "%s %s: fd: %d", (char *)base, __func__, (int)event_info->opt_fd);
ESP_LOGD(TAG, "%s: fd: %d", __func__, (int)event_info->opt_fd);
uint8_t buf[MB_TCP_BUFF_MAX_SIZE] = {0};
mb_drv_check_suspend_shutdown(ctx);
// Get frame from queue, check for correctness, push back correct frame and generate receive condition.
Expand Down Expand Up @@ -676,7 +661,7 @@ MB_EVENT_HANDLER(mbm_on_recv_data)
MB_EVENT_HANDLER(mbm_on_close)
{
mb_event_info_t *event_info = (mb_event_info_t *)data;
ESP_LOGD(TAG, "%s %s, fd: %d", (char *)base, __func__, (int)event_info->opt_fd);
ESP_LOGD(TAG, "%s, fd: %d", __func__, (int)event_info->opt_fd);
port_driver_t *drv_obj = MB_GET_DRV_PTR(ctx);
mb_node_info_t *pnode = NULL;
// if close all sockets event is received
Expand All @@ -699,7 +684,7 @@ MB_EVENT_HANDLER(mbm_on_close)
pnode = mb_drv_get_node(drv_obj, event_info->opt_fd);
if (pnode && (MB_GET_NODE_STATE(pnode) >= MB_SOCK_STATE_OPENED)) {
ESP_LOGD(TAG, "%p, Close node %d, sock #%d, intentionally.", ctx, (int)event_info->opt_fd, pnode->sock_id);
if ((pnode->sock_id < 0) && FD_ISSET(pnode->sock_id, &drv_obj->open_set)) {
if (FD_ISSET(pnode->index, &drv_obj->open_set)) {
mb_drv_close(drv_obj, event_info->opt_fd);
}
}
Expand All @@ -711,11 +696,9 @@ MB_EVENT_HANDLER(mbm_on_timeout)
{
// Socket read/write timeout is triggered
mb_event_info_t *event_info = (mb_event_info_t *)data;
ESP_LOGD(TAG, "%s %s: fd: %d", (char *)base, __func__, (int)event_info->opt_fd);
ESP_LOGD(TAG, "%s: fd: %d", __func__, (int)event_info->opt_fd);
// Todo: this event can be used to check network state (keep empty for now)
mb_drv_check_suspend_shutdown(ctx);
// Intentionally allow IDLE task to trigger if other tasks do not perform it properly.
vTaskDelay(1);
}

#endif
1 change: 0 additions & 1 deletion modbus/mb_ports/tcp/port_tcp_master.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#include "esp_event.h" // for esp event loop

#include "mb_common.h"
#include "mb_frame.h"
Expand Down
Loading