Page 1 of 1

Slow WebSocket Data Transfer on ESP32-S3

Posted: Thu Nov 21, 2024 1:19 pm
by adarsh-k
Hi everyone,

I'm working on a project with an ESP32-S3 that connects to my WiFi and transfers data to a cloud server via WebSocket. However, I'm experiencing extremely slow transfer speeds. It takes about 300ms to 600ms to send just a 1KBytes packet.

Interestingly, if I set up an Nginx proxy on my local machine, which creates a port for the ESP32 to connect to and forwards all packets to the cloud, the transfer speed improves significantly to around 50ms to 100ms for the same 1KB packet.

Additionally, when I run the server locally on my machine, I get even better speeds—between 10ms and 50ms for 1KB packets.

What could be the issue :?:
Any suggestions or optimizations are welcome!

  • IDF version: v5.3.1
  • esp websocket client version: 1.3.0
Code I’m using on the ESP32:

Code: Untitled.c Select all


#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "nvs_flash.h"
#include "esp_websocket_client.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_timer.h"
#include <inttypes.h>

static const char *TAG = "WebSocketApp";

#define WIFI_SSID "SSID"
#define WIFI_PASSWORD "PASSWORD"
// #define WEBSOCKET_URI "ws://CLOUD_URL:PORT" // cloud server
#define WEBSOCKET_URI "ws://LOCAL_PROXY_IP:PORT" // Nginx proxy

/* FreeRTOS synchronization primitives */
static EventGroupHandle_t wifi_event_group;
const int WIFI_CONNECTED_BIT = BIT0;

/* WebSocket client handle */
static esp_websocket_client_handle_t client = NULL;

#define DATA_PACKET_SIZE 1024 // Size of the data packet to send in bytes
#define SEND_INTERVAL_MS 0001 // Interval between packets in milliseconds

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) {
esp_wifi_connect();
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_BIT);
}
}

static void wifi_init(void)
{
wifi_event_group = xEventGroupCreate();

ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();

wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));

ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
&wifi_event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP,
&wifi_event_handler, NULL));

wifi_config_t wifi_config = {
.sta = {
.ssid = WIFI_SSID,
.password = WIFI_PASSWORD,
},
};

ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
}

static void websocket_event_handler(void *handler_args, esp_event_base_t base,
int32_t event_id, void *event_data)
{
switch (event_id) {
case WEBSOCKET_EVENT_CONNECTED:
ESP_LOGI(TAG, "WebSocket connected");
break;
case WEBSOCKET_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "WebSocket disconnected");
break;
case WEBSOCKET_EVENT_DATA:
// ESP_LOGI(TAG, "Received WebSocket data");
break;
default:
ESP_LOGW(TAG, "Unhandled WebSocket event ID: %" PRIi32, event_id);
break;
}
}

static void websocket_init(void)
{
esp_websocket_client_config_t websocket_cfg = {
.uri = WEBSOCKET_URI,
};

client = esp_websocket_client_init(&websocket_cfg);
ESP_ERROR_CHECK(esp_websocket_register_events(client, WEBSOCKET_EVENT_ANY,
websocket_event_handler, NULL));
ESP_ERROR_CHECK(esp_websocket_client_start(client));
}

static void websocket_send_task(void *arg)
{
uint8_t *data = malloc(DATA_PACKET_SIZE);
if (!data) {
ESP_LOGE(TAG, "Failed to allocate memory for data packet");
vTaskDelete(NULL);
}

// Fill the data packet with `0xff`
memset(data, 0xff, DATA_PACKET_SIZE);

while (true) {
if (esp_websocket_client_is_connected(client)) {
unsigned long start_t = esp_timer_get_time();
int err = esp_websocket_client_send_bin(client, (const char *)data, DATA_PACKET_SIZE, portMAX_DELAY);
unsigned long delta_t = esp_timer_get_time() - start_t;

if (err != -1) {
ESP_LOGI(TAG, "Sent data of size %d bytes in %.3f ms", DATA_PACKET_SIZE, (delta_t / 1000.0));
} else {
ESP_LOGE(TAG, "Failed to send data");
}
} else {
ESP_LOGW(TAG, "WebSocket not connected");
}

vTaskDelay(pdMS_TO_TICKS(SEND_INTERVAL_MS));
}

free(data);
}

void app_main(void)
{
// Initialize NVS
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);

