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:
  1. #include <string.h>
  2. #include "freertos/FreeRTOS.h"
  3. #include "freertos/task.h"
  4. #include "freertos/event_groups.h"
  5. #include "esp_system.h"
  6. #include "esp_wifi.h"
  7. #include "nvs_flash.h"
  8. #include "esp_websocket_client.h"
  9. #include "esp_event.h"
  10. #include "esp_log.h"
  11. #include "esp_timer.h"
  12. #include <inttypes.h>
  13.  
  14. static const char *TAG = "WebSocketApp";
  15.  
  16. #define WIFI_SSID      "SSID"
  17. #define WIFI_PASSWORD  "PASSWORD"
  18. // #define WEBSOCKET_URI  "ws://CLOUD_URL:PORT" // cloud server
  19. #define WEBSOCKET_URI  "ws://LOCAL_PROXY_IP:PORT" // Nginx proxy
  20.  
  21. /* FreeRTOS synchronization primitives */
  22. static EventGroupHandle_t wifi_event_group;
  23. const int WIFI_CONNECTED_BIT = BIT0;
  24.  
  25. /* WebSocket client handle */
  26. static esp_websocket_client_handle_t client = NULL;
  27.  
  28. #define DATA_PACKET_SIZE 1024   // Size of the data packet to send in bytes
  29. #define SEND_INTERVAL_MS 0001   // Interval between packets in milliseconds
  30.  
  31. static void wifi_event_handler(void* arg, esp_event_base_t event_base,
  32.                                int32_t event_id, void* event_data)
  33. {
  34.     if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
  35.         esp_wifi_connect();
  36.     } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
  37.         esp_wifi_connect();
  38.     } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
  39.         xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_BIT);
  40.     }
  41. }
  42.  
  43. static void wifi_init(void)
  44. {
  45.     wifi_event_group = xEventGroupCreate();
  46.  
  47.     ESP_ERROR_CHECK(esp_netif_init());
  48.     ESP_ERROR_CHECK(esp_event_loop_create_default());
  49.     esp_netif_create_default_wifi_sta();
  50.  
  51.     wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
  52.     ESP_ERROR_CHECK(esp_wifi_init(&cfg));
  53.  
  54.     ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
  55.                                                &wifi_event_handler, NULL));
  56.     ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP,
  57.                                                &wifi_event_handler, NULL));
  58.  
  59.     wifi_config_t wifi_config = {
  60.         .sta = {
  61.             .ssid = WIFI_SSID,
  62.             .password = WIFI_PASSWORD,
  63.         },
  64.     };
  65.  
  66.     ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
  67.     ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
  68.     ESP_ERROR_CHECK(esp_wifi_start());
  69. }
  70.  
  71. static void websocket_event_handler(void *handler_args, esp_event_base_t base,
  72.                                     int32_t event_id, void *event_data)
  73. {
  74.     switch (event_id) {
  75.         case WEBSOCKET_EVENT_CONNECTED:
  76.             ESP_LOGI(TAG, "WebSocket connected");
  77.             break;
  78.         case WEBSOCKET_EVENT_DISCONNECTED:
  79.             ESP_LOGI(TAG, "WebSocket disconnected");
  80.             break;
  81.         case WEBSOCKET_EVENT_DATA:
  82.             // ESP_LOGI(TAG, "Received WebSocket data");
  83.             break;
  84.         default:
  85.             ESP_LOGW(TAG, "Unhandled WebSocket event ID: %" PRIi32, event_id);
  86.             break;
  87.     }
  88. }
  89.  
  90. static void websocket_init(void)
  91. {
  92.     esp_websocket_client_config_t websocket_cfg = {
  93.         .uri = WEBSOCKET_URI,
  94.     };
  95.  
  96.     client = esp_websocket_client_init(&websocket_cfg);
  97.     ESP_ERROR_CHECK(esp_websocket_register_events(client, WEBSOCKET_EVENT_ANY,
  98.                                                   websocket_event_handler, NULL));
  99.     ESP_ERROR_CHECK(esp_websocket_client_start(client));
  100. }
  101.  
  102. static void websocket_send_task(void *arg)
  103. {
  104.     uint8_t *data = malloc(DATA_PACKET_SIZE);
  105.     if (!data) {
  106.         ESP_LOGE(TAG, "Failed to allocate memory for data packet");
  107.         vTaskDelete(NULL);
  108.     }
  109.  
  110.     // Fill the data packet with `0xff`
  111.     memset(data, 0xff, DATA_PACKET_SIZE);
  112.  
  113.     while (true) {
  114.         if (esp_websocket_client_is_connected(client)) {
  115.             unsigned long start_t = esp_timer_get_time();
  116.             int err = esp_websocket_client_send_bin(client, (const char *)data, DATA_PACKET_SIZE, portMAX_DELAY);
  117.             unsigned long delta_t = esp_timer_get_time() - start_t;
  118.  
  119.             if (err != -1) {
  120.                 ESP_LOGI(TAG, "Sent data of size %d bytes in %.3f ms", DATA_PACKET_SIZE, (delta_t / 1000.0));
  121.             } else {
  122.                 ESP_LOGE(TAG, "Failed to send data");
  123.             }
  124.         } else {
  125.             ESP_LOGW(TAG, "WebSocket not connected");
  126.         }
  127.  
  128.         vTaskDelay(pdMS_TO_TICKS(SEND_INTERVAL_MS));
  129.     }
  130.  
  131.     free(data);
  132. }
  133.  
  134. void app_main(void)
  135. {
  136.     // Initialize NVS
  137.     esp_err_t ret = nvs_flash_init();
  138.     if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
  139.         ESP_ERROR_CHECK(nvs_flash_erase());
  140.         ret = nvs_flash_init();
  141.     }
  142.     ESP_ERROR_CHECK(ret);
  143.  
  144.     // Initialize WiFi and wait for connection
  145.     wifi_init();
  146.     xEventGroupWaitBits(wifi_event_group, WIFI_CONNECTED_BIT, false, true, portMAX_DELAY);
  147.     ESP_LOGI(TAG, "Connected to WiFi");
  148.  
  149.     // Initialize WebSocket
  150.     websocket_init();
  151.  
  152.     // Start WebSocket send task
  153.     BaseType_t ret_val = xTaskCreate(websocket_send_task, "WebSocket Send Task", 4096, NULL, 5, NULL);
  154.     assert(ret_val == pdPASS);
  155. }

