ESP32: Remote Control With Sockets


Introduction: ESP32: Remote Control With Sockets

About: Do you like technology? Follow my channel on Youtube and my Blog. In them I put videos every week of microcontrollers, arduinos, networks, among other subjects. New tutorials on Tuesdays and Fridays.

Today, I’m going to show the communication between two ESPs, in this case, the ESP32, which I think is just awesome.

In our assembly, one of the microcontrollers works as a server, with a LED on the protoboard, in AP mode. The second is the Client, or in other words, the control. A button connected to this second ESP will control the LED connected to the server.

In this project we use Socket, a communication mechanism used to implement a client / server model, permitting the exchange of messages between the processes of a machine / server application and a machine / client application.

In this case, the first ESP, which functions as a server, is the AP (Access Point), and will have established connection with the Client. A router is not necessary! To control a drone, remote control car, or model airplane, for example, the device can go far with the use of the ESP32, especially if you have some kind of antenna. With the LORA network, distances can reach up to 3.6 kilometers from ESP to ESP.

Concerning the sequence, I have a LED on the server and a button on the Client. I did this with only one button, but with this assembly, you can do it with as many buttons as you want.

Step 1: ​NodeMCU-32S - Pinout

Here, I leave the pinning of the component.

Step 2: ​Mounting Server

I turn on the LED on the server, our receiver, and start from the Access Point. The Client then connects to it. Remember that in this case we are using port D23.

Step 3: ​Client Assembly

On the same port, D23, now on the Client device, I connect the key that is equivalent to the PushButton, and also a Pull Down resistor.

Step 4: ​Source Code

For this project, we have three files: one from the server, one from the Client, and a third one that uses both of these, which is ESP32Socket.ino.


We then start with the name of our network: ESP32 Server. Select any number for the password and set the size for the Socket, in this case, 5,000. Then, in the C language, you have a feature that is called enum = enumeration, which defines which pin is pressed with the button , the pin value and the buffer size. And then we have a buffer, which is actually an array. Then the protocol sequence is defined. First, it defines the pin. Ssecond comes the value of the pin, whether it’s at 0 or at 1, and lastly, the size of the buffer. This enumeration will serve as an index of the buffer array that will be sent to the server. It indicates the button and the button value. And it is possible to have several buttons.

Another important thing here is the compilation directive. If I define a constant (IS_SERVER), I'm saying that this device is a server, which will have an IF for checking and compiling, if applicable.

//Arquivo principal. Neste arquivo vão os 'includes' e as configurações principais
//que são compartilhadas entre os outros arquivos .ino #include <WiFi.h> #define SSID "ESP32Server" #define PASSWORD "87654321" #define SERVER_PORT 5000 //Protocolo que o Server e o Client utilizarão para se comunicar enum Protocol{ PIN, //Pino que se deseja alterar o estado VALUE, //Estado para qual o pino deve ir (HIGH = 1 ou LOW = 0) BUFFER_SIZE //O tamanho do nosso protocolo. IMPORTANTE: deixar sempre como último do enum }; //Diretiva de compilação que informará qual arquivo que queremos que seja compilado //Caso queira que o arquivo Client.ino seja compilado, remova ou comente a linha do '#define' //abaixo //Caso queira que o arquivo Server.ino seja compilado, deixe o '#define IS_SERVER' abaixo descomentado #define IS_SERVER


Here at Server.ino, we start with #ifdef. So, in the case of servers, the whole code below will be compiled. In this part, we create the object, the WiFiServer here, and pass the SERVER-PORT into it. Already in the Setup, I start this ESP as the Access Point, and following the next steps of the code, we define the name of the network, among other details, to permit the entry of the Client.

It’s not necessary to define an IP, since the device already has an IP Default of its own Lib. Also, for the Loop, we check if there is a connected Client, and if it is available. It then creates a buffer size, which receives the buffer from the Client. Now define a len variable, which is the size and makes the client read, because when you are dealing with the socket, it is not Post and Get, which is HTTP. In the case of the Socket, we use READ and WRITE. Then, the server points the pin number to the position of the buffer, defines the value, which involves the assumption on whether or not the button is tight, and also decides on the pinMode, which is the output, and is the D23 port in this case.

