-
Notifications
You must be signed in to change notification settings - Fork 17
Description
Checklist
- Checked the issue tracker for similar issues to ensure this is not a duplicate.
- Provided a clear description of your suggestion.
- Included any relevant context or examples.
Issue or Suggestion Description
Hello,
I'm using the ESP32-P4-Function-EV-Board to download a file over WiFi to my microSD card (Over SDIO). Currently, I'm getting an average of 0.68MB/s on the download speed, which seems a bit slow? I've already implemented the changes listed in the documentation regarding performance optimization, but I can't seem to get the speed above this limit. I've already double checked the WiFi speed and the server, and ensured that they are capable of a much higher throughput. I've also ensured that my SD card can support the speed, and it's class 10 which means it should be fine? My current thought process is that there may be something wrong with my code or maybe with the settings on the slave ESP32C6? I'm using the default settings on the slave, all I've done is update to the latest version (2.5.5).
Is 0.68MB/s in line with what I should expect for a file of ~100MB? Or can I get higher? I was expecting around 1-1.5MB/s.
I've put my code below, any thoughts or advice would be greatly appreciated.
`/*
- SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
- SPDX-License-Identifier: Apache-2.0
*/
#include "esp_crt_bundle.h"
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include <errno.h>
#include "nvs_flash.h"
#include "nvs.h"
#include "esp_log.h"
#include "esp_err.h"
#include "esp_check.h"
#include "esp_memory_utils.h"
#include "esp_http_client.h"
#include "esp_vfs_fat.h"
#include "sdmmc_cmd.h"
#include "esp_wifi_remote.h"
#include "esp_event.h"
#include "esp_tls.h"
#include "driver/sdmmc_host.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "lvgl.h"
#include "bsp/esp-bsp.h"
#include "bsp/display.h"
#include "bsp_board_extra.h"
#include "lv_demos.h"
#include "esp_sntp.h"
// Note: BSP_SD_MOUNT_POINT should be defined in your BSP header
// If not defined, it defaults to "/sdcard"
#ifndef BSP_SD_MOUNT_POINT
#define BSP_SD_MOUNT_POINT "/sdcard"
#endif
#define MOUNT_POINT BSP_SD_MOUNT_POINT
#define DOWNLOAD_BUFFER_SIZE (1024 * 1024) // Increased to 1MB
#define FILE_WRITE_BUFFER_SIZE (2 * 1024 * 1024) // 2MB write buffer in PSRAM
// WiFi Configuration - UPDATE THESE WITH YOUR CREDENTIALS
#define WIFI_SSID "WiFi_Network"
#define WIFI_PASS "Password"
#define WIFI_MAXIMUM_RETRY 5
// FreeRTOS event group to signal when we are connected
static EventGroupHandle_t s_wifi_event_group;
// The event group allows multiple bits for each event, but we only care about two events:
// - we are connected to the AP with an IP
// - we failed to connect after the maximum amount of retries
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
static int s_retry_num = 0;
static const char *TAG = "MAIN";
typedef struct {
int file;
size_t total_bytes;
char *write_buffer; // Buffer in PSRAM for batching writes
size_t buffer_pos; // Current position in write buffer
size_t buffer_size; // Size of write buffer
} download_context_t;
extern const char isrg_root_x1_pem_start[] asm("_binary_isrg_root_x1_pem_start");
extern const char isrg_root_x1_pem_end[] asm("_binary_isrg_root_x1_pem_end");
TickType_t start_ticks;
TickType_t end_ticks;
TickType_t elapsed_ticks;
// WiFi event handler
static void wifi_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
if (s_retry_num < WIFI_MAXIMUM_RETRY) {
esp_wifi_connect();
s_retry_num++;
ESP_LOGI(TAG, "Retry to connect to the AP (attempt %d/%d)",
s_retry_num, WIFI_MAXIMUM_RETRY);
} else {
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
}
ESP_LOGI(TAG,"Connect to the AP failed");
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(TAG, "Got IP:" IPSTR, IP2STR(&event->ip_info.ip));
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
}
}
// Initialize and sync time via SNTP
void initialize_sntp(void)
{
ESP_LOGI(TAG, "Initializing SNTP");
esp_sntp_setoperatingmode(SNTP_OPMODE_POLL);
esp_sntp_setservername(0, "pool.ntp.org");
esp_sntp_init();
// Wait for time to be set
time_t now = 0;
struct tm timeinfo = { 0 };
int retry = 0;
const int retry_count = 10;
while (timeinfo.tm_year < (2024 - 1900) && ++retry < retry_count) {
ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)", retry, retry_count);
vTaskDelay(pdMS_TO_TICKS(2000));
time(&now);
localtime_r(&now, &timeinfo);
}
if (timeinfo.tm_year < (2024 - 1900)) {
ESP_LOGW(TAG, "Failed to get time from NTP server, using default time");
} else {
char strftime_buf[64];
strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
ESP_LOGI(TAG, "Current time: %s", strftime_buf);
}
}
// Initialize WiFi in station mode
esp_err_t wifi_init_sta(void)
{
s_wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
// Create netif with custom config for better performance
esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
// Increase TCP window size for better throughput
esp_netif_dhcp_status_t status;
if (esp_netif_dhcpc_get_status(sta_netif, &status) == ESP_OK) {
// Set DNS server
esp_netif_dns_info_t dns;
dns.ip.u_addr.ip4.addr = ipaddr_addr("8.8.8.8");
dns.ip.type = IPADDR_TYPE_V4;
esp_netif_set_dns_info(sta_netif, ESP_NETIF_DNS_MAIN, &dns);
}
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
// Enable 802.11n in the init config
cfg.ampdu_rx_enable = 1;
cfg.ampdu_tx_enable = 1;
cfg.amsdu_tx_enable = 1;
cfg.nvs_enable = 1;
cfg.nano_enable = 0;
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
esp_event_handler_instance_t instance_any_id;
esp_event_handler_instance_t instance_got_ip;
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&wifi_event_handler,
NULL,
&instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP,
&wifi_event_handler,
NULL,
&instance_got_ip));
wifi_config_t wifi_config = {
.sta = {
.ssid = WIFI_SSID,
.password = WIFI_PASS,
.threshold.authmode = WIFI_AUTH_WPA2_PSK,
.sae_pwe_h2e = WPA3_SAE_PWE_BOTH,
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
// CRITICAL: Force 802.11n BEFORE setting config
esp_err_t ret_protocol = esp_wifi_set_protocol(WIFI_IF_STA, WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N);
ESP_LOGI(TAG, "Set WiFi protocol result: %s", esp_err_to_name(ret_protocol));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
// Try setting bandwidth before start
esp_err_t ret_bw = esp_wifi_set_bandwidth(WIFI_IF_STA, WIFI_BW_HT40);
ESP_LOGI(TAG, "Set WiFi bandwidth result: %s", esp_err_to_name(ret_bw));
ESP_ERROR_CHECK(esp_wifi_start());
ESP_LOGI(TAG, "wifi_init_sta finished.");
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY);
if (bits & WIFI_CONNECTED_BIT) {
ESP_LOGI(TAG, "Connected to AP SSID:%s", WIFI_SSID);
// Check WiFi performance metrics
wifi_ap_record_t ap_info;
if (esp_wifi_sta_get_ap_info(&ap_info) == ESP_OK) {
ESP_LOGI(TAG, "WiFi RSSI: %d dBm", ap_info.rssi);
ESP_LOGI(TAG, "WiFi Channel: %d", ap_info.primary);
ESP_LOGI(TAG, "WiFi 11b: %d, 11g: %d, 11n: %d",
ap_info.phy_11b, ap_info.phy_11g, ap_info.phy_11n);
ESP_LOGI(TAG, "WiFi 2nd channel: %d", ap_info.second);
}
// Check what protocol we're actually using
uint8_t protocol;
esp_wifi_get_protocol(WIFI_IF_STA, &protocol);
ESP_LOGI(TAG, "Active protocols - 11b:%d 11g:%d 11n:%d",
(protocol & WIFI_PROTOCOL_11B) ? 1 : 0,
(protocol & WIFI_PROTOCOL_11G) ? 1 : 0,
(protocol & WIFI_PROTOCOL_11N) ? 1 : 0);
wifi_bandwidth_t bw;
esp_wifi_get_bandwidth(WIFI_IF_STA, &bw);
ESP_LOGI(TAG, "Bandwidth: %s", bw == WIFI_BW_HT20 ? "20MHz" : "40MHz");
return ESP_OK;
} else if (bits & WIFI_FAIL_BIT) {
ESP_LOGI(TAG, "Failed to connect to SSID:%s", WIFI_SSID);
return ESP_FAIL;
} else {
ESP_LOGE(TAG, "UNEXPECTED EVENT");
return ESP_FAIL;
}
}
// HTTP event handler
esp_err_t http_event_handler(esp_http_client_event_t *evt)
{
download_context_t *ctx = (download_context_t *)evt->user_data;
switch(evt->event_id) {
case HTTP_EVENT_ERROR:
ESP_LOGE(TAG, "HTTP_EVENT_ERROR");
break;
case HTTP_EVENT_ON_CONNECTED:
ESP_LOGI(TAG, "HTTP_EVENT_ON_CONNECTED");
break;
case HTTP_EVENT_HEADER_SENT:
ESP_LOGI(TAG, "HTTP_EVENT_HEADER_SENT");
break;
case HTTP_EVENT_ON_HEADER:
// Reduce logging verbosity during download
if (strcmp(evt->header_key, "Content-Length") == 0) {
ESP_LOGI(TAG, "Content-Length: %s", evt->header_value);
}
break;
case HTTP_EVENT_ON_DATA:
// Write data to buffer first, then flush to file when buffer is full
if (!esp_http_client_is_chunked_response(evt->client)) {
if (ctx->write_buffer && ctx->file) {
size_t bytes_to_process = evt->data_len;
size_t offset = 0;
while (bytes_to_process > 0) {
size_t space_left = ctx->buffer_size - ctx->buffer_pos;
size_t to_copy = bytes_to_process < space_left ? bytes_to_process : space_left;
// Copy to PSRAM buffer
memcpy(ctx->write_buffer + ctx->buffer_pos, (char*)evt->data + offset, to_copy);
ctx->buffer_pos += to_copy;
ctx->total_bytes += to_copy;
offset += to_copy;
bytes_to_process -= to_copy;
// Flush buffer to file when full
if (ctx->buffer_pos >= ctx->buffer_size) {
size_t written = write(ctx->file, ctx->write_buffer, ctx->buffer_pos);
if (written != ctx->buffer_pos) {
ESP_LOGE(TAG, "Failed to write all buffered data to file");
}
ctx->buffer_pos = 0;
// Log progress every 2MB (less frequent)
if (ctx->total_bytes % (2 * 1024 * 1024) < FILE_WRITE_BUFFER_SIZE) {
end_ticks = xTaskGetTickCount();
elapsed_ticks = end_ticks - start_ticks;
uint32_t elapsed_ms = elapsed_ticks * portTICK_PERIOD_MS;
if (elapsed_ms > 0) {
float speed_mbps = (ctx->total_bytes / (1024.0 * 1024.0)) / (elapsed_ms / 1000.0);
ESP_LOGI(TAG, "Downloaded: %zu MB, %.2f MB/s",
ctx->total_bytes / (1024 * 1024), speed_mbps);
}
}
}
}
}
}
break;
case HTTP_EVENT_ON_FINISH:
ESP_LOGI(TAG, "HTTP_EVENT_ON_FINISH");
// Flush any remaining data in buffer
if (ctx->write_buffer && ctx->file && ctx->buffer_pos > 0) {
size_t written = write(ctx->file, ctx->write_buffer, ctx->buffer_pos);
if (written != ctx->buffer_pos) {
ESP_LOGE(TAG, "Failed to write remaining buffered data");
}
ctx->buffer_pos = 0;
}
// Final flush to disk
if (ctx->file) {
fsync(ctx->file);
}
break;
case HTTP_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED");
break;
case HTTP_EVENT_REDIRECT:
ESP_LOGI(TAG, "HTTP_EVENT_REDIRECT");
break;
}
return ESP_OK;
}
// Download file from URL to SD card (supports both HTTP and HTTPS)
esp_err_t download_file_to_sd(const char *url, const char *filename)
{
esp_err_t err = ESP_OK;
download_context_t ctx = {0};
char *file_buffer = NULL;
// Create full path
char filepath[256];
snprintf(filepath, sizeof(filepath), "%s/%s", MOUNT_POINT, filename);
ESP_LOGI(TAG, "Opening file: %s", filepath);
ctx.file = open(filepath, O_RDWR | O_CREAT | O_TRUNC, 0);
if (ctx.file == NULL) {
ESP_LOGE(TAG, "Failed to open file for writing");
return ESP_FAIL;
}
// Allocate large file buffer in PSRAM
file_buffer = heap_caps_malloc(DOWNLOAD_BUFFER_SIZE, MALLOC_CAP_SPIRAM);
if (file_buffer) {
// setvbuf(ctx.file, file_buffer, _IOFBF, DOWNLOAD_BUFFER_SIZE);
ESP_LOGI(TAG, "Allocated %d KB file buffer in PSRAM", DOWNLOAD_BUFFER_SIZE / 1024);
} else {
ESP_LOGW(TAG, "Failed to allocate file buffer in PSRAM, using default buffering");
}
// Allocate write buffer in PSRAM for batching writes
ctx.buffer_size = FILE_WRITE_BUFFER_SIZE;
ctx.write_buffer = heap_caps_malloc(ctx.buffer_size, MALLOC_CAP_SPIRAM);
ctx.buffer_pos = 0;
if (ctx.write_buffer == NULL) {
ESP_LOGE(TAG, "Failed to allocate write buffer in PSRAM");
close(ctx.file);
if (file_buffer) heap_caps_free(file_buffer);
return ESP_FAIL;
}
ESP_LOGI(TAG, "Allocated %d KB write buffer in PSRAM", ctx.buffer_size / 1024);
// Check if URL is HTTPS
bool is_https = (strncmp(url, "https://", 8) == 0);
// Configure HTTP client with optimized settings
esp_http_client_config_t config = {
.url = url,
.event_handler = http_event_handler,
.user_data = &ctx,
.timeout_ms = 30000,
.buffer_size = DOWNLOAD_BUFFER_SIZE,
.buffer_size_tx = 32768,
.keep_alive_enable = true,
.keep_alive_idle = 5,
.disable_auto_redirect = false,
.max_redirection_count = 10,
};
// Add certificate bundle for HTTPS
if (is_https) {
ESP_LOGI(TAG, "HTTPS connection detected, configuring TLS");
config.crt_bundle_attach = esp_crt_bundle_attach;
ESP_LOGI(TAG, "Using certificate bundle");
}
esp_http_client_handle_t client = esp_http_client_init(&config);
if (client == NULL) {
ESP_LOGE(TAG, "Failed to initialize HTTP client");
close(ctx.file);
if (file_buffer) heap_caps_free(file_buffer);
heap_caps_free(ctx.write_buffer);
return ESP_FAIL;
}
// Perform HTTP GET request
ESP_LOGI(TAG, "Starting download from: %s", url);
err = esp_http_client_perform(client);
if (err == ESP_OK) {
int status_code = esp_http_client_get_status_code(client);
int content_length = esp_http_client_get_content_length(client);
ESP_LOGI(TAG, "HTTP GET Status = %d, content_length = %d",
status_code, content_length);
ESP_LOGI(TAG, "Downloaded %zu bytes to %s", ctx.total_bytes, filepath);
if (status_code != 200 && status_code != 206) {
ESP_LOGE(TAG, "HTTP request failed with status %d", status_code);
err = ESP_FAIL;
}
} else {
ESP_LOGE(TAG, "HTTP GET request failed: %s", esp_err_to_name(err));
}
ESP_LOGI(TAG, "Syncing to SD card...");
fsync((ctx.file));
// Cleanup
close(ctx.file);
esp_http_client_cleanup(client);
// Free PSRAM buffers
if (file_buffer) {
heap_caps_free(file_buffer);
}
heap_caps_free(ctx.write_buffer);
ESP_LOGI(TAG, "File closed, total bytes written: %zu", ctx.total_bytes);
// Calculate final speed
end_ticks = xTaskGetTickCount();
elapsed_ticks = end_ticks - start_ticks;
uint32_t elapsed_ms = elapsed_ticks * portTICK_PERIOD_MS;
if (elapsed_ms > 0) {
float speed_mbps = (ctx.total_bytes / (1024.0 * 1024.0)) / (elapsed_ms / 1000.0);
ESP_LOGI(TAG, "Average download speed: %.2f MB/s", speed_mbps);
}
return err;
}
// Initialize SD card
esp_err_t init_sd_card(sdmmc_card_t **out_card)
{
esp_err_t ret;
ret = bsp_sdcard_mount();
if(ret==ESP_OK) {
ESP_LOGI(TAG, "SD card mounted successfully");
// Print detailed card info
ESP_LOGI(TAG, "Card speed: %d kHz", bsp_sdcard->max_freq_khz);
ESP_LOGI(TAG, "Card bus width: %d-bit",
(bsp_sdcard->log_bus_width == 0) ? 1 :
(bsp_sdcard->log_bus_width == 1) ? 4 : 8);
ESP_LOGI(TAG, "Card capacity: %llu MB",
((uint64_t)bsp_sdcard->csd.capacity) * bsp_sdcard->csd.sector_size / (1024 * 1024));
ESP_LOGI(TAG, "Card CSD version: %d", bsp_sdcard->csd.csd_ver);
}
// Card has been initialized, print its properties
sdmmc_card_print_info(stdout, bsp_sdcard);
if (out_card) {
*out_card = bsp_sdcard;
}
return ESP_OK;
}
void app_main(void)
{
esp_err_t 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);
vTaskDelay(pdMS_TO_TICKS(1000));
// Initialize WiFi and connect
ESP_LOGI(TAG, "Initializing WiFi...");
ret = wifi_init_sta();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to connect to WiFi. Aborting.");
return;
}
ESP_LOGI(TAG, "WiFi connected successfully!");
initialize_sntp();
vTaskDelay(pdMS_TO_TICKS(1000));
sdmmc_card_t *card;
ESP_ERROR_CHECK(init_sd_card(&card));
// Give SD card a moment to stabilize
vTaskDelay(pdMS_TO_TICKS(500));
// Example: Download a file
ESP_LOGI(TAG, "Starting file download...");
start_ticks = xTaskGetTickCount();
ret = download_file_to_sd("https://ash-speed.hetzner.com/100MB.bin", "100MB.bin");
if (ret == ESP_OK) {
ESP_LOGI(TAG, "HTTPS file downloaded successfully!");
} else {
ESP_LOGE(TAG, "File download failed!");
}
bsp_sdcard_unmount();
while(1)
{
vTaskDelay(pdMS_TO_TICKS(1000));
}
}`