Python code for server:
  1. import asyncio
  2. import websockets
  3. import json
  4.  
  5. async def handle_connection(websocket, path):
  6.     try:
  7.         print(f"[DEBUG] Client connected from {websocket.remote_address}")
  8.  
  9.         while True:
  10.             try:
  11.                 # Wait for a message from the client
  12.                 message = await websocket.recv()
  13.                
  14.                 # Handle binary data
  15.                 if isinstance(message, bytes):
  16.                     print(f"Received binary packet of size: {len(message)} bytes")
  17.  
  18.                 # Handle JSON messages
  19.                 else:
  20.                     try:
  21.                         packet = json.loads(message)
  22.                         print(f"Received JSON packet of size: {len(message)} bytes")
  23.                     except json.JSONDecodeError:
  24.                         print(f"Received invalid JSON message of size: {len(message)} bytes")
  25.  
  26.             except websockets.exceptions.ConnectionClosed:
  27.                 print("[DEBUG] Client disconnected")
  28.                 break
  29.  
  30.     except Exception as e:
  31.         print(f"[ERROR] Error handling connection: {str(e)}")
  32.  
  33. async def main():
  34.     try:
  35.         server = await websockets.serve(
  36.             handle_connection,
  37.             "0.0.0.0",
  38.             8765
  39.         )
  40.         print("[DEBUG] WebSocket server started on ws://0.0.0.0:8765")
  41.         await server.wait_closed()
  42.     except Exception as e:
  43.         print(f"[ERROR] Failed to start server: {str(e)}")
  44.  
  45. if __name__ == "__main__":
  46.     asyncio.run(main())