// Initialize WiFi and wait for connection
wifi_init();
xEventGroupWaitBits(wifi_event_group, WIFI_CONNECTED_BIT, false, true, portMAX_DELAY);
ESP_LOGI(TAG, "Connected to WiFi");

// Initialize WebSocket
websocket_init();

// Start WebSocket send task
BaseType_t ret_val = xTaskCreate(websocket_send_task, "WebSocket Send Task", 4096, NULL, 5, NULL);
assert(ret_val == pdPASS);
}

Python code for server:

Code: Untitled.py Select all


import asyncio
import websockets
import json

async def handle_connection(websocket, path):
try:
print(f"[DEBUG] Client connected from {websocket.remote_address}")

while True:
try:
# Wait for a message from the client
message = await websocket.recv()

# Handle binary data
if isinstance(message, bytes):
print(f"Received binary packet of size: {len(message)} bytes")

# Handle JSON messages
else:
try:
packet = json.loads(message)
print(f"Received JSON packet of size: {len(message)} bytes")
except json.JSONDecodeError:
print(f"Received invalid JSON message of size: {len(message)} bytes")

except websockets.exceptions.ConnectionClosed:
print("[DEBUG] Client disconnected")
break

except Exception as e:
print(f"[ERROR] Error handling connection: {str(e)}")

async def main():
try:
server = await websockets.serve(
handle_connection,
"0.0.0.0",
8765
)
print("[DEBUG] WebSocket server started on ws://0.0.0.0:8765")
await server.wait_closed()
except Exception as e:
print(f"[ERROR] Failed to start server: {str(e)}")

if __name__ == "__main__":
asyncio.run(main())

Re: Slow WebSocket Data Transfer on ESP32-S3

Posted: Mon Nov 25, 2024 7:56 am
by adarsh-k
I tested the iperf example from ESP [GitHub repository](https://github.com/espressif/esp-idf/tr ... wifi/iperf) to measure the speeds, and it showed around 1.5-1.8 Mbps for TCP. While comparing my `sdkconfig` file with the one from this project, I found a few differences. I added the following changes to my `sdkconfig.default`, which resolved the issue.

Code: Untitled.txt Select all


CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=16
CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=64
CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER=y
CONFIG_ESP_WIFI_TX_BUFFER_TYPE=1
CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER_NUM=64
CONFIG_ESP_WIFI_TX_BA_WIN=32
CONFIG_ESP_WIFI_RX_BA_WIN=32

CONFIG_LWIP_ND6=y
CONFIG_LWIP_ESP_MLDV6_REPORT=y
CONFIG_LWIP_MLDV6_TMR_INTERVAL=40
CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=64

CONFIG_LWIP_TCP_TMR_INTERVAL=250
CONFIG_LWIP_TCP_SND_BUF_DEFAULT=65535
CONFIG_LWIP_TCP_WND_DEFAULT=65535
CONFIG_LWIP_TCP_RECVMBOX_SIZE=64
CONFIG_LWIP_TCP_OOSEQ_MAX_PBUFS=4

CONFIG_LWIP_UDP_RECVMBOX_SIZE=64

CONFIG_MBEDTLS_DYNAMIC_BUFFER=n

CONFIG_IPERF_SOCKET_RX_TIMEOUT=10
CONFIG_IPERF_SOCKET_TCP_TX_TIMEOUT=10
CONFIG_IPERF_TRAFFIC_TASK_PRIORITY=23
CONFIG_IPERF_REPORT_TASK_PRIORITY=24
CONFIG_IPERF_DEF_TCP_TX_BUFFER_LEN=16384
CONFIG_IPERF_DEF_TCP_RX_BUFFER_LEN=16384
CONFIG_IPERF_DEF_IPV4_UDP_TX_BUFFER_LEN=1470
CONFIG_IPERF_DEF_IPV6_UDP_TX_BUFFER_LEN=1450
CONFIG_IPERF_DEF_UDP_RX_BUFFER_LEN=16384

CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=16
CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=64

CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER=y
CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=1
CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM=64

CONFIG_ESP32_WIFI_TX_BA_WIN=32
CONFIG_ESP32_WIFI_RX_BA_WIN=32
CONFIG_ESP32_WIFI_RX_BA_WIN=32

CONFIG_TCPIP_RECVMBOX_SIZE=64

CONFIG_TCP_SND_BUF_DEFAULT=65535
CONFIG_TCP_WND_DEFAULT=65535
CONFIG_TCP_RECVMBOX_SIZE=64
CONFIG_UDP_RECVMBOX_SIZE=64