Skip to main content
voyvoda .
Senior
March 1, 2021
Question

LWIP Socket API, TCP and UDP connection on the same STM32 MCU

  • March 1, 2021
  • 9 replies
  • 11039 views

Hello,

I am trying to implement TCP and UDP server connection on my STM32F407-Discovery Board. I use Socket API with FreeRTOS.

I am able to run TCP or UDP seperately. Single server or client works fine.

When I combine TCP and UDP server tasks at the same code. None of them work. I can not even PING my STM32 board. I created different two tasks for each TCP and UDP server.

I tried to increase HEAP size of LWIP above 32KB. However, it did not resolved my issue.

Are the any solution to my issue ?

Thank you in advance for your help. Best Regards

This topic has been closed for replies.

9 replies

Ozone
Principal
March 1, 2021

Do resource acquisition functions (socket, bind, etc.) return errors during combined operation, or does it just silently fail ?

What does wireshark say ?

voyvoda .
voyvoda .Author
Senior
March 1, 2021

no function returns error. I have check returns and if it fails, I know it.

I have not checked it by wireshark.

Ozone
Principal
March 1, 2021

I would check how far the communication proceeds, perhaps the core is occasionally overloaded (interrupts), and drops ethernet packages.

In my experience, this is a very common problem with IoT projects.

voyvoda .
voyvoda .Author
Senior
March 1, 2021

I think there is a memory allocation problem

/* USER CODE BEGIN 4 */
void udp_client_task()
{
	char payload[] = "Message from STM32\r\n";
 
 while (1) {
 
 struct sockaddr_in dest_addr_udp;
 dest_addr_udp.sin_addr.s_addr = inet_addr("192.168.0.100");
 dest_addr_udp.sin_family = AF_INET;
 dest_addr_udp.sin_port = htons(5555);
 
 int sock_udp = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
 if (sock_udp < 0) {
 break;
 }
 
 while (1) {
 
 int err = sendto(sock_udp, payload, strlen(payload), 0, (struct sockaddr *)&dest_addr_udp, sizeof(dest_addr_udp));
 if (err < 0) {
 break;
 }
 
 osDelay(1000);
 }
 
 close(sock_udp);
 }
 
}
 
void tcp_client_task()
{
	char payload[] = "Message from STM32\r\n";
 
 while (1) {
 
 struct sockaddr_in dest_addr_tcp;
 dest_addr_tcp.sin_addr.s_addr = inet_addr("192.168.0.100");
 dest_addr_tcp.sin_family = AF_INET;
 dest_addr_tcp.sin_port = htons(5000);
 
 int sock_tcp = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
 if (sock_tcp < 0) {
 
 break;
 }
 
 
 int err = connect(sock_tcp, (struct sockaddr *)&dest_addr_tcp, sizeof(struct sockaddr_in));
 if (err != 0) {
 	osDelay(1000);
 continue;
 }
 
 while (1) {
 int err = send(sock_tcp, payload, strlen(payload), 0);
 if (err < 0) {
 
 break;
 }
 
 osDelay(1000);
 }
 
 close(sock_tcp);
 }
 
}
/* USER CODE END 4 */
 
/* USER CODE BEGIN Header_StartDefaultTask */
/**
 * @brief Function implementing the defaultTask thread.
 * @param argument: Not used
 * @retval None
 */
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void const * argument)
{
 /* init code for LWIP */
 MX_LWIP_Init();
 /* USER CODE BEGIN 5 */
 tcp_client_task();
 /* Infinite loop */
 for(;;)
 {
 osDelay(1);
 }
 /* USER CODE END 5 */
}
 
/* USER CODE BEGIN Header_StartTask02 */
/**
* @brief Function implementing the myTask02 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask02 */
void StartTask02(void const * argument)
{
 /* USER CODE BEGIN StartTask02 */
	udp_client_task();
 /* Infinite loop */
 for(;;)
 {
 osDelay(1);
 }
 /* USER CODE END StartTask02 */
}

0693W000007ZnazQAC.png 

