ESP32 y Arduino

Diseño de un sensor ESP32 alimentado por batería: placas, reguladores e intervalos de publicación MQTT

Guía práctica de diseño para un sensor ESP32 alimentado por batería: elección de placa, reguladores de baja corriente de reposo, vigilancia de la tensión de la batería, reconexiones Wi-Fi rápidas e intervalos de publicación MQTT que alargan la autonomía.

Nodo sensor ESP32 alimentado por batería con una celda LiPo y un divisor de tensión enviando lecturas MQTT a la nube
Pruébalo en tu propio broker MQTTConsigue un broker MQTT privado y gestionado con el plan Free y empieza con ejemplos de código listos para usar.
Pruébalo gratis Ejemplos de código Sin tarjeta

Un sensor ESP32 alimentado por batería puede funcionar durante meses con una sola celda, pero solo si el hardware y el firmware se diseñan conjuntamente. Esta guía recorre la elección de placa, los reguladores, la vigilancia de la batería y el ciclo de publicación para que tu nodo MQTT despierte, informe y vuelva a dormir gastando la menor energía posible.

Por qué la autonomía es sobre todo una decisión de diseño

El ESP32 no es un chip de bajo consumo en el sentido en que lo es un microcontrolador sin radio. Con el Wi-Fi activo consume mucha corriente, y las ráfagas de transmisión son los momentos más caros de su vida. En deep sleep, el chip por sí solo baja a microamperios. La diferencia entre un nodo que dura una semana y otro que dura una temporada está en cuánto tiempo pasa en cada estado y en cuánto desperdicia el circuito que lo rodea mientras el chip duerme.

Eso te da tres palancas: la corriente en reposo (normalmente dominada por el regulador, el chip USB-serie y los LED, no por el ESP32), el tiempo despierto (arranque, Wi-Fi, DHCP, conexión MQTT, publicación) y la frecuencia de despertar (reducir el intervalo a la mitad duplica aproximadamente la energía de la radio). La placa suele ser la palanca más grande, así que empieza por ahí.

Elegir una placa para un sensor ESP32 alimentado por batería

Las placas de desarrollo están pensadas para la comodidad en el escritorio. Suelen incluir un puente USB-UART siempre encendido, un LED de alimentación y un regulador lineal elegido por precio y no por su corriente de reposo. Cada uno de ellos puede consumir en reposo mucho más que el ESP32.

Opción Comportamiento en reposo Adecuada para
DevKit genérico (ESP32-WROOM + AMS1117 + puente USB) Malo: el regulador y el chip USB consumen miliamperios de forma continua Solo para prototipar firmware
Placas anunciadas como de bajo consumo (cargador LiPo, LDO de baja Iq, USB conmutable) Bueno: diseñadas para deep sleep, a menudo con conector de batería Series pequeñas y pilotos de campo
Módulo desnudo (ESP32-WROOM / ESP32-C3 / ESP32-C6) en tu propia PCB El mejor: tú eliges cada componente Productos y despliegues largos
Placas basadas en ESP32-C3 o C6 Bueno, un solo núcleo, menor corriente activa que el ESP32 clásico en muchas cargas de trabajo Sensores sencillos que solo necesitan Wi-Fi

Al evaluar una placa, quita el LED de alimentación, comprueba si el chip USB-serie cuelga del raíl de 3,3 V y busca la corriente de reposo del regulador. Después mide la corriente en reposo con un multímetro en serie con la batería o con un perfilador de corriente; un medidor de potencia USB no es lo bastante sensible.

Reguladores y química de la batería

El ESP32 necesita aproximadamente 3,0–3,6 V y puede demandar picos breves de varios cientos de miliamperios al transmitir por Wi-Fi. Tu ruta de alimentación debe entregar esos picos sin caídas de tensión y no desperdiciar casi nada en reposo.

Una celda LiPo / Li-ion (3,0–4,2 V). La opción más habitual. Usa un regulador de baja caída con una corriente de reposo de pocos microamperios y margen suficiente para los picos de transmisión. A medida que la celda se descarga hacia 3,3 V el LDO deja de regular, así que cuenta con que el nodo dejará de informar con fiabilidad en algún punto en torno a 3,4–3,5 V de celda, y envía un aviso de batería baja bastante antes.

