368 lines
12 KiB
Plaintext
368 lines
12 KiB
Plaintext
void app_main(void)
|
|
{
|
|
esp_err_t ret;
|
|
size_t i;
|
|
|
|
ESP_LOGI(TAG, "Starting Modbus RTU device");
|
|
|
|
/* -------------------------------------------------------------
|
|
RS485 DE PIN TEST
|
|
Toggle the configured DE pin so we can verify the shield
|
|
actually responds to the GPIO controlling transmit enable.
|
|
Watch the TX LED on the RS485 shield.
|
|
------------------------------------------------------------- */
|
|
|
|
ESP_LOGI(TAG, "Testing RS485 DE pin control");
|
|
|
|
gpio_reset_pin(GPIO_NUM_4);
|
|
gpio_set_direction(GPIO_NUM_4, GPIO_MODE_OUTPUT);
|
|
|
|
for (int i = 0; i < 10; i++)
|
|
{
|
|
ESP_LOGI(TAG, "DE LOW");
|
|
gpio_set_level(GPIO_NUM_4, 0);
|
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
|
|
|
ESP_LOGI(TAG, "DE HIGH");
|
|
gpio_set_level(GPIO_NUM_4, 1);
|
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
|
}
|
|
|
|
ESP_LOGI(TAG, "DE pin test finished");
|
|
|
|
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,
|
|
"RTU config: uart=%u baud=%u parity=%d stop_bits=%u tx=%d rx=%d de=%d",
|
|
(unsigned)g_device_cfg.rtu.uart_num,
|
|
(unsigned)g_device_cfg.rtu.baud_rate,
|
|
(int)g_device_cfg.rtu.parity,
|
|
(unsigned)g_device_cfg.rtu.stop_bits,
|
|
g_device_cfg.rtu.tx_pin,
|
|
g_device_cfg.rtu.rx_pin,
|
|
g_device_cfg.rtu.de_pin);
|
|
|
|
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;
|
|
}
|
|
|
|
if (g_devices != NULL)
|
|
{
|
|
free(g_devices);
|
|
g_devices = NULL;
|
|
}
|
|
|
|
g_devices = calloc(g_device_cfg.modbus.device_count, sizeof(modbus_device_t));
|
|
if (g_devices == NULL)
|
|
{
|
|
ESP_LOGE(TAG, "Failed to allocate device table for %u devices",
|
|
(unsigned)g_device_cfg.modbus.device_count);
|
|
return;
|
|
}
|
|
|
|
g_device_count = 0U;
|
|
|
|
modbus_rtu_init(&g_rtu_ctx,
|
|
(uart_port_t)g_device_cfg.rtu.uart_num,
|
|
g_device_cfg.rtu.baud_rate);
|
|
|
|
ESP_ERROR_CHECK(modbus_uart_init(&g_device_cfg));
|
|
|
|
ESP_LOGI(TAG,
|
|
"UART driver installed: uart=%d tx=%d rx=%d de=%d baud=%d",
|
|
g_device_cfg.rtu.uart_num,
|
|
g_device_cfg.rtu.tx_pin,
|
|
g_device_cfg.rtu.rx_pin,
|
|
g_device_cfg.rtu.de_pin,
|
|
(int)g_device_cfg.rtu.baud_rate);
|
|
|
|
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];
|
|
memset(dev, 0, sizeof(*dev));
|
|
|
|
dev->unit_id = cfg_dev->unit_id;
|
|
|
|
strncpy(dev->name, cfg_dev->name, sizeof(dev->name) - 1U);
|
|
dev->name[sizeof(dev->name) - 1U] = '\0';
|
|
|
|
if (strncmp(cfg_dev->csv, "/spiffs/", 8U) == 0)
|
|
{
|
|
strncpy(dev->csv_path, cfg_dev->csv, sizeof(dev->csv_path) - 1U);
|
|
dev->csv_path[sizeof(dev->csv_path) - 1U] = '\0';
|
|
}
|
|
else
|
|
{
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
abort();
|
|
}
|
|
|
|
if (!modbus_memory_init(&dev->mem, &dev->db))
|
|
{
|
|
ESP_LOGE(TAG, "Memory init failed for device %u",
|
|
(unsigned)dev->unit_id);
|
|
continue;
|
|
}
|
|
|
|
if (!modbus_rtu_add_device(&g_rtu_ctx,
|
|
dev->unit_id,
|
|
&dev->mem,
|
|
dev->name))
|
|
{
|
|
ESP_LOGE(TAG, "Failed to register RTU device unit_id=%u name=%s",
|
|
(unsigned)dev->unit_id,
|
|
dev->name);
|
|
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;
|
|
size_t d;
|
|
|
|
for (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 RTU");
|
|
return;
|
|
}
|
|
|
|
{
|
|
BaseType_t rc;
|
|
|
|
ESP_LOGI(TAG, "Creating modbus_rtu_task");
|
|
rc = xTaskCreate(modbus_rtu_task,
|
|
"modbus_rtu_task",
|
|
8192,
|
|
NULL,
|
|
5,
|
|
NULL);
|
|
ESP_LOGI(TAG, "modbus_rtu_task create rc=%ld", (long)rc);
|
|
|
|
ESP_LOGI(TAG, "Creating modbus_override_task");
|
|
rc = xTaskCreate(modbus_override_task,
|
|
"modbus_override_task",
|
|
8192,
|
|
NULL,
|
|
5,
|
|
NULL);
|
|
ESP_LOGI(TAG, "modbus_override_task create rc=%ld", (long)rc);
|
|
}
|
|
} |