Piranha
Principal III
March 1, 2021

As you are using ST's broken bloatware... Take a note that, while netconn and socket APIs are inherently thread-safe, Ethernet link status and DHCP processing code by ST are still broken in this regard. These and other issues are documented here:

https://community.st.com/s/question/0D50X0000BOtfhnSQB/how-to-make-ethernet-and-lwip-working-on-stm32

voyvoda .
voyvoda .Author
Senior
March 1, 2021

​In the link below, it is suggested about multi-treading. I need to apply this to my code.

https://www.nongnu.org/lwip/2_1_x/multithreading.html 

In short: Copy the functions sys_mark_tcpip_thread() and sys_check_core_locking() to your port and modify them to work with your OS. Then let LWIP_ASSERT_CORE_LOCKED() and LWIP_MARK_TCPIP_THREAD() point to these functions.

If you use LWIP_TCPIP_CORE_LOCKING, you also need to copy and adapt the functions sys_lock_tcpip_core() and sys_unlock_tcpip_core(). Let LOCK_TCPIP_CORE() and UNLOCK_TCPIP_CORE() point to these functions.

Ivan Pletnev
Associate II
March 3, 2021

Hello!

First, your clients are using same local port . You should use differents ports for udp and tcp clients at least.

Second. Every new connection should use another port for prevent tcp errors like "Reusing port" and so on.

Third. While your tcp client trying to connect to server in loop, it take all availible memory in tcp_pcb memory pool.

I,m highly recommend to use traffic analyzer like Wireshark to undestand, whats going on.

Ozone
Principal
March 3, 2021

> Second. Every new connection should use another port for prevent tcp errors like "Reusing port" and so on.

Servers usually have a "linger period" assigned to ports that are not explicitly closed, sometimes up to 15 minutes.

> I,m highly recommend to use traffic analyzer like Wireshark to undestand, whats going on.

I second that - as mentioned initially.

Ivan Pletnev
Associate II
March 4, 2021

0693W000008w2BYQAY.pngThis is a my tcp client. It is works fine together with tcp server on the same mcu.

