cancel
Showing results for 
Search instead for 
Did you mean: 

LwIP TCP server send periodic data

loribru
Associate II

Hi all, I am developing a TCP server starting from the STM32F4 TCP server echo sample.

My goal is to keep the echo functionality and send in addition a periodic message.
I modified slightly the original file and I managed to have the echo working and the method tcp_echoserver_periodic_notification called every second, but that message does not get to my client.
I am probably doing something wrong but I do not understand what since it's my first time with LwIP and I must say it's not very straightforward.
Can you please help me, what am I doing wrong?
Also, I see other examples using the methods pbuf_take and pbuf_alloc, but the ST example does not use them, what is best to do here?
Thanks.

1 ACCEPTED SOLUTION

Accepted Solutions

Hello, at the end I recreated my project from scratch in the MX interface. This time I used FreeRTOS with CMSIS v2 and it worked.

It's not super straightforward to say what changed.

I increased DEFAULT_THREAD_STACKSIZE from 512 to 1024.

I disabled LWIP_NETIF_STATUS_CALLBACK.

TCPIP_THREAD_PRIO changed from 3 to 24 (probably a CMSIS v1 vs v2 change here).

I am not sure if it's any of these changes that made it work, but now the 2 threads are running to handle the receive and send of the messages using Netconn.

The only remaining issue so far is that FreeRTOS sometimes hangs upon TCP connection. Not very reproducible behavior. It's not a hard fault, but all tasks stop all of a sudden.

View solution in original post

12 REPLIES 12
Bob S
Principal

 There are several issues.  I don't see where you call tcp_echoserver_periodic_notification().  This sample code uses the LwIP raw interface. With that interface you don't get to call its functions directly from your code, other than starting the server from tcp_echoserver_init().  After that, the LwIP kernel calls the other functions based on activity on that socket, asynchronous to the rest of your program.

I am only a little familiar with this raw interface. There may be a way to do what you want, but I don't know what it is.

I suggest you instead use the netconn or higher level sockets layer functions.  It will be much easier to inject your periodic output.  See https://www.nongnu.org/lwip/2_1_x/group__netconn.html  and https://www.nongnu.org/lwip/2_1_x/group__socket.html .  There are examples for those layers floating around, your search engine of choice should be able to find them (search "netconn tcp server example" or "lwip socket tcp server example").

The tcp_echoserver_periodic_notification method is called somewhere else, a timer with 1 Hz frequency.

That's indeed a very good suggestion, in fact after struggling for a few days, I got to the same conclusion myself, I am happy to see my ideas confirmed though.
I thought that, as a beginner, trying to implement something with raw APIs would be easier, but actually, the opposite is true.
I am currently working on a FreeRTOS + Netconn TCP server version and so far the process has given me less headaches than the raw approach. I still need to implement the periodic notification part though, so far the echo part works nicely and more smoothly.

I guess it's a good idea to have a dedicated thread that handles the "send" part and on the "read" part, and I think I would need to put in place some FreeRTOS-specific mechanisms for passing the data between threads. Am I going in the right direction?

If you are using an RTOS, then only 1 thread should access each TCP connection - both to read and write.  So that thread needs to wait for one of 2 events: incoming data is ready, or time to send periodic data.  So in that case, yes, you need a way to pass the periodic output to that task.  The netconn_recv() can be configured as non-blocking, or to return after a user-defined timeout if no data is available.  You task can then alternate between checking for incoming data and checking for periodic data to send.

It may be possible to have a mutex that controls access to the connection (i.e the tcb).  One task grabs the mutex, checks for incoming data, processes it (maybe sending data) then releases the mutex and sleeps a short time.  The other task, when it has periodic data to send, waits for the mutex, sends the data then releases the mutex.  I haven't tried this arrangement but it might work.  It also requires somehow sharing the tcb between tasks via making it a global variable (YUCK!) or somehow passing it (and the mutex) as parameters to both tasks.  Probably not a clean a solution as the 1st suggestion.

That's a lot of hints that I will investigate, thanks a lot. That's very valuable help!
For the moment I am trying to start 2 threads, one for the reading, and one for the periodic writing.
So far I have not managed to start the second task, only the first one gets started.
I switch their position, it's still the first one that runs, and the second does not.
I also gave them different priorities, but this does not change anything.

