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");
}