This example demonstrates how to implement a TCP server on the ESP32 in WiFi Station (STA) mode. After connecting to a specified WiFi network, the ESP32 acts as a TCP server waiting for client connections, receives data from clients, and displays echo messages on the TFT LCD screen.
wifi_STA_TCP_Server_test.ino fileconst char *ssid = "yourssid"; // Change to your WiFi name
const char *password = "yourpwd"; // Change to your WiFi password
int port = 10000; // Change to your desired port, recommended 8080
Hello ESP32! in the debug tool's send box and click SendHello ESP32! (byte-by-byte output, matching the sent content)Serial.write(c) is outside the if(c == '\r') block, outputting to serial for every byte receivedclient.print() and my_lcd.drawString() are inside the if(c == '\r') block, only executed when a carriage return is received\r directly in the input box is not recognized as a carriage return (0x0D); it is treated as two separate characters \ and r48 65 6C 6C 6F 0D (corresponds to Hello\r)
if(c == '\r') to if(c == '\n') and enable "Append Line Feed (LF)" in the debug toolThis example has four independent data flow channels:
| Channel | Direction | Trigger Condition | Description |
|---|---|---|---|
| TCP Client → ESP32 | Debug tool send area → ESP32 | Triggered on Send click | Data sent from the debug tool is transmitted to ESP32 via TCP |
| ESP32 → Serial Monitor | ESP32 → Arduino Serial | Triggered per byte received | Serial.write() outputs byte-by-byte, no carriage return required |
| ESP32 → TCP Client | ESP32 → Debug tool receive area | Triggered on \r received |
client.print() echoes data, no echo without \r |
| ESP32 → TFT Screen | ESP32 → LCD | Triggered on \r received |
my_lcd.drawString() displays data, no display without \r |
⚠️ Key Notes:
Serial.write(c) is outside if(c == '\r'), outputting to serial immediately for every byte receivedclient.print() and my_lcd.drawString() are inside if(c == '\r'), only executed when a carriage return (0x0D) is receivedClient sends data without carriage return \r:
client.print("Hello, this is client!"); // No \r at the end
The Server's if(c == '\r') never triggers, so no data is echoed back.
Client uses readStringUntil('\n') to receive data:
String line = client.readStringUntil('\n'); // Waits for line feed \n
Even if the Server echoes data back, the Client cannot find \n and will wait until timeout.
To use both examples together, modify the Client code:
// Modification 1: Append carriage return when sending
client.print("Hello, this is client!\r");
// Modification 2: Wait for carriage return instead of line feed when receiving
String line = client.readStringUntil('\r');
\r (0x0D) for the server to echo a responseThis example code is based on the ESP32-WROOM-32E development board. Before use, you need to correctly configure the WiFi network parameters and server port.
The program requires the user to modify the following parameters according to their actual situation:
const char *ssid = "yourssid";
const char *password = "yourpwd";
int port = 10000;
⚠️ Parameters that must be modified:
The setup() function initializes the serial port, screen, and WiFi, then starts the TCP server and displays connection information after WiFi is connected.
void setup()
{
Serial.begin(115200);
my_lcd.begin();
my_lcd.setRotation(0);
my_lcd.fillScreen(TFT_WHITE);
my_lcd.setTextColor(TFT_RED);
WiFi.mode(WIFI_STA);
WiFi.setSleep(false);
my_lcd.drawString("Start connecting to WiFi ...", 10,55,2);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
my_lcd.fillRect(10, 55, my_lcd.width()-1, 25,TFT_WHITE);
my_lcd.setTextColor(TFT_GREEN);
my_lcd.drawString("Connection WIFI successful!!!", 10,55,2);
my_lcd.setTextColor(TFT_BLUE);
sprintf(t_buf, "SSID : %s", ssid);
my_lcd.drawString(t_buf, 10,75,2);
sprintf(t_buf, " IP : %s", WiFi.localIP().toString().c_str());
my_lcd.drawString(t_buf, 10,95,2);
sprintf(t_buf, " MAC : %s", WiFi.macAddress().c_str());
my_lcd.drawString(t_buf, 10,115,2);
sprintf(t_buf, " PORT : %d", port);
my_lcd.drawString(t_buf, 10,135,2);
my_lcd.setTextColor(TFT_RED);
server.begin(); //Start server
my_lcd.drawString("Waiting for client connection", 10,155,2);
}
Serial.begin(115200);
my_lcd.begin();
my_lcd.setRotation(0);
my_lcd.fillScreen(TFT_WHITE);
Serial.begin(115200): Initializes serial communication with a baud rate of 115200my_lcd.begin(): Initializes the TFT screenmy_lcd.setRotation(0): Sets screen rotation to 0 degrees (portrait mode)my_lcd.fillScreen(TFT_WHITE): Clears the screen with a white backgroundWiFi.mode(WIFI_STA);
WiFi.setSleep(false);
WiFi.mode(WIFI_STA): Sets WiFi to Station mode, connecting to a router as a clientWiFi.setSleep(false): Disables WiFi sleep mode to maintain a stable connection⚠️ Note:
WIFI_AP or WIFI_AP_STA modeWiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
WiFi.begin(ssid, password): Starts WiFi connection using the configured SSID and passwordWiFi.status() != WL_CONNECTED: Loops until connection is successfuldelay(500): Checks every 500 milliseconds⚠️ Notes:
sprintf(t_buf, "SSID : %s", ssid);
my_lcd.drawString(t_buf, 10,75,2);
sprintf(t_buf, " IP : %s", WiFi.localIP().toString().c_str());
my_lcd.drawString(t_buf, 10,95,2);
sprintf(t_buf, " MAC : %s", WiFi.macAddress().c_str());
my_lcd.drawString(t_buf, 10,115,2);
sprintf(t_buf, " PORT : %d", port);
my_lcd.drawString(t_buf, 10,135,2);
The program displays the following information on the screen:
server.begin();
my_lcd.drawString("Waiting for client connection", 10,155,2);
server.begin(): Starts the TCP server, begins listening on the specified port⚠️ Notes:
server.begin() must be called after WiFi connection is successful, otherwise clients cannot connectThe loop() function implements the TCP server's client connection waiting, data reception, and echo functionality.
void loop()
{
WiFiClient client = server.available();
if(client)
{
my_lcd.fillRect(10, 155, my_lcd.width()-1, 25,TFT_WHITE);
my_lcd.drawString("Client connected.", 10,155,2);
String readBuff = "";
while (client.connected())
{
if (client.available())
{
char c = client.read();
Serial.write(c);
readBuff += c;
if(c == '\r')
{
client.print("Received: " + readBuff);
my_lcd.fillRect(10, 175, my_lcd.width()-1, 25,TFT_WHITE);
my_lcd.drawString("Received: " + readBuff, 10,175,2);
readBuff = "";
}
}
}
client.stop();
my_lcd.fillRect(10, 155, my_lcd.width()-1, 25,TFT_WHITE);
my_lcd.drawString("Client Disconnected.", 10,155,2);
}
}
WiFiClient client = server.available();
if(client)
{
my_lcd.drawString("Client connected.", 10,155,2);
...
}
server.available(): Checks if a client is attempting to connect, returns a WiFiClient objectif(client): Determines whether a client is connected⚠️ Notes:
server.available() is non-blocking, returns an empty object when no client is connectedString readBuff = "";
while (client.connected())
{
if (client.available())
{
char c = client.read();
Serial.write(c);
readBuff += c;
if(c == '\r')
{
client.print("Received: " + readBuff);
my_lcd.fillRect(10, 175, my_lcd.width()-1, 25,TFT_WHITE);
my_lcd.drawString("Received: " + readBuff, 10,175,2);
readBuff = "";
}
}
}
String readBuff = "": Creates an empty string to accumulate received dataclient.connected(): Checks if the client is still connectedclient.available(): Checks if there are readable bytesclient.read(): Reads one byteSerial.write(c): Outputs to the Serial Monitor immediately for every byte received, regardless of whether \r is receivedreadBuff += c: Appends the byte to the bufferif(c == '\r'): Detects carriage return (0x0D), only when this condition is met are the following operations executedclient.print("Received: " + readBuff): Echoes data back to the client via TCP (debug tool receive area)my_lcd.drawString(): Displays the received data on the TFT screenreadBuff = "": Clears the buffer, ready to receive the next message⚠️ Notes:
Serial.write(c) is outside the if(c == '\r') block, outputting to serial for every byte received, unaffected by carriage returnclient.print() and my_lcd.drawString() are inside the if(c == '\r') block, only executed when a carriage return is received\r is not sent, data only appears in the Serial Monitor; the TFT screen and debug tool receive area show nothingclient.stop();
my_lcd.drawString("Client Disconnected.", 10,155,2);
client.stop(): Closes the client connection, releases resourcesThis example implements the ESP32 WiFi TCP server functionality through the following steps:
Key functions used in the program:
WiFi.mode(): Set WiFi operating modeWiFi.begin(): Start WiFi connectionWiFi.status(): Get WiFi connection statusWiFi.localIP(): Get local IP addressWiFi.macAddress(): Get MAC addressserver.begin(): Start TCP serverserver.available(): Check for client connectionsclient.connected(): Check client connection statusclient.available(): Check for readable dataclient.read(): Read one byteclient.print(): Send data to clientclient.stop(): Close client connectionmy_lcd.drawString(): Display text on screenmy_lcd.fillRect(): Fill a rectangular areaIf you need to modify the code for different application scenarios, refer to the following aspects:
const char *ssid = "your_wifi_name"; // Change to your WiFi name
const char *password = "your_wifi_password"; // Change to your WiFi password
int port = 8080; // Change to your desired port
if(c == '\n') // Use line feed as end character
{
// ...
}
// Or
if(c == '!') // Use a custom character as end character
{
// ...
}
client.print("Custom response: " + readBuff); // Modify echo content
unsigned long startTime = millis();
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
if (millis() - startTime > 10000) // 10-second timeout
{
Serial.println("Connection timeout!");
break;
}
}
You can add more status information or adjust display positions as needed:
my_lcd.drawString("Custom info", x, y, font_size);
WiFiClient clients[5]; // Support up to 5 clients
// Use server.available() in loop() to get new clients and manage them
unsigned long lastReceiveTime = millis();
if (millis() - lastReceiveTime > 5000) // No data for 5 seconds
{
client.stop();
}
WiFi Connection Failed
Client Cannot Connect to Server
server.begin() has been called)Abnormal Screen Display
No Display in Debug Tool Receive Area (Normal Behavior)
\r (0x0D) to echo data back\r0x0D, or enable "Append Carriage Return", or modify the code's end characterFrequent Disconnections