2023-12-08 02:52 AM
Hello I'm developing a TCP client for STM32F4 MCU.
I'm using the LWIP sockets api and freeRTOS to do so.
This client will both send and receive data when triggered by RTC (every hour or so) and based on an ADC reading.
My understanding is both sockets and netconn api's are based on state machine so they need to run in a different thread than MX_LWIP_Init() so after this function is called I am creating a thread that communicates to my server application.
I initialise the thread with xTaskCreate(TcpThread, "TCP", 128 * 4, NULL, osPriorityNormal, &tcp_thread_id);
In this thread I am creating, connecting writing and closing the socket and checking for errors for each with UART as one should do.
After this I delete the thread with vTaskDelete(NULL).
I then create the thread again 5s afterwards by calling osDelay(5000).
The application seems to run fine but crashes anywhere from 24 to 1100 connections without showing errors over UART.
Is there something wrong with my implementation?
I've attached main and client source files to this post.
If anyone has tips on how to solve this I would very much appreciate them for it.
Thank you,
Marc
Solved! Go to Solution.
2023-12-08 11:23 AM
> My understanding is both sockets and netconn api's are based on state machine so they need to run in a different thread than MX_LWIP_Init()
Not quite. netconn and socket APIs need to be called from a different task/thread than the tcp main thread. But that is always the case. tcpip_init() creates the tcpcip task to handle the API calls, so any thread you create is "different" than that.
Don't delete your task and re-start/re-create it. That is in-efficient and unnecessary. Have the task wait on a semaphore. When your A/D data is ready, set that semaphore (from a separate task, obviously). Your task then opens the socket, sends data, closes the socket and goes back to waiting for the semaphore.
Or - if the tcp task also reads the A/D data, the have that task start a (FreeRTOS) timer with a period of 5 seconds then wait for a semaphore. The timer callback will set that semaphore, thus waking up your tcp task which then collects data, opens the socket, sends the data, closes the socket then goes back to waiting for the semaphore.
2023-12-08 11:23 AM
> My understanding is both sockets and netconn api's are based on state machine so they need to run in a different thread than MX_LWIP_Init()
Not quite. netconn and socket APIs need to be called from a different task/thread than the tcp main thread. But that is always the case. tcpip_init() creates the tcpcip task to handle the API calls, so any thread you create is "different" than that.
Don't delete your task and re-start/re-create it. That is in-efficient and unnecessary. Have the task wait on a semaphore. When your A/D data is ready, set that semaphore (from a separate task, obviously). Your task then opens the socket, sends data, closes the socket and goes back to waiting for the semaphore.
Or - if the tcp task also reads the A/D data, the have that task start a (FreeRTOS) timer with a period of 5 seconds then wait for a semaphore. The timer callback will set that semaphore, thus waking up your tcp task which then collects data, opens the socket, sends the data, closes the socket then goes back to waiting for the semaphore.
2023-12-09 08:43 AM
Ok thank you Bob!
I just learned that I could check if the memory was being freed as it apparently isn't done before exiting the function as I thought would be the case with the higher level blocking api. That is what is causing the crash.
So semaphore's would not be bad? Even for long intervals between connections, longer than an hour?
Thanks again for your help,
Marc
2023-12-09 09:45 AM
There is no time limit - one can wait indefinitely, if that is appropriate. Why would semaphores be bad? Anyway, the thread events/notifications are even simpler and more efficient.
2023-12-09 12:06 PM
Thank you!
I will look into thread notifications.
2023-12-09 02:05 PM
Generally for 99% of the projects all threads should just run forever. And by "run" I mean run the code or wait for some thread synchronization primitive.
2023-12-10 01:51 AM
I guess that makes sense since threads are created with dynamic memory allocation it is better to create them at startup.
I assumed deleting the task wouldn't be bad because I'd be freeing resources for other tasks but there isn't any point, this could just lead me to problems down the line.
2023-12-10 06:25 AM
You don't have to use a dynamic memory allocation either:
https://www.freertos.org/Static_Vs_Dynamic_Memory_Allocation.html
I am setting configSUPPORT_DYNAMIC_ALLOCATION to 0 and in addition to improved reliability all of the used memory is accounted by a compiler and the size of executable code reduces because there is no heap code at all.
By the way... For lwIP the core API, of course, is the most flexible and efficient one, but, if you want a thread-safe API, I recommend using Netconn API. The sockets API is built on top of Netconn API anyway and are provided mainly as a compatibility layer for applications ported from the desktop OSes.