Ethernet TCP Server Example

(This example is based on using a wired Ethernet port, the setup and event_handler would need adapting to use for the WiFi port)


#include <stdio.h>
#include <string.h>
#include <sys/fcntl.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "esp_wifi.h"
#include "esp_system.h"
#include "esp_err.h"
#include "esp_event_loop.h"
#include "freertos/event_groups.h"
#include "esp_event.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "esp_eth.h"

#include "rom/ets_sys.h"
#include "rom/gpio.h"

#include "soc/dport_reg.h"
#include "soc/io_mux_reg.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/gpio_reg.h"
#include "soc/gpio_sig_map.h"

#include "tcpip_adapter.h"
#include "nvs_flash.h"
#include "driver/gpio.h"

#include "eth_phy/phy_lan8720.h"

#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include "lwip/netdb.h"
#include "lwip/dns.h"

#define DEFAULT_ETHERNET_PHY_CONFIG phy_lan8720_default_ethernet_config
// #define PIN_PHY_POWER CONFIG_PHY_POWER_PIN				//not needed for our board configuration

#define PIN_SMI_MDC   GPIO_NUM_23
#define PIN_SMI_MDIO  GPIO_NUM_18

#define	TCP_SERVER_PORT			3000



//******************************************
//******************************************
//********** ETHERNET GPIO CONFIG **********
//******************************************
//******************************************
static void ethernet_gpio_config_rmii(void)
{
    //RMII data pins are fixed:
    //	TXD0 = GPIO19
    //	TXD1 = GPIO22
    //	TX_EN = GPIO21
    //	RXD0 = GPIO25
    //	RXD1 = GPIO26
    //	CLK == GPIO0
    phy_rmii_configure_data_interface_pins();
    phy_rmii_smi_configure_pins(PIN_SMI_MDC, PIN_SMI_MDIO);
}


//*****************************************
//*****************************************
//********** ETHERNET INITIALISE **********
//*****************************************
//*****************************************
void ethernet_initialise (void)
{
	esp_err_t ret = ESP_OK;
	tcpip_adapter_init();
	ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) );

	eth_config_t config = DEFAULT_ETHERNET_PHY_CONFIG;
	
	config.phy_addr = CONFIG_PHY_ADDRESS;				//Set the PHY address in configuration
	config.gpio_config = ethernet_gpio_config_rmii;
	config.tcpip_input = tcpip_adapter_eth_input;

	ret = esp_eth_init(&config);

	if(ret == ESP_OK)
	{
		esp_eth_enable();
		tcpserver_event_group = xEventGroupCreate();
		xTaskCreate(&tcp_server, "tcp_server", 4096, NULL, 5, NULL);
	}
}



//***************************************
//***************************************
//********** ESP EVENT HANDLER **********
//***************************************
//***************************************
//Call this at startup:
//ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) );
static esp_err_t event_handler(void *ctx, system_event_t *event)
{
	switch(event->event_id)
	{
	case SYSTEM_EVENT_ETH_CONNECTED:
		//Ethernet phy link up
		xEventGroupSetBits(tcpserver_event_group, CONNECTED_BIT);
		break;

	case SYSTEM_EVENT_ETH_DISCONNECTED:
		//Ethernet phy link down
		xEventGroupClearBits(tcpserver_event_group, CONNECTED_BIT);
		break;

	case SYSTEM_EVENT_ETH_GOT_IP:
		//Ethernet got IP from connected AP

		break;

	default:
		break;
	}
	return ESP_OK;
}



