¿Cómo construir un servidor web con el ESP32 que controle la posición del eje de un servo motor usando un slider?

Libro Guía del Microcontrolador ESP32

En este tutorial vas a aprender cómo construir un servidor web con el ESP32 que controle la posición del eje de un servo motor usando un slider. Primero, veremos cómo controlar un servo con el ESP32 y después construiremos el servidor web.

Componentes necesarios

Para seguir este tutorial necesitas: una placa ESP32 DOIT DEVKIT V1, un micro servo motor S0009/S90 (o un servo motor S0003), y cables jumper.

Conectar el servo motor al ESP32

Los servo motores tienen tres cables: alimentación, tierra y señal. La alimentación suele ser roja, el GND es negro o marrón, y el cable de señal suele ser amarillo, naranja o blanco.

Cuando usas un servo pequeño como el S0009, puedes alimentarlo directamente desde el ESP32. Pero si usas más de un servo o un modelo diferente, probablemente necesitarás alimentar los servos con una fuente de alimentación externa.

Si usas un servo pequeño como el S0009, necesitas conectar: GND al pin GND del ESP32, alimentación al pin VIN del ESP32, y señal al GPIO 13 (o cualquier otro pin PWM).

Nota: en este caso, puedes usar cualquier GPIO del ESP32, porque cualquier GPIO puede producir una señal PWM. Sin embargo, no recomendamos usar los GPIO 9, 10 y 11, que están conectados a la flash SPI integrada y no se recomiendan para otros usos.

Esquema de conexión

En nuestros ejemplos, conectaremos el cable de señal al GPIO 13. Puedes seguir el esquema para cablear tu servo motor.

Nota: este esquema usa la versión del módulo ESP32 DEVKIT V1 con 36 GPIOs. Si usas otro modelo, comprueba el pinout de tu placa.

¿Cómo controlar un servo motor?

Puedes posicionar el eje del servo en diferentes ángulos de 0 a 180º. Los servos se controlan usando una señal de modulación por ancho de pulsos (PWM). El ciclo de trabajo de la señal PWM enviada al motor determinará la posición del eje.

Para controlar el motor puedes simplemente usar las capacidades PWM del ESP32 enviando una señal de 50 Hz con el ancho de pulso adecuado. O puedes usar una librería para hacer esta tarea mucho más sencilla.

Preparar el Arduino IDE

Antes de continuar, asegúrate de tener instaladas las placas ESP32 en tu Arduino IDE y la libería ServoESP32.

Instalar la librería ESP32Servo

La libería ESP32Servo facilita el control de un servo motor con tu ESP32 usando Arduino IDE. Sigue los siguientes pasos para instalar la librería en Arduino IDE:

Ve a Programa > Incluir Librería > Gestionar Librerías…. Busca ESP32Servo. Instala la librería.

Probar un ejemplo

Después de instalar la librería, ve a tu Arduino IDE y copia el siguiente código:

/*
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Detalles completos del proyecto en https://RandomNerdTutorials.com/esp32-servo-motor-web-server-arduino-ide/
  Basado en el ejemplo Sweep de ESP32Servo
*/

#include <ESP32Servo.h>

static const int servoPin = 13;

Servo servo1;

void setup() {

  Serial.begin(115200);
  servo1.attach(servoPin);
}

void loop() {
  for(int posDegrees = 0; posDegrees <= 180; posDegrees++) {
    servo1.write(posDegrees);
    Serial.println(posDegrees);
    delay(20);
  }

  for(int posDegrees = 180; posDegrees >= 0; posDegrees--) {
    servo1.write(posDegrees);
    Serial.println(posDegrees);
    delay(20);
  }
}

Entender el código

Este sketch rota el servo 180 grados hacia un lado, y 180 grados hacia el otro. Veamos cómo funciona.

Primero, necesitas incluir la librería Servo:

#include <ESP32Servo.h>

Define el pin conectado al pin de datos del servo. En este caso, conectamos al GPIO 13, pero puedes usar cualquier otro pin adecuado.

static const int servoPin = 13;

Después, necesitas crear un objeto Servo. En este caso, se llama servo1:

Servo servo1;

La función setup()

En el setup(), se inicializa una comunicación serie para propósitos de depuración y se asocia el GPIO 13 al objeto servo:

void setup() {
  Serial.begin(115200);
  servo1.attach(servoPin);
}

La función loop()

En el loop(), cambiamos la posición del eje del motor de 0 a 180 grados, y luego de 180 a 0 grados. Para establecer el eje en una posición particular, solo necesitas usar el método write() en el objeto Servo. Le pasas como argumento un número entero con la posición en grados:

servo1.write(pos);

Probar el sketch

Sube el código a tu ESP32. Después de subir el código, deberías ver el eje del motor rotando hacia un lado y luego hacia el otro.

Crear el servidor web del ESP32

Ahora que sabes cómo controlar un servo con el ESP32, creemos el servidor web para controlarlo. El servidor web que construiremos:

Contiene un slider de 0 a 180, que puedes ajustar para controlar la posición del eje del servo. El valor actual del slider se actualiza automáticamente en la página web sin necesidad de refrescarla. Para esto, usamos AJAX para enviar peticiones HTTP al ESP32 en segundo plano. Refrescar la página web no cambia el valor del slider ni la posición del eje.

Crear la página HTML

Empecemos viendo el texto HTML que el ESP32 necesita enviar a tu navegador:

<!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" href="data:,">
  <style>
    body {
      text-align: center;
      font-family: "Trebuchet MS", Arial;
      margin-left:auto;
      margin-right:auto;
    }
    .slider {
      width: 300px;
    }
  </style>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
  <h1>ESP32 with Servo</h1>
  <p>Position: <span id="servoPos"></span></p>
  <input type="range" min="0" max="180" class="slider" id="servoSlider" onchange="servo(this.value)"/>
  <script>
    var slider = document.getElementById("servoSlider");
    var servoP = document.getElementById("servoPos");
    servoP.innerHTML = slider.value;
    slider.oninput = function() {
      slider.value = this.value;
      servoP.innerHTML = this.value;
    }
    $.ajaxSetup({timeout:1000});
    function servo(pos) {
      $.get("/?value=" + pos + "&");
      {Connection: close};
    }
  </script>
</body>
</html>

Crear un slider

La página HTML para este proyecto implica crear un slider. Para crear un slider en HTML se usa la etiqueta <input>. La etiqueta <input> especifica un campo donde el usuario puede introducir datos. Hay una amplia variedad de tipos de input. Para definir un slider, usa el atributo type con el valor range. En un slider, también necesitas definir el rango mínimo y máximo usando los atributos min y max.

<input type="range" min="0" max="180" class="slider" id="servoSlider" onchange="servo(this.value)"/>

También necesitas definir otros atributos como: la class para estilizar el slider, el id para actualizar la posición actual mostrada en la página web, y finalmente, el atributo onchange para llamar a la función servo y enviar una petición HTTP al ESP32 cuando el slider se mueve.

Añadir JavaScript al archivo HTML

A continuación, necesitas añadir algo de código JavaScript a tu archivo HTML usando las etiquetas <script> y </script>. Este fragmento del código actualiza la página web con la posición actual del slider:

var slider = document.getElementById("servoSlider");
var servoP = document.getElementById("servoPos");
servoP.innerHTML = slider.value;
slider.oninput = function() {
  slider.value = this.value;
  servoP.innerHTML = this.value;
}

Y las siguientes líneas hacen una petición HTTP GET en la dirección IP del ESP32 en esta ruta URL específica: /?value=[SLIDER_POSITION]&.

$.ajaxSetup({timeout:1000});
function servo(pos) {
  $.get("/?value=" + pos + "&");
}

Por ejemplo, cuando el slider está en 0, haces una petición HTTP GET en la siguiente URL:

http://192.168.1.135/?value=0&

Y cuando el slider está a 180 grados, tendrás algo como:

http://192.168.1.135/?value=180&

De esta forma, cuando el ESP32 recibe la petición GET, puede obtener el parámetro value en la URL y mover el servo motor en consecuencia.

Código completo del servidor web

Ahora, necesitamos incluir el texto HTML anterior en el sketch y rotar el servo en consecuencia. Este siguiente sketch hace precisamente eso.

Copia el siguiente código en tu Arduino IDE, pero no lo subas todavía. Primero, veremos cómo funciona:

/*
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Detalles completos del proyecto en https://RandomNerdTutorials.com/esp32-servo-motor-web-server-arduino-ide/
*/

#include <WiFi.h>
#include <ESP32Servo.h>

Servo myservo;  // crea el objeto servo para controlar un servo
// se pueden crear doce objetos servo en la mayoría de las placas

// GPIO al que está conectado el servo
static const int servoPin = 13;

// Reemplaza con las credenciales de tu red
const char* ssid     = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// Establece el puerto del servidor web al 80
WiFiServer server(80);

// Variable para almacenar la petición HTTP
String header;

// Decodifica el valor GET de HTTP
String valueString = String(5);
int pos1 = 0;
int pos2 = 0;

// Tiempo actual
unsigned long currentTime = millis();
// Tiempo previo
unsigned long previousTime = 0; 
// Define el tiempo de timeout en milisegundos (ejemplo: 2000ms = 2s)
const long timeoutTime = 2000;

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

  myservo.attach(servoPin);  // asocia el servo en servoPin al objeto servo

  // Conecta a la red WiFi con SSID y contraseña
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  // Imprime la dirección IP local e inicia el servidor web
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  server.begin();
}

void loop(){
  WiFiClient client = server.available();   // Escucha clientes entrantes

  if (client) {                             // Si un nuevo cliente se conecta,
    currentTime = millis();
    previousTime = currentTime;
    Serial.println("New Client.");          // imprime un mensaje por el puerto serie
    String currentLine = "";                // crea un String para almacenar los datos entrantes del cliente
    while (client.connected() && currentTime - previousTime <= timeoutTime) { // bucle mientras el cliente esté conectado
      currentTime = millis();
      if (client.available()) {             // si hay bytes para leer del cliente,
        char c = client.read();             // lee un byte, luego
        Serial.write(c);                    // lo imprime en el monitor serie
        header += c;
        if (c == '\n') {                    // si el byte es un salto de línea
          // si la línea actual está vacía, tienes dos saltos de línea seguidos.
          // ese es el final de la petición HTTP del cliente, así que envía una respuesta:
          if (currentLine.length() == 0) {
            // Las cabeceras HTTP siempre empiezan con un código de respuesta (ej. HTTP/1.1 200 OK)
            // y un content-type para que el cliente sepa qué viene, luego una línea en blanco:
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println("Connection: close");
            client.println();

            // Muestra la página web HTML
            client.println("<!DOCTYPE html><html>");
            client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
            client.println("<link rel=\"icon\" href=\"data:,\">");
            // CSS para dar estilo a los botones on/off 
            client.println("<style>body { text-align: center; font-family: \"Trebuchet MS\", Arial; margin-left:auto; margin-right:auto;}");
            client.println(".slider { width: 300px; }</style>");
            client.println("<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script>");
                     
            // Página web
            client.println("</head><body><h1>ESP32 with Servo</h1>");
            client.println("<p>Position: <span id=\"servoPos\"></span></p>");          
            client.println("<input type=\"range\" min=\"0\" max=\"180\" class=\"slider\" id=\"servoSlider\" onchange=\"servo(this.value)\" value=\""+valueString+"\"/>");
            
            client.println("<script>var slider = document.getElementById(\"servoSlider\");");
            client.println("var servoP = document.getElementById(\"servoPos\"); servoP.innerHTML = slider.value;");
            client.println("slider.oninput = function() { slider.value = this.value; servoP.innerHTML = this.value; }");
            client.println("$.ajaxSetup({timeout:1000}); function servo(pos) { ");
            client.println("$.get(\"/?value=\" + pos + \"&\"); {Connection: close};}</script>");
           
            client.println("</body></html>");     
            
            //GET /?value=180& HTTP/1.1
            if(header.indexOf("GET /?value=")>=0) {
              pos1 = header.indexOf('=');
              pos2 = header.indexOf('&');
              valueString = header.substring(pos1+1, pos2);
              
              //Rota el servo
              myservo.write(valueString.toInt());
              Serial.println(valueString); 
            }         
            // La respuesta HTTP termina con otra línea en blanco
            client.println();
            // Sale del bucle while
            break;
          } else { // si tienes un salto de línea, limpia currentLine
            currentLine = "";
          }
        } else if (c != '\r') {  // si tienes cualquier otra cosa menos un retorno de carro,
          currentLine += c;      // lo añade al final de currentLine
        }
      }
    }
    // Limpia la variable header
    header = "";
    // Cierra la conexión
    client.stop();
    Serial.println("Client disconnected.");
    Serial.println("");
  }
}

