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);
    }
}