Celda LiFePO4 (unos 3,2–3,3 V nominales). Su tensión se mantiene dentro del rango de funcionamiento del ESP32 durante la mayor parte de la curva de descarga, por lo que algunos diseños prescinden del regulador; comprueba antes la tensión máxima de carga frente a los valores máximos absolutos.

Pilas AA. Las pilas alcalinas caen bajo los picos de transmisión, sobre todo con frío. Combínalas con un convertidor buck-boost de baja corriente de reposo o usa pilas primarias de litio.

Añade un condensador de bulk (unos cientos de microfaradios) cerca del módulo. Suministra las ráfagas de transmisión y evita que el detector de brownout reinicie el chip, una de las causas más comunes de bucles de reinicio misteriosos en nodos con batería.

Vigilar la tensión de la batería

Un sensor que muere en silencio es peor que uno que muere avisando. Mide la celda con un divisor de tensión hacia un pin ADC e inclúyela en cada payload MQTT.

Un divisor de dos resistencias iguales de valor alto (por ejemplo, 2 × 1 MΩ) divide por dos la tensión de la celda para llevarla al rango del ADC, consumiendo solo microamperios. Los divisores de valor alto tienen una impedancia de fuente elevada, así que añade un pequeño condensador (unos 100 nF) entre el pin ADC y masa para darle al ADC una muestra estable. Si incluso esa fuga importa, conmuta el lado bajo del divisor con un MOSFET controlado por un GPIO.

En el core Arduino del ESP32 2.x y 3.x, analogReadMilliVolts() aplica la calibración de fábrica y devuelve milivoltios directamente, lo que es mucho más preciso que escalar tú mismo los valores en bruto de analogRead(). Promedia varias muestras, porque las lecturas individuales tienen ruido. En el ESP32 clásico, usa un pin del ADC1 (GPIO 32–39), ya que el ADC2 no está disponible mientras el Wi-Fi está activo.

Un patrón de firmware despertar–publicar–dormir

El firmware de abajo es un sketch de Arduino completo para un nodo con batería. Despierta del deep sleep, se reconecta al Wi-Fi usando el canal y el BSSID guardados en la memoria RTC, publica un mensaje JSON con la tensión de la batería, se desconecta limpiamente y vuelve a dormir. Usa broker.connectmq.com, el servidor MQTT público gratuito pensado para pruebas y prototipos, así que elige un prefijo de topic único y no envíes nada sensible ni datos de producción.

#include <WiFi.h>
#include <PubSubClient.h>

const char* WIFI_SSID = "YOUR_WIFI";
const char* WIFI_PASS = "YOUR_WIFI_PASSWORD";
const char* MQTT_HOST = "broker.connectmq.com";
const uint16_t MQTT_PORT = 1883;
const char* TOPIC = "connectmq/demo/7f3a91/garden-node/telemetry";

const uint64_t SLEEP_SECONDS = 600;     // publicar cada 10 minutos
const int BATTERY_PIN = 34;             // pin ADC1, divisor 2 x 1 MOhm
const float DIVIDER_RATIO = 2.0;

RTC_DATA_ATTR uint32_t bootCount = 0;
RTC_DATA_ATTR uint8_t savedBssid[6];
RTC_DATA_ATTR int32_t savedChannel = 0;

WiFiClient net;
PubSubClient mqtt(net);

float readBatteryVolts() {
  uint32_t sum = 0;
  for (int i = 0; i < 16; i++) sum += analogReadMilliVolts(BATTERY_PIN);
  return (sum / 16.0f) * DIVIDER_RATIO / 1000.0f;
}

bool connectWifi() {
  WiFi.mode(WIFI_STA);
  WiFi.persistent(false);
  if (savedChannel > 0) {
    WiFi.begin(WIFI_SSID, WIFI_PASS, savedChannel, savedBssid);
  } else {
    WiFi.begin(WIFI_SSID, WIFI_PASS);
  }
  unsigned long start = millis();
  while (WiFi.status() != WL_CONNECTED) {
    if (millis() - start > 10000) {
      savedChannel = 0;                 // forzar un escaneo completo la próxima vez
      return false;
    }
    delay(20);
  }
  savedChannel = WiFi.channel();
  memcpy(savedBssid, WiFi.BSSID(), 6);
  return true;
}