void tcpecho_init(void)
{
	 sys_sem_new(&tcpsem, 0); // the semaphore would prevent simultaneous access to tcpsend
	 sys_thread_new("tcpecho_thread", tcpecho_thread, NULL, DEFAULT_THREAD_STACKSIZE, TCPECHO_THREAD_PRIO);
	 sys_thread_new("tcpsend_thread", tcpsend_thread, NULL, DEFAULT_THREAD_STACKSIZE, TCPECHO_THREAD_PRIO + 1);
}

 

Hello @loribru ,

try to give the second task higher priority (numerically lower ) and add a delay between the creation of the tasks as creating tasks too quickly can cause issues .

void tcpecho_init(void)
{
	 sys_sem_new(&tcpsem, 0); // the semaphore would prevent simultaneous access to tcpsend
	 sys_thread_new("tcpecho_thread", tcpecho_thread, NULL, DEFAULT_THREAD_STACKSIZE, TCPECHO_THREAD_PRIO);
// Add some delay between creating tasks
  vTaskDelay(pdMS_TO_TICKS(100));

	 sys_thread_new("tcpsend_thread", tcpsend_thread, NULL, DEFAULT_THREAD_STACKSIZE, TCPECHO_THREAD_PRIO - 1);
vTaskStartScheduler();
}

hope this solves your issue .

BR 

In order to give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

Hello @STea 
Adding the vTaskStartScheduler call makes the MCU go to hardfault, I don't think it's needed here, is it?
The tcpecho_init function is already running in a task itself ( I am based on the example LwIP_UDPTCP_Echo_Server_Netconn_RTOS for STM32F4 eval board).

The addition of delay and change of priority has no effect: the first line of the second method is never hit.

loribru
Associate II

So to give some more info on the task issue.

I am using STM32F4 package, latest version 1.28.0.
I generated the project with STM32CubeIDE, using FreeRTOS CMSIS v1 (v2 hard faults randomly most of the time, so I went for v1) and LwIP with RTOS support.
in my main I create this task:

 

  osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 640);
  defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);

  /* Start scheduler */
  osKernelStart();

 Which is the following:

void StartDefaultTask(void const * argument)
{
  /* init code for LWIP */
  MX_LWIP_Init();
  /* USER CODE BEGIN 5 */

  /* Initialize tcp echo server */
  tcpecho_init();

  /* Infinite loop */
  for(;;)
  {
    osDelay(1);
  }
  /* USER CODE END 5 */
}

Inside the last function, I just try to instantiate the threads, the first one is the one that handles the NETConn communication, same as the sample from the F4 repository.
In the second task I just toggle a LED:

void tcpecho_init(void)
{
	  sys_thread_new("tcpecho_thread", tcpecho_thread, NULL, DEFAULT_THREAD_STACKSIZE, TCPECHO_THREAD_PRIO);
	  sys_thread_new("test_thread", test_thread, NULL, DEFAULT_THREAD_STACKSIZE, TCPECHO_THREAD_PRIO);
}

static void test_thread(void *arg)
{
	while(1)
	{
		HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
		osDelay(500);
	}
}

The problem is that the second task (where eventually I want to decouple write and read TCP operations using semaphores) does not start at all.
It starts only if I put it before the other one (but then it's the latter that does not start). It seems only the first one is run.

What am I doing wrong?

Hello @loribru ,

you can also try increasing the stack size of the test_thread to see if that helps. If that doesn't work, you can try using a mutex or a semaphore to synchronize the access to the shared resources between the two threads.

-I think that you need to check the FreeRtos configuration (stack size memory allocation..) :

DEFAULT_THREAD_STACKSIZE and TOTAL_HEAP_SIZE and try increasing them to see the effect .

-maybe create a semaphore that gets released by the testthread and acquired in the beginning of the other thread to see the effect .

Looking forward to the results of the tests .

BR

In order to give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

Hello, at the end I recreated my project from scratch in the MX interface. This time I used FreeRTOS with CMSIS v2 and it worked.

It's not super straightforward to say what changed.

I increased DEFAULT_THREAD_STACKSIZE from 512 to 1024.

I disabled LWIP_NETIF_STATUS_CALLBACK.

TCPIP_THREAD_PRIO changed from 3 to 24 (probably a CMSIS v1 vs v2 change here).

I am not sure if it's any of these changes that made it work, but now the 2 threads are running to handle the receive and send of the messages using Netconn.

The only remaining issue so far is that FreeRTOS sometimes hangs upon TCP connection. Not very reproducible behavior. It's not a hard fault, but all tasks stop all of a sudden.