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.
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);
}
Gracias por tu aporte! 😉
Hola,
Quizá te pueda servir esto: https://pastebin.com/BgkwtGwr
No he podido probarlo, pero posiblemente funcione.
También te recomiendo echarte un vistazo al protocolo ESP-Now, ya que no necesitas conectarte como tal, sino que envías el dato desde el master a la dirección MAC del esclavo y este lo recibe sin más.
Un saludo.
Gracias Daniel por su respuesta, me podría dar una pista de como sería esa tarea? como reconecto? muchas gracias por su tiempo y paciencia.
Una pregunta: cuando se pierde el enlace BT es necesario dar un reset al ESP32?, se puede recuperar la conexiòn sin reiniciar el ESP?
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.
Gracias es un trabajo muy claro y muy útil, yo estaba empantanado con algunas definiciones sobre BLE y esto me a ayudado, nuevamente gracias!