//********************************
//********************************
//********** TCP SERVER **********
//********************************
//********************************
void tcp_server(void *pvParam)
{
	int socket_id;
	int bytes_received;
	char recv_buf[64];
	int client_socket;

	ESP_LOGI(EthernetLogTag,"tcp_server task started \n");
	struct sockaddr_in tcpServerAddr;
	tcpServerAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	tcpServerAddr.sin_family = AF_INET;
	tcpServerAddr.sin_port = htons( TCP_SERVER_PORT );
	static struct sockaddr_in remote_addr;
	static unsigned int socklen;
	socklen = sizeof(remote_addr);


	//----- WAIT FOR ETHERNET CONNECTED -----
	ESP_LOGI(EthernetLogTag, "... waiting for ethernet connect \n");
	xEventGroupWaitBits(tcpserver_event_group, CONNECTED_BIT, false, true, portMAX_DELAY);
	while(1)
	{
		//----- ALLOCATE SOCKET -----
		socket_id = socket(AF_INET, SOCK_STREAM, 0);
		if(socket_id < 0)
		{
			//Couldn't allocate socket
			ESP_LOGE(EthernetLogTag, "... Failed to allocate socket.\n");
			vTaskDelay(1000 / portTICK_PERIOD_MS);
			continue;
		}
		ESP_LOGI(EthernetLogTag, "... allocated socket\n");

		//----- BIND -----
		if(bind(socket_id, (struct sockaddr *)&tcpServerAddr, sizeof(tcpServerAddr)) != 0)
		{
			ESP_LOGE(EthernetLogTag, "... socket bind failed errno=%d \n", errno);
			close(socket_id);
			vTaskDelay(4000 / portTICK_PERIOD_MS);
			continue;
		}
		ESP_LOGI(EthernetLogTag, "... socket bind done \n");

		//----- LISTEN -----
		if(listen (socket_id, 2) != 0)
		{
			ESP_LOGE(EthernetLogTag, "... socket listen failed errno=%d \n", errno);
			close(socket_id);
			vTaskDelay(4000 / portTICK_PERIOD_MS);
			continue;
		}

		while (1)
		{
			//Once the socket has been created it doesn't matter if the network connection is lost, the Ethernet cable is unplugged and then re-plugged in,
			//this while loop will still continue to work the next time a client connects

			//----- WAIT FOR A CLIENT CONNECTION -----
			ESP_LOGI(EthernetLogTag,"Waiting for new client connection...");
			client_socket = accept(socket_id,(struct sockaddr *)&remote_addr, &socklen);		//<<<< WE STALL HERE WAITING FOR CONNECTION

			//----- A CLIENT HAS CONNECTED -----
			ESP_LOGI(EthernetLogTag,"New client connection");

			//Optionally set O_NONBLOCK
			//If O_NONBLOCK is set then recv() will return, otherwise it will stall until data is received or the connection is lost.
			//fcntl(client_socket,F_SETFL,O_NONBLOCK);

			bzero(recv_buf, sizeof(recv_buf));
			while (1)
			{

				bytes_received = recv(client_socket, recv_buf, sizeof(recv_buf)-1, 0);		//<<<< WE STALL HERE WAITING FOR BYTES RECEIVED
				if (bytes_received == 0)
				{
					//----- CONNECTION LOST -----
					//There is no client any more - must have disconnected
					ESP_LOGI(EthernetLogTag,"Client connection lost");
					break;
				}
				else if (bytes_received < 0)
				{
					//----- NO DATA WAITING -----
					//We'll only get here if O_NONBLOCK was set

					//vTaskDelay(50 / portTICK_PERIOD_MS);		//Release to RTOS scheduler
				}
				else
				{
					//----- DATA RECEIVED -----
					ESP_LOGI(EthernetLogTag,"Data received:");
					for(int i = 0; i < bytes_received; i++)
						putchar(recv_buf[i]);
					ESP_LOGI(EthernetLogTag,"Data receive complete");

					//Clear the buffer for next time (not acutally needed but may as well)
					bzero(recv_buf, sizeof(recv_buf));

					//----- TRANSMIT -----
					if (write(client_socket , "Hello!" , strlen("Hello!")) < 0)
					{
						ESP_LOGE(EthernetLogTag, "Transmit failed");
						close(socket_id);
						vTaskDelay(4000 / portTICK_PERIOD_MS);
						continue;
					}
					ESP_LOGI(EthernetLogTag, "Transmit complete");
				}
			} //while (1)

			//We won't actually get here, the while loop never exits (unless its implementation gets changed!)

			//----- CLOSE THE SOCKET -----
			ESP_LOGI(EthernetLogTag, "Closing socket");
			close(client_socket);
		}
		ESP_LOGI(EthernetLogTag, "TCP server will be opened again in 3 secs...");
		vTaskDelay(3000 / portTICK_PERIOD_MS);


	}
	ESP_LOGI(EthernetLogTag, "TCP client task closed\n");
}

 

UK Electronic Designer
USEFUL?
We benefit hugely from resources on the web so we decided we should try and give back some of our knowledge and resources to the community by opening up many of our company’s internal notes and libraries through mini sites like this. We hope you find the site helpful.
Please feel free to comment if you can add help to this page or point out issues and solutions you have found, but please note that we do not provide support on this site. If you need help with a problem please use one of the many online forums.