2025-07-18 2:02 AM - edited 2025-07-18 4:30 AM
Hello.
I've been struggling for several weeks to fix an issue when sending data over MQTT using LwIP on an STM32F407 microcontroller.
For periodic message publishing, I'm using the following code:
void MqttPublishTask(void const * argument)
{
	err_t err;
	while(1)
	{
        xEventGroupWaitBits(mqttSendEventGroup,
                            pdFALSE,
                            pdTRUE,
                            portMAX_DELAY);
		if(mqtt_client_is_connected(&client))
		{
			cJSON *root = cJSON_CreateObject();
			for(uint8_t i = 0; i < (nodeID - 64); i++)
			{
				cJSON *tap_metrics = cJSON_CreateObject();
				cJSON_AddNumberToObject(tap_metrics, "ER", taps[i].alarmCode);
				cJSON_AddNumberToObject(tap_metrics, "C", taps[i].coldWater);
				cJSON_AddNumberToObject(tap_metrics, "H", taps[i].hotWater);
				cJSON_AddNumberToObject(tap_metrics, "E", taps[i].totalEnergy);
				cJSON_AddNumberToObject(tap_metrics, "T", taps[i].waterFlowTime);
				cJSON_AddNumberToObject(tap_metrics, "N", taps[i].numberOfUses);
				snprintf(serialString, sizeof(serialString), "%lu", taps[i].serial);
				cJSON_AddItemToObject(root, serialString, tap_metrics);
			}
			char *pub_payload = cJSON_PrintUnformatted(root);
			uint8_t qos = 0;
			uint8_t retain = 0;
			snprintf(serialString, sizeof(serialString), "%lu", OD_PERSIST_COMM.x1018_identity.serialNumber);
			err = mqtt_publish(&client, serialString, pub_payload, strlen(pub_payload), qos, retain, mqtt_pub_request_cb, 0);
			if(err != ERR_OK)
			{
			  printf("Publish err: %d\n", err);
			}
			cJSON_Delete(root);
			free(pub_payload);
		}
		osDelay(30000);
	}
}To establish and check the connection with the broker and also for debugging purposes, I’m using the following task:
void MqttConnectTask(void const *argument)
{
	mbedtls_threading_set_alt( cmsis_os_mutex_init,
    							cmsis_os_mutex_free,
								cmsis_os_mutex_lock,
								cmsis_os_mutex_unlock );
    if (tls_config == NULL)
    {
        tls_config = altcp_tls_create_config_client(
            (const unsigned char *)mbedtls_root_certificate,
            sizeof(mbedtls_root_certificate));
        if (tls_config == NULL)
        {
            printf("Failed to create TLS config\n");
            vTaskSuspend(NULL);
        }
    }
	ip_addr_t mqtt_server_ip;
    while (netconn_gethostbyname("my.mqtt.broker.es", &mqtt_server_ip) != ERR_OK)
    {
        printf("MQTT Server DNS resolve failed\n");
        osDelay(5000);
    }
    memset(&ci, 0, sizeof(ci));
    snprintf(serialString, sizeof(serialString), "%ldc", OD_PERSIST_COMM.x1018_identity.serialNumber);
    ci.client_id  = serialString;
    ci.client_user = "username";
    ci.client_pass = "password";
    ci.keep_alive = 14;
    ci.tls_config = tls_config;
    while (1)
    {
    	if (!mqtt_client_is_connected(&client) && netif_is_link_up(&gnetif))
    	{
    		xEventGroupClearBits(mqttSendEventGroup, MQTT_SEND_EVENT_RUN_BIT);
	        mqtt_disconnect(&client);
    		if (client.conn != NULL)
    	    {
    	        altcp_abort(client.conn);
    	        client.conn = NULL;
    	    }
    	    memset(&client, 0, sizeof(client));
    	    osDelay (100);
    	    printf("Attempting MQTT connect...\n");
    	    err_t err = mqtt_client_connect(&client, &mqtt_server_ip, 8883, mqtt_connection_cb, 0, &ci);
    	    if (err != ERR_OK)
    	    {
    	        printf("mqtt_connect return %d\n", err);
    	        if (client.conn != NULL)
    	        {
    	        	altcp_abort(client.conn);
    	        	client.conn = NULL;
    	        }
    	        memset(&client, 0, sizeof(client));
    	    }
    	}
    	else if (!netif_is_link_up(&gnetif))
    	{
    		xEventGroupClearBits(mqttSendEventGroup, MQTT_SEND_EVENT_RUN_BIT);
	        mqtt_disconnect(&client);
    		if (client.conn != NULL)
    	    {
    	        altcp_abort(client.conn);
    	        client.conn = NULL;
    	    }
    	    memset(&client, 0, sizeof(client));
    	    osDelay (100);
    	}
    	else
    	{
    		xEventGroupSetBits(mqttSendEventGroup, MQTT_SEND_EVENT_RUN_BIT);
    	}
        // LwIP heap (MEM_SIZE)
        	    printf("LwIP heap: used: %u / %u bytes (max used: %u bytes)\n",
        	           lwip_stats.mem.used,
        	           lwip_stats.mem.avail,
        	           lwip_stats.mem.max);
        	    // FreeRTOS heap
        	    printf("FreeRTOS heap: current free: %u bytes, minimum ever free: %u bytes\n",
        	           xPortGetFreeHeapSize(),
        	           xPortGetMinimumEverFreeHeapSize());
        	    printf("LwIP pools (current / max / avail):\n");
        	    printf("  PBUF        : %u / %u / %u\n",
        	           lwip_stats.memp[MEMP_PBUF]->used,
        	           lwip_stats.memp[MEMP_PBUF]->max,
        	           lwip_stats.memp[MEMP_PBUF]->avail);
        	    printf("  TCP_PCB     : %u / %u / %u\n",
        	           lwip_stats.memp[MEMP_TCP_PCB]->used,
        	           lwip_stats.memp[MEMP_TCP_PCB]->max,
        	           lwip_stats.memp[MEMP_TCP_PCB]->avail);
        	    printf("  TCP_SEG     : %u / %u / %u\n",
        	           lwip_stats.memp[MEMP_TCP_SEG]->used,
        	           lwip_stats.memp[MEMP_TCP_SEG]->max,
        	           lwip_stats.memp[MEMP_TCP_SEG]->avail);
        	    printf("  ALTCP_PCB   : %u / %u / %u\n",
        	           lwip_stats.memp[MEMP_ALTCP_PCB]->used,
        	           lwip_stats.memp[MEMP_ALTCP_PCB]->max,
        	           lwip_stats.memp[MEMP_ALTCP_PCB]->avail);
                UBaseType_t stackLeft = uxTaskGetStackHighWaterMark(NULL);
                printf("Free stack space MqttConnectTask: %lu words (%lu bytes)\n", stackLeft, stackLeft * sizeof(StackType_t));
        osDelay(5000);
    }
}Everything works perfectly, but once every few hours the following appears in the log:
LwIP heap: used: 19708 / 36864 bytes (max used: 24452 bytes)
FreeRTOS heap: current free: 12672 bytes, minimum ever free: 10424 bytes
LwIP pools (current / max / avail):
  PBUF        : 0 / 0 / 16
  TCP_PCB     : 1 / 2 / 5
  TCP_SEG     : 0 / 3 / 16
  ALTCP_PCB   : 2 / 2 / 5
