0

$USD

$USD
TUTORIALS ESP32

ESP32 Socket Server: Controlling a relay remotely

DFRobot Jun 11 2018 1256
Introduction In this tutorial we will check how to control a relay connected to the ESP32 remotely, using sockets. The code will be developed using the Arduino core.

The ESP32 will be acting as a socket server, receiving a connection from a client, which will send commands to turn the relay on or off. The commands will be very simple, a ‘1’ will correspond to turning the relay on and a ‘0’ to turn it off.

We will use Putty as a socket client, to avoid having to code a client ourselves. You can check how to reach a socket server hosted on the ESP32 using Putty on this previous tutorial.

The tests of this ESP32 tutorial were performed using a DFRobot’s ESP-WROOM-32 device integrated in a ESP32 FireBeetle board and a DFRobot relay board.


The electrical diagram

To make things simpler, I’m assuming the use of a ready to use relay module, which allows to control the relay directly from a pin of a microcontroller.

These type of modules already contain all the electronics needed so we can use a digital pin from a microcontroller without damaging it. In my case, as already mentioned in the introductory section, I’m using a DFRobot relay board.

Depending on your relay board, it may work with a power supply of 3.3 V or 5 V. The one I’m using works well with 3.3 V, as illustrated in the connection diagram of figure 1.

esp32-control-relay-diagram-3v3

Figure 1 – Electrical schematic of the connection between the ESP32 and the relay board.

Note that the labels of the pins may differ between different modules of relays, so in figure 1 the input pin for controlling the relay was generically called SIG.

The power needed for the relay module may be provided directly from your ESP32 board, in case it has a power pin capable of providing enough current. If you are not sure if your board has such pin or what is the maximum current draw supported, my recommendation is to use an external power supply such as this.

If you use an external power supply, don’t forget to have a common ground for all the devices.

As a final note and as already mentioned in other tutorials, the control logic we will see below in the code is independent of the actuator, as long as it can be controlled by a single digital pin of the ESP32. So, although we are using a relay on this tutorial to exemplify a potencial use case, the code below can be used for a much more vast group of actuators.


The code

We start the code by importing the WiFi.h library, so we can connect the ESP32 to a Wireless Network. In order to be able to connect to the network, we will need to store its access credentials, namely the network name (service set identifier or SSID) and the password.

Additionally, we need an object of class WiFiServer, which will be used to configure the socket server and to receive the connections from the socket clients. Remember that the constructor for this class receives the port where the server will be listening as argument. I will use port 80, but you can test with other values, as long as you use them later when testing the whole system.

Finally, we will also store the number of the pin that will control the relay on a global variable, so it is easily changeable. I will be using pin 23 but you can test with others.

#include "WiFi.h"

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

WiFiServer wifiServer(80);

int relayPin = 23;

As usual, we will use the Arduino setup function to take care of initializing a serial connection to output the results of the program, and also to connect the ESP32 to the Wireless Network.

After that, we will call the begin method on the previously created WiFiServer object. This method is called for the server to start listening to the incoming connections.

Finally, at the end of the setup function, we will set the pin of the microcontroller that will control the relay as output. This is done with a call to the pinMode function, passing as first argument the number of the pin we have stored in a global variable and as second the constant OUTPUT.

As an additional safeguard, we will set the status of the pin to a low digital state. This way, we always know the initial state of the relay when the program starts running.

We do it using the digitalWrite function, which receives as first input the number of the pin and as second a constant, either LOW or HIGH, depending if we want to set the pin to a low or high digital state, respectively. In our case, we should use LOW.

void setup() {

  Serial.begin(115200);

  delay(1000);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }

  Serial.println("Connected to the WiFi network");
  Serial.println(WiFi.localIP());

  wifiServer.begin();

  pinMode(relayPin, OUTPUT);
  digitalWrite(relayPin, LOW);
}

Moving on to the main loop function, we will handle the reception of connections from socket clients.

We start by calling the available method on the WiFiServer object, which will return an object of class WiFiClient. We will use this object to receive the actual data from the client.

WiFiClient client = wifiServer.available();

Nonetheless, before receiving the data, we need to check if a client is indeed connected, since the previously called available method is non-blocking by default, and thus it always returns a WiFiClient object, which may be connected or not.

Remember from the previous tutorials that the WiFiClient class overrides the C++ bool operator to return the value of the connected method of that class, which tells us if the client is effectively connected or not. Thus, we simply need to enclose the object in a IF statement.

if (client) {
// Handle the reception of data
}

Inside this conditional block, we start reading the data while the client is connected. We use the connected method to check if the client is still connected and we use the available method to check if there is data available to read. These methods are both called on the WiFiClient object.

If there is data to read, we read each byte with a call to the read method on the WiFiClient object. We will pass the result of this method call to a function we will analyze below, which we will call processedReceivedValue. This function will analyze the value of the received command and actuate over the relay accordingly.

Additionally, for debugging, we will write the received byte to the serial port.

while (client.connected()) {

    while (client.available()>0) {
      char c = client.read();
      processReceivedValue(c);
      Serial.write(c);
    }

    delay(10);
}

Once the client disconnects, we call the stop method on the WiFiClient object, to free the resources. Then, we complete the loop function and go back to the beginning, checking if there are new clients available

client.stop();

To finalize, we will declare the function that will process the received command, which takes the form of a byte. Note that we are interpreting it as a char since it will be sent from a command line tool where the user will type the command.

The logic of the function will be as simple as comparing the received value with ‘1’ and ‘0’. If the received value was ‘1’, then we turn on the relay, and if it was ‘0’ we turn it off. Otherwise, we do nothing.

As mentioned, these values will be typed by the user, so we are doing the comparison assuming the values are characters, which is why we compare it with the chars ‘1’ and ‘0’ and not with byte values 1 and 0.

void processReceivedValue(char command){

  if(command == '1'){ digitalWrite(relayPin, HIGH); }
  else if(command == '0'){ digitalWrite(relayPin, LOW);}

  return;
}

You can check the final source code below.

#include "WiFi.h"

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

WiFiServer wifiServer(80);

int relayPin = 23;

void processReceivedValue(char command){

  if(command == '1'){ digitalWrite(relayPin, HIGH); }
  else if(command == '0'){ digitalWrite(relayPin, LOW);}

   return;
}

void setup() {

  Serial.begin(115200);

  delay(1000);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }

  Serial.println("Connected to the WiFi network");
  Serial.println(WiFi.localIP());

  wifiServer.begin();

  pinMode(relayPin, OUTPUT);
  digitalWrite(relayPin, LOW);
}

void loop() {

  WiFiClient client = wifiServer.available();

  if (client) {

    while (client.connected()) {

      while (client.available()>0) {
        char c = client.read();
        processReceivedValue(c);
        Serial.write(c);
      }

      delay(10);
    }

    client.stop();
    Serial.println("Client disconnected");

  }
}


Testing the code

To test the whole system, first compile and upload the code to the ESP32. Once the procedure finishes, open the Arduino IDE serial monitor and copy the IP address that gets printed as soon as the ESP32 finishes connecting to the WiFi network.

Then, open Putty and configure it accordingly to what is illustrated in figure 2. As shown, we need to choose “Raw as connection type and then put the IP address copied from the Arduino IDE serial monitor on the checkbox labeled as “Host Name (or IP Address)”. We also need to use the port defined in the Arduino code (80).

Putty Socket Client to ESP32 Socket server

Figure 2 – Configuring Putty to reach the ESP32 socket server.

After clicking “Open“, a command line should appear. There you can type the values 1 and 0 to turn on and off the relay, respectively.