Modbus TCP - Multi Devices

This commit is contained in:
Death7z
2026-04-14 14:41:00 -05:00
commit 894d35a256
1483 changed files with 238546 additions and 0 deletions

918
main/main.c Normal file
View File

@@ -0,0 +1,918 @@
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/select.h>
#include <dirent.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "config_store.h"
#include "wifi_manager.h"
#include "esp_log.h"
#include "esp_err.h"
#include "nvs_flash.h"
#include "esp_spiffs.h"
#include "lwip/sockets.h"
#include "lwip/netdb.h"
#include "lwip/inet.h"
#include "lwip/ip_addr.h"
#include "lwip/ip4_addr.h"
#include "esp_netif_ip_addr.h"
#include "modbus_memory.h"
#include "modbus_points.h"
#include "modbus_tcp.h"
#define DEVICE_CONFIG_PATH "/spiffs/device_config.ini"
#define TAG "MODBUS_MAIN"
#define MODBUS_RX_BUF_SIZE MODBUS_TCP_MAX_ADU_LEN
#define MODBUS_TX_BUF_SIZE MODBUS_TCP_MAX_ADU_LEN
static device_config_t g_device_cfg;
typedef struct
{
int sock;
struct sockaddr_in addr;
} modbus_client_ctx_t;
static volatile int g_active_modbus_clients = 0;
typedef struct
{
uint8_t unit_id;
char name[DEVICE_NAME_MAX_LEN];
char csv_path[128];
modbus_db_t db;
modbus_memory_t mem;
} modbus_device_t;
static modbus_device_t g_devices[MAX_VIRTUAL_DEVICES];
static size_t g_device_count = 0;
/* ------------------------------------------------------------ */
/* Helpers */
/* ------------------------------------------------------------ */
static modbus_device_t *find_device_by_unit_id(uint8_t unit_id)
{
size_t i;
for (i = 0; i < g_device_count; i++)
{
if (g_devices[i].unit_id == unit_id)
return &g_devices[i];
}
return NULL;
}
static bool file_exists(const char *path)
{
FILE *fp;
if (path == NULL)
return false;
fp = fopen(path, "r");
if (fp == NULL)
return false;
fclose(fp);
return true;
}
static void spiffs_list_files(void)
{
DIR *dir;
struct dirent *entry;
dir = opendir("/spiffs");
if (dir == NULL)
{
ESP_LOGE(TAG, "Failed to open /spiffs for listing");
return;
}
ESP_LOGI(TAG, "----- SPIFFS file list -----");
while ((entry = readdir(dir)) != NULL)
{
ESP_LOGI(TAG, "SPIFFS file: %s", entry->d_name);
}
ESP_LOGI(TAG, "----------------------------");
closedir(dir);
}
/* ------------------------------------------------------------ */
/* SPIFFS */
/* ------------------------------------------------------------ */
static esp_err_t spiffs_init(void)
{
esp_vfs_spiffs_conf_t conf = {
.base_path = "/spiffs",
.partition_label = NULL,
.max_files = 8,
.format_if_mount_failed = true
};
esp_err_t ret = esp_vfs_spiffs_register(&conf);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "Failed to mount SPIFFS (%s)", esp_err_to_name(ret));
return ret;
}
{
size_t total = 0;
size_t used = 0;
ret = esp_spiffs_info(NULL, &total, &used);
if (ret == ESP_OK)
{
ESP_LOGI(TAG, "SPIFFS mounted: total=%u used=%u",
(unsigned)total, (unsigned)used);
}
}
return ESP_OK;
}
/* ------------------------------------------------------------ */
/* Modbus TCP server */
/* ------------------------------------------------------------ */
static void modbus_client_task(void *arg)
{
modbus_client_ctx_t *ctx = (modbus_client_ctx_t *)arg;
uint8_t rx_buf[MODBUS_RX_BUF_SIZE];
uint8_t tx_buf[MODBUS_TX_BUF_SIZE];
if (ctx == NULL)
{
vTaskDelete(NULL);
return;
}
ESP_LOGI(TAG, "Client connected: %s:%d (active=%d)",
inet_ntoa(ctx->addr.sin_addr),
ntohs(ctx->addr.sin_port),
g_active_modbus_clients);
while (1)
{
int len;
size_t resp_len = 0;
bool ok;
modbus_device_t *dev;
uint8_t unit_id = 0;
len = recv(ctx->sock, rx_buf, sizeof(rx_buf), 0);
if (len < 0)
{
ESP_LOGW(TAG, "recv failed: errno=%d", errno);
break;
}
else if (len == 0)
{
ESP_LOGI(TAG, "Client disconnected: %s:%d",
inet_ntoa(ctx->addr.sin_addr),
ntohs(ctx->addr.sin_port));
break;
}
if (len < 7)
{
continue;
}
unit_id = rx_buf[6];
dev = find_device_by_unit_id(unit_id);
if (dev == NULL)
{
ESP_LOGW(TAG, "Unknown Unit ID %u from %s:%d",
(unsigned)unit_id,
inet_ntoa(ctx->addr.sin_addr),
ntohs(ctx->addr.sin_port));
continue;
}
ok = modbus_tcp_process_request(&dev->mem,
rx_buf,
(size_t)len,
tx_buf,
sizeof(tx_buf),
&resp_len);
if (!ok)
{
if (len >= 8 && (rx_buf[6] == 1U || rx_buf[6] == 2U))
{
ESP_LOGW(TAG, "Unsupported Modbus request from %s:%d (unit_id=%u fc=0x%02X)",
inet_ntoa(ctx->addr.sin_addr),
ntohs(ctx->addr.sin_port),
(unsigned)rx_buf[6],
(unsigned)rx_buf[7]);
}
continue;
}
if (resp_len > 0U)
{
int sent = send(ctx->sock, tx_buf, (int)resp_len, 0);
if (sent < 0)
{
ESP_LOGW(TAG, "send failed: errno=%d", errno);
break;
}
}
}
shutdown(ctx->sock, 0);
close(ctx->sock);
if (g_active_modbus_clients > 0)
g_active_modbus_clients--;
free(ctx);
vTaskDelete(NULL);
}
static void modbus_server_task(void *arg)
{
int listen_sock = -1;
struct sockaddr_in server_addr;
int opt = 1;
(void)arg;
listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if (listen_sock < 0)
{
ESP_LOGE(TAG, "Unable to create socket: errno=%d", errno);
vTaskDelete(NULL);
return;
}
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(g_device_cfg.modbus.port);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(listen_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
{
ESP_LOGE(TAG, "Socket bind failed: errno=%d", errno);
close(listen_sock);
vTaskDelete(NULL);
return;
}
if (listen(listen_sock, g_device_cfg.modbus.max_clients) < 0)
{
ESP_LOGE(TAG, "Socket listen failed: errno=%d", errno);
close(listen_sock);
vTaskDelete(NULL);
return;
}
ESP_LOGI(TAG, "Modbus TCP server listening on port %u (max_clients=%u)",
(unsigned)g_device_cfg.modbus.port,
(unsigned)g_device_cfg.modbus.max_clients);
while (1)
{
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int client_sock = accept(listen_sock, (struct sockaddr *)&client_addr, &client_len);
if (client_sock < 0)
{
ESP_LOGW(TAG, "accept failed: errno=%d", errno);
continue;
}
if (g_active_modbus_clients >= (int)g_device_cfg.modbus.max_clients)
{
ESP_LOGW(TAG, "Rejecting client %s:%d (active=%d max=%u)",
inet_ntoa(client_addr.sin_addr),
ntohs(client_addr.sin_port),
g_active_modbus_clients,
(unsigned)g_device_cfg.modbus.max_clients);
shutdown(client_sock, 0);
close(client_sock);
continue;
}
{
modbus_client_ctx_t *ctx = malloc(sizeof(modbus_client_ctx_t));
if (ctx == NULL)
{
ESP_LOGE(TAG, "Out of memory allocating client context");
shutdown(client_sock, 0);
close(client_sock);
continue;
}
ctx->sock = client_sock;
ctx->addr = client_addr;
g_active_modbus_clients++;
if (xTaskCreate(modbus_client_task,
"modbus_client_task",
6144,
ctx,
5,
NULL) != pdPASS)
{
ESP_LOGE(TAG, "Failed to create client task");
g_active_modbus_clients--;
shutdown(client_sock, 0);
close(client_sock);
free(ctx);
}
}
}
}
/* ------------------------------------------------------------ */
/* Override Sync Task */
/* ------------------------------------------------------------ */
static void modbus_override_task(void *arg)
{
(void)arg;
while (1)
{
size_t d;
for (d = 0; d < g_device_count; d++)
{
modbus_device_t *dev = &g_devices[d];
size_t i;
for (i = 0; i < dev->db.count; i++)
{
modbus_point_t *pt = &dev->db.points[i];
if (pt->is_helper)
continue;
if (!pt->has_control_address)
continue;
if (pt->data_type == MB_DATA_BOOL)
{
uint8_t value;
if (modbus_memory_read_bit(&dev->mem,
modbus_points_type_to_mem(pt->control_type),
pt->control_offset,
&value))
{
modbus_memory_write_bit(&dev->mem,
modbus_points_type_to_mem(pt->type),
pt->offset,
value);
}
}
else if (pt->data_type == MB_DATA_UINT16)
{
uint16_t value;
if (modbus_memory_read_reg(&dev->mem,
modbus_points_type_to_mem(pt->control_type),
pt->control_offset,
&value))
{
modbus_memory_write_reg(&dev->mem,
modbus_points_type_to_mem(pt->type),
pt->offset,
value);
}
}
else if (pt->data_type == MB_DATA_UINT32 ||
pt->data_type == MB_DATA_FLOAT)
{
uint16_t w1, w2;
if (modbus_memory_read_reg(&dev->mem,
modbus_points_type_to_mem(pt->control_type),
pt->control_offset,
&w1) &&
modbus_memory_read_reg(&dev->mem,
modbus_points_type_to_mem(pt->control_type),
(uint16_t)(pt->control_offset + 1U),
&w2))
{
modbus_memory_write_reg(&dev->mem,
modbus_points_type_to_mem(pt->type),
pt->offset,
w1);
modbus_memory_write_reg(&dev->mem,
modbus_points_type_to_mem(pt->type),
(uint16_t)(pt->offset + 1U),
w2);
}
}
else if (pt->data_type == MB_DATA_DOUBLE)
{
uint16_t w1, w2, w3, w4;
if (modbus_memory_read_reg(&dev->mem,
modbus_points_type_to_mem(pt->control_type),
pt->control_offset,
&w1) &&
modbus_memory_read_reg(&dev->mem,
modbus_points_type_to_mem(pt->control_type),
(uint16_t)(pt->control_offset + 1U),
&w2) &&
modbus_memory_read_reg(&dev->mem,
modbus_points_type_to_mem(pt->control_type),
(uint16_t)(pt->control_offset + 2U),
&w3) &&
modbus_memory_read_reg(&dev->mem,
modbus_points_type_to_mem(pt->control_type),
(uint16_t)(pt->control_offset + 3U),
&w4))
{
modbus_memory_write_reg(&dev->mem,
modbus_points_type_to_mem(pt->type),
pt->offset,
w1);
modbus_memory_write_reg(&dev->mem,
modbus_points_type_to_mem(pt->type),
(uint16_t)(pt->offset + 1U),
w2);
modbus_memory_write_reg(&dev->mem,
modbus_points_type_to_mem(pt->type),
(uint16_t)(pt->offset + 2U),
w3);
modbus_memory_write_reg(&dev->mem,
modbus_points_type_to_mem(pt->type),
(uint16_t)(pt->offset + 3U),
w4);
}
}
}
}
vTaskDelay(pdMS_TO_TICKS(100));
}
}
/* ------------------------------------------------------------ */
/* Helpers for app_main */
/* ------------------------------------------------------------ */
static const char *data_type_to_str(modbus_data_type_t type)
{
switch (type)
{
case MB_DATA_BOOL:
return "bool";
case MB_DATA_UINT16:
return "uint16";
case MB_DATA_UINT32:
return "uint32";
case MB_DATA_FLOAT:
return "float";
case MB_DATA_DOUBLE:
return "double";
default:
return "invalid";
}
}
static uint8_t data_type_internal_cost(modbus_data_type_t type)
{
switch (type)
{
case MB_DATA_BOOL:
case MB_DATA_UINT16:
return 1U;
case MB_DATA_UINT32:
case MB_DATA_FLOAT:
return 2U;
case MB_DATA_DOUBLE:
return 4U;
default:
return 0U;
}
}
static uint8_t pics_type_cost(pics_data_type_t type)
{
switch (type)
{
case PICS_DATA_BOOLEAN:
return 1U;
case PICS_DATA_INTEGER:
return 2U;
case PICS_DATA_FLOAT:
return 4U;
case PICS_DATA_NONE:
default:
return 0U;
}
}
static void print_device_type_mix(const modbus_device_t *dev)
{
size_t i;
size_t bool_count = 0;
size_t uint16_count = 0;
size_t uint32_count = 0;
size_t float_count = 0;
size_t double_count = 0;
size_t pics_bool_count = 0;
size_t pics_int_count = 0;
size_t pics_float_count = 0;
size_t pics_none_count = 0;
size_t internal_points_from_base_rows = 0;
size_t pics_registers_from_base_rows = 0;
size_t base_rows = 0;
if (dev == NULL)
return;
for (i = 0; i < dev->db.count; i++)
{
const modbus_point_t *pt = &dev->db.points[i];
if (pt->is_helper)
continue;
base_rows++;
switch (pt->data_type)
{
case MB_DATA_BOOL:
bool_count++;
break;
case MB_DATA_UINT16:
uint16_count++;
break;
case MB_DATA_UINT32:
uint32_count++;
break;
case MB_DATA_FLOAT:
float_count++;
break;
case MB_DATA_DOUBLE:
double_count++;
break;
default:
break;
}
switch (pt->pics_data_type)
{
case PICS_DATA_BOOLEAN:
pics_bool_count++;
break;
case PICS_DATA_INTEGER:
pics_int_count++;
break;
case PICS_DATA_FLOAT:
pics_float_count++;
break;
case PICS_DATA_NONE:
default:
pics_none_count++;
break;
}
internal_points_from_base_rows += data_type_internal_cost(pt->data_type);
pics_registers_from_base_rows += pics_type_cost(pt->pics_data_type);
}
ESP_LOGI(TAG,
"Device %u (%s) type mix: base_rows=%u internal_points=%u pics_regs=%u",
(unsigned)dev->unit_id,
dev->name,
(unsigned)base_rows,
(unsigned)internal_points_from_base_rows,
(unsigned)pics_registers_from_base_rows);
ESP_LOGI(TAG,
" data_type counts: bool=%u uint16=%u uint32=%u float=%u double=%u",
(unsigned)bool_count,
(unsigned)uint16_count,
(unsigned)uint32_count,
(unsigned)float_count,
(unsigned)double_count);
ESP_LOGI(TAG,
" pics_data_type counts: none=%u Boolean=%u Integer=%u Float=%u",
(unsigned)pics_none_count,
(unsigned)pics_bool_count,
(unsigned)pics_int_count,
(unsigned)pics_float_count);
}
/* ------------------------------------------------------------ */
/* app_main */
/* ------------------------------------------------------------ */
void app_main(void)
{
esp_err_t ret;
size_t i;
ESP_LOGI(TAG, "Starting Modbus TCP device");
ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
ESP_ERROR_CHECK(spiffs_init());
spiffs_list_files();
if (!config_store_load(DEVICE_CONFIG_PATH, &g_device_cfg))
{
ESP_LOGE(TAG, "Failed to load device config: %s", DEVICE_CONFIG_PATH);
return;
}
ESP_LOGI(TAG, "Modbus Port: %u", (unsigned)g_device_cfg.modbus.port);
ESP_LOGI(TAG, "Configured device_count = %u", (unsigned)g_device_cfg.modbus.device_count);
for (i = 0; i < g_device_cfg.modbus.device_count; i++)
{
ESP_LOGI(TAG,
"CFG device[%u]: enabled=%u unit_id=%u name=%s csv=%s",
(unsigned)i,
g_device_cfg.devices[i].enabled ? 1 : 0,
(unsigned)g_device_cfg.devices[i].unit_id,
g_device_cfg.devices[i].name,
g_device_cfg.devices[i].csv);
}
if (g_device_cfg.modbus.device_count == 0U)
{
ESP_LOGE(TAG, "No Modbus devices configured");
return;
}
if (g_device_cfg.modbus.device_count > MAX_VIRTUAL_DEVICES)
{
ESP_LOGE(TAG, "Too many virtual devices (%u)", (unsigned)g_device_cfg.modbus.device_count);
return;
}
memset(g_devices, 0, sizeof(g_devices));
g_device_count = 0U;
for (i = 0; i < g_device_cfg.modbus.device_count; i++)
{
modbus_device_t *dev;
const virtual_device_settings_t *cfg_dev = &g_device_cfg.devices[i];
if (!cfg_dev->enabled)
{
ESP_LOGI(TAG, "Skipping disabled device[%u]", (unsigned)i);
continue;
}
dev = &g_devices[g_device_count];
dev->unit_id = cfg_dev->unit_id;
strncpy(dev->name, cfg_dev->name, sizeof(dev->name) - 1U);
dev->name[sizeof(dev->name) - 1U] = '\0';
snprintf(dev->csv_path,
sizeof(dev->csv_path),
"/spiffs/%s",
cfg_dev->csv);
ESP_LOGI(TAG,
"Preparing device[%u]: unit_id=%u name=%s csv=%s",
(unsigned)i,
(unsigned)dev->unit_id,
dev->name,
dev->csv_path);
if (!file_exists(dev->csv_path))
{
ESP_LOGE(TAG, "CSV file not found for device %u: %s",
(unsigned)dev->unit_id,
dev->csv_path);
continue;
}
ESP_LOGI(TAG, "CSV file found for device %u: %s",
(unsigned)dev->unit_id,
dev->csv_path);
modbus_points_init(&dev->db);
if (!modbus_points_load(&dev->db, dev->csv_path))
{
ESP_LOGE(TAG, "Failed to load CSV for device %u: %s",
(unsigned)dev->unit_id,
dev->csv_path);
continue;
}
/* Safety check: prevent memory overflow */
if (dev->db.count > MODBUS_MAX_POINTS)
{
ESP_LOGE(TAG,
"Device %u exceeded MODBUS_MAX_POINTS (%u > %u)",
(unsigned)dev->unit_id,
(unsigned)dev->db.count,
(unsigned)MODBUS_MAX_POINTS);
ESP_LOGE(TAG, "Startup aborted to prevent memory corruption.");
abort();
}
if (!modbus_memory_init(&dev->mem, &dev->db))
{
ESP_LOGE(TAG, "Memory init failed for device %u",
(unsigned)dev->unit_id);
continue;
}
ESP_LOGI(TAG, "Device %u (%s) loaded %u Modbus points from %s",
(unsigned)dev->unit_id,
dev->name,
(unsigned)dev->db.count,
dev->csv_path);
g_device_count++;
}
ESP_LOGI(TAG, "----- Runtime Modbus Device Table -----");
for (i = 0; i < g_device_count; i++)
{
ESP_LOGI(TAG,
"RUNTIME device[%u]: unit_id=%u name=%s csv=%s points=%u",
(unsigned)i,
(unsigned)g_devices[i].unit_id,
g_devices[i].name,
g_devices[i].csv_path,
(unsigned)g_devices[i].db.count);
}
ESP_LOGI(TAG, "---------------------------------------");
ESP_LOGI(TAG, "----- Modbus Point Capacity -----");
{
size_t total_points_used = 0;
size_t total_points_capacity = g_device_count * MODBUS_MAX_POINTS;
size_t total_points_remaining = 0;
size_t free_heap = heap_caps_get_free_size(MALLOC_CAP_8BIT);
size_t largest_block = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT);
size_t point_struct_size = sizeof(modbus_point_t);
size_t heap_equivalent_points = 0;
if (point_struct_size > 0U)
heap_equivalent_points = free_heap / point_struct_size;
for (i = 0; i < g_device_count; i++)
{
const modbus_device_t *dev = &g_devices[i];
size_t remaining = 0;
float pct = 0.0f;
if (dev->db.count < MODBUS_MAX_POINTS)
remaining = MODBUS_MAX_POINTS - dev->db.count;
if (MODBUS_MAX_POINTS > 0U)
pct = (100.0f * (float)dev->db.count) / (float)MODBUS_MAX_POINTS;
ESP_LOGI(TAG,
"Device %u (%s): used=%u / max=%u, remaining=%u (%.1f%% used)",
(unsigned)dev->unit_id,
dev->name,
(unsigned)dev->db.count,
(unsigned)MODBUS_MAX_POINTS,
(unsigned)remaining,
pct);
total_points_used += dev->db.count;
total_points_remaining += remaining;
}
ESP_LOGI(TAG,
"Configured capacity: used=%u / max=%u, remaining=%u",
(unsigned)total_points_used,
(unsigned)total_points_capacity,
(unsigned)total_points_remaining);
ESP_LOGI(TAG,
"Free heap: %u bytes, largest block: %u bytes, point size: %u bytes",
(unsigned)free_heap,
(unsigned)largest_block,
(unsigned)point_struct_size);
ESP_LOGI(TAG,
"Heap-equivalent points available (informational only): %u",
(unsigned)heap_equivalent_points);
}
ESP_LOGI(TAG, "---------------------------------");
ESP_LOGI(TAG, "----- Point Cost Guide -----");
ESP_LOGI(TAG, "Internal point usage (counts against MODBUS_MAX_POINTS):");
ESP_LOGI(TAG, " data_type=bool -> 1 point");
ESP_LOGI(TAG, " data_type=uint16 -> 1 point");
ESP_LOGI(TAG, " data_type=uint32 -> 2 points");
ESP_LOGI(TAG, " data_type=float -> 2 points");
ESP_LOGI(TAG, " data_type=double -> 4 points");
ESP_LOGI(TAG, "PICS staging usage (for pics_address spacing only):");
ESP_LOGI(TAG, " pics_data_type=Boolean -> 1 register");
ESP_LOGI(TAG, " pics_data_type=Integer -> 2 registers");
ESP_LOGI(TAG, " pics_data_type=Float -> 4 registers");
ESP_LOGI(TAG, " pics_data_type=None -> 0 registers");
ESP_LOGI(TAG, "Planning examples:");
ESP_LOGI(TAG, " 100 uint16 CSV rows -> about 100 internal points");
ESP_LOGI(TAG, " 100 float CSV rows -> about 200 internal points");
ESP_LOGI(TAG, " 100 double CSV rows -> about 400 internal points");
ESP_LOGI(TAG, "--------------------------------");
ESP_LOGI(TAG, "----- Device Type Mix -----");
for (i = 0; i < g_device_count; i++)
{
print_device_type_mix(&g_devices[i]);
}
ESP_LOGI(TAG, "---------------------------");
ESP_LOGI(TAG, "----- Modbus Memory Usage -----");
size_t total_points = 0;
for (size_t d = 0; d < g_device_count; d++)
{
const modbus_device_t *dev = &g_devices[d];
ESP_LOGI(TAG,
"Device %u (%s): %u / %u points used (%.1f%%)",
(unsigned)dev->unit_id,
dev->name,
(unsigned)dev->db.count,
(unsigned)MODBUS_MAX_POINTS,
(100.0f * (float)dev->db.count) / (float)MODBUS_MAX_POINTS);
total_points += dev->db.count;
}
ESP_LOGI(TAG,
"TOTAL POINTS LOADED: %u",
(unsigned)total_points);
ESP_LOGI(TAG, "--------------------------------");
if (g_device_count == 0U)
{
ESP_LOGE(TAG, "No valid Modbus devices loaded, not starting server");
return;
}
ESP_ERROR_CHECK(wifi_manager_init());
ESP_ERROR_CHECK(wifi_manager_start_sta(&g_device_cfg.wifi));
if (!wifi_manager_wait_connected(pdMS_TO_TICKS(15000)))
{
ESP_LOGW(TAG, "Wi-Fi not connected within timeout, server will still start");
}
xTaskCreate(modbus_server_task, "modbus_server_task", 8192, NULL, 5, NULL);
xTaskCreate(modbus_override_task, "modbus_override_task", 4096, NULL, 5, NULL);
}