Configuración Wifi dinámica en ESP32 con Firebase
Introducción
Si eres un programador que suele frecuentar los microcontroladores para sus proyectos personales o no tan personales, ni falta que hace mencionarte lo tedioso que es el tema de la configuración wifi dentro de éstos. No obstante, si no estás tan familiarizado puede que te estés preguntando, tedioso ¿por qué?…
Bueno, la configuración de la conexión wifi es algo que se define dentro del programa, y se da por supuesto que durante la ejecución del mismo no se va a modificar. Pero, ¿y si además de necesitar cambiarlo durante la ejecución se da el caso de que no tenemos los medios para realizar el cambio en el código fuente de nuestro programa? Realmente es una gran limitación. De esta forma, supongamos que vamos a diseñar un producto que se conecta a Internet y hace X, ¿venderías dicho producto con una configuración wifi preestablecida e imposible de modificar por el usuario final? No lo creo.
Con estos ejemplos simplemente nos ponemos en situación de las limitaciones que nos afectan al tener una configuración estática. Por ello, en este tutorial haremos un approach a una de las posibles soluciones del problema.
Materiales
Para la realización de este proyecto vamos a necesitar los siguientes materiales:
- x1 ESP32
- x1 PushButton
Requerimientos
Para poder completar todos los pasos necesitaremos:
- Cuenta Gmail
- Conocimientos básicos sobre Firebase y su funcionamiento
- Arduino IDE
Esquema
A continuación deberemos conectar nuestro botón según el esquema proporcionado.
Firebase
Lo primero que haremos será crear un proyecto nuevo de Firebase con nuestra cuenta de Gmail. En él estableceremos la estructura de datos en su Realtime Database. Para ello nos dirigiremos a Página Principal Firebase > Ir a consola > Agregar Proyecto (si no nos sale la opción de la consola es porque debemos iniciar sesión).
ESP32
Por fin, llegamos a la parte interesante, donde cargaremos el código en nuestro microcontrolador. Una vez esté subido y corriendo podremos juguetear con los valores de nuestra Realtime Database.
Primero deberemos importar las librerías de las que se hará uso.
#include <WiFi.h>
#include <IOXhop_FirebaseESP32.h>
#include <Preferences.h>
#include <ArduinoJson.h>
#include <AceButton.h>
#define FIREBASE_HOST "YOUR_HOST"
#define FIREBASE_AUTH "YOUR_AUTH"
#define PREFERENCES_APP "EEPROM-TEST"
#define SETTINGS_NODE "/settings"
#define REBOOT_NODE "/need_reboot"
#define WIFI_NODE "/wifi"
#define SSID_NODE "/wifi/ssid"
#define PASSWORD_NODE "/wifi/password"
#define DEFAULT_SSID "YOUR_SSID"
#define DEFAULT_PASSWORD "YOUR_PASSWORD"
#define RESET_BUTTON_PIN 18
using namespace ace_button;
Preferences preferences; // Se declara una variable para manejar la EEPROM (memoria permanente)
AceButton resetButton(RESET_BUTTON_PIN); // Se declara el botón en el pin definido
// Se define el callback que usará nuestro botón
void handleResetButtonEvent(AceButton* /* button */, uint8_t eventType, uint8_t buttonState) {
switch (eventType) {
// Si recibe un evento de larga pulsación resetea el nombre de red y contraseña de la EEPROM y se reinicia
case AceButton::kEventLongPressed:
preferences.begin(PREFERENCES_APP, false);
preferences.clear();
preferences.end();
Serial.println("nResetting SSID and PASSWORD");
ESP.restart();
break;
}
}
void setup() {
Serial.begin(115200);
// Se inicializa el botón de reset con una resistencia interna
pinMode(RESET_BUTTON_PIN, INPUT_PULLUP);
// Se inicializa el botón con su configuración y su callback
ButtonConfig* resetButtonConfig = resetButton.getButtonConfig();
resetButtonConfig->setEventHandler(handleResetButtonEvent);
resetButtonConfig->setFeature(ButtonConfig::kFeatureLongPress);
// Se inicializa la biblioteca para controlar la EEPROM
preferences.begin(PREFERENCES_APP, true);
// Se recuperan las credenciales guardadas o se establecen unas por defecto
String strWIFI_SSID = preferences.getString("SSID", DEFAULT_SSID);
String strWIFI_PASSWORD = preferences.getString("PASSWORD", DEFAULT_PASSWORD);
// Se muestran las credenciales que se van a usar por consola
Serial.printf("Current SSID: %sn", strWIFI_SSID.c_str());
Serial.printf("Current PASSWORD: %sn", strWIFI_PASSWORD.c_str());
// Se termina la edición de la EEPROM
preferences.end();
// Se convierten las credenciales a tipo char
char charWIFI_SSID[strWIFI_SSID.length()+1];
char charWIFI_PASSWORD[strWIFI_PASSWORD.length()+1];
strWIFI_SSID.toCharArray(charWIFI_SSID, strWIFI_SSID.length()+1);
strWIFI_PASSWORD.toCharArray(charWIFI_PASSWORD, strWIFI_PASSWORD.length()+1);
// Se inicializa la conexión wifi con las credenciales
WiFi.begin(charWIFI_SSID, charWIFI_PASSWORD);
Serial.printf("Connecting to %s", strWIFI_SSID.c_str());
while (WiFi.status() != WL_CONNECTED) {
resetButton.check();
Serial.print(".");
delay(500);
}
Serial.print("nConnected with IP: ");
Serial.println(WiFi.localIP());
// Se inicializa la conexión con Firebase
Firebase.begin(FIREBASE_HOST, FIREBASE_AUTH);
// Se establece el listener de la conexión con Firebase
Firebase.stream(SETTINGS_NODE, [](FirebaseStream stream) {
String eventType = stream.getEvent();
eventType.toLowerCase();
Serial.printf("event: %sn", eventType.c_str());
if (eventType == "put") {
String path = stream.getPath(); // Se obtiene la ruta del cambio
if (path == WIFI_NODE) { // Si el cambio se realizó en el nodo entero de las credenciales wifi
JsonObject& streamData = stream.getData();
String ssid = streamData["ssid"].as<String>();
String password = streamData["password"].as<String>();
Serial.printf("WIFI NODE - SSID: %sn", ssid.c_str());
Serial.printf("WIFI NODE - PASSWORD: %sn", password.c_str());
preferences.begin(PREFERENCES_APP, false);
preferences.putString("SSID", ssid);
preferences.putString("PASSWORD", password);
preferences.end();
}
else if (path == SSID_NODE) { // Si el cambio se realizó en el nombre de la red
String streamData = stream.getDataString();
Serial.printf("New SSID: %sn", streamData.c_str());
preferences.begin(PREFERENCES_APP, false);
preferences.putString("SSID", streamData);
preferences.end();
}
else if (path == PASSWORD_NODE) { // Si el cambio se realizó en la contraseña
String streamData = stream.getDataString();
Serial.printf("New Password: %sn", streamData.c_str());
preferences.begin(PREFERENCES_APP, false);
preferences.putString("PASSWORD", streamData);
preferences.end();
}
else if (path == REBOOT_NODE) { // Si el cambio se realizó en el nodo de reinicio
bool streamData = stream.getDataBool();
Serial.printf("Need Reboot?: %sn", streamData ? "true" : "false");
if (streamData) { // Se reinicia el dispositivo si se ha indicado que es necesario
String route = SETTINGS_NODE;
route = route + REBOOT_NODE;
Firebase.setBool(route, false);
ESP.restart();
}
}
Serial.printf("Path: %sn", path.c_str()); // Se muestra por consola la ruta donde se realizaron los cambios
}
});
}
void loop() {
// Se comprueba el estado del botón constantemente
resetButton.check();
}
#include <WiFi.h>
#include <IOXhop_FirebaseESP32.h>
#include <Preferences.h>
#include <ArduinoJson.h>
#include <AceButton.h>
#define FIREBASE_HOST "YOUR_HOST"
#define FIREBASE_AUTH "YOUR_AUTH"
#define PREFERENCES_APP "EEPROM-TEST"
#define SETTINGS_NODE "/settings"
#define REBOOT_NODE "/need_reboot"
#define WIFI_NODE "/wifi"
#define SSID_NODE "/wifi/ssid"
#define PASSWORD_NODE "/wifi/password"
#define DEFAULT_SSID "YOUR_SSID"
#define DEFAULT_PASSWORD "YOUR_PASSWORD"
#define RESET_BUTTON_PIN 18
using namespace ace_button;
Preferences preferences; // Se declara una variable para manejar la EEPROM (memoria permanente)
AceButton resetButton(RESET_BUTTON_PIN); // Se declara el botón en el pin definido
// Se define el callback que usará nuestro botón
void handleResetButtonEvent(AceButton* /* button */, uint8_t eventType, uint8_t buttonState) {
switch (eventType) {
// Si recibe un evento de larga pulsación resetea el nombre de red y contraseña de la EEPROM y se reinicia
case AceButton::kEventLongPressed:
preferences.begin(PREFERENCES_APP, false);
preferences.clear();
preferences.end();
Serial.println("nResetting SSID and PASSWORD");
ESP.restart();
break;
}
}
void setup() {
Serial.begin(115200);
// Se inicializa el botón de reset con una resistencia interna
pinMode(RESET_BUTTON_PIN, INPUT_PULLUP);
// Se inicializa el botón con su configuración y su callback
ButtonConfig* resetButtonConfig = resetButton.getButtonConfig();
resetButtonConfig->setEventHandler(handleResetButtonEvent);
resetButtonConfig->setFeature(ButtonConfig::kFeatureLongPress);
// Se inicializa la biblioteca para controlar la EEPROM
preferences.begin(PREFERENCES_APP, true);
// Se recuperan las credenciales guardadas o se establecen unas por defecto
String strWIFI_SSID = preferences.getString("SSID", DEFAULT_SSID);
String strWIFI_PASSWORD = preferences.getString("PASSWORD", DEFAULT_PASSWORD);
// Se muestran las credenciales que se van a usar por consola
Serial.printf("Current SSID: %sn", strWIFI_SSID.c_str());
Serial.printf("Current PASSWORD: %sn", strWIFI_PASSWORD.c_str());
// Se termina la edición de la EEPROM
preferences.end();
// Se convierten las credenciales a tipo char
char charWIFI_SSID[strWIFI_SSID.length()+1];
char charWIFI_PASSWORD[strWIFI_PASSWORD.length()+1];
strWIFI_SSID.toCharArray(charWIFI_SSID, strWIFI_SSID.length()+1);
strWIFI_PASSWORD.toCharArray(charWIFI_PASSWORD, strWIFI_PASSWORD.length()+1);
// Se inicializa la conexión wifi con las credenciales
WiFi.begin(charWIFI_SSID, charWIFI_PASSWORD);
Serial.printf("Connecting to %s", strWIFI_SSID.c_str());
while (WiFi.status() != WL_CONNECTED) {
resetButton.check();
Serial.print(".");
delay(500);
}
Serial.print("nConnected with IP: ");
Serial.println(WiFi.localIP());
// Se inicializa la conexión con Firebase
Firebase.begin(FIREBASE_HOST, FIREBASE_AUTH);
// Se establece el listener de la conexión con Firebase
Firebase.stream(SETTINGS_NODE, [](FirebaseStream stream) {
String eventType = stream.getEvent();
eventType.toLowerCase();
Serial.printf("event: %sn", eventType.c_str());
if (eventType == "put") {
String path = stream.getPath(); // Se obtiene la ruta del cambio
if (path == WIFI_NODE) { // Si el cambio se realizó en el nodo entero de las credenciales wifi
JsonObject& streamData = stream.getData();
String ssid = streamData["ssid"].as<String>();
String password = streamData["password"].as<String>();
Serial.printf("WIFI NODE - SSID: %sn", ssid.c_str());
Serial.printf("WIFI NODE - PASSWORD: %sn", password.c_str());
preferences.begin(PREFERENCES_APP, false);
preferences.putString("SSID", ssid);
preferences.putString("PASSWORD", password);
preferences.end();
}
else if (path == SSID_NODE) { // Si el cambio se realizó en el nombre de la red
String streamData = stream.getDataString();
Serial.printf("New SSID: %sn", streamData.c_str());
preferences.begin(PREFERENCES_APP, false);
preferences.putString("SSID", streamData);
preferences.end();
}
else if (path == PASSWORD_NODE) { // Si el cambio se realizó en la contraseña
String streamData = stream.getDataString();
Serial.printf("New Password: %sn", streamData.c_str());
preferences.begin(PREFERENCES_APP, false);
preferences.putString("PASSWORD", streamData);
preferences.end();
}
else if (path == REBOOT_NODE) { // Si el cambio se realizó en el nodo de reinicio
bool streamData = stream.getDataBool();
Serial.printf("Need Reboot?: %sn", streamData ? "true" : "false");
if (streamData) { // Se reinicia el dispositivo si se ha indicado que es necesario
String route = SETTINGS_NODE;
route = route + REBOOT_NODE;
Firebase.setBool(route, false);
ESP.restart();
}
}
// Se muestra por consola la ruta donde se realizaron los cambios
Serial.printf("Path: %sn", path.c_str());
}
});
}
void loop() {
// Se comprueba el estado del botón constantemente
resetButton.check();
}
Prueba
Una vez tengamos el código subido al ESP32 nos dirigiremos a Firebase para cambiar los valores de nuestra red. Cabe destacar que para esta prueba utilizaré la red Wifi de mi casa y la de mi móvil.Descarga
Analyst Frontend Developer en Comunytek (BBVA CIB – NOVA).
Autodidacta, apasionado de las nuevas tecnologías y de los proyectos DIY.