ESP32: Empezando a usar el Bluetooth (SPP)

Muy probablemente te hayas inclinado por este microcontrolador por sus opciones de conectividad. Entre ellas se incluye la conectividad Bluetooth, y en este post vamos a aprender lo básico acerca de cómo usarla. En este post os hablaré de la conectividad SPP (Serial Port Profile), que nos permitirá enviar datos como si de un puerto serie se tratase.

Antes de nada, yo para tener todo unificado y no complicarme utilizo el IDE de Arduino, aunque a veces escribo librerías en Visual Studio Code, siempre termino compilando en el IDE de Arduino por comodidad. Si no sabes cómo instalar esta placa en el IDE de Arduino, te recomiendo que te pases por este post.

Actualmente el módulo ESP32 dispone de un módulo de Bluetooth 4.2 con BLE (Bluetooth Low Energy). Esto nos permitirá conectar el dispositivo manteniendo un bajo consumo de energía. Este tema es extenso, por lo que lo trataré en otro post al que te recomiendo que estés atento, ya que enseñare a añadir autenticación.

Modo host (Serial Bluetooth)

Lo primero que aprenderemos es cómo configurar nuestro ESP32 en modo host en modo serial para recibir y enviar datos. Esto nos permitirá conectarnos desde otros dispositivos y controlar el funcionamiento de este microcontrolador, o simplemente enviar datos.

Para ello, utilizaremos este ejemplo en el que activamos un LED conectado al microcontrolador de forma remota a través del móvil. En mi caso tuve que usar un LED externo debido a que el led de la tarjeta está conectado al pin serial, y dado que vamos a usar dicha comunicación, no funciona correctamente.

/*
  Program to control LED (ON/OFF) from ESP32 using Serial Bluetooth
  by Daniel Carrasco -> https://www.electrosoftcloud.com/
*/
#include "BluetoothSerial.h"

#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif

#define LED 23
BluetoothSerial BT; // Objeto Bluetooth

void setup() {
  Serial.begin(9600); // Inicialización de la conexión en serie para la depuración
  BT.begin("ESP32_LED_Control"); // Nombre de su dispositivo Bluetooth y en modo esclavo
  Serial.println("El dispositivo Bluetooth está listo para emparejarse");

  pinMode (LED, OUTPUT); // Cambie el pin LED a OUTPUT
}

void loop() {
  if (BT.available()) // Compruebe si recibimos algo de Bluetooth
  {
    int incoming = BT.read(); // Lee lo que recibimos
    Serial.print("Recibido: ");
    Serial.println(incoming);

    if (incoming == 49){ // 1 en ASCII
      digitalWrite(LED, HIGH); // LED Encendido
      BT.println("LED encendido"); // Envía el mensaje de texto a través de BT Serial
    }

    if (incoming == 48){ // 0 en ASCII
      digitalWrite(LED, LOW); // LED Apagado
      BT.println("LED apagado"); // Envía el mensaje de texto a través de BT Serial
    }
  }
  delay(20);
}

El esquema de conexión del LED que utilizaremos será el siguiente:

Ahora nos conectaremos al microcontrolador usando un programa de conexión Serial a través de Bluetooth. Yo he utilizado uno llamado Serial Bluetooth terminal y me ha funcionado correctamente, pero cualquiera te puede valer. Nos conectaremos al ESP32 desplegando el menú lateral y dando a Devices, donde seleccionaremos el dispositivo que se llama como indicamos arriba (ESP32_LED_Control). Una vez conectados, simplemente enviando 1 a través del móvil hacia el ESP32 el led se encenderá, y enviando 0 se apagará.

Como ves, es muy fácil, y con ello podremos enviar y recibir datos a través de bluetooth.

Modo cliente

Ahora que ya hemos aprendido a cómo configurar el ESP32 en modo Serial Bluetooth para poder enviar y recibir datos, aprenderemos a cómo conectarlo como cliente. Para ello utilizaremos en el ESP32 que hará de servidor, el mismo código que hemos utilizado en el apartado anterior.

/*
  Program to control LED (ON/OFF) from ESP32 using Serial Bluetooth
  by Daniel Carrasco -> https://www.electrosoftcloud.com/
*/
#include "BluetoothSerial.h"

#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif

#define LED 23
BluetoothSerial BT; // Objeto Bluetooth

void setup() {
  Serial.begin(9600); // Inicialización de la conexión en serie para la depuración
  BT.begin("ESP32_LED_Control"); // Nombre de su dispositivo Bluetooth y en modo esclavo
  Serial.println("El dispositivo Bluetooth está listo para emparejarse");

  pinMode (LED, OUTPUT); // Cambie el pin LED a OUTPUT
}

