STM32 ETHERNET #5. TCP CLIENT

This is the 5th tutorial in the STM32 ETHERNET series, and today we will see how to use our STM32 as the TCP Client. I would recommend you to go through the previous tutorials first, as I won’t explain everything here, like connections.
Go through the link above, and see how the connection was made in the first tutorial. Also see the TCP server tutorial, so that it will be a little easier to understand this one.

Some Insight into the CODE

Initializing the TCP Client

void tcp_client_init(void)
{
	/* 1. create new tcp pcb */
	struct tcp_pcb *tpcb;

	tpcb = tcp_new();

	/* 2. Connect to the server */
	ip_addr_t destIPADDR;
	IP_ADDR4(&destIPADDR, 192, 168, 0, 102);
	tcp_connect(tpcb, &destIPADDR, 31, tcp_client_connected);
}
  • First we need to create the TCP Block using the function tcp_new ()
  • Next we will connect to the server with the IP 192.168.0.102 and Port 31

Once the client is connected to the server, the client connected callback will be called. Below is the code for the same.


Client Connected Callback

This callback is called, once the connection between the server and the client is successful.

static err_t tcp_client_connected(void *arg, struct tcp_pcb *newpcb, err_t err)
{
  err_t ret_err;
  struct tcp_client_struct *es;

  LWIP_UNUSED_ARG(arg);
  LWIP_UNUSED_ARG(err);

  /* allocate structure es to maintain tcp connection information */
  es = (struct tcp_client_struct *)mem_malloc(sizeof(struct tcp_client_struct));
  if (es != NULL)
  {
    es->state = ES_CONNECTED;
    es->pcb = newpcb;
    es->retries = 0;
    es->p = NULL;

    /* pass newly allocated es structure as argument to newpcb */
    tcp_arg(newpcb, es);

    /* initialize lwip tcp_recv callback function for newpcb  */
    tcp_recv(newpcb, tcp_client_recv);

    /* initialize lwip tcp_poll callback function for newpcb */
    tcp_poll(newpcb, tcp_client_poll, 0);


    /* initialize LwIP tcp_sent callback function */
    tcp_sent(newpcb, tcp_client_sent);

    /* handle the TCP data */
    tcp_client_handle(newpcb, es);

    ret_err = ERR_OK;
  }
  else
  {
    /*  close tcp connection */
    tcp_client_connection_close(newpcb, es);
    /* return memory error */
    ret_err = ERR_MEM;
  }
  return ret_err;
}
  • As you can see above, this callback initializes few other callbacks.
  • for example, the tcp_poll callback, tcp_sent callback, tcp_recv callback etc.
  • These are needed for the processing, and you shouldn’t modify them.
  • Here we are only interested in the tcp_recv callback. This is where we will write our code to handle the incoming data.

Client Receive Callback

tcp_recv initializes the tcp_client_recv function. It is basically a callback, which is called whenever the client receive some data from the server.

We will write our code to handle this received data.

static err_t tcp_client_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
  struct tcp_client_struct *es;
  err_t ret_err;
  
  ..............................
  ..............................
  ..............................
  
  
  else if(es->state == ES_CONNECTED)
  {
   /* store reference to incoming pbuf (chain) */
    es->p = p;

    // tcp_sent has already been initialized in the beginning.
//    /* initialize LwIP tcp_sent callback function */
//    tcp_sent(tpcb, tcp_client_sent);

    /* Acknowledge the received data */
    tcp_recved(tpcb, p->tot_len);

    /* handle the received data */
    tcp_client_handle(tpcb, es);

    pbuf_free(p);

    ret_err = ERR_OK;
  }
  
  ...........................
  ...........................
  ...........................
  
  return ret_err;
}
  • The dots in the code above shows some predefined code to handle the errors. You should leave it as it is.
  • If the state is switched to connected, we can process the received data.
  • here tpcb stores all the info about the server and client, and pbuf stores all the info about the data.
  • Here I am storing the reference to the incoming buffer into the “es” structure, which is later passed to client handle, so that we can make use of this incoming data.
  • Then we need to Acknowledge the received data, and we do that by using the function tcp_recved
  • Now tcp_client_hanldle is called to handle the data received from the server.
  • We could have handled the data here also, but I am trying to keep the similarities in the coding between different protocols, and this is why I have created that another function just for the purpose of handling data.
  • After the data has been processed, we will free the buffer using pbuf_free




Client data Handler

tcp_client_handle handles the incoming data from the server.

