Creating a Web Server with ESP32 PLC

December 5, 2023 by
Creating a Web Server with ESP32 PLC
Boot & Work Corp. S.L., Queralt del Águila Munté

Introduction

In this blog post, we will explore how to establish a connection to your ESP32 PLC from a computer or a mobile device using the same Wi-Fi network subnet. We will explain how to create a web server on your ESP32 PLC and connect it to your local Wi-Fi network to ensure that both, your ESP32 and the device you want to control it with, are on the same subnet. Using the following codes, you will be able to easily learn how to control your ESP32 PLC from another computer or mobile device, and how to use a micro SD card to implement a larger web page and avoid using the ESP32's flash memory.

Web Server Example

This code demonstrates how to create a basic web server using an ESP32 PLC. It relies on two libraries, WiFi.h and WebServer.h, which are provided by the Arduino IDE.

When you run this code, it sets up a web server on the ESP32. This server hosts a web page that you can access from a computer or smartphone. There is a button on this web page. By clicking this button, you can switch a digital output (in this case is Q0.0) on the ESP32 PLC on and off. This interaction is made possible by the HTML and CSS code embedded in the web page, which provides a user-friendly interface.

When running the code in the Arduino IDE, make sure that you have selected the correct model of ESP32 PLC to ensure compatibility and correct operation.

In summary, this code provides a practical example of how to create a web-based interface to control a specific function (Q0.0) of an ESP32 PLC over a Wi-Fi network.

#include <WiFi.h>
#include <WebServer.h>

/* Make sure to write your SSID and Password to access to your Wifi */
const char* ssid = "SSID";
const char* password = "PASSWORD";


WebServer server(80);

bool ledStatus = LOW;

void handle_OnConnect() {
  ledStatus = LOW;
  Serial.println("Q0_0: OFF");
  server.send(200, "text/html", SendHTML(ledStatus));
}

void handle_ledOn() {
  ledStatus = HIGH;
  Serial.println("Q0_0: ON");
  server.send(200, "text/html", SendHTML(ledStatus));
}

void handle_ledOff() {
  ledStatus = LOW;
  Serial.println("Q0_0: OFF");
  server.send(200, "text/html", SendHTML(ledStatus));
}

void handle_NotFound() {
  server.send(404, "text/plain", "La pagina no existeix");
}

/* HTML and CSS of the Web Server */

String SendHTML(uint8_t ledStatus) {
  String ptr = "<!DOCTYPE html> <html>\n";
 
  ptr += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
  ptr += "<title>Control LED</title>\n";

  ptr += "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";
  ptr += "body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n";
  ptr += ".button {display: block;width: 80px;background-color: #3498db;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}\n";
  ptr += ".button-on {background-color: #3498db;}\n";
  ptr += ".button-on:active {background-color: #2980b9;}\n";
  ptr += ".button-off {background-color: #34495e;}\n";
  ptr += ".button-off:active {background-color: #2c3e50;}\n";
  ptr += "p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";
  ptr += "</style>\n";
  ptr += "</head>\n";
  ptr += "<body>\n";

  ptr += "<h1>ESP32 Web Server</h1>\n";
  ptr += "<h3>Using Station Mode</h3>\n";

  if (ledStatus) {
    ptr += "<p>Estat LED: ON</p><a class=\"button button-off\" href=\"/ledOff\">OFF</a>\n";
  }
  else {
    ptr += "<p>Estat LED: OFF</p><a class=\"button button-on\" href=\"/ledOn\">ON</a>\n";
  }

  ptr += "</body>\n";
  ptr += "</html>\n";
  return ptr;
}

void setup() {
  Serial.begin(115200);
  pinMode(Q0_0, OUTPUT);

  /* Station Mode */
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");

  /* Wait for connection */
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

/* http://ip_esp32/ */

  server.on("/", handle_OnConnect);
  server.on("/ledOn", handle_ledOn);
  server.on("/ledOff", handle_ledOff);
  server.onNotFound(handle_NotFound);

  server.begin();
  Serial.println("Server HTTP initiated");
}

void loop() {
  server.handleClient();
  if (ledStatus) {
    digitalWrite(Q0_0, HIGH);
  }
  else
  {
    digitalWrite(Q0_0, LOW);
  }
}

After running the previous code, you will obtain an IP address. To access your ESP32 PLC, simply type "http://[ip address]" in your web browser, making sure you are on the same subnet to which it is connected. This will establish a connection between your computer and the webserver hosted on your ESP32, enabling you to interact with the HTTP code you have implemented and control the on/off status of a LED on your ESP32 PLC.

If everything is configured correctly, you should be able to see this webpage:

Scan Wifi

If you're unable to connect to the same subnet, it's possible that your ESP32 doesn't recognize the Wi-Fi SSID you're trying to connect to. To verify if this is the issue, you can print the Wi-Fi status. If the print shows a value of 1, it indicates "WL_NO_SSID_AVAIL," meaning no SSID is available.

To ensure that your ESP32 is correctly detecting the Wi-Fi networks with its antenna, you can use the following code to scan and list all the SSIDs it detects:

#include <WiFi.h>

void setup() {
  Serial.begin(115200);
  delay(1000);

  // Start Wi-Fi scanning
  WiFi.mode(WIFI_STA);
  WiFi.disconnect();
  delay(100);

  Serial.println("Scanning Wi-Fi networks...");
  int numNetworks = WiFi.scanNetworks();

  if (numNetworks == 0) {
    Serial.println("No Wi-Fi networks found.");
  } else {
    Serial.print("Found ");
    Serial.print(numNetworks);
    Serial.println(" Wi-Fi networks:");

    for (int i = 0; i < numNetworks; i++) {
      Serial.print("Network SSID: ");
      Serial.println(WiFi.SSID(i));
    }
  }
}