void loop() {
  if (BT.available()) // Compruebe si recibimos algo de Bluetooth
  {
    int incoming = BT.read(); // Lee lo que recibimos
    Serial.print("Recibido: ");
    Serial.println(incoming);

    if (incoming == 49){ // 1 en ASCII
      digitalWrite(LED, HIGH); // LED Encendido
      BT.println("LED encendido"); // Envía el mensaje de texto a través de BT Serial
    }

    if (incoming == 48){ // 0 en ASCII
      digitalWrite(LED, LOW); // LED Apagado
      BT.println("LED apagado"); // Envía el mensaje de texto a través de BT Serial
    }
  }
  delay(20);
}

Es muy importante que nos aseguremos de que la función BT.begin inicializa el bluetooth como esclavo o no funcionará (modo por defecto). Una vez configurado el ESP32 que recibirá las conexiones, es hora de configurar el cliente para que se conecte y mande las órdenes de encender y apagar el led. para este propósito utilizaremos el siguiente código:

/*
  Program to control LED (ON/OFF) from ESP32 using Serial Bluetooth
  by Daniel Carrasco -> https://www.electrosoftcloud.com/
*/
#include "BluetoothSerial.h"

#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif

BluetoothSerial BT; // Objeto Bluetooth
String clientName = "ESP32_LED_Control";
bool connected;

void setup() {
  Serial.begin(9600); // Inicialización de la conexión en serie para la depuración
  BT.begin("ESP32_client", true); // Nombre de su dispositivo Bluetooth y en modo maestro
  Serial.println("El dispositivo Bluetooth está en modo maestro. Conectando con el anfitrión ...");

  connected = BT.connect(clientName);
  if(connected) {
    Serial.println("¡Conectado exitosamente!");
  } else {
    while(!BT.connected(10000)) {
      Serial.println("No se pudo conectar. Asegúrese de que el dispositivo remoto esté disponible y dentro del alcance, luego reinicie la aplicación."); 
    }
  }
}

void loop() {
  delay(500);
  BT.write(49); // Envía 1 en ASCII
  delay(500);
  BT.write(48); // Envía 0 en ASCII
}

Nos fijaremos en que para iniciar el bluetooth en modo Master hemos añadido la opción al ejecutar el begin. Esto es muy importante, porque sino no serás capaz de conectarte. Para que esto funcione es necesario que enciendas el ESP32 que hace de Host primero para que el bluetooth sea detectable, y luego enciendas el que hará de cliente para que se conecte y empiece a mandar los datos a través del puerto serie.

Función Callback

Una última cosa que nos falta por conocer, es la función callback. Para los que no sepan qué es esta función, os diré que es simplemente una función que se ejecutará cuando haya algún evento bluetooth. Por poner un ejemplo, cuando un cliente se conecta, se desconecta, recive y envía datos… El listado completo de los eventos que disparan esta función es la siguiente:

  • ESP_SPP_INIT_EVT: Cuando el modo SPP es inicializado
  • ESP_SPP_UNINIT_EVT: Cuando el modo SPP es desinicializado
  • ESP_SPP_DISCOVERY_COMP_EVT: Cuando se completa el descubrimiento de servicios
  • ESP_SPP_OPEN_EVT: Cuando un cliente SPP abre una conexión
  • ESP_SPP_CLOSE_EVT: Cuando se cierra una conexión SPP
  • ESP_SPP_START_EVT: Cuando se inicializa el servidor SPP
  • ESP_SPP_CL_INIT_EVT: Cuando un cliente SPP inicializa una conexión
  • ESP_SPP_DATA_IND_EVT: Al recibir datos a través de una conexión SPP
  • ESP_SPP_CONG_EVT: Cuando cambia el estado de congestión en una conexión SPP
  • ESP_SPP_WRITE_EVT: Al enviar datos a través de SPP.
  • ESP_SPP_SRV_OPEN_EVT: Cuando un cliente se conecta al servidor SPP
  • ESP_SPP_SRV_STOP_EVT: Cuando el servidor SPP se para

Como puedes observar, disponemos de una buena cantidad de eventos disponibles. Estos pueden ser bastante útiles, porque por ejemplo, puedes detectar si se ha desconectado el cliente y relanzar la conexión, o detectar cuando se reciben datos en lugar de estar preguntando continuamente. Por ejemplo, el ejemplo de Host con el que controlamos el LED puede ser remplazado por el siguiente:

/*
  Program to control LED (ON/OFF) from ESP32 using Serial Bluetooth
  by Daniel Carrasco -> https://www.electrosoftcloud.com/
*/
#include "BluetoothSerial.h"

#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif

#define LED 23
BluetoothSerial BT; // Objeto Bluetooth