static void tcp_client_handle (struct tcp_pcb *tpcb, struct tcp_client_struct *es)
{
	/* get the Remote IP */
	ip4_addr_t inIP = tpcb->remote_ip;
	uint16_t inPort = tpcb->remote_port;

	/* Extract the IP */
	char *remIP = ipaddr_ntoa(&inIP);

//	esTx->state = es->state;
//	esTx->pcb = es->pcb;
//	esTx->p = es->p;

	esTx = es;
	pcbTx = tpcb;

	counter++;

}
  • This is the function, where you can write the code according to your requirement.
  • I am not doing anything special, but just getting the IP of the server and incrementing the counter variable.
  • This counter variable will be later used while sending the data to the server.
  • If you want to make use of the incoming data, the information about the data can be found in the “es” structure, and the info about the server and client can be found in the “tpcb” structure.

Sending the data to the server

Just like the UDP client, here also I have created a timer callback, which will be called every second.
inside the callback, we will send the data to the server.

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	char buf[100];

	/* Prepare the first message to send to the server */
	int len = sprintf (buf, "Sending TCPclient Message %d\n", counter);

	if (counter !=0)
	{
		/* allocate pbuf */
		esTx->p = pbuf_alloc(PBUF_TRANSPORT, len , PBUF_POOL);


		/* copy data to pbuf */
		pbuf_take(esTx->p, (char*)buf, len);

		tcp_client_send(pcbTx, esTx);

		pbuf_free(esTx->p);
	}

}
  • Here you can see above I am merging the counter value with a string.
  • Then pbuf_alloc will allocate the memory for the pbuf we are going to send
  • pbuf_take will copy the string into the payload of the pbuf we are sending
  • tcp_client_send will send the pbuf to the server
  • And finally we will free the memory using pbuf_free
  • SO the counter value will only increment, if the client handle function is called, which will only happen when the client receive some data from the server
  • otherwise the client will keep sending the same counter value to the server every second


RESULT

  • As shown in the figure above, the client was sending the old counter value.
  • As soon as the server sent some data, the counter was incremented, and the client started sending new counter value every second

Check out the Video Below










Info

You can help with the development by DONATING
To download the code, click DOWNLOAD button and view the Ad. The project will download after the Ad is finished.

15 Comments. Leave new

  • Kenza Recham
    April 12, 2024 3:06 PM

    Thanks a lot, this was too helpful.I just wanna point out one little issue with ur tcp_client_send, the fuction does not handle sending one message immediately.
    For example, In a loop that goes from 0 to 9, it only sends 9 messages instead of 10, to overcome this issue, add the tcp_output() function call in case the next buffer is null.

    Reply
  • Hello, thank you very much for the article. Can you give me advice on the 2 issues below?
    1. I’m using freeRTOS, is there any problem using RawAPI? I know that the NetConn library needs FreeRTOS to run, but there is no documentation on whether RawAPI should run under FreeRTOS or not.
    2. When my server disconnect with board and then reconnect again, client can’t connect. Why is that, what problem am I having?

    Hope your answer from you.

    Reply
  • Hi, what should I do for 8 different ports?

    Reply
  • Hello, How can ı make with this code structure Modbus TCP client ? Thanks.

    Reply
  • Hello, thank you for your tutorial. I have a qustion (maybe stupid, sorry for this) Where you apply ths functions,fro example tcp_client_recv() in main, if in cycle there is just
    while (1)
    {ethernetif_input(&gnetif);
     sys_check_timeouts();
    }

    Reply
    • When we initialise the tcp_client (tcp_client_init ()), it basically binds the rest of the functions to their respective handles. Later the ethernet_if_input function calls the respective handle to handle the data.

      Reply
  • Hello
    I need to coming data from server. How can ı reach it
    Thanks in advance

    Reply
  • Thanks you so much

    Reply
  • Thamanoon Kedwiriyakarn
    March 6, 2022 7:32 AM

    Thank you very much Sir.

    Would you mind If I were to translate the information on your website into Thai language. for students to read together

    Reply
  • بنیامین چشمی
    December 25, 2021 11:04 PM

    hi,not connect tcp client nucleof429zi?
    5241 1885.190401 192.168.0.200 192.168.0.144 TCP 60 [TCP Retransmission] [TCP Port numbers reused] 52432 → 7 [SYN] Seq=0 Win=2144 Len=0 MSS=536

    Reply
  • Wolfgang Zehetmair
    December 7, 2021 10:18 PM

    Hello, from where did you derive the TCPClientRAW code contained in this session?

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.

keyboard_arrow_up

Adblocker detected! Please consider reading this notice.

We've detected that you are using AdBlock Plus or some other adblocking software which is preventing the page from fully loading.

We don't have any banner, Flash, animation, obnoxious sound, or popup ad. We do not implement these annoying types of ads!

We need money to operate the site, and almost all of it comes from our online advertising.

Please add controllerstech.com to your ad blocking whitelist or disable your adblocking software.

×