Free stack space MqttConnectTask: 652 words (2608 bytes)
Assertion "unhandled error" failed at line 1079 in ../Middlewares/Third_Party/LwIP/src/apps/altcp_tls/altcp_tls_mbedtls.c
LwIP heap: used: 22300 / 36864 bytes (max used: 24452 bytes)
FreeRTOS heap: current free: 12672 bytes, minimum ever free: 10424 bytes
LwIP pools (current / max / avail):
  PBUF        : 0 / 0 / 16
  TCP_PCB     : 1 / 2 / 5
  TCP_SEG     : 4 / 5 / 16
  ALTCP_PCB   : 2 / 2 / 5
Free stack space MqttConnectTask: 652 words (2608 bytes)That is, before the message "Assertion "unhandled error" failed at line 1079" appears, there are 0 TCP_SEG in use and 19708 bytes used from the LwIP heap.
After the error, I see 2 TCP_SEG in use and 22300 bytes used from the heap — and this memory is never freed.
After 5, 10, or 20 hours, the same error happens again, and these values increase further. This continues until all available TCP_SEG or heap memory is exhausted, at which point the task simply hangs and stops the data transfer.
At line 1079 in altcp_tls_mbedtls.c, there's only this line:
LWIP_ASSERT("unhandled error", 0);
return ERR_VAL;This error is not handled by LwIP in any way, so the memory is not freed, leading to a memory leak.
I tried disconnecting and re-establishing the connection, replacing the lines above with the following:
LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_ssl_write failed: %d\n", ret));
if (conn) 
{ 
altcp_abort(conn); 
} 
return ERR_ABRT;Has anyone encountered a similar issue?
There are many reports about MQTT + mbedTLS hanging after some time, but I haven’t found any working solution yet.