void goToSleep() {
  mqtt.disconnect();
  WiFi.disconnect(true);
  WiFi.mode(WIFI_OFF);
  esp_sleep_enable_timer_wakeup(SLEEP_SECONDS * 1000000ULL);
  esp_deep_sleep_start();
}

void setup() {
  bootCount++;
  float battery = readBatteryVolts();   // leer antes de que la radio cargue la celda

  if (!connectWifi()) goToSleep();

  String clientId = "esp32-batt-" + String((uint32_t)ESP.getEfuseMac(), HEX);
  mqtt.setServer(MQTT_HOST, MQTT_PORT);
  mqtt.setSocketTimeout(5);
  if (!mqtt.connect(clientId.c_str())) goToSleep();

  char payload[96];
  snprintf(payload, sizeof(payload),
           "{\"battery\":%.2f,\"boot\":%lu,\"rssi\":%d}",
           battery, (unsigned long)bootCount, WiFi.RSSI());
  mqtt.publish(TOPIC, payload);
  mqtt.loop();
  delay(50);                            // dejar que la pila TCP vacíe sus búferes
  goToSleep();
}

void loop() {}

Aquí importan algunos detalles:

  • La batería se lee antes de arrancar el Wi-Fi. Bajo carga de transmisión la tensión de la celda cae, así que una lectura tomada después parece más baja de lo que es.
  • Todas las rutas de fallo terminan durmiendo. Si el Wi-Fi o el broker no son accesibles, el nodo no reintenta indefinidamente agotando la celda. Lo vuelve a intentar en el siguiente despertar.
  • El canal y el BSSID en caché permiten que la radio se salte el escaneo completo, lo que acorta cada despertar. Si el punto de acceso cambia, el timeout borra la caché y el siguiente arranque escanea con normalidad.
  • Una IP estática (mediante WiFi.config()) elimina también el intercambio DHCP. Úsala cuando controles la red.

Para profundizar en los modos de sueño, las fuentes de despertar y cómo conservar el estado entre despertares, consulta la guía de ESP32 deep sleep MQTT.

Elegir el intervalo de publicación

El intervalo adecuado depende de lo rápido que cambie el valor y de lo desfasada que pueda quedar una lectura.

Medida Intervalo útil típico Notas
Temperatura de una sala o invernadero 5–15 minutos Cambia despacio; promedia varias muestras por despertar
Humedad del suelo 30–60 minutos Muy lenta; plantéate muestrear más a menudo pero publicar solo cuando cambie
Nivel de un depósito de agua 10–30 minutos Añade una publicación inmediata al cruzar un umbral
Puerta o movimiento Por eventos Despertar por GPIO (ext0/ext1), no por temporizador
Tensión de la batería En cada despertar Barato de incluir; esencial para el mantenimiento

Dos técnicas estiran todavía más el presupuesto. Agrupar: despierta brevemente sin Wi-Fi para tomar una lectura, guárdala en la memoria RTC y conéctate solo cada N despertares para publicar el lote como un único array JSON. Informar al cambiar: compara el valor nuevo con el último publicado guardado en la memoria RTC y no enciendas la radio si no se ha movido más allá de una banda muerta. Publica siempre al menos un latido periódico para poder distinguir un sensor silencioso de uno muerto.

Ajustes MQTT que ahorran energía

Las decisiones de protocolo en el lado del dispositivo tienen un coste directo en tiempo de emisión:

  • QoS 0 para la telemetría rutinaria. QoS 1 añade un viaje de ida y vuelta de PUBACK y mantiene la radio encendida más tiempo. Úsalo solo para lecturas que no te puedas permitir perder; los niveles de QoS de MQTT explicados cubre ese equilibrio.
  • Mensajes retenidos para el último valor. Publica la telemetría con el flag retain y un dashboard que se conecte entre despertares seguirá viendo la última lectura de inmediato.
  • Sesiones limpias y cortas. Un nodo que duerme no necesita una suscripción persistente. Conéctate con clean session, publica y desconéctate.
  • Payloads compactos. Las claves JSON cortas bastan con unas pocas lecturas por hora.
  • TLS cuesta un handshake. Las conexiones cifradas en el puerto 8883 tardan bastante más en establecerse que TCP sin cifrar en el 1883 y mantienen la radio encendida más tiempo. En producción ese precio merece la pena, y MQTT sobre SSL/TLS en ESP32 muestra cómo abaratar el handshake todo lo posible.