Cómo funciona el código paso a paso

Primero, incluimos la librería Servo y creamos un objeto Servo llamado myservo.

#include <ESP32Servo.h> 
Servo myservo; // crea el objeto servo para controlar un servo

También creamos una variable para almacenar el número de GPIO al que está conectado el servo. En este caso, GPIO 13:

const int servoPin = 13;

No olvides que necesitas modificar las siguientes dos líneas para incluir tus credenciales de red:

const char* ssid     = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Después, creamos variables que se usarán para extraer la posición del slider de la petición HTTP:

String valueString = String(5); 
int pos1 = 0; 
int pos2 = 0;

La función setup()

En el setup(), necesitas asociar el servo al GPIO al que está conectado, con myservo.attach():

myservo.attach(servoPin); // asocia el servo en servoPin al objeto servo

La función loop()

La primera parte del loop() crea el servidor web y envía el texto HTML para mostrar la página web. Usamos el mismo método que hemos usado en otros proyectos de servidor web.

La siguiente parte del código recupera el valor del slider de la petición HTTP:

//GET /?value=180& HTTP/1.1
if(header.indexOf("GET /?value=")>=0) {
  pos1 = header.indexOf('=');
  pos2 = header.indexOf('&');
  valueString = header.substring(pos1+1, pos2);

Cuando mueves el slider, haces una petición HTTP en la siguiente URL, que contiene la posición del slider entre los signos = y &:

http://tu-esp-ip-address/?value=[SLIDER_POSITION]&

La posición del slider se guarda en la variable valueString. Después, establecemos el servo en esa posición específica usando myservo.write() con la variable valueString como argumento. La variable valueString es un string, así que necesitamos usar el método toInt() para convertirla en un número entero, el tipo de dato aceptado por el método write().

myservo.write(valueString.toInt());

Probar el servidor web

Ahora puedes subir el código a tu ESP32. Asegúrate de tener la placa y el puerto COM correctos seleccionados. Tampoco olvides modificar el código para incluir tus credenciales de red.

Después de subir el código, abre el Monitor Serie a 115200 baudios. Pulsa el botón EN/RST del ESP32 para reiniciar la placa, y copia la dirección IP del ESP32 que aparece en el Monitor Serie.

Abre tu navegador, pega la dirección IP del ESP32, y deberías ver la página web que creaste antes. Mueve el slider para controlar el servo motor.

En el Monitor Serie, también puedes ver las peticiones HTTP que estás enviando al ESP32 cuando mueves el slider.

Experimenta con tu servidor web durante un rato para ver si funciona correctamente.

Conclusión: lo que has aprendido

En resumen, en este tutorial has aprendido cómo controlar un servo motor con el ESP32 y cómo crear un servidor web con un slider para controlar su posición.

Este es solo un ejemplo de cómo controlar un servo motor. En lugar de un slider, puedes usar un campo de entrada de texto, varios botones con ángulos predefinidos, o cualquier otro campo de entrada adecuado.

Este patrón de servidor web + slider + actuador físico es directamente aplicable a multitud de proyectos: controlar la posición de una cámara, mover un brazo robótico, ajustar la apertura de una válvula, o incluso controlar la dirección de un coche RC. El servo motor es el actuador más sencillo de controlar con PWM, y combinado con la interfaz web que has construido, te permite controlar el mundo físico desde cualquier dispositivo con navegador.

La combinación de ESP32 + servo + servidor web es una de las más versátiles en el mundo maker. Con muy pocos componentes, puedes crear sistemas de control preciso, interfaces de usuario intuitivas y proyectos que impresionan tanto a principiantes como a expertos. Ahora que dominas esta técnica, las posibilidades son casi ilimitadas.

Vistas: 0

Descubre más desde CIBERED

Suscríbete y recibe las últimas entradas en tu correo electrónico.

Scroll al inicio