Here, we close the Client because it brings a certain security, and we give an #endif, closes our compilation directive.

//Apenas vai compilar o código contido neste arquivo
//caso IS_SERVER esteja definido #ifdef IS_SERVER //Cria o server na porta definida por 'SERVER_PORT' WiFiServer server(SERVER_PORT); void setup() { //Coloca este ESP como Access Point WiFi.mode(WIFI_AP); //SSID e Senha para se conectarem a este ESP WiFi.softAP(SSID, PASSWORD); //Inicia o server server.begin(); } void loop() { //Verifica se tem algum cliente se conectando WiFiClient client = server.available(); if (client) { //Se o cliente tem dados que deseja nos enviar if (client.available()) {//Criamos um buffer para colocar os dados uint8_t buffer[Protocol::BUFFER_SIZE]; //Colocamos os dados enviados pelo cliente no buffer int len =, Protocol::BUFFER_SIZE); //Verificamos qual o pino que o cliente enviou int pinNumber = buffer[Protocol::PIN]; //Verificamos qual o valor deste pino int value = buffer[Protocol::VALUE]; //Colocamos o pino em modo de saída pinMode(pinNumber, OUTPUT); //Alteramos o estado do pino para o valor passado digitalWrite(pinNumber, value); } //Fecha a conexão com o cliente client.stop(); } } //Encerra o #ifdef do começo do arquivo #endif


In the Client.ino, notice the compilation directive: #ifndef (if it is not defined that it is the server). I compile all the code up to the ifend, which is the end. It then defines where the button controlled by the client is connected. In the Setup, we do a pinMode and we connect the Access Point created by the other ESP by WiFi, and wait for the connection.

In the Loop, the WiFiClient creates the object. If it is not connected to the connection point, it returns. Otherwise, continue with the steps. Then create the array to be sent to the server, read the pin and put the number of this pin in the buffer. It informs the current value of the pin, and sends and closes the connection. Related to this, if you want to mount a remote control car, then you will need several buttons. And you will have to focus on the part of the code concerning values and pin numbers to assemble the array and write in the buffer size. In summary, the client.write fills the buffer and the client.flush sends the information to the server. Finally, the client.stop closes the connection to the socket.

//Apenas vai compilar o código contido neste arquivo
//caso IS_SERVER NÃO esteja definido //(if n def, atenção para o 'n') #ifndef IS_SERVER //Pino que vamos fazer a leitura #define IN_PIN 23 void setup(){ //Colocamos o pino em modo de leitura pinMode(IN_PIN, INPUT); //Conectamos Access Point criado //pelo outro ESP WiFi.begin(SSID, PASSWORD); //Esperamos conectar while (WiFi.status() != WL_CONNECTED){ delay(500); } } void loop(){ //Variável que utlizaremos para conectar ao servidor WiFiClient client; //Se não conseguiu se conectar então retornamos if (!client.connect(WiFi.gatewayIP(), SERVER_PORT)){ return; } //Criamos um buffer para colocar os dados uint8_t buffer[Protocol::BUFFER_SIZE]; //Fazemos a leitura do pino int value = digitalRead(IN_PIN); //Colocamos no buffer o número do pino //cujo estado queremos enviar buffer[Protocol::PIN] = IN_PIN; //Colocamos no buffer o estado atual do pino buffer[Protocol::VALUE] = value; //Enviamos e finalizamos a conexão client.write(buffer, Protocol::BUFFER_SIZE); client.flush(); client.stop(); } //Encerra o #ifndef do começo do arquivo #endif

Step 5: Files


Download the files:





    • Water Contest

      Water Contest
    • Creative Misuse Contest

      Creative Misuse Contest
    • Clocks Contest

      Clocks Contest


    Cool project. You should enter this into the Remote Control contest that is currently running.