2024-08-06 06:00 AM - edited 2024-10-15 01:10 AM
In this article, we guide the users of the Azure NetX Duo middleware to handle multiple TCP clients on the STM32H563 microcontroller as an example.
This guide is divided into two parts:
We base our manipulation on the already working example found in the CubeH5Firmware example
STM32Cube\Repository\STM32Cube_FW_H5_V1.2.0\Projects\NUCLEO-H563ZI\Applications\NetXDuo\Nx_UDP_Echo_Server.
You can find attached the file app_netxduo++clients_same_port.c that you can replace in this project to test this use case.
We start from there as the basic configuration of NetX Duo can be obtained from the following article.
nx_system_initialize()
.nx_packet_pool_create()
.nx_ip_create()
.nx_arp_enable()
, nx_icmp_enable()
, nx_tcp_enable()
, and nx_udp_enable()
respectively.tx_thread_create()
.tx_thread_create()
.tx_thread_create()
.nx_dhcp_create()
.nx_dhcp_start()
.nx_ip_address_change_notify()
.tx_thread_resume()
.nx_tcp_socket_create()
.nx_tcp_server_socket_listen()
.nx_tcp_server_socket_accept()
.nx_tcp_socket_receive_notify()
.nx_tcp_socket_receive()
.nx_ip_interface_status_check()
./* TCP server thread entry */
static VOID tcp_thread_entry(ULONG thread_input)
{
UINT status;
/* Create all sockets */
for (INT i = 0; i < APP_TCP_SOCKET_NUM; i++)
{
status = nx_tcp_socket_create(&NetXDuoEthIpInstance, g_tcp_sck + i, "TCP Socket",
NX_IP_NORMAL, NX_FRAGMENT_OKAY,
NX_IP_TIME_TO_LIVE, 512, NX_NULL,
g_tcp_sck_disconn_cb);
if (NX_SUCCESS != status)
{
__BKPT(0);
}
}
/* Start listening on the first socket */
status = nx_tcp_server_socket_listen(&NetXDuoEthIpInstance, APP_TCP_PORT, g_tcp_sck, 0,
g_tcp_sck_listen_cb);
if (NX_SUCCESS != status)
{
__BKPT(0);
}
while (1)
{
/* Receive pointers to TCP socket and network packet from TCP callback */
ULONG msg[2];
status = tx_queue_receive(&g_tcp_q, msg, TX_WAIT_FOREVER);
if (TX_SUCCESS == status)
{
NX_TCP_SOCKET * p_sck = (NX_TCP_SOCKET *) msg[0];
NX_PACKET * p_packet = (NX_PACKET *) msg[1];
status = nx_tcp_socket_send(p_sck, p_packet, NX_NO_WAIT);
if (NX_SUCCESS != status)
{
nx_packet_release(p_packet);
}
}
else
{
/* Unrecoverable error */
tx_thread_suspend(tx_thread_identify());
}
}
}
/* TCP listen callback */
static void g_tcp_sck_listen_cb(NX_TCP_SOCKET * p_sck, UINT port)
{
/* Incoming connection, accept and queueing new requests */
nx_tcp_server_socket_accept(p_sck, NX_NO_WAIT);
nx_tcp_server_socket_unlisten(&NetXDuoEthIpInstance, port);
nx_tcp_socket_receive_notify(p_sck, g_tcp_sck_receive_cb);
/* Attempt to find another idle socket to start listening on */
ULONG state = 0;
for (INT i = 0; i < APP_TCP_SOCKET_NUM; i++)
{
/* Get socket state value */
nx_tcp_socket_info_get(g_tcp_sck + i, 0, 0, 0, 0, 0, 0, 0, &state, 0, 0, 0);
/* Start listening if socket is idle */
if (NX_TCP_CLOSED == state)
{
nx_tcp_server_socket_listen(&NetXDuoEthIpInstance, port, g_tcp_sck + i, 0,
g_tcp_sck_listen_cb);
break;
}
}
/* Ran out of sockets, set appropriate flag to let next socket to
* disconnect know that it should start listening right away. */
if (NX_TCP_CLOSED != state)
{
g_not_listening = TX_TRUE;
}
}
/* TCP receive callback */
static void g_tcp_sck_receive_cb(NX_TCP_SOCKET * p_sck)
{
NX_PACKET * p_packet;
/* This callback is invoked when data is already received. Retrieving
* packet with no suspension. */
nx_tcp_socket_receive(p_sck, &p_packet, NX_NO_WAIT);
/* Send packet back on the same TCP socket */
nx_tcp_socket_send(p_sck, p_packet, NX_NO_WAIT);
}
/* TCP disconnect callback */
static void g_tcp_sck_disconn_cb(NX_TCP_SOCKET * p_sck)
{
nx_tcp_server_socket_unaccept(p_sck);
/* If all sockets are busy, start listening again */
if (TX_TRUE == g_not_listening)
{
nx_tcp_server_socket_listen(&NetXDuoEthIpInstance, APP_TCP_PORT, p_sck, 0,
g_tcp_sck_listen_cb);
g_not_listening = TX_FALSE;
}
}
We can see that we are receiving incoming connections on the same port.
You can find attached the file app_netxduo++clients_separate_port.c that you can replace in this project to test this use case.
/* Allocate the memory for TCP server thread */
if (tx_byte_allocate(byte_pool, (VOID **) &pointer,2 * DEFAULT_MEMORY_SIZE, TX_NO_WAIT) != TX_SUCCESS)
{
return TX_POOL_ERROR;
}
/* create the TCP server thread */
ret = tx_thread_create(&AppTCPThread, "App TCP Thread", App_TCP_Thread_Entry, 0, pointer, 2 * DEFAULT_MEMORY_SIZE,
DEFAULT_PRIORITY, DEFAULT_PRIORITY, TX_NO_TIME_SLICE, TX_DONT_START);
if (tx_byte_allocate(byte_pool, (VOID **) &pointer,2 * DEFAULT_MEMORY_SIZE, TX_NO_WAIT) != TX_SUCCESS)
{
return TX_POOL_ERROR;
}
ret = tx_thread_create(&AppTCPThread2, "App TCP2 Thread", App_TCP_Thread_Entry, 1, pointer, 2 * DEFAULT_MEMORY_SIZE,
DEFAULT_PRIORITY+1, DEFAULT_PRIORITY+1, TX_NO_TIME_SLICE, TX_DONT_START);
nx_tcp_socket_create()
.nx_tcp_server_socket_listen()
.nx_tcp_server_socket_accept()
.nx_tcp_socket_receive_notify()
.nx_tcp_socket_receive()
.nx_ip_interface_status_check()
./* TCP server thread entry */
static VOID App_TCP_Thread_Entry(ULONG thread_input)
{
UINT ret;
UCHAR data_buffer[512];
UINT ti = (UINT)thread_input; // thread index
ULONG source_ip_address;
NX_PACKET *data_packet;
UINT source_port;
ULONG bytes_read;
/* create the TCP socket */
ret = nx_tcp_socket_create(&NetXDuoEthIpInstance, &TCPSocket[ti], "TCP Server Socket", NX_IP_NORMAL, NX_FRAGMENT_OKAY,
NX_IP_TIME_TO_LIVE, WINDOW_SIZE, NX_NULL, NX_NULL);
if (ret)
{
Error_Handler();
}
/*
* listen to new client connections.
* the TCP_listen_callback will release the 'Semaphore' when a new connection is available
*/
ret = nx_tcp_server_socket_listen(&NetXDuoEthIpInstance, server_ports[ti], &TCPSocket[ti], MAX_TCP_CLIENTS, tcp_listen_callback);
if (ret)
{
Error_Handler();
}
else
{
printf("TCP Server listening on PORT %d ..\r\n", server_ports[ti]);
}
if(tx_semaphore_get(&DHCPSemaphore, TX_WAIT_FOREVER) != TX_SUCCESS)
{
Error_Handler();
}
else
{
/* accept the new client connection before starting data exchange */
ret = nx_tcp_server_socket_accept(&TCPSocket[ti], TX_WAIT_FOREVER);
if (ret)
{
Error_Handler();
}
}
while(1)
{
ULONG socket_state;
TX_MEMSET(data_buffer, '\0', sizeof(data_buffer));
/* get the socket state */
nx_tcp_socket_info_get(&TCPSocket[ti], NULL, NULL, NULL, NULL, NULL, NULL, NULL, &socket_state, NULL, NULL, NULL);
/* if the connections is not established then accept new ones, otherwise start receiving data */
if(socket_state != NX_TCP_ESTABLISHED)
{
ret = nx_tcp_server_socket_accept(&TCPSocket[ti], NX_IP_PERIODIC_RATE);
}
if(ret == NX_SUCCESS)
{
/* receive the TCP packet send by the client */
ret = nx_tcp_socket_receive(&TCPSocket[ti], &data_packet, NX_WAIT_FOREVER);
if (ret == NX_SUCCESS)
{
HAL_GPIO_TogglePin(GPIOI, GPIO_PIN_9);
/* get the client IP address and port */
nx_udp_source_extract(data_packet, &source_ip_address, &source_port);
/* retrieve the data sent by the client */
nx_packet_data_retrieve(data_packet, data_buffer, &bytes_read);
/* print the received data */
PRINT_DATA(source_ip_address, source_port, data_buffer);
/* immediately resend the same packet */
ret = nx_tcp_socket_send(&TCPSocket[ti], data_packet, NX_IP_PERIODIC_RATE);
if (ret == NX_SUCCESS)
{
HAL_GPIO_TogglePin(GPIOI, GPIO_PIN_9);
}
}
else
{
nx_tcp_socket_disconnect(&TCPSocket[ti], NX_WAIT_FOREVER);
nx_tcp_server_socket_unaccept(&TCPSocket[ti]);
nx_tcp_server_socket_relisten(&NetXDuoEthIpInstance, server_ports[ti], &TCPSocket[ti]);
}
}
else
{
/*toggle the green led to indicate the idle state */
HAL_GPIO_TogglePin(GPIOI, GPIO_PIN_9);
}
}
}
To test the application, we use a packet sender to generate two packets on different ports (5000,5001).
We can see in this trace that the server running on the STM32H5 is receiving both messages from different ports.
By following this guide, you should be able to handle multiple clients on the same port or on separate ports using the STM32H5 microcontroller and the NetX Duo library. The key steps involve initializing the system, creating and managing threads, and handling TCP connections using callbacks.