The Serial port is a staple for Arduino programmers. Whether it is debugging, commanding or program output, the simple serial port has you covered.
But what if you want to cut the cable and go wireless? A TCP Server running on the ESP32 is just what you need.
TCP is very similar to a serial port. Both let you send and receive data through a connection between the ESP32 and another computer. There are two main differences:
- You don’t need any wires for TCP
- Several computers can connect to the ESP32 at the same time.
This article covers implementing a TCP Server on an ESP32 so you can send text back and forth. With just a few modifications, this will work with the ESP8266 or Arduino MRK WiFi 1010 as well.
What is a TCP Server?
A TCP Server is a part of the software that will run on the ESP32. Its job is to listen for incoming connections at a port on the device’s IP address. When a remote computer, or client, reaches out the TCP Server creates a channel for the ESP32 and the remote client to communicate.
An Arduino program for the ESP32 will use a WiFiServer
object to create the server. When a new connection is made, the server will provide a WiFiClient
object as the communication channel.
To communicate a client, such as MegunoLink’s TCP Client connection, will connect to the TCP Server using the ESP32’s IP address and the port that the server is listening on. Finding a ESP32’s IP address and port is easier if you setup multicast DNS (mDNS) on the ESP32 to advertise the connection to your network.
Creating a TCP Server
The WiFiServer
class in the Arduino library implements a TCP Server for the ESP32. You only need to supply a port for the server to listen on.
The port can be any number between 1 and 65,535, though ports below 1023 are reserved for specific applications. Port 80, for example, is used by web servers. Port 23 is reserved for Telnet, which is an unencrypted text communication protocol. It is a good choice for sending commands to and from your Arduino program.
To create the server and have it listen on port 23, add a WiFiServer
variable and call its begin()
method:
1 2 3 4 5 6 7 |
const uint ServerPort = 23; WiFiServer Server(ServerPort); void setup() { Server.begin(); } |
Accepting Connections
Your ESP32 program must accept connections from remote clients if it wants to communicate with them. The hasClient
method of the WiFiServer
object will return true whenever a remote computer is trying to connect. To accept the connection, retrieve the client object from the WiFiServer
‘s available()
method and save it.
The CheckForConnection function looks for a new connection and saves it in the RemoteClient
variable. There is only one place to store the connection so we can only communicate with one remote client at a time. If a new connection comes in the program could either close the current connection to accept the new one, or reject the new connection until the current connection is disconnected by the remote. Here, the new connection is rejected.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
WiFiClient RemoteClient; void CheckForConnections() { if (Server.hasClient()) { // If we are already connected to another computer, // then reject the new connection. Otherwise accept // the connection. if (RemoteClient.connected()) { Serial.println("Connection rejected"); Server.available().stop(); } else { Serial.println("Connection accepted"); RemoteClient = Server.available(); } } } |
Normally, you should call CheckForConnections
in the loop()
method.
Try to avoid long delay(…)
as well. You might miss a client trying to connect while waiting for the delay to complete. For example, if this program will spend most of its time in the delay on line 5. It will only check for new connections every second and could miss a connection.
1 2 3 4 5 6 |
void loop() { CheckForConnections(); SendSensorValue(); delay(1000); } |
Instead, use our ArduinoTimer
library:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include "ArduinoTimer.h" ArduinoTimer SendTimer; : void loop() { CheckForConnections(); if (SendTimer.TimePassed_Milliseconds(400)) { SendSensorValue(); } } |
Sending Data to Remote Clients
The WiFiClient
supports print methods. This means you can could send the message Hello World! to a remote client using code like: RemoteClient.println("Hello World!");
You can also use the WiFiClient
as a destination for sending data to MegunoLink visualizers. For example, to send the value from the hall effect sensor inside an ESP32 to a TimePlot visualizer, you can:
1 2 3 4 5 6 7 8 9 10 11 |
#include "MegunoLink.h" : : void SendHallValue() { if (RemoteClient.connected()) { TimePlot HallPlot("", RemoteClient); HallPlot.SendData("Magnetic Field", hallRead()); } } |
Receiving from Remote Clients
Receiving data from connected clients is just as easy with TCP connections as it is for Serial connections. Simply call the available()
method on a WiFiClient
to see how much data is available. If it isn’t zero, you can call:
read()
to get the next character from the buffer, orread(Buffer, BufferSize)
to get up toBufferSize
characters from the buffer; the number of bytes read is returned.
This example echos any data received from a connected client back to it:
1 2 3 4 5 6 7 8 9 |
void EchoReceivedData() { uint8_t ReceiveBuffer[30]; while (RemoteClient.connected() && RemoteClient.available()) { int Received = RemoteClient.read(ReceiveBuffer, sizeof(ReceiveBuffer)); RemoteClient.write(ReceiveBuffer, Received); } } |
Making the Connection in MegunoLink
Use a TCP Client connection to connect to the TCP Servers on an Arduino. Create and configure the connection with the Connection Manager.
MegunoLink supports connecting to remote devices using a mDNS name, an IP address or a DNS name. mDNS, or multicast DNS, makes it easy to find your devices on a wireless network.
Recap
TCP Servers let you send and receive data from wireless devices like the ESP32/ESP8266 in much the same way as you’d use a Serial connection on an Arduino Uno. You need to create a WiFiServer object and set the port it will listen for connections on. Check for new clients periodically. Once a client has connected, send it data using the print
, write
, etc methods. Receive data from a connected client using the available()
and read()
methods.
I’m unable to get the scripts to run on a ESP32 Wroover board, I cant see anything in the script to stop this compiling for the board.
I tried building for the Wrover with version 2.4 of the ESP library and the latest version of the MegunoLink library and Arduino IDE 1.8.5 and it worked. Do you have the latest version of everything?
Can you please update the tutorial with a complete example?
Thanks in advance.
Can you please update the tutorial with a complete example?
Thanks in advance.
Yes I agree, a simple example for an ESP and the corresponding Meguunolink program would be helpful.
Could you explain how to choose the buffer size? How do you know if it should be 30?
Got it to work fine if I start the server before any client attemp to connect. But if I start a client first, and it keeps trying to connect to the « not ready yet » server, then whenever the server gets ready, it never detect any client. Any idea why is that?