Un sensor Pico W a batería con MQTT solo funciona si la placa pasa casi toda su vida dormida y apenas unos segundos despierta. En este tutorial construirás un nodo sensor en MicroPython que despierta con un temporizador, lee un sensor y la tensión de su propia batería, publica un mensaje QoS 1 en un broker MQTT, apaga la radio y vuelve a lightsleep. También aprenderás qué decisiones de diseño influyen de verdad en la autonomía y cuáles son esfuerzo perdido.
Por qué la alimentación por batería es difícil en la Pico W
La Pico W se diseñó como una placa de microcontrolador barata y flexible, no como una plataforma de sensores de ultra bajo consumo. El RP2040 en sí es bastante eficiente, pero tres cosas juegan en tu contra cuando funcionas con baterías:
- El chip Wi-Fi CYW43439 consume mucha más corriente mientras se asocia y transmite que el microcontrolador mientras calcula. Cada segundo con la radio encendida cuesta más que minutos de sueño.
- El regulador buck-boost integrado tiene una corriente de reposo que nunca desaparece, ni siquiera cuando el RP2040 duerme.
- Los modos de sueño en MicroPython son limitados en comparación con chips como el ESP32.
machine.lightsleep()detiene los relojes y conserva la RAM, lo que es útil, pero la placa sigue consumiendo bastante más que un nodo diseñado específicamente para bajo consumo.
La consecuencia práctica es sencilla: la autonomía depende sobre todo de con qué frecuencia despiertas y de cuánto tarda la conexión Wi-Fi. Optimizar el código Python que lee un sensor apenas cambia nada. Pasar de un intervalo de publicación de 30 segundos a 5 minutos, o recortar el tiempo de conexión de ocho a tres segundos, cambia muchísimo.
Si todavía no has conectado una Pico W a MQTT, empieza por la guía de Raspberry Pi Pico W y MQTT y vuelve aquí cuando funcione una publicación básica.
El diseño del ciclo de trabajo
Cada despertar sigue el mismo guion breve. Mantenerlo estrictamente lineal (sin tareas en segundo plano ni conexiones de larga duración) es lo que lo hace predecible.
- Despertar de lightsleep (o arrancar en frío tras un corte de alimentación).
- Leer la tensión de la batería antes de encender el Wi-Fi.
- Leer el sensor.
- Encender el Wi-Fi con una configuración estática y un timeout estricto.
- Conectar con el broker, publicar un mensaje con QoS 1 y esperar la confirmación.
- Desconectar limpiamente, apagar la radio y desinicializarla.
- Entrar en lightsleep durante el intervalo configurado y repetir.
| Decisión de diseño | Efecto en la autonomía | Recomendación |
|---|---|---|
| Intervalo de publicación | El factor individual más importante | De 5 a 15 minutos para datos de clima interior |
| Tiempo de conexión Wi-Fi | El segundo factor más importante | Buena señal, WPA2, timeout corto |
| Mantener MQTT conectado durante el sueño | La radio sigue parcialmente activa | Nunca con baterías |
| Publicación con QoS 1 | Un viaje de ida y vuelta extra | Merece la pena: sabes que el mensaje llegó |
Lightsleep frente a time.sleep() activo |
Lightsleep detiene los relojes | Usa siempre lightsleep entre ciclos |
| LED parpadeando para indicar estado | Consumo pequeño pero constante | Desactívalo en producción |
| Chip temporizador externo (TPL5110 o similar) | Corta por completo la alimentación de la placa | La mejor opción para varios meses de autonomía |
Hardware y cableado
Necesitas una Pico W (o una Pico 2 W), un sensor y una fuente de alimentación en VSYS. Una configuración habitual son tres pilas AA o AAA en serie conectadas directamente a VSYS, o una sola celda LiPo a través de una placa de protección. No superes el límite de VSYS que indica la hoja de datos oficial y nunca conectes baterías a VBUS con el USB enchufado sin un diodo.
Como sensor sirve cualquier componente I2C con baja corriente en reposo. Este ejemplo usa un BME280 en I2C0 (GP4 = SDA, GP5 = SCL), pero la lógica del ciclo de trabajo es independiente del sensor.
La Pico W puede medir su propia tensión de alimentación en ADC3, que está conectado a VSYS mediante un divisor 1:3. En la Pico W ese pin (GP29) se comparte con el reloj SPI del chip Wi-Fi, así que debes leerlo con la radio apagada y con GP25 a nivel alto. Por eso la lectura de tensión se hace al principio del ciclo.
Prepara MicroPython y la librería MQTT
Graba una versión actual de MicroPython para la Pico W desde la documentación oficial de Raspberry Pi y después instala la librería umqtt.simple y un driver para el BME280 con mpremote:
pip install mpremote
mpremote mip install umqtt.simple
mpremote cp bme280.py :bme280.py
Crea un secrets.py en la placa para que las credenciales queden fuera del script principal:
# secrets.py
WIFI_SSID = "your-ssid"
WIFI_PASSWORD = "your-wifi-password"
MQTT_HOST = "broker.connectmq.com"
MQTT_PORT = 1883
MQTT_CLIENT_ID = "pico-bat-7f3a91" # must be non-empty and unique
MQTT_USER = None # set YOUR_USERNAME for a private broker
MQTT_PASSWORD = None # set YOUR_PASSWORD for a private broker
TOPIC = "connectmq/demo/7f3a91/garden/climate"
INTERVAL_S = 600 # 10 minutes between readings
El servidor público broker.connectmq.com acepta conexiones anónimas con cualquier client ID no vacío. Todo lo que se publica allí puede leerlo cualquiera, así que usa un prefijo de topic aleatorio y nunca envíes datos personales. La guía del servidor MQTT público gratuito enumera sus límites.
El script completo de MQTT con batería
Guárdalo como main.py. Se ejecuta automáticamente en cada arranque y, como el bucle usa lightsleep en lugar de un reinicio, el estado en RAM, como el contador de ciclos, se conserva entre despertares.
# main.py - battery-powered Pico W MQTT sensor
import time
import json
import network
import machine
from machine import Pin, ADC, I2C
from umqtt.simple import MQTTClient
import bme280
import secrets
led = Pin("LED", Pin.OUT)
led.off()
i2c = I2C(0, sda=Pin(4), scl=Pin(5), freq=100_000)
cycle = 0
def read_vsys():
"""Read VSYS on the Pico W. Must run while the Wi-Fi chip is off."""
Pin(25, Pin.OUT).value(1) # deselect the CYW43 SPI bus
Pin(29, Pin.IN, None) # release the shared pin for the ADC
adc = ADC(3)
total = 0
for _ in range(8):
total += adc.read_u16()
time.sleep_ms(2)
raw = total / 8
return round(raw * 3.3 * 3 / 65535, 3)
def read_sensor():
sensor = bme280.BME280(i2c=i2c)
t, p, h = sensor.read_compensated_data()
# The driver returns hundredths of a degree, Pa*256 and %RH*1024
return round(t / 100, 2), round(h / 1024, 1), round(p / 25600, 1)
def wifi_up(timeout_ms=10_000):
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(secrets.WIFI_SSID, secrets.WIFI_PASSWORD)
start = time.ticks_ms()
while not wlan.isconnected():
if time.ticks_diff(time.ticks_ms(), start) > timeout_ms:
return wlan, False
time.sleep_ms(100)
return wlan, True
def wifi_down(wlan):
try:
wlan.disconnect()
except OSError:
pass
wlan.active(False)
wlan.deinit()
def publish(payload):
client = MQTTClient(
secrets.MQTT_CLIENT_ID,
secrets.MQTT_HOST,
port=secrets.MQTT_PORT,
user=secrets.MQTT_USER,
password=secrets.MQTT_PASSWORD,
keepalive=30,
)
client.connect()
# QoS 1: publish() blocks until the broker returns PUBACK
client.publish(secrets.TOPIC, payload, qos=1)
client.disconnect()
while True:
cycle += 1
vbat = read_vsys()
try:
temp_c, rh, hpa = read_sensor()
except OSError:
temp_c, rh, hpa = None, None, None
awake_start = time.ticks_ms()
wlan, ok = wifi_up()
if ok:
msg = json.dumps({
"t": temp_c, "rh": rh, "hpa": hpa,
"vbat": vbat, "cycle": cycle,
"rssi": wlan.status("rssi"),
"awake_ms": time.ticks_diff(time.ticks_ms(), awake_start),
})
try:
publish(msg)
except OSError as exc:
print("MQTT failed:", exc)
wifi_down(wlan)
# Back off harder when the battery is low to protect the cells
sleep_s = secrets.INTERVAL_S if vbat > 3.3 else secrets.INTERVAL_S * 3
machine.lightsleep(sleep_s * 1000)
Algunos detalles son deliberados. El script informa de awake_ms y rssi en cada mensaje para que puedas ver, desde el lado del broker, si un nodo tarda demasiado en conectarse. La rama de batería baja triplica el intervalo en lugar de detenerse por completo, lo que te da tiempo para cambiar las pilas sin perder el nodo. Y deinit() después de active(False) garantiza que el driver CYW43 libere la radio antes de que la placa duerma.
Prueba los mensajes MQTT de la Pico W con batería
Antes de usar pilas, prueba por USB con un intervalo corto, como 30 segundos. Suscríbete desde tu portátil:
mosquitto_sub -h broker.connectmq.com -p 1883 -i laptop-7f3a91 \
-t 'connectmq/demo/7f3a91/#' -v
Deberías ver un mensaje JSON por ciclo. Fíjate en el campo awake_ms. En una red sana se estabiliza en un rango estrecho y repetible. Si varía mucho, el problema es el punto de acceso, no tu código.
Ten en cuenta que, cuando la Pico W duerme conectada por USB, el REPL serie se desconecta. Es normal. Para recuperar el control, desenchúfala, mantén pulsado BOOTSEL si hace falta o usa mpremote justo después de encenderla, antes de que main.py entre en su primer sueño. Un truco útil es añadir al principio de main.py una ventana de dos segundos que salga al REPL si se mantiene pulsado un botón conectado a un GPIO.
Exprimir más autonomía de la Pico W
Una vez que el ciclo básico funciona, estas mejoras ofrecen el mejor retorno:
- Alarga primero el intervalo. Reducir a la mitad el número de despertares reduce aproximadamente a la mitad la energía de la radio, que es el mayor consumidor.
- Agrupa las lecturas. Lee el sensor cada minuto durante el lightsleep, guarda los valores en una lista y publícalos todos en un único mensaje cada diez minutos. Pagas el Wi-Fi una vez en lugar de diez.
- Mejora el enlace Wi-Fi. Coloca el nodo donde la señal sea buena. Una señal débil implica reintentos, velocidades de datos más bajas y más tiempo despierto.
- Usa un temporizador de alimentación externo. Un chip temporizador que corta la alimentación de toda la placa y la restablece según un horario elimina por completo las corrientes del regulador y del sueño. En ese diseño, el script se ejecuta una vez desde el arranque en frío, publica y luego indica al temporizador que corte la alimentación. Pierdes la RAM entre ciclos, así que guarda los contadores en flash con moderación o deja que el broker conserve el estado mediante mensajes retenidos.
- Desactiva todo lo que parpadee. Los LED de estado de la Pico y de las placas de los sensores consumen corriente todo el tiempo. Quita o corta el LED de alimentación de las placas de sensores cuando puedas.
Producción: pasa a credenciales privadas y TLS
El servidor público es ideal para el desarrollo, pero un sensor desplegado debe usar tus propias credenciales. En un broker MQTT gestionado como ConnectMQ, creas un broker en el portal y recibes un usuario, una contraseña y un client ID generados. Ponlos en secrets.py como YOUR_USERNAME, YOUR_PASSWORD y YOUR_CLIENT_ID. El client ID está vinculado a las credenciales emitidas, así que cada nodo necesita el que se generó para él.
TLS en el puerto 8883 añade un handshake a cada despertar, lo que cuesta tiempo despierto adicional y, por tanto, batería. Para sensores en una red doméstica de confianza hay quien acepta TCP sin cifrar; para cualquier cosa que salga de un edificio, el cifrado compensa el coste. El tutorial de Pico W con MQTT sobre TLS muestra la configuración del contexto SSL en MicroPython. Mide awake_ms antes y después para conocer el impacto real en tu nodo.
Los mensajes retenidos también son útiles aquí. Si publicas la última lectura con el flag retain, un panel que se abra horas después mostrará de inmediato el valor más reciente en lugar de esperar hasta diez minutos al siguiente despertar. ConnectMQ admite mensajes retenidos en todos los planes, y el panel y el explorador de topics en vivo de su portal te permiten ver cómo llegan los mensajes de cada nodo sin escribir un suscriptor.
Solución de problemas
| Síntoma | Causa probable | Solución |
|---|---|---|
vbat marca casi cero o valores aleatorios |
Lectura del ADC con el Wi-Fi activo | Lee VSYS antes de wlan.active(True) |
| El nodo publica una vez y se detiene | Wi-Fi no desinicializado antes del lightsleep | Llama a active(False) y deinit() |
OSError: -2 al conectar |
Falló el DNS o el Wi-Fi no estaba del todo activo | Comprueba isconnected() y reintenta una vez |
awake_ms no deja de crecer |
Señal débil o punto de acceso saturado | Mueve el nodo o el punto de acceso |
| La batería se agota mucho antes de lo esperado | LED, reposo del sensor o consumo del regulador | Quita los LED, revisa el modo de reposo del sensor, añade un temporizador de alimentación |
Empieza con ConnectMQ
Cuando tu prototipo de Pico W esté listo para salir del banco de trabajo, crea un broker privado en el portal de ConnectMQ en app.connectmq.com. El plan gratuito Serverless no requiere tarjeta de crédito, admite QoS 1 y hasta 25 conexiones, lo que cubre con holgura una pequeña flota de sensores a batería. Para más proyectos con Pi, el tutorial de Raspberry Pi y MQTT reúne la serie completa.
Preguntas frecuentes
¿Cuánto dura una Pico W con baterías usando MQTT?
Depende sobre todo del intervalo de publicación, del tiempo de conexión Wi-Fi y de las pilas que uses, así que mide tu propio nodo en lugar de fiarte de una cifra única. Registrar awake_ms y vbat durante unos días te da una tendencia fiable para extrapolar.
¿Puede la Pico W seguir conectada a MQTT mientras duerme?
No de una forma compatible con las baterías. Mantener viva la sesión MQTT exige que la radio siga asociada, lo que agota las pilas rápidamente, así que reconecta en cada despertar.
¿Debo usar QoS 0 o QoS 1 en un sensor a batería?
QoS 1 suele ser la mejor opción, porque publish() espera la confirmación del broker antes de que apagues la radio. El viaje de ida y vuelta adicional es pequeño comparado con el coste de unirse al Wi-Fi.
¿Es machine.deepsleep mejor que lightsleep en la Pico W?
En el port de MicroPython para RP2040 la diferencia es modesta, y deepsleep reinicia la placa, con lo que pierdes el estado en RAM. Para la máxima autonomía, un temporizador externo que corte la alimentación por completo supera a ambos.