void callback_function(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) {
  if (event == ESP_SPP_START_EVT) {
    Serial.println("Inicializado SPP");
  }
  else if (event == ESP_SPP_SRV_OPEN_EVT ) {
    Serial.println("Cliente conectado");
  }
  else if (event == ESP_SPP_CLOSE_EVT  ) {
    Serial.println("Cliente desconectado");
  }
  else if (event == ESP_SPP_DATA_IND_EVT ) {
    Serial.println("Datos recibidos");
    while (BT.available()) { // Mientras haya datos por recibir
      int incoming = BT.read(); // Lee un byte de los datos recibidos
      Serial.print("Recibido: ");
      Serial.println(incoming);

      if (incoming == 49) { // 1 en ASCII
        digitalWrite(LED, HIGH); // Encender el LED
        BT.println("LED encendido"); // Envía el texto a través del puerto Serial del BT
      }
      else if (incoming == 48) { // 0 en ASCII
        digitalWrite(LED, LOW); // Apagar el LED
        BT.println("LED apagado"); // Envía el texto a través del puerto Serial del BT
      }
    }
  }
}

void setup() {
  Serial.begin(115200); // Inicializando la conexión serial para debug
  BT.begin("ESP32_LED_Control"); // Nombre de tu Dispositivo Bluetooth y en modo esclavo
  Serial.println("El dispositivo Bluetooth está listo para emparejar");

  BT.register_callback(callback_function); // Registramos la función "callback_function" como función callback.

  pinMode (LED, OUTPUT); // Cambia el PIN del led a OUTPUT
}

void loop() {
}

Como puedes ver, hemos liberado el loop principal y ya no tiene que estar preguntando todo el rato si hay datos, sino que el propio programa lanza la función que le hemos indicado cada vez que hay un evento, y nosotros actuamos en consecuencia. Esto hará que nuestro programa sea más limpio y esté mejor optimizado.

Espero que os sirva y no dudéis en comentar cualquier cosa. Un saludo.

7 comentarios en «ESP32: Empezando a usar el Bluetooth (SPP)»

  1. Una mejora que reconecta, y pinta en ASCII

    #include «BluetoothSerial.h»
    #if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
    #error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
    #endif
    BluetoothSerial BT; // Objeto Bluetooth
    String clientName = «Nivel_Gasoil»;
    bool connected;
    #define LED 23
    String cadena;

    void callback_function(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) {
    if (event == ESP_SPP_START_EVT) {
    Serial.println(«Inicializado SPP»);
    }
    else if (event == ESP_SPP_SRV_OPEN_EVT ) {
    Serial.println(«Cliente conectado»);
    connected = true;
    }
    else if (event == ESP_SPP_CLOSE_EVT ) {
    Serial.println(«Cliente desconectado»);
    connected = false;
    }
    else if (event == ESP_SPP_DATA_IND_EVT ) {
    // Serial.println(«»);
    while (BT.available()) { // Mientras haya datos por recibir
    int incoming = BT.read(); // Lee un byte de los datos recibidos
    cadena = cadena + String((char)incoming);
    if (incoming == 13) Serial.print(cadena);
    }
    }
    }

    String getValue(String data, char separator, int index) // Saca los tres valores del HTML RESPONSE
    {
    int found = 0;
    int strIndex[] = {0, -1};
    int maxIndex = data.length()-1;
    for(int i=0; i<=maxIndex && foundindex ? data.substring(strIndex[0], strIndex[1]) : «»;
    }

    void setup() {
    Serial.begin(9600); // Inicialización de la conexión en serie para la depuración
    BT.begin(«ESP32_client», true); // Nombre de su dispositivo Bluetooth y en modo maestro
    Serial.println(«El dispositivo Bluetooth está en modo maestro. Conectando con el anfitrión …»);
    connected = BT.connect(clientName);

    if(connected) {
    Serial.println(«¡Conectado exitosamente!»);
    } else {
    while(!BT.connected(10000)) {
    Serial.println(«No se pudo conectar. Asegúrese de que el dispositivo remoto esté disponible y dentro del alcance, luego reinicie la aplicación.»);
    }
    }

    BT.register_callback(callback_function); // Registramos la función «callback_function» como función callback.
    pinMode (LED, OUTPUT); // Cambia el PIN del led a OUTPUT
    }

    void loop() {

    if (!connected){
    connected = BT.connect(clientName);
    }
    delay(2000);

    }

    Responder
    • Hola,

      Me embarqué en otro proyecto y no pude usar Bluetooth por necesitar el WiFi (los dos a la vez son problemáticos), así que me pasé a ESP-Now para las conexiones entre mis dos ESP32 y no llegué a testearlo lo suficiente. De todas formas hasta donde yo sé, no se intenta conectarse de nuevo, pero es un problema de fácil solución ya que la reconexión se puede gestionar fácilmente creando por ejemplo una tarea que se encargue de conectar, y utilizando los callbacks de conexión y desconexión para activarla y desactivarla cuando sea necesario.

      Un saludo.

      Responder

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.