Introduction
In this ESP32 tutorial we will check how to send data with a socket client on the Arduino core.

Since we will need to reach a socket server, we will implement our own using Python. You can check in detail how to set a socket server in Python on this previous post.

The tests were performed using a DFRobot’s ESP32 module integrated in a ESP32 development board.

The Python code
To get started, we will import Python’s socket module, which will make available the functions we need to set the socket server.

import socket

After that, we create an object of class socket, which we will use to configure the server and to listen to incoming connections. 

s = socket.socket()

Now that we have our socket object, we need to bind it to an IP and port. These will be the two parameters that our socket client needs to know in order to reach the server.

To perform this binding, we need to call the bind method on our socket object, passing as first input the IP, as a string, and as second input the port, as a number.

Since we are going to expose the server to be reached in the local network, we will use the ‘0.0.0.0’ IP, which will bind the socket to all the IP addresses on the machine. Later, we will need to find out what is the actual local IP of the machine, so we can reach it from the client.

We will use port 8090 in this code, but you can try with other port. Just make sure the port you are going to use is available and not already in use by other application.

s.bind(('0.0.0.0', 8090 ))

After the binding, we need to make our server start listening to connections. We do it by calling the listen method on the socket object.

Note that this method receives as input the number of unaccepted connections that are allowed before refusing new connections [1]. Since our simple use case doesn’t involve multiple clients, we can pass the value 0.

s.listen(0)

To start receiving the actual connections, we need to call the accept method on our socket object. This is a blocking method, which means the execution of the program will stop until a new client connects to the server.

Once a client connects to the server, this method returns a pair with a new socket object and the address of the client. We can then use this new socket object to establish the communication with the connected client.

Note that since sockets are bi-directional mechanisms, we can both send and receive data from it.

Assuming that we want our server to run indefinitely, we do this accept method call inside an infinite loop.

while True:

    client, addr = s.accept()
    # client handling code

Now we will start receiving data from the client. We will also assume that the client will be the one closing the connection after sending all the data, so we also read the data inside a nested loop, which will only break when the client disconnects.

To receive data sent by the client, we simply need to call the recv method on the socket object returned by the previously called accept method.

Note that in Python 3.x (the one I’m using for this tutorial) the recv method returns the data as a bytes object. On other hand, on Python 2.x, the data is returned as a string, so if you are using an older Python version you should adapt your code accordingly.

As input, the recv receives the maximum number of bytes to receive at once. If more than the specified number of bytes are sent by the client, they can be retrieved with other calls to the recv method.

One important thing to keep in mind is that the recv method is also blocking. So, after calling this method, the execution will block until either the client sends some data or disconnects.

In case of disconnection, the recv method will return an empty bytes object, which we can leverage as stopping condition for the data reading loop.

while True:

    client, addr = s.accept()

    while True:
        content = client.recv(32)

        if len(content) ==0:
           break

        else:
            print(content)

 Finally, when the client disconnects, we call the close method on the client socket object to free the resources and go back to listening to a new connection. The final Python code can be seen below and already includes this call.

import socket

s = socket.socket()         

s.bind(('0.0.0.0', 8090 ))
s.listen(0)                 

while True:

    client, addr = s.accept()

    while True:
        content = client.recv(32)

        if len(content) ==0:
           break

        else:
            print(content)

    print("Closing connection")
    client.close()

The Arduino code

In the Arduino code, we will start to include the WiFi.h library, so we can connect to a WiFi network and then establish the socket connection.

#include <WiFi.h>

As global variables, we will declare the credentials of the WiFi network to which we are going to connect. We will need the network name (SSID) and password.

const char* ssid = "yourNetworkName";
const char* password = "yourNetworkPass";

We will also declare as global variables the IP address and port of the socket server we have implemented in Python. As we have seen in the Python code, we did not bind the server to a particular IP address, but rather used the ‘0.0.0.0’ IP, which means the server should be available in all the IPs of that machine.

Thus, we need to figure out the local IP of the machine on the local network, so we can use that IP on the Arduino code. The easiest way to obtain the IP is by typing the ipconfig command on the command line if you are on Windows, or the ifconfig command if you are on Linux. These commands should be sent on the machine that will be running the Python code.

Note that for the ESP32 to be able to reach the Python server hosted on a computer, both devices need to be on the same network since we are not doing any port forwarding and we are simply using local IPs.

Alternatively, you can obtain your machine’s local IP from this website.

Note that in the code below I’m using the local IP of my computer on my local network. Yours will most likely be different. The port was 8090, as we have defined in the Python code.

const uint16_t port = 8090;
const char * host = "192.168.1.83";

Moving on o the Arduino setup function, we will simply open a serial connection to output the results of our program and take care of connecting to the WiFi network, using the previously declared credentials. 

void setup()
{

  Serial.begin(115200);

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.println("...");
  }

  Serial.print("WiFi connected with IP: ");
  Serial.println(WiFi.localIP());

}

Next, on the Arduino loop, we will establish the connection to the server periodically and send some data to it.

First, we declare an object of class WiFiClient, which we will use to establish the connection and send the data.

WiFiClient client;

Then, to establish the actual connection, we need to call the connect method on our WiFiClient object, passing as first input the IP of the server and as second the port.

This method returns 1 if the connection was successful and 0 otherwise, so we can use this to do some error checking. In case the connection fails, then we print a message to the serial connection and delay for a second before trying to connect again.

if (!client.connect(host, port)) {

   Serial.println("Connection to host failed");

   delay(1000);
   return;
}

In case of success, we move on and send the actual data to the server, which is done by calling the print method on the WiFiClient object and passing as input the string to send.

Note that there other methods that we can use to send data to the server, such as the write method.

client.print("Hello from ESP32!");

Since this is a simple introductory tutorial, we will not be expecting data from the server, so we can simply finish the connection by calling the stop method on ourWiFiClient object, thus freeing the resources. 

client.stop();

The final source code is shown below. It contains some additional prints and a 10 seconds delay between each iteration of the Arduino loop.

#include <WiFi.h>

const char* ssid = "yourNetworkName";
const char* password =  "yourNetworkPass";

const uint16_t port = 8090;
const char * host = "192.168.1.83";

void setup()
{

  Serial.begin(115200);

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.println("...");
  }

  Serial.print("WiFi connected with IP: ");
  Serial.println(WiFi.localIP());

}

void loop()
{
    WiFiClient client;

    if (!client.connect(host, port)) {

        Serial.println("Connection to host failed");

        delay(1000);
        return;
    }

    Serial.println("Connected to server successful!");

    client.print("Hello from ESP32!");

    Serial.println("Disconnecting...");
    client.stop();

    delay(10000);
}

Testing the code

To test the whole system, we will start by compiling and uploading the Arduino code to the ESP32. Once the procedure finishes, simply open the Arduino IDE serial monitor.

You should get an output similar to figure 1. Since the Python server is not yet running, then the connection attempts should fail.

 ESP32 Arduino Socket Client fail connection.png             Figure 1 – Output of the program when the Python socket server is not connected.

Next, run the Python code on the tool of your choice. In my case, I’m running it on IDLE, the Python IDE that comes with the language installation.

Once the server is up and running, you should start receiving the messages from the ESP32, as illustrated in figure 2.

ESP32 Arduino Socket client to Python program.png

              Figure 2 – Output of the Python program when receiving data from the ESP32.

If you go back to the Arduino IDE serial monitor, you should start seeing the connection and disconnection messages we have included in our code, as shown in figure 3.

ESP32 Arduino Socket Client Reaching Python server.png

                 Figure 3 – Successful connection to the Python socket server on the ESP32.