01 · Objetivo
Qué vamos a construir
El proyecto es un nodo ambiental con batería, con los requisitos de un dispositivo real:
- Medir la temperatura y la humedad con un BME280 (el sensor I²C de los temas 16 a 20), en modo forzado para que se apague solo al terminar.
- Medir su propia batería con un divisor de tensión y el conversor del ESP32, sin comerse el presupuesto de energía.
- Publicar cada lectura en un mensaje binario de 10 bytes, con versión, número de nodo y número de secuencia, como datos del fabricante de un paquete de publicidad BLE (sin conexión, como una baliza del tema 38).
- Dormir en sueño profundo entre ciclos, con un período de 300 s configurable, conservando el contador de secuencia en la memoria del reloj de tiempo real.
- Declarar la falta de dato: si el sensor no responde, el mensaje se envía igual con el valor «sin dato» (
0x8000, como en la característica de temperatura del tema 38). - Recibir en una computadora con Python (
bleak), validar el mensaje, descartar repeticiones y contar los mensajes perdidos por saltos en la secuencia. - Cumplir un objetivo de duración: más de dos años con una batería de litio de 2000 mAh.
Lo importante no son las líneas de código sino las decisiones de diseño. Una: dividir la vida del nodo en un ciclo breve y un sueño largo, y calcular la carga de cada fase. Dos: que el mensaje sea autodescriptivo y mida su propia calidad de enlace. Tres: atacar primero lo que más pesa en la cuenta, que casi nunca es lo que parece. Es la integración de todo lo visto en esta parte: el enlace (temas 34 y 39), BLE (38), el ahorro de energía de Wi-Fi (36) y el diseño de mensajes que se verá en profundidad en los temas 41 a 46.
02 · Materiales y cableado
Materiales y cableado
| Cantidad | Componente | Nota |
|---|---|---|
| 1 | Módulo ESP32 (por ejemplo, ESP32-WROOM-32 en una placa mínima) | Sin chip USB ni LED de alimentación. Para programarlo se usa un adaptador USB-serie externo que se retira después |
| 1 | Regulador de 3,3 V de baja corriente de reposo (MCP1700-3302, HT7333 o similar) | El AMS1117 de las placas comunes gasta unos 5 mA solo por existir |
| 1 | Celda de litio de 3,7 V y 2000 mAh (con protección) | O dos pilas AA con otro regulador; las pilas de botón no soportan el pico de la radio |
| 1 | BME280 (placa con la dirección 0x76) | Funciona con 3,3 V; en modo forzado duerme con menos de 1 µA |
| 2 | Resistencias de 1 MΩ | Divisor para medir la batería |
| 3 | Condensadores: 100 nF (en el conversor), 10 µF y 100 µF (cerca del módulo) | Los picos de la radio llegan a 100 mA y sin condensador hunden la tensión |
| 1 | Un segundo ESP32 o una computadora con Bluetooth y Python 3 | El receptor, con la biblioteca bleak |
| 1 | Multímetro con escala de microamperios (y, opcional, un analizador de corriente) | Para medir el sueño y la carga del ciclo |
| Opcional | Módulo SX1276 (RFM95) y antena | Para la variante por LoRa (tema 39) |
| Desde | Hasta | Función |
|---|---|---|
| Batería (+) | Entrada del regulador y R1 | Alimentación y medición |
| Salida del regulador (3,3 V) | 3V3 del módulo y VCC del BME280 | Alimentación |
| GPIO 21 | SDA del BME280 | Datos I²C (la placa del BME280 trae sus resistencias de elevación) |
| GPIO 22 | SCL del BME280 | Reloj I²C |
| Punto medio R1 – R2 (con 100 nF a masa) | GPIO 34 | Entrada del conversor (ADC1; solo entrada) |
| R2 y el condensador | Masa | Referencia |
| GND de todo | GND común | Masa |
Una advertencia sobre el módulo: la placa de desarrollo del ESP32 (la más común, con el conversor USB y un regulador lineal) no sirve para este proyecto. Ni siquiera con el sueño profundo activado baja de 5 a 10 mA, por el chip USB y el regulador, y con 2000 mAh duraría unos 13 días (herramienta 1). Se la usa para programar y depurar, y para el nodo definitivo se pasa a un módulo desnudo o a una placa pensada para baterías, con el regulador de la tabla.
03 · Diseñar antes de programar
El presupuesto de energía
Antes de escribir una línea hay que decidir si el proyecto es viable, y para eso alcanza una cuenta: la carga de cada fase (corriente por tiempo, en milicoulombs si las unidades son miliamperios y segundos) más la del sueño, repartida en el período, da la corriente media; la capacidad útil de la batería dividida por esa corriente da la duración. Es el mismo cálculo del tema 34 y del 38, ahora con tres fases despiertas. La herramienta 1 del laboratorio la hace con los valores que siguen:
| Fase | Qué ocurre | Tiempo | Corriente | Carga |
|---|---|---|---|---|
| Arranque | El procesador sale del sueño profundo y arranca el programa desde el principio | 60 ms | 30 mA | 1,80 mC |
| Medición | El BME280 mide en modo forzado y el conversor lee la batería | 25 ms | 25 mA | 0,63 mC |
| Radio | Se inicializa BLE, se publica en tres o cuatro eventos y se apaga | 250 ms | 70 mA | 17,50 mC |
| Sueño | Procesador en sueño profundo, regulador, divisor y sensor en reposo | casi 300 s | 15 µA | 4,50 mC |
| Total por período de 300 s | 24,4 mC | |||
La corriente media es 24,4 mC / 300 s = 81,4 µA. Con una batería de 2000 mAh y el 80 % utilizable son 1600 mAh: 1600 / 0,0814 ≈ 19 650 h, unos 2,2 años. Cumple el objetivo, pero con poco margen, y la tabla enseña dónde está el costo: la radio pesa el 72 % de la energía (y no por la transmisión misma, que dura apenas milisegundos, sino por inicializar BLE y mantenerlo encendido), el sueño pesa el 18 % y el arranque y la medición, el 10 %. Dos conclusiones prácticas:
- Lo que más ayuda es acortar la radio. Reducir la fase de radio de 250 a 60 ms (con menos eventos de publicidad y una inicialización más corta) baja la media a 37 µA y la duración a 4,9 años: con la herramienta 1 se ve de inmediato.
- El sueño no es despreciable. Cada 15 µA de más valen lo mismo que casi 20 mC por ciclo de radio; y con la placa de desarrollo (5 mA), el sueño se come el 99 % de todo y la duración cae a 13 días. En el otro extremo, con un período de una hora, el sueño pasa a ser el 73 % del total: a períodos largos, lo que manda es lo que consume el nodo dormido.
La cuenta inversa es igual de útil: dada la duración que se pide, la herramienta 5 calcula cada cuánto se puede medir. Para dos años con 2000 mAh y 19,9 mC por ciclo, el período mínimo es de 261 s: los 300 s elegidos cumplen. Para cinco años, 15 minutos.
04 · El formato de los datos
El mensaje
Un nodo que publica sin conexión no puede preguntar nada: el mensaje tiene que explicarse solo. El diseño sigue las reglas del tema 38 y anticipa las de los temas 41 y 46: tamaño fijo, enteros de 16 bits, little-endian, unidad entera y un valor reservado para «sin dato».
| Byte | Campo | Tipo | Contenido |
|---|---|---|---|
| 0 | Versión | uint8 | 1. Permite cambiar el formato sin romper a los receptores viejos |
| 1 | Nodo | uint8 | Identificador del emisor (0 a 255) |
| 2 y 3 | Secuencia | uint16 | Sube de a uno por cada medición y da la vuelta en 65 535 |
| 4 y 5 | Temperatura | int16 | Centésimas de grado (23,50 °C = 2350); 0x8000 es «sin dato» |
| 6 y 7 | Humedad | uint16 | Centésimas de por ciento; 0xFFFF es «sin dato» |
| 8 y 9 | Batería | uint16 | Milivolts |
Con 23,5 °C, 55 % de humedad, 3800 mV, la secuencia 1234 y el nodo 1, el mensaje es 01 01 D2 04 2E 09 7C 15 D8 0E: 1234 es 0x04D2 (D2 04), 2350 es 0x092E (2E 09), 5500 es 0x157C (7C 15) y 3800 es 0x0ED8 (D8 0E). Va como datos del fabricante (tipo 0xFF) con el identificador de empresa 0xFFFF, el valor que la Bluetooth SIG reserva para pruebas: el paquete completo es 02 01 06 0D FF FF FF seguido del mensaje, 17 de los 31 bytes y 264 µs por paquete en el aire (herramienta 2). Quedan 14 bytes: lugar para una segunda lectura o para un nombre.
El número de secuencia es la pieza más barata y más útil del formato. Como el nodo no recibe confirmaciones, el receptor mide la calidad del enlace por los saltos del contador: una diferencia de 1 con el último mensaje es lo normal, de 0 es una repetición (la publicidad repite el mismo paquete en varios eventos y canales) y de más de 1 son mensajes perdidos. La resta se hace módulo 65 536, de modo que el contador puede dar la vuelta sin problemas (herramienta 4). Los datos no llevan un CRC propio porque BLE ya trae uno de 24 bits en cada paquete: un mensaje corrupto no llega a la aplicación. En un enlace sin esa protección (un UART sin más, tema 44) habría que agregarlo.
Medir la batería
La tensión de una celda de litio llega a 4,2 V y el conversor del ESP32 admite unos 3,1 V, de modo que se necesita un divisor. Y como queda conectado a la batería para siempre, su corriente es parte del sueño: con 100 kΩ y 100 kΩ gasta 19 µA, más que el procesador dormido; con 1 MΩ y 1 MΩ, menos de 2 µA. El precio de una resistencia grande es que el conversor necesita una fuente de baja impedancia: se agrega un condensador de 100 nF en el pin y se promedian varias lecturas (el programa lee ocho). La herramienta 3 convierte la lectura del pin en tensión, estima el porcentaje y calcula la fuga. El porcentaje es una estimación grosera: la tensión de una celda de litio es plana en el medio de la descarga y depende de la temperatura y de la carga, de modo que conviene enviar los milivolts y decidir en el receptor.
05 · La estructura del programa
Un programa que nunca se queda esperando
El nodo no tiene un loop() que haga algo: cada despertar es un arranque completo. Al salir del sueño profundo, el ESP32 pierde toda la memoria de trabajo, reinicia el programa desde el principio (setup()) y solo conserva una pequeña zona de memoria de reloj de tiempo real, marcada con RTC_DATA_ATTR. El programa, entonces, es una receta lineal que termina en esp_deep_sleep_start(), que no retorna:
- Despertar: incrementar el contador de secuencia (que está en la memoria RTC).
- Medir: iniciar el BME280 en modo forzado, esperar el resultado y leerlo; leer la batería promediando ocho muestras. Si el sensor no responde, usar los valores de «sin dato».
- Armar el mensaje de 10 bytes con enteros de tamaño fijo.
- Publicar: inicializar BLE, cargar el paquete, publicitar unos 100 ms (varios eventos, porque no hay confirmación) y apagar.
- Dormir: programar el despertar por temporizador y entrar en sueño profundo.
La falta de un bucle y de esperas es una decisión de consumo: cada milisegundo despierto cuesta, y por eso el programa no usa delay() salvo cuando espera algo imprescindible (los 100 ms de publicidad). Tampoco abre el puerto serie en producción (Serial.begin alarga el arranque), no inicializa Wi-Fi y apaga lo que enciende. El número de secuencia hace de única memoria: cualquier otro estado (como la última lectura, para enviar solo si cambia) tendría que vivir también en la memoria RTC.
06 · Lo que pasa mientras no pasa nada
Dormir de verdad
«Sueño profundo» es una promesa del procesador, no del montaje. El ESP32 solo, en ese modo, consume unos 10 µA (el reloj de tiempo real y la memoria RTC encendidos); todo lo demás de la placa suma lo suyo. La lista de los culpables habituales es la parte más valiosa del taller:
| Elemento | Consumo típico | Qué hacer |
|---|---|---|
| Chip USB-serie de la placa de desarrollo | Algunos mA | Módulo desnudo o placa pensada para baterías |
| Regulador lineal común (AMS1117) | 5 a 10 mA de reposo | Regulador con corriente de reposo de µA (MCP1700, HT7333, TPS7A02) |
| LED de alimentación | 1 a 5 mA | Quitarlo o cortar su pista |
| Divisor de la batería | Batería / (R1 + R2): 19 µA con 100 kΩ, 1,9 µA con 1 MΩ | Resistencias de 1 MΩ y condensador (herramienta 3), o un transistor que lo conecte solo al medir |
| Sensor | BME280 forzado: < 1 µA; otros sensores, de 10 µA a mA | Elegir un sensor con modo de reposo, o alimentarlo desde un pin del procesador y apagarlo |
| Resistencias de elevación de I²C | 3,3 V / 10 kΩ = 330 µA si una línea queda a nivel bajo | Dejar las líneas en alto antes de dormir y desconectar el sensor si hace falta |
| Pines flotantes | Fugas de µA en entradas sin definir | Fijar el nivel de los pines que quedan conectados (gpio_hold) |
El método para encontrar un culpable es medir, no adivinar: se mide la corriente de sueño del montaje real (sección 7) y, si es mayor que los 10 a 20 µA esperados, se desconectan los componentes de a uno hasta que baje. La herramienta 1 muestra cuánto vale cada microamperio: con 15 µA, el sueño es el 18 % del consumo a 5 minutos y el 73 % a una hora.
Otras particularidades del sueño profundo
- El reloj interno no es exacto. El temporizador del sueño usa el oscilador RC del chip (unos 150 kHz), con un error de hasta el 5 % y dependiente de la temperatura. Un período de «300 s» es de 285 a 315 s. Para una lectura ambiental no importa; para sincronizar nodos o poner la hora, sí (hace falta un cristal de 32 kHz o sincronizar con el receptor).
millis()vuelve a cero en cada despertar, porque es un arranque nuevo. Cualquier cuenta de tiempo larga se hace con el contador de secuencia o con el reloj de tiempo real.- El pico de la radio (más de 100 mA durante milisegundos) hunde la tensión de una batería vieja, de una pila pequeña o de un regulador sin condensador, y el ESP32 se reinicia con el detector de bajón de tensión. Condensadores de 10 y 100 µF cerca del módulo y una batería que entregue ese pico son parte del diseño.
- Hay otras fuentes de despertar: un pin (un botón, un sensor de movimiento, una puerta), el tacto o un coprocesador de ultra bajo consumo. Un nodo que despierta por un evento en lugar de por tiempo consume lo mismo dormido y no gasta ciclos en vano.
07 · Poner a andar el nodo
Plan de pruebas
Se prueba de a una capa, de abajo hacia arriba, y con cada paso se compara con una cifra del presupuesto:
- El sensor, en el banco. Con la placa de desarrollo y el puerto serie, comprobar que
bme.begin(0x76)responde y que las lecturas son plausibles (la temperatura ambiente, la humedad entre 20 y 80 %). Si no responde, el programa debe seguir y publicar «sin dato»: se prueba desconectando el sensor. - El mensaje, con una aplicación. Con nRF Connect (tema 38), buscar el dispositivo: aparece sin nombre, con Manufacturer data de la empresa 0xFFFF y los 10 bytes. Verificar los bytes con la herramienta 2 y comprobar que la secuencia sube de a uno.
- El receptor. Con el programa de Python, comprobar que imprime las lecturas, que descarta las repeticiones (el mismo número de secuencia llega más de una vez) y que cuenta cero pérdidas a un metro.
- La corriente de sueño. Alimentar el nodo definitivo desde la batería, con el multímetro en la escala de µA en serie con la alimentación, sin el adaptador USB conectado. Esperar que el nodo duerma (unos segundos). Esperado: 10 a 20 µA; si da miliamperios, hay un culpable de la tabla anterior.
- La carga de un ciclo. Medir la corriente en el tiempo con un analizador (o con una resistencia de 10 Ω en serie y un osciloscopio: 70 mA son 0,7 V) y comparar el área con los 19,9 mC del presupuesto. Los tiempos de cada fase salen del gráfico, y a menudo difieren de lo supuesto (la inicialización de BLE puede tardar el doble).
- El alcance. Alejar el nodo del receptor y mirar la tasa de pérdidas por la secuencia (herramienta 4) y el RSSI: es la medición de campo del tema 43, con una tasa de pérdida real. Con la publicidad sin confirmación, una pérdida de pocos por ciento es normal en interiores.
- La batería. Comparar el valor enviado con el de un multímetro en la batería: si difieren más de unos 50 mV, se calibra (la lectura del conversor del ESP32 no es lineal y varía de un chip a otro).
- La duración. No se espera dos años: se mide la corriente media durante unas horas y se calcula. Si coincide con el presupuesto (± 20 %), el diseño está validado.
| Medición | Instrumento | Valor esperado | Si da distinto |
|---|---|---|---|
| Corriente de sueño | Multímetro en µA, en serie | 10 a 20 µA | Buscar el elemento de la tabla de la sección 6 |
| Tiempo despierto | Osciloscopio con una resistencia en serie, o el analizador | Unos 300 ms | Quitar el puerto serie, acortar la inicialización de la radio |
| Carga por ciclo | Analizador de corriente (o el área en el osciloscopio) | Cerca de 20 mC | Medir cada fase por separado |
| Paquetes por ciclo | nRF Connect o el receptor | 3 a 4 eventos de publicidad | Ajustar el tiempo de publicidad |
| Pérdidas | Receptor con secuencia | Menos del 5 % a pocos metros | Antena, orientación, repetir el envío |
08 · Con el microcontrolador
El código completo
El programa es el que sigue. Usa las bibliotecas Adafruit BME280 (con Adafruit Unified Sensor) y el soporte de BLE del paquete de placas ESP32 (núcleo 3.x; en el 2.x, algunas funciones usan std::string en lugar de String).
#include <Wire.h>
#include <Adafruit_BME280.h>
#include <BLEDevice.h>
#include <BLEAdvertising.h>
#include <esp_sleep.h>
// ---------- Configuración ----------
const uint8_t NODO = 1;
const uint32_t PERIODO_S = 300; // segundos de sueño entre mediciones
const uint8_t PIN_BAT = 34; // punto medio del divisor (ADC1)
const float DIVISOR = 2.0; // R1 = R2: la batería es el doble de lo que lee el pin
const uint16_t TEMP_SIN_DATO = 0x8000;
const uint16_t HUM_SIN_DATO = 0xFFFF;
#define DEPURAR 1 // 0 en producción: el puerto serie alarga el arranque
RTC_DATA_ATTR uint16_t secuencia = 0; // sobrevive al sueño profundo (la memoria común no)
// Formato del mensaje: el mismo que lee el receptor
struct __attribute__((packed)) Mensaje {
uint8_t version;
uint8_t nodo;
uint16_t secuencia;
uint16_t temperatura; // centésimas de grado, entero de 16 bits con signo
uint16_t humedad; // centésimas de por ciento
uint16_t bateria_mv;
};
Adafruit_BME280 bme;
// ---------- Medición ----------
void medir(Mensaje &m) {
m.temperatura = TEMP_SIN_DATO; // si el sensor falla, el mensaje sale igual
m.humedad = HUM_SIN_DATO;
if (bme.begin(0x76)) {
bme.setSampling(Adafruit_BME280::MODE_FORCED, // mide una vez y se duerme solo
Adafruit_BME280::SAMPLING_X1,
Adafruit_BME280::SAMPLING_NONE, // sin presión
Adafruit_BME280::SAMPLING_X1,
Adafruit_BME280::FILTER_OFF);
if (bme.takeForcedMeasurement()) {
m.temperatura = (uint16_t)(int16_t)lroundf(bme.readTemperature() * 100);
m.humedad = (uint16_t)lroundf(bme.readHumidity() * 100);
}
}
uint32_t suma = 0;
for (int i = 0; i < 8; i++) suma += analogReadMilliVolts(PIN_BAT); // el promedio compensa la impedancia alta del divisor
m.bateria_mv = (uint16_t)(suma / 8 * DIVISOR);
}
// ---------- Publicación por BLE ----------
void publicar(const Mensaje &m) {
BLEDevice::init(""); // sin nombre: ahorra bytes en el paquete
BLEAdvertising *pub = BLEDevice::getAdvertising();
BLEAdvertisementData datos;
datos.setFlags(0x06); // descubrible, sin Bluetooth clásico
const uint8_t empresa[2] = { 0xFF, 0xFF }; // identificador reservado para pruebas
String fabricante;
fabricante.concat((const char *)empresa, 2);
fabricante.concat((const char *)&m, sizeof(m)); // concat con largo: acepta bytes en cero
datos.setManufacturerData(fabricante);
pub->setAdvertisementData(datos);
pub->setAdvertisementType(ADV_TYPE_NONCONN_IND); // solo difunde: no admite conexión
pub->start();
delay(100); // con intervalos de 20 a 40 ms, tres o cuatro eventos
pub->stop();
}
// ---------- Programa ----------
void setup() {
#if DEPURAR
Serial.begin(115200);
#endif
Mensaje m = { 1, NODO, secuencia++, 0, 0, 0 };
Wire.begin(); // I²C en los pines 21 y 22
medir(m);
publicar(m);
#if DEPURAR
Serial.printf("n.º %u: %d centésimas de grado, %u de humedad, %u mV\n", m.secuencia, (int16_t)m.temperatura, m.humedad, m.bateria_mv);
Serial.flush();
#endif
esp_sleep_enable_timer_wakeup(PERIODO_S * 1000000ULL);
esp_deep_sleep_start(); // no retorna: al despertar, el programa empieza otra vez
}
void loop() {} // nunca se ejecuta
Qué hay que notar:
- Un solo flujo, sin bucle.
setup()hace todo y termina enesp_deep_sleep_start(). El contadorsecuenciase incrementa antes de armar el mensaje y sobrevive al sueño gracias aRTC_DATA_ATTR. Tras un reinicio completo (se quitó la batería) vuelve a cero, y el receptor lo ve como un salto: es normal. - El mensaje es una estructura empaquetada (
packed): sus 10 bytes son exactamente los de la tabla y en el orden del ESP32 (little-endian). La temperatura, un entero con signo, se guarda en un campo sin signo para copiar los bits tal cual; el receptor la reinterpreta con signo. - La falta de dato no detiene nada: si el BME280 no responde, el mensaje sale con los valores reservados. Un nodo que se queda mudo es mucho más difícil de diagnosticar que uno que avisa que su sensor falló.
- El modo forzado mide una vez y devuelve el sensor al reposo: no hay que apagarlo.
SAMPLING_NONEen la presión ahorra tiempo. - Se publica 100 ms con un tipo no conectable. Como no hay confirmación, se envía en varios eventos (cada uno en tres canales) para tener varias oportunidades de llegar. El mismo número de secuencia llega repetido, y es el receptor quien lo descarta.
- Con
DEPURARen 0 desaparecen el puerto serie y su demora: es una de las primeras cosas que se hacen al medir consumo.Serial.flush()evita que el sueño corte el último mensaje. - La API de BLE cambia entre versiones del núcleo ESP32 (el tipo de cadena de
setManufacturerData, entre otros). Si no compila, revisá los ejemplos de la biblioteca o usá NimBLE-Arduino, que gasta menos memoria y arranca más rápido.
La variante por LoRa
Para más alcance, la función publicar se reemplaza por una que use un módulo SX1276 con la biblioteca RadioLib (tema 39). El resto del programa (medición, mensaje, sueño) no cambia, porque el mensaje es el mismo:
#include <RadioLib.h>
SX1276 radio = new Module(5, 26, 14, 33); // CS, DIO0, RST, DIO1 (según el cableado)
void publicar(const Mensaje &m) {
// 868,0 MHz en Europa; 915,0 en América. SF9, 125 kHz, codificación 4/5, 14 dBm
int estado = radio.begin(868.0, 125.0, 9, 5, RADIOLIB_SX127X_SYNC_WORD, 14);
if (estado == RADIOLIB_ERR_NONE) {
radio.transmit((uint8_t *)&m, sizeof(m)); // 10 bytes: unos 144 ms en el aire con SF9
}
radio.sleep(); // el módulo duerme con menos de 1 µA
}
Con SF9 y 10 bytes, el mensaje dura unos 144 ms en el aire (tema 39), y con 35 mA son unos 5,4 mC: menos que los 17,5 mC de la radio de BLE en el ESP32, porque BLE gasta la mayor parte en inicializarse. Conviene recordar la otra cara: el alcance es de kilómetros, pero no hay teléfono que lo reciba, y el receptor necesita su propio módulo LoRa (o una puerta de enlace). Con el 1 % de ciclo de trabajo de Europa, un mensaje de 144 ms permite uno cada 14,4 s como mínimo: de sobra para uno cada 5 minutos.
09 · Ver lo que pasa
El receptor y la medición
El receptor es un programa de Python que escucha la publicidad con la biblioteca bleak (pip install bleak). Aplica lo del taller: valida el formato (empresa, largo y versión), descarta las repeticiones, cuenta las pérdidas por la secuencia y muestra el RSSI (la potencia recibida), que da una idea del margen del enlace.
import asyncio
import struct
from bleak import BleakScanner
ID_EMPRESA = 0xFFFF
ultimo = {} # última secuencia recibida por nodo
perdidos = {}
def procesar(dispositivo, anuncio):
datos = anuncio.manufacturer_data.get(ID_EMPRESA)
if datos is None or len(datos) != 10 or datos[0] != 1: # lo que llega por el aire no es confiable
return
version, nodo, secuencia, t, h, bateria = struct.unpack("<BBHhHH", datos)
if nodo in ultimo:
salto = (secuencia - ultimo[nodo]) & 0xFFFF # el contador es de 16 bits y da la vuelta
if salto == 0 or salto >= 0x8000:
return # repetición: el mismo paquete llega en varios eventos
perdidos[nodo] = perdidos.get(nodo, 0) + salto - 1
ultimo[nodo] = secuencia
temp = "sin dato" if t == -32768 else f"{t / 100:.2f} °C"
hum = "sin dato" if h == 0xFFFF else f"{h / 100:.2f} %"
print(f"nodo {nodo}, n.º {secuencia}: {temp}, {hum}, {bateria} mV, {anuncio.rssi} dBm, perdidos {perdidos.get(nodo, 0)}")
async def main():
async with BleakScanner(detection_callback=procesar):
await asyncio.sleep(3600) # escuchar una hora
asyncio.run(main())
La secuencia hace posible algo que parecía imposible con un nodo que solo habla: medir la calidad del enlace sin ningún tráfico de vuelta. Con ese dato se decide si hace falta repetir cada lectura, acercar el receptor o cambiar de tecnología. En un sistema real, el receptor sería una puerta de enlace que reenvía cada lectura por MQTT (tema 32) a una base de datos.
Instrumentos de medición
| Instrumento | Qué mide bien | Limitaciones |
|---|---|---|
| Multímetro en µA | La corriente de sueño (estable) | No ve los picos (promedia), tiene una resistencia en serie que hunde la tensión del nodo, y en la escala de mA se pasa de rango y reinicia el nodo |
| Resistencia de 10 Ω y osciloscopio | La forma del ciclo: tiempos y picos | No ve el sueño (10 µA son 100 µV) y la resistencia también cae tensión |
| Analizador de corriente (Nordic PPK2, Joulescope o similar) | Todo: del nanoamperio al amperio, con integración de la carga | Cuesta dinero, pero es la herramienta que transforma el trabajo |
| Medidor USB | Nada útil para este proyecto | Resolución de mA y muestreo lento: no ve ni el sueño ni los picos |
Con el multímetro, un método útil es medir la energía de un ciclo largo: poner un condensador grande como fuente, o mirar cuánto cae la tensión de la batería de prueba en un día. Pero en la práctica, un analizador de corriente es la inversión que más rinde cuando el proyecto es de bajo consumo: se ve en un gráfico qué fase pesa y se corrige en el acto.
10 · Analogía con programación
El nodo en Python
La lógica del nodo (el presupuesto, el mensaje, la batería, las pérdidas y el período) se prueba sin hardware. El modelo siguiente reproduce las reglas del programa y de la página, y es la base del diseño: se ajustan los números hasta que el presupuesto cierra, y recién entonces se conecta el sensor.
Python en tu navegador. Los bloques son editables: cambiá los valores y presioná «Ejecutar Python». La primera vez tarda unos segundos en cargar.
import struct
I_ARRANQUE, I_MEDICION = 30, 25 # mA
def presupuesto(periodo_s, arranque_ms, medicion_ms, radio_ms, i_radio_ma, sueno_ua, capacidad_mah, util=0.8):
activo_ms = arranque_ms + medicion_ms + radio_ms
q = (arranque_ms * I_ARRANQUE + medicion_ms * I_MEDICION + radio_ms * i_radio_ma) / 1000 # mC por ciclo
q_sueno = sueno_ua / 1000 * (periodo_s - activo_ms / 1000)
media_ma = (q + q_sueno) / periodo_s
return q, media_ma * 1000, capacidad_mah * util / media_ma / 24 # mC, µA, días
def empaquetar(version, nodo, secuencia, temperatura, humedad, bateria_mv):
return struct.pack("<BBHhHH", version, nodo, secuencia, round(temperatura * 100), round(humedad * 100), bateria_mv)
def desempaquetar(datos):
version, nodo, secuencia, t, h, bateria = struct.unpack("<BBHhHH", datos)
return {"version": version, "nodo": nodo, "secuencia": secuencia, "temperatura": t / 100, "humedad": h / 100, "bateria_mv": bateria}
def publicidad(cuerpo):
return bytes([0x02, 0x01, 0x06, len(cuerpo) + 3, 0xFF, 0xFF, 0xFF]) + cuerpo # banderas y datos del fabricante (ID 0xFFFF)
def bateria_mv(pin_mv, r1_k, r2_k):
return pin_mv * (r1_k + r2_k) / r2_k
def fuga_ua(bat_mv, r1_k, r2_k):
return bat_mv / (r1_k + r2_k) # mV sobre kΩ son µA
def contar_perdidas(secuencias):
ultimo, perdidos, repetidos = secuencias[0], 0, 0
for s in secuencias[1:]:
salto = (s - ultimo) & 0xFFFF # el contador es de 16 bits y da la vuelta
if salto == 0 or salto >= 0x8000:
repetidos += 1
continue
perdidos += salto - 1
ultimo = s
return perdidos, repetidos
def periodo_minimo_s(capacidad_mah, util, anios, q_mc, sueno_ua):
i_max_ma = capacidad_mah * util / (anios * 8766)
return q_mc / (i_max_ma - sueno_ua / 1000)
for periodo in (300, 60, 3600):
q, ua, dias = presupuesto(periodo, 60, 25, 250, 70, 15, 2000)
print(f"cada {periodo:>4} s: {q:.2f} mC por ciclo, {ua:6.1f} µA de media, {dias:6.0f} días ({dias / 365:.1f} años)")
q, ua, dias = presupuesto(300, 60, 25, 60, 70, 15, 2000)
print(f"radio de 60 ms: {q:.2f} mC, {ua:.1f} µA, {dias / 365:.1f} años")
q, ua, dias = presupuesto(300, 60, 25, 250, 70, 5000, 2000)
print(f"sueño de 5 mA (placa de desarrollo): {ua:.0f} µA, {dias:.1f} días")
cuerpo = empaquetar(1, 1, 1234, 23.5, 55.0, 3800)
print("mensaje:", cuerpo.hex(" ").upper(), len(cuerpo), "bytes")
print("paquete:", publicidad(cuerpo).hex(" ").upper(), len(publicidad(cuerpo)), "de 31 bytes")
print("al leerlo:", desempaquetar(cuerpo))
print("-5,25 °C:", empaquetar(1, 1, 65535, -5.25, 55.0, 3800).hex(" ").upper())
print("batería con 1900 mV en el pin (1 MΩ y 1 MΩ):", bateria_mv(1900, 1000, 1000), "mV, fuga", round(fuga_ua(3800, 1000, 1000), 2), "µA")
print("con 100 kΩ y 100 kΩ, fuga", round(fuga_ua(3800, 100, 100), 1), "µA")
lecturas = [65530, 65531, 65531, 65533, 65534, 65535, 0, 1, 1, 5, 6]
print("perdidos y repetidos:", contar_perdidas(lecturas))
print(f"período mínimo para 2 años con 2000 mAh: {periodo_minimo_s(2000, 0.8, 2, 19.9, 15):.0f} s")
print(f"para 5 años: {periodo_minimo_s(2000, 0.8, 5, 19.9, 15) / 60:.1f} min")
Resultado: con un ciclo de 60 ms de arranque, 25 de medición y 250 de radio a 70 mA, 15 µA de sueño y 2000 mAh (80 % útil), cada ciclo gasta 19,93 mC y la corriente media es de 81,4 µA a 300 s (819 días, 2,2 años), 347,0 µA a 60 s (192 días, 0,5 años) y 20,5 µA a 3600 s (3247 días, 8,9 años). Con la radio en 60 ms, 6,62 mC, 37,1 µA y 4,9 años. Con 5 mA de sueño (la placa de desarrollo), 5061 µA y 13,2 días. El mensaje es 01 01 D2 04 2E 09 7C 15 D8 0E (10 bytes), el paquete de publicidad, 02 01 06 0D FF FF FF 01 01 D2 04 2E 09 7C 15 D8 0E (17 de 31 bytes), y al leerlo se recupera nodo 1, secuencia 1234, 23,5 °C, 55,0 % y 3800 mV; con −5,25 °C y secuencia 65 535, 01 01 FF FF F3 FD 7C 15 D8 0E. Con 1900 mV en el pin y dos resistencias de 1 MΩ la batería es de 3800 mV y la fuga, 1,9 µA; con 100 kΩ y 100 kΩ, 19,0 µA. La lista de secuencias 65530, 65531, 65531, 65533, 65534, 65535, 0, 1, 1, 5, 6 tiene 4 mensajes perdidos y 2 repetidos. El período mínimo para 2 años con 2000 mAh es de 261 s y para 5 años, de 15,4 minutos.
La lección que sale del modelo es la que el taller repite: la tecnología de radio decide menos que el montaje. Pasar de la placa de desarrollo a un módulo con un regulador adecuado cambia la duración de 13 días a años; pasar de BLE a LoRa cambia la carga de radio en un factor de tres; pasar de un divisor de 100 kΩ a uno de 1 MΩ ahorra 17 µA. Nada de eso se ve en el código del sensor.
11 · Exploración interactiva
Laboratorio: ciclo, mensaje, batería, pérdidas y período
El laboratorio tiene cinco herramientas. La primera calcula el presupuesto de energía de un ciclo y la duración de la batería. La segunda arma el mensaje de 10 bytes y su paquete de publicidad. La tercera convierte la lectura del divisor de la batería y calcula lo que gasta. La cuarta cuenta las pérdidas a partir de una lista de números de secuencia. La quinta calcula, al revés, el período mínimo para una duración deseada.
1. Presupuesto de energía
Elegí los tiempos y la corriente de cada fase.
2. Mensaje y paquete de publicidad
Escribí una lectura.
3. Divisor para medir la batería
Elegí las resistencias y la lectura.
4. Pérdidas por número de secuencia
Escribí los números de secuencia.
5. Período mínimo para una duración objetivo
Elegí la batería y la duración.
En la herramienta 1, el arranque se calcula a 30 mA y la medición a 25 mA, y la corriente de sueño es la de todo el nodo (procesador, regulador, divisor y sensor). En la 2, el tiempo en el aire cuenta el preámbulo, la dirección de acceso, la cabecera, la dirección del dispositivo y el CRC a 1 Mbit/s. En la 3, el pin admite hasta unos 3100 mV. En la 4, el contador da la vuelta en 65 535. La 5 usa 8766 horas por año.
Recorrido guiado
- En la herramienta 1, con los valores iniciales: 19,93 mC por ciclo (1,80 + 0,63 + 17,50) con 335 ms despierto, 81,4 µA de media y 2,2 años; la radio pesa el 71,7 %. Con 60 s de período: 347 µA y 192 días (en rojo). Con 3600: 20,5 µA y 8,9 años, y manda el sueño (73 %). Con la radio en 60 ms: 6,63 mC, 37,1 µA y 4,9 años.
- Con un sueño de 5000 µA (la placa de desarrollo): 5061 µA, 13,2 días y el sueño es el 98,7 % del total. Con 3000 ms de arranque y 3000 de medición (un programa con esperas con
delay): 182,5 mC por ciclo, 623 µA y 107 días: el ciclo despierto vuelve a mandar. El período mínimo que admite la herramienta es de 10 s. - En la herramienta 2, con los valores iniciales:
01 01 D2 04 2E 09 7C 15 D8 0E, el paquete02 01 06 0D FF FF FF …de 17 de 31 bytes, 264 µs por paquete (792 µs en los tres canales) y la lectura exacta. Con −5,25 °C y la secuencia 65535:FF FFyF3 FD. Con 23.456: el receptor lee 23,46 °C («se recupera con la resolución de 0,01»). Con humedad 101: valor inválido. - En la herramienta 3, con 1000 kΩ, 1000 kΩ y 1900 mV: 3800 mV, 55 %, y una fuga de 1,90 µA (el 0,2 de lo que gasta el ESP32 dormido). Con 100 y 100 kΩ: la misma lectura, pero 19 µA de fuga (1,9 veces el sueño del ESP32) en rojo. Con 1650 mV y 1 MΩ: 3300 mV y 3 %, la batería casi agotada.
- En la herramienta 4, con la lista inicial: 9 mensajes distintos, 2 repeticiones, 4 perdidos, 30,8 % de pérdida y una racha de 3. Con
1 2 3 4 5: sin pérdidas. Con10 11 14 20 21 22 23 24 25 30: 11 perdidos y 52,4 %. Con un solo número: falta información. - En la herramienta 5, con los valores iniciales (2000 mAh, 80 %, 2 años, 19,9 mC, 15 µA): corriente media permitida de 91,3 µA y un período mínimo de 4,3 min (331 mensajes por día). Con 5 años: 36,5 µA y 15,4 min. Con un sueño de 400 µA: «imposible». Con una carga de 4 mC (la radio en 60 ms): 52 s. Con 220 mAh (una pila pequeña) y 1 año: 20,1 µA y 1,1 h entre mensajes.
Desafío: la estación meteorológica del techo
Un cliente quiere cinco nodos como este en el techo de una casa, que reporten cada 5 minutos a un receptor ubicado en el interior (a unos 15 m, con una losa de por medio) y que no se les cambie la batería en dos años. Con la herramienta 1: los valores iniciales dan 81,4 µA y 2,2 años con 2000 mAh y 80 % útil, pero (1) el frío reduce la capacidad de una celda de litio a casi la mitad en invierno (a −10 °C), así que el 80 % es optimista: con el 50 % la duración baja a 1,4 años. (2) La temperatura del sueño también influye: las fugas de los reguladores aumentan con el calor. (3) El enlace: con 15 m y una losa, la publicidad de BLE llega con poco margen; hay que medir el RSSI y la pérdida (herramienta 4), y si es mala, repetir cada envío o acercar el receptor. (4) Cinco nodos comparten el medio: con 335 ms cada 300 s, la probabilidad de que dos se pisen es pequeña (menos del 1 %), y la secuencia por nodo permite medirla. (5) La hora: como cada nodo mide cada «300 s ± 5 %», los instantes de medición no están sincronizados: el receptor pone el sello de tiempo al recibir. Soluciones realistas: una celda mayor con una protección contra el frío, baja potencia de los reguladores, un receptor mejor ubicado y, si el techo queda lejos, pasar a LoRa para cubrir con margen (tema 39).
12 · Qué viene después
Ampliaciones y buenas prácticas
- Repetir el envío. Si la pérdida medida es alta, publicar más tiempo o enviar cada lectura dos veces con la misma secuencia: cuesta energía, pero sube la probabilidad de llegada.
- Enviar solo si cambia. Guardar la última lectura en la memoria RTC y publicar solo cuando la diferencia supere un umbral, con un envío periódico de «sigo vivo» (latido): una forma de ahorro muy eficaz en magnitudes que cambian poco.
- Medir más seguido que enviar. Medir cuesta unos 0,6 mC y enviar, 17 mC. Un nodo puede guardar varias lecturas en la memoria RTC y mandarlas juntas: con 12 lecturas por hora se envía una vez, y el costo de la radio se reparte (el tamaño de un paquete de publicidad, 31 bytes, limita cuántas entran; con conexión o con publicidad extendida se pueden enviar más).
- Cifrado y autenticación. La publicidad es pública. Si los datos importan, se cifran en la aplicación (por ejemplo, AES con el contador de secuencia como parte del vector inicial), y el receptor rechaza las repeticiones; es el contenido del tema 49.
- Un modo de configuración. Publicar de forma conectable durante unos segundos tras un reinicio (con un botón) para cambiar el período o el número de nodo con una característica GATT (tema 38), y guardar el valor en la memoria no volátil.
- Perro guardián. Un reinicio por tiempo si el programa se cuelga: en un dispositivo desatendido, un cuelgue con el procesador despierto agota la batería en días.
- Más sensores. Presión y gas del propio BME280 (SAMPLING_X1 en lugar de NONE, a costa de unos milisegundos), luz, movimiento por despertar de pin. Cada sensor suma al sueño: se elige el que tenga modo de reposo.
- Un SoC de BLE de bajo consumo. Con un nRF52 o un EFR32, la fase de radio baja a unos 3 ms a 8 mA (24 µC, lo que se vio en el tema 38): el mismo diseño dura diez años con una pila de botón. Es la ampliación que más cambia el resultado.
- Energía del ambiente. Una celda solar pequeña y un supercondensador o una batería recargable: con 20 µA de media, una celda de pocos centímetros cuadrados alcanza.
- Gestión de la flota. Con muchos nodos, la tensión de la batería y la tasa de pérdida de cada uno, publicadas por el receptor por MQTT, avisan cuál hay que atender antes de que muera.
Con este taller termina la parte de comunicación inalámbrica. Lo que sigue en el curso es lo que viaja dentro de cualquiera de estos enlaces: cómo se diseña un protocolo propio, cómo se direcciona un paquete, cómo se detectan y corrigen los errores. El mensaje de 10 bytes con versión, nodo y secuencia de este taller es un primer ejemplo, y los temas 41 a 47 lo convierten en un método.
13 · Comprensión
Confusiones frecuentes
«Con deep sleep, mi placa de desarrollo consume microamperios»
El procesador sí, la placa no. El chip USB-serie, el regulador y el LED gastan miliamperios y no se apagan con el sueño del procesador. Con 5 mA de reposo, 2000 mAh duran menos de dos semanas. Para medir bien hace falta un módulo desnudo o una placa pensada para baterías.
«La batería dura su capacidad dividida por la corriente»
Eso es el máximo teórico. Solo se aprovecha una parte (80 % o menos, y mucho menos con frío o con picos altos), la autodescarga actúa todo el tiempo y la capacidad envejece. La cuenta se hace con la capacidad útil, y se valida midiendo.
«Lo que más gasta es la transmisión»
Casi nunca la transmisión en sí: es inicializar la radio, esperar a que el procesador arranque y mantener todo encendido. En este nodo, los 250 ms de radio son el 72 % de la energía, y el paquete tarda menos de 1 ms por canal. Se mide cada fase antes de optimizar.
«Medir la batería no cuesta nada»
El divisor está siempre conectado: con 100 kΩ y 100 kΩ gasta 19 µA, el doble que el ESP32 dormido. Se usan resistencias de 1 MΩ y un condensador, o un transistor que conecte el divisor solo al medir.
«El multímetro me da la corriente media del nodo»
En µA mide bien el sueño, pero no sigue los picos, mete una resistencia en serie que hunde la tensión de alimentación y, si el nodo despierta y la escala es de µA, puede protegerse con un fusible o reiniciar el nodo. Para el ciclo hace falta un analizador de corriente o un osciloscopio.
«Si no hay confirmación, no puedo saber si llegaron los datos»
Sí se puede: con un número de secuencia, el receptor detecta los saltos y mide la tasa de pérdida sin ningún tráfico de vuelta. Lo que no puede hacer el nodo es saberlo él mismo; si necesita saberlo, hace falta un canal de retorno (un ACK, como en el tema 45).
«Para no perder datos, envío más seguido»
Cada envío cuesta 20 mC: duplicar la frecuencia casi duplica el consumo y acorta la vida a la mitad. Para no perder datos se repite el envío de la misma lectura, se guarda un histórico o se mejora el enlace; no se miden más lecturas de las que hacen falta.
«El temporizador del sueño es exacto»
El oscilador interno tiene un error de hasta el 5 % y depende de la temperatura: un período de 300 s es de 285 a 315 s. Si se necesita una hora real, se sincroniza con el receptor o se usa un cristal de 32 kHz.
«Los datos que viajan por publicidad son seguros porque nadie conoce el formato»
Cualquiera con un teléfono ve los bytes, y el formato se deduce en minutos. La publicidad es pública: si el contenido es sensible se cifra, y si el receptor actúa sobre él se autentica y se protege contra la repetición (tema 49).
«LoRa gasta menos que BLE»
Depende del chip y del tiempo despierto, no del protocolo. En este diseño, LoRa con un SX1276 gasta menos carga de radio (unos 5,4 mC contra 17,5 mC) porque el ESP32 pasa mucho tiempo inicializando BLE. Un SoC de BLE de bajo consumo gasta menos que los dos (24 µC).
14 · Práctica guiada
Predecí antes de comprobar
1. Presupuesto
Un nodo tiene 60 ms de arranque a 30 mA, 25 ms de medición a 25 mA y 100 ms de radio a 70 mA, con 15 µA de sueño, mide cada 600 s y usa una batería de 2000 mAh con el 80 % útil. ¿Cuál es la carga por ciclo, la corriente media y la duración?
Ver solución
Arranque: 1,8 mC; medición: 0,625 mC; radio: 7,0 mC; total 9,43 mC. Sueño: 0,015 mA · (600 − 0,185) s ≈ 9,0 mC. Total por período: 18,4 mC; media: 18,4 / 600 = 30,7 µA. Duración: 1600 / 0,0307 ≈ 52 100 h, unos 2171 días (5,9 años). Es casi tres veces más que con 250 ms de radio y 300 s.
2. Un mensaje
Armá los 10 bytes del nodo 3, secuencia 258, 30,00 °C, 40,00 % y 4100 mV.
Ver solución
Versión 1 y nodo 3: 01 03. La secuencia 258 es 0x0102: 02 01. La temperatura 3000 es 0x0BB8: B8 0B. La humedad 4000 es 0x0FA0: A0 0F. La batería 4100 es 0x1004: 04 10. El mensaje: 01 03 02 01 B8 0B A0 0F 04 10.
3. El divisor
Con resistencias de 470 kΩ y 470 kΩ y una lectura de 2000 mV en el pin, ¿cuál es la tensión de la batería, qué porcentaje es y cuánto gasta el divisor?
Ver solución
Batería = 2000 · (470 + 470) / 470 = 4000 mV, un 82 % según la curva de la herramienta. El divisor gasta 4000 mV / 940 kΩ ≈ 4,3 µA: menos que el sueño del ESP32, pero no despreciable (un tercio del presupuesto de sueño de 15 µA). Con 1 MΩ serían 2 µA.
4. Pérdidas
El receptor ve las secuencias 100, 101, 103, 103, 104, 107. ¿Cuántos mensajes llegaron, cuántos se perdieron y cuál es la tasa?
Ver solución
Llegaron 100, 101, 103, 104 y 107: 5 distintos, con un 103 repetido. Se perdieron el 102, el 105 y el 106: 3. La tasa es 3 de 8: 37,5 %, con una racha máxima de 2.
5. Período mínimo
Con 2000 mAh (80 % útil), 3 años de duración, 9,4 mC por ciclo y 15 µA de sueño, ¿cada cuánto se puede medir?
Ver solución
Corriente media permitida: 1600 / (3 · 8766) ≈ 0,0608 mA = 60,8 µA. Quedan 60,8 − 15 = 45,8 µA para los ciclos: período = 9,4 mC / 0,0458 mA ≈ 205 s, unos 3,4 minutos entre mensajes como mínimo.
6. Un diagnóstico
Un nodo medido con el multímetro da 8 mA dormido. Con 2000 mAh y 80 % útil, ¿cuánto dura y qué se hace?
Ver solución
1600 / 8 = 200 h: 8,3 días. Algo más que el procesador está encendido: 8 mA es típico del chip USB-serie, el regulador lineal y el LED de una placa de desarrollo. Se pasa a un módulo desnudo con un regulador de bajo consumo, se quita el LED y se vuelve a medir; el objetivo son 10 a 20 µA.
7. BLE o LoRa
Para el mismo nodo, ¿cuánta carga gasta la fase de radio con LoRa (SF9, 10 bytes, con unos 10 ms de preparación del módulo a 35 mA) y cómo se compara con los 17,5 mC de BLE? ¿Qué otra condición hay que mirar?
Ver solución
Tiempo en el aire con SF9 y 10 bytes: (12,25 + 23) · 4,096 ms ≈ 144 ms; con la preparación, unos 154 ms a 35 mA: 5,4 mC, un tercio de los 17,5 mC de BLE en el ESP32. A cambio, el receptor tiene que ser otro módulo LoRa (o una puerta de enlace) y no sirve un teléfono, y hay que respetar el ciclo de trabajo (un mensaje de 144 ms con el 1 % obliga a esperar 14,4 s, que sobran con 300 s de período). La elección depende de quién recibe y a qué distancia, no solo de la energía.
15 · Síntesis
Ideas para recordar
- Un sensor inalámbrico con batería es un ciclo breve y un sueño largo. Se diseña con un presupuesto: la carga de cada fase (corriente por tiempo) más la del sueño, repartida en el período, da la corriente media, y la capacidad útil dividida por ella da la duración. Se calcula antes de programar.
- En el nodo del taller (arranque de 60 ms a 30 mA, medición de 25 ms, radio de 250 ms a 70 mA y 15 µA de sueño, cada 300 s), la corriente media es de 81,4 µA y la duración, de 2,2 años con 2000 mAh. La radio es el 72 % de la energía, por inicializarse y no por transmitir.
- El sueño profundo es una propiedad del montaje, no del procesador: una placa de desarrollo (chip USB, regulador lineal, LED) consume miliamperios y reduce la vida a días. Se usa un módulo desnudo, un regulador de reposo de µA, un sensor con modo de reposo y se miden los pines flotantes.
- Medir la batería cuesta: un divisor de 100 kΩ gasta 19 µA, más que el procesador dormido. Con resistencias de 1 MΩ y un condensador de 100 nF baja a menos de 2 µA. El porcentaje de carga es una estimación; mejor enviar los milivolts.
- El mensaje es de tamaño fijo, con enteros de 16 bits en little-endian, una versión, un identificador de nodo, un valor reservado para «sin dato» y un número de secuencia. El mensaje de 10 bytes cabe en 17 de los 31 bytes de un paquete de publicidad.
- El número de secuencia permite medir las pérdidas sin ningún tráfico de vuelta: una diferencia (módulo 65 536) de 0 es una repetición, de 1 es lo normal y de más son mensajes perdidos. Con publicidad sin confirmación es la única forma de conocer la calidad del enlace.
- El programa no tiene bucle: cada despertar es un arranque nuevo, mide, publica y duerme. Solo la memoria RTC (
RTC_DATA_ATTR) conserva el contador. El reloj interno tiene un error de hasta 5 %,millis()vuelve a cero y los picos de la radio exigen condensadores y una batería que los entregue. - Con LoRa, el resto del programa no cambia: solo la función de publicar. Con SF9 y 10 bytes, la radio gasta unos 5,4 mC contra los 17,5 de BLE en un ESP32, pero se necesita otro módulo LoRa como receptor. Con un SoC de BLE de bajo consumo, la radio baja a unos 24 µC.
- Se verifica con instrumentos: multímetro en µA para el sueño, analizador de corriente u osciloscopio para el ciclo, nRF Connect y un receptor con secuencia para el mensaje y las pérdidas. La cuenta inversa dice cada cuánto se puede medir para una duración dada: 261 s para dos años y 15 minutos para cinco.
¿Listo para el siguiente tema?
Antes de avanzar, explicá por qué una placa de desarrollo no sirve para un nodo con batería aunque use el sueño profundo; calculá la corriente media de un nodo con 20 mC por ciclo, 15 µA de sueño y un período de 10 minutos; y decidí qué campo agregarías al mensaje para que el receptor detecte las pérdidas y cómo lo calcularía con un contador que da la vuelta. Si podés justificar las tres respuestas, entendés cómo se diseña un sensor de bajo consumo.
En el próximo tema se abre una nueva parte del curso: el diseño de protocolos propios. El mensaje de este taller (versión, nodo, secuencia, datos) es un primer ejemplo; ahora se estudia el método: delimitadores, campos de longitud y tipo de mensaje, la máquina de estados de recepción y los criterios para que un formato sobreviva al paso del tiempo.