void tcp_client_socket_thread(void *arg) {
 
	serverParams *pSrvParams = (serverParams*) arg;
	portSettingsType *pPortSettings =
			&eepromSettings.portSettings[pSrvParams->number];
	volatile uint8_t currentPortNumber = 0;
	int socket, newconn = 0;
	struct ip4_addr tempIp;
	struct sockaddr_in addr, localhost;
	osThreadId socketReceiveTaskHandle;
	osThreadId socketSendTaskHandle;
	uint16_t localPort = 0;
	uint8_t i = 0;
 
	currentPortNumber = pSrvParams->number;
 
	memset(&localhost, 0, sizeof (localhost));
	localhost.sin_family = AF_INET;
	localhost.sin_len = sizeof(struct sockaddr_in);
	localhost.sin_addr.s_addr = INADDR_ANY;
	localhost.sin_port = 0;
 
	memset(&addr, 0, sizeof(addr));
	addr.sin_len = sizeof(addr);
	addr.sin_family = AF_INET;
	IP4_ADDR(&tempIp, pPortSettings->remoteIpAddress[0],
			pPortSettings->remoteIpAddress[1],
			pPortSettings->remoteIpAddress[2],
			pPortSettings->remoteIpAddress[3]);
	addr.sin_addr.s_addr = tempIp.addr;
	addr.sin_port = htons(pPortSettings->remoteTcpPort);
 
	osDelay(3000);
 
	for (;;){
		if (pSrvParams->connCounter < MAX_SOCKET_CONN){
			localPort = (uint16_t)(HAL_RNG_GetRandomNumber(&hrng)%16383+49152);
			for (i=0; i<NO_OF_PORTS; i++){
				if (localPort == eepromSettings.portSettings[i].localTcpPort){
					localPort +=10+i;
					if (localPort < 49152){
						localPort = 49152+i;
					}
				}
			}
			localhost.sin_port = htons (localPort);
			socket = socket (AF_INET, SOCK_STREAM, 0);
			if (socket < 0){
				puts ("Create client socket failed\r\n");
				return;
			} else {
				printf ("1. TCP client socket # %i for Port # %u has been created\r\n", socket, pSrvParams->number);
				if (bind (socket, (struct sockaddr*)&localhost, sizeof (struct sockaddr)) < 0){
							puts("Bind failed\r\n");
							return;
				} else {
					puts("Bind socket to localhost OK\r\n");
					newconn = connect(socket, (struct sockaddr*)&addr, sizeof (addr));
					if (newconn < 0){
						printf("2. Unable to connect to remote server, newconn = %i\r\n\r\n", newconn);
						osDelay(10000);
					} else {
						printf("Connection to remote server established. Socket No %i \r\n\r\n", newconn);
						pSrvParams->currentFd = socket;
						pSrvParams->connCounter++;
						switch (pSrvParams->number) {
						case 0:
							HAL_GPIO_WritePin(SOCK1_GPIO_Port, SOCK1_Pin, GPIO_PIN_SET);
							break;
						case 1:
							HAL_GPIO_WritePin(SOCK2_GPIO_Port, SOCK2_Pin, GPIO_PIN_SET);
							break;
						}
						socketReceiveTaskHandle = sys_thread_new(
								pSrvParams->receiveTaskName, socketReceiveTask,
								(void*) &srvParams[currentPortNumber], 256,
								osPriorityNormal);
						pSrvParams->receiveTaskHandle = &socketReceiveTaskHandle;
						socketSendTaskHandle = sys_thread_new(pSrvParams->sendTaskName,
								socketSendTask, (void*) &srvParams[currentPortNumber],
								256, osPriorityNormal);
						pSrvParams->sendTaskHandle = &socketSendTaskHandle;
					}
				}//connect
			}//bind
		}//if (connCounter)
		osDelay(10);
	} //infinite loop
}//tcp_client_socket_thread

Ivan Pletnev
Associate II
March 4, 2021

Oh, I forgot close socket after unsuccessful attempt to connect.

newconn = connect(socket, (struct sockaddr*)&addr, sizeof (addr));
					if (newconn < 0){
						printf("2. Unable to connect to remote server, newconn = %i\r\n\r\n", newconn);
 close (socket);
						osDelay(10000);

voyvoda .
voyvoda .Author
Senior
March 4, 2021

As you can see my code, my clients are not using the same struct. They are​dest_addr_tcp and dest_addr_udp.

Why you said that your clients are using the same local port ?​

Ivan Pletnev
Associate II
March 4, 2021

Because you assign destination port in that structures. Local port is assigned by LWIP at boot. And it is the same for your udp and udp clients. You can assign local port to your socket only by calling bind().

Piranha
Principal III
March 6, 2021

@voyvoda .​​, the main problem is that ST's Ethernet link status and DHCP code uses RAW API, but doesn't do core locking. And it's also broken in other ways, as explained in my topic.

@Ivan Pletnev​, you are doing a bunch of needless work by reimplementing lwIP stack's functionality. Only servers have to bind local ports to a specific port. For clients the lwIP assigns local ports automatically from a proper range. As bind() and netconn_bind() uses RAW API underneath, read this:

https://www.nongnu.org/lwip/2_1_x/group__udp__raw.html#gac7fbda8b12b9b9360e92b51e805e799e

port local UDP port to bind with. Use 0 to automatically bind to a random port between UDP_LOCAL_PORT_RANGE_START and UDP_LOCAL_PORT_RANGE_END.

Optionally, if LWIP_RAND is defined, the start of the range is initialized to a random number in the range. Also the binding is done automatically on the first send operation, therefore clients don't have to do manual binding at all.

Ivan Pletnev
Associate II
March 6, 2021

@voyvoda .​ Yes, l found tcp_port_new() function in LWIP code. Thank you a lot for your comment, It was really helpfully for me. LWIP automatic port assigning is really works. I removed randomizer code from my client.