Lista de comprobación antes del despliegue

  1. Mide la corriente en deep sleep de la placa terminada.
  2. Registra el tiempo despierto por ciclo y busca valores atípicos por reintentos de Wi-Fi.
  3. Confirma que el nodo sobrevive a un arranque en frío con la tensión de celda más baja prevista sin bucles de brownout.
  4. Publica la tensión de la batería y un contador de arranques en cada mensaje.
  5. Configura en el dashboard una alerta por tensión de batería baja y por mensajes que faltan.
  6. Sella la caja, pero mantén la antena alejada de metales y baterías.
  7. Pasa del área pública de pruebas a credenciales privadas antes de instalar nada fuera de tu mesa.

Del broker de pruebas a producción

El servidor público de broker.connectmq.com es ideal mientras ajustas los intervalos, porque puedes ver llegar los mensajes con mosquitto_sub sin registrarte. Para despliegues reales, cambia a un broker privado para que nadie más pueda leer tus datos. ConnectMQ da a cada broker credenciales generadas y un client ID vinculado a ellas; solo cambia la llamada de conexión:

mqtt.setServer("broker.connectmq.com", 8883);   // con WiFiClientSecure y la CA ISRG Root X1
mqtt.connect("YOUR_CLIENT_ID", "YOUR_USERNAME", "YOUR_PASSWORD");

En el portal de ConnectMQ, el explorador de topics en vivo y las reglas de alerta por umbral te permiten vigilar la tensión de batería de toda la flota y recibir un aviso cuando un nodo baje de tu umbral de sustitución. Si vas a construir muchos nodos, el tutorial de IoT con ESP32 muestra cómo encaja el lado del dispositivo en un proyecto completo, y la guía del broker MQTT en la nube explica de qué te libera un broker MQTT gestionado.

Empieza con ConnectMQ

Prototipa hoy tu nodo con batería contra broker.connectmq.com y después crea un broker privado en el plan gratuito Serverless en app.connectmq.com, sin tarjeta de crédito. Es un broker MQTT gestionado, así que dedicas tu tiempo al presupuesto energético en lugar de al mantenimiento del broker.

Preguntas frecuentes

¿Cuánto puede durar un sensor ESP32 alimentado por batería con una celda LiPo?

Depende de la corriente en reposo de la placa, la duración de cada despertar y el intervalo de publicación. Una placa con un regulador de baja corriente de reposo y sin un chip USB siempre encendido, que despierta cada pocos minutos para una publicación breve, puede funcionar durante meses; un DevKit de serie con la misma celda puede durar solo días.

¿Debo usar deep sleep o light sleep para un sensor MQTT?

Usa deep sleep cuando el nodo informa periódicamente y no necesita mantener una conexión. Light sleep conserva la RAM y puede mantener la asociación Wi-Fi, pero su corriente en reposo es mucho mayor, así que conviene a nodos que deben reaccionar rápido a comandos.

¿Por qué mi ESP32 con batería se reinicia al arrancar el Wi-Fi?

Normalmente es el detector de brownout reaccionando a la caída de tensión durante las ráfagas de transmisión. Añade un condensador de bulk cerca del módulo, usa un regulador capaz de suministrar la corriente de pico y comprueba la resistencia interna de la batería, sobre todo con frío.

¿Puedo medir la tensión de la batería sin desperdiciar corriente?

Sí. Usa un divisor de valor alto con un pequeño condensador en el pin ADC, o desconecta el divisor con un MOSFET entre lecturas, y léelo con analogReadMilliVolts() en un pin del ADC1.

Artículos relacionados

Ejemplo de ESP-IDF MQTT: crea un cliente esp-mqtt desde cero

Construye un proyecto ESP-IDF desde una carpeta vacía con el componente esp-mqtt: configuración de Wi-Fi en modo estación, el manejador de eventos MQTT, publicación y suscripción, propiedades de MQTT 5 y TLS con el bundle de certificados.

9 min de lectura

Todos los artículos →

Tu broker MQTT privado, a unos pocos clics

Crea una cuenta, obtén las credenciales de tu broker y conecta tu primer dispositivo, sin servidores que instalar, parchear ni escalar.