How to flush TX buffer before closing Wi-Fi connection?

esp32enjoyer32
Posts: 1
Joined: Tue Mar 03, 2026 6:15 pm

How to flush TX buffer before closing Wi-Fi connection?

Postby esp32enjoyer32 » Tue Mar 03, 2026 8:50 pm

Hello,

I'm trying to create an application that connects to Wi-Fi, sends a beacon and then immediately goes to deep sleep.

Looking at Wireshark on the target machine, I do not receive the last message sent from ESP32 unless I add a delay before disconnecting from the Wi-Fi. Even despite attempts to properly deinitialize everything, the last message isn't transmitted.

For TCP, I was able to kind-of mitigate the problem with SO_LINGER (it still doesn't work properly since ESP shuts down Wi-Fi before acknowledging FIN, so the remote host wastes bandwidth for retransmissions, but at least the entire buffer is received by the remote host and there's no difference from the usermode). It doesn't seem to work for UDP, though. Apparently, close() for UDP socket with SO_LINGER returns even if the data hasn't been sent, and ESP32 just shuts down Wi-Fi immediately after calling esp_wifi_disconnect() regardless of data remaining to be transmitted in the network stack.

Example code that demonstrates the problem. You need to enable SO_LINGER in menuconfig for it to run properly. My chip is ESP32-C3 on a Super Mini board.

Code: Select all

#include <esp_wifi.h>
#include <nvs_flash.h>
#include <stdio.h>

#include <lwip/sockets.h>
#include <freertos/FreeRTOS.h>

#define WIFI_SSID "my wifi ssid"
#define WIFI_PASS "my wifi password"

#define TARGET_IP "10.12.34.56"
#define TARGET_PORT 1234

static EventGroupHandle_t s_wifi_event_group;

static esp_netif_t *netif = NULL;

static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
	printf("event %s, %ld\n", event_base, event_id);

	if (event_base == WIFI_EVENT) {
		if (event_id == WIFI_EVENT_STA_START) {
			esp_wifi_connect();
		}
	} else if (event_base == IP_EVENT) {
		if (event_id == IP_EVENT_STA_GOT_IP) {
			xEventGroupSetBits(s_wifi_event_group, BIT0);
		}
	}
}


static void wifi_init_sta(void)
{
	esp_event_handler_instance_t instance_any_id;
	esp_event_handler_instance_t instance_got_ip;

	wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();

	s_wifi_event_group = xEventGroupCreate();

	ESP_ERROR_CHECK(esp_netif_init());

	ESP_ERROR_CHECK(esp_event_loop_create_default());

	netif = esp_netif_create_default_wifi_sta();

	ESP_ERROR_CHECK(NULL == netif);

	ESP_ERROR_CHECK(esp_wifi_init(&cfg));

	ESP_ERROR_CHECK(
		esp_event_handler_instance_register(
			WIFI_EVENT,
			ESP_EVENT_ANY_ID,
			&event_handler,
			NULL,
			&instance_any_id
		)
	);

	ESP_ERROR_CHECK(
		esp_event_handler_instance_register(
			IP_EVENT,
			IP_EVENT_STA_GOT_IP,
			&event_handler,
			NULL,
			&instance_got_ip
		)
	);

	wifi_config_t wifi_config = {
		.sta = {
			.ssid = WIFI_SSID,
			.password = WIFI_PASS,
			.threshold.authmode = WIFI_AUTH_WPA2_PSK,
		},
	};

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

	xEventGroupWaitBits(
		s_wifi_event_group,
		BIT0,
		pdFALSE,
		pdFALSE,
		portMAX_DELAY
	);
}

void app_main(void)
{
	ESP_ERROR_CHECK(nvs_flash_init());

	wifi_init_sta();

	struct sockaddr_in dest_addr;

	inet_pton(AF_INET, TARGET_IP, &dest_addr.sin_addr);

	dest_addr.sin_family = AF_INET;
	dest_addr.sin_port = htons(TARGET_PORT);

	int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);

	printf("socket = %d\n", sock);

	struct linger ling =  {
		.l_onoff = 1,
		.l_linger = 30
	};

	char *message = "Hello World";

	// Need to enable SO_LINGER in menuconfig
	printf("setsockopt = %d\n", setsockopt(sock, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling)));

	printf("sendto = %d\n", sendto(sock, message, strlen(message), 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)));

	printf("close = %d\n", close(sock));
	
	// Works if you uncomment the delay.
	// vTaskDelay(pdMS_TO_TICKS(10000));

	printf("esp_wifi_disconnect = %d\n", esp_wifi_disconnect());

	printf("esp_wifi_stop = %d\n", esp_wifi_stop());

	printf("esp_wifi_deinit = %d\n", esp_wifi_deinit());

	esp_netif_destroy(netif);
}

Debug output:

Code: Select all

event IP_EVENT, 0
socket = 54
setsockopt = 0
sendto = 11
close = 0
I (1516) wifi:state: run -> init (0x0)
I (1526) wifi:pm stop, total sleep time: 806871 us / 1039138 us

I (1526) wifi:<ba-del>idx:0, tid:0
I (1526) wifi:new:<1,0>, old:<1,0>, ap:<255,255>, sta:<1,0>, prof:1, snd_ch_cfg:0x0
event WIFI_EVENT, 5
esp_wifi_disconnect = 0
event WIFI_EVENT, 3
I (1526) wifi:flush txq
I (1526) wifi:stop sw txq
I (1536) wifi:lmac stop hw txq
esp_wifi_stop = 0
I (1536) wifi:Deinit lldesc rx mblock:10
esp_wifi_deinit = 0
I (1546) main_task: Returned from app_main()

nopnop2002
Posts: 347
Joined: Thu Oct 03, 2019 10:52 pm
Contact:

Re: How to flush TX buffer before closing Wi-Fi connection?

Postby nopnop2002 » Fri Mar 06, 2026 1:10 am

What happens if you use the TCP_NODELAY option?
int option = 1;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &option, sizeof(option));

Who is online

Users browsing this forum: akashgaur0001, Bing [Bot], ChatGPT-User, Google [Bot], Qwantbot, Semrush [Bot] and 3 guests