void loop() {
  // Your main code here
}

Once you see the list of available networks on the screen, you can choose one from the list to connect your ESP32 to. This connection is necessary to set up a web server on your ESP32 PLC.

Using a micro SD card


The ESP32 PLCs are equipped with a micro SD card reader to facilitate external storage and retrieval of data. Therefore, you can use a micro SD card to load all the files of the web page, saving flash memory on the ESP32, and allowing the creation of a larger web page. From the code of the previous example, below is how to do it using the SD.h library of the Arduino IDE.


Create a function to get the appropiate HTML code depending of the LED status and call it from the handlers instead of calling the SendHTML() function:

String get_html(uint8_t ledStatus){
  String ptr="";
  if (ledStatus){
    html1=SD.open("/led_on.html", FILE_READ);
    if (!html1) { 
    Serial.println("Card failed, or not present");
    }
    else {
      for (int n = 0 ; n < html1.size() ; n++){
ptr += (char)html1.read();
}
html1.close();
}
}
else{
    html2=SD.open("/led_off.html", FILE_READ);
    if (!html2) { 
        Serial.println("Card failed, or not present");
    }
    else {
      for (int n = 0 ; n < html2.size() ; n++){
        ptr += (char)html2.read();
      }
      html2.close();
}
}
return ptr;
}

Write the needed handlers for the different files of the webpage, in this case we will use a CSS file, an image and the bootstrap framework:

void handle_styleCSS() {
  File file = SD.open("/style.css");
  if (file) {
    server.streamFile(file, "text/css");
    file.close();
  }
}

void handle_img1() {
  File file = SD.open("/img1.jpg");
  if (file) {
    server.streamFile(file, "image/jpeg");
    file.close();
  }
}

void handle_bootstrapCSS() {
  File file = SD.open("/bootstrap-5.0.2-dist/css/bootstrap.min.css");
  if (file) {
    server.streamFile(file, "text/css");
    file.close();
  }
}

void handle_bootstrapJS() {
  File file = SD.open("/bootstrap-5.0.2-dist/js/bootstrap.bundle.min.js");
  if (file) {
    server.streamFile(file, "application/javascript");
    file.close();
  }
}

Initialize the SD inside the setup() function, where the chip select (CS) is the 13:

pinMode(chipSelect, OUTPUT);

if (!SD.begin(chipSelect)) {
  Serial.println("initialization failed");
  while (true);
}

Serial.println("initialization done.");

Using the server.on() statement associate some URL paths with its corresponding handling function created above:

server.on("/style.css", HTTP_GET, handle_styleCSS);
server.on("/img1.jpg", HTTP_GET, handle_img1);
server.on("/bootstrap-5.0.2-dist/css/bootstrap.min.css", HTTP_GET, handle_bootstrapCSS);
server.on("/bootstrap-5.0.2-dist/js/bootstrap.bundle.min.js", HTTP_GET, handle_bootstrapJS);

Note that this example uses the framework bootstrap which must first be downloaded previously to the SD card.


Remember that in the first example no micro SD card was used to store the files, and they were loaded directly from the program. Now, the files need to be written inside the micro SD card before executing the code. We recommend doing this manually by moving the files to the SD card from your PC. However, you can also write them from the code. For instance:

html1=SD.open("/led_on.html", FILE_WRITE);
html1.println(SendHTML(HIGH));
html1.close();
 
html2=SD.open("/led_off.html", FILE_WRITE);
html2.println(SendHTML(LOW));
html2.close();

Store variables from the web server to the SD

To enhance the previous code for saving variables on the web server, we'll introduce a new webpage accessible through the IP address, specifically at "ip.address/signin." On this page, users will encounter a registration form prompting them for their name and password. It is crucial to assign names to all input elements in the HTML file. Below is an example of how the HTML file might look:


<form action="/signin" method="POST">
	​​<label for="user">User: </label>
	​​<input type="text" id="user" name="user">
  	​​<label for="pwd">Password: </label>
  	​<input type="password" id="pwd" name="pwd">
  	​​<input type="submit" value="Submit">
</form>


To incorporate the changes in the main code, we'll first introduce a new function named get_index() to retrieve the HTML code for the registration page:


String get_index(){
  String ptr="";
  File html = SD.open("/index.html", FILE_READ);
  if (!html) { 
    Serial.println("Card failed, or not present");
  }
  else {
    for (int n = 0 ; n < html.size() ; n++){
      ptr += (char)(html.read());
    }
    html.close();
  }
  return ptr;
}


Additionally, a handler function needs to be implemented. This handler function will take user inputs from the webpage and save them to a "file.txt":


void handle_SignedIn() {
  String username = server.arg("user");
  String password = server.arg("pwd");

  String response = "Name: " + username + " Password: " + password;

  File users = SD.open("/users.txt", FILE_APPEND);
  if(!users) {
    Serial.println("Could not open the file");
  }
  else {
    users.println(response);
    users.close();
  }

  server.send(200, "text/html", get_index());
}


Inside the setup() function needs to be written the function to call the handler:


server.on("/signin", handle_SignedIn);


​Search in our Blog

Creating a Web Server with ESP32 PLC
Boot & Work Corp. S.L., Queralt del Águila Munté December 5, 2023
Share this post

Looking for your ideal Programmable Logic Controller?

Take a look at this product comparison with other industrial controllers Arduino-based. 

We are comparing inputs, outputs, communications and other features with the ones of the relevant brands.


Industrial PLC comparison >>>