Raspberry Pi

Pico W MQTT TLS: conexiones seguras en MicroPython por el puerto 8883 con verificación de certificados

Cifra y autentica el tráfico MQTT de tu Raspberry Pi Pico W. Esta guía cubre las versiones de firmware y librería, la carga del certificado raíz correcto, el ajuste del reloj, la verificación del servidor, la medición del tiempo de handshake y la solución de los errores TLS habituales.

Conexión Pico W MQTT TLS con un candado entre la Raspberry Pi Pico W y un broker MQTT en la nube en el puerto 8883
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

Pico W MQTT TLS es lo que convierte una conexión de aficionado en una en la que puedes confiar: el tráfico va cifrado y la placa verifica que realmente está hablando con tu broker antes de enviar una contraseña. Esta guía toma un sketch MQTT de MicroPython que ya funciona y lo lleva al puerto 8883 con verificación completa de certificados, cubriendo las versiones de firmware que necesitas, el certificado raíz que debes cargar, por qué importa el reloj, cuánto tarda el handshake en un RP2040 y cómo corregir los errores que con más probabilidad verás.

Por qué TLS importa en un microcontrolador

MQTT sin cifrar en el puerto 1883 envía todo en texto plano, incluidos el usuario y la contraseña del paquete CONNECT. Cualquiera en la misma red Wi-Fi, en el proveedor de internet o en el camino hasta el broker puede leer tus lecturas y reutilizar tus credenciales. TLS resuelve dos problemas a la vez:

  1. Cifrado. Los payloads y las credenciales son ilegibles en tránsito.
  2. Autenticación del servidor. El dispositivo comprueba la cadena de certificados del broker, de modo que un punto de acceso falso o un secuestro de DNS no pueden suplantar al broker y recopilar credenciales.

El segundo punto es el que la mayoría de los tutoriales se saltan. Activar TLS sin verificar el certificado te da cifrado frente a escuchas pasivas, pero ninguna protección frente a un atacante activo. Esta guía lo hace bien. Para una visión más amplia, consulta las buenas prácticas de seguridad en MQTT.

Si nunca has conectado una Pico W a MQTT, empieza por el tutorial básico de Raspberry Pi Pico W y MQTT y vuelve cuando funcione.

Versiones de firmware y librería

El soporte de TLS en MicroPython ha cambiado en las últimas versiones, y muchos ejemplos antiguos ya no funcionan tal como están escritos.

Componente Qué usar Por qué
Firmware de MicroPython Una versión actual para RPI_PICO_W (1.23 o posterior) Proporciona ssl.SSLContext con load_verify_locations y una gestión correcta del hostname
umqtt.simple La versión actual de micropython-lib mediante mip Las versiones recientes aceptan un SSLContext en el argumento ssl
Certificado raíz ISRG Root X1 en formato DER El ancla de confianza de los certificados de Let's Encrypt

Los ejemplos antiguos pasan ssl=True junto con un diccionario ssl_params. Si ves un TypeError sobre argumentos con nombre inesperados, o AttributeError: 'bool' object has no attribute 'wrap_socket', estás mezclando un ejemplo antiguo con una librería nueva o al revés. Actualiza tanto el firmware como la librería y usa el código basado en contexto que aparece más abajo.

Comprueba qué estás ejecutando desde el REPL:

import sys, os
print(sys.version)
print(os.uname().machine)

Paso 1: consigue el certificado raíz correcto

Los brokers de ConnectMQ, incluido el servidor MQTT público gratuito broker.connectmq.com, usan certificados emitidos por Let's Encrypt. Puedes confirmar la cadena tú mismo:

openssl s_client -connect broker.connectmq.com:8883 -servername broker.connectmq.com -showcerts </dev/null \
  | grep -E "s:|i:"

La salida enumera el certificado del servidor, el intermedio de Let's Encrypt y, al final, un emisor ISRG Root X1. Esa raíz es en la que debe confiar la Pico W. Confiar en la raíz y no en el certificado del servidor es importante: los certificados de Let's Encrypt se renuevan cada pocos meses y los intermedios cambian de vez en cuando, pero la raíz sigue siendo válida durante años.

Descarga la raíz en formato DER, que MicroPython carga directamente:

curl -o isrgrootx1.der https://letsencrypt.org/certs/isrgrootx1.der
# or convert a PEM copy you already have
openssl x509 -in isrgrootx1.pem -outform DER -out isrgrootx1.der

Cópiala a la placa con la vista de archivos de Thonny o con:

mpremote cp isrgrootx1.der :isrgrootx1.der

Paso 2: ajusta el reloj

La Pico W no tiene un reloj respaldado por batería. Tras cada encendido, su reloj de tiempo real arranca en una fecha por defecto del pasado. Según la compilación del firmware, los periodos de validez de los certificados pueden comprobarse con ese reloj, y un dispositivo que cree estar años atrás rechazará como todavía no válido un certificado emitido el mes pasado. Aunque la comprobación no se aplique, tener la hora correcta es útil para los registros y las marcas de tiempo, así que ajústala mediante NTP justo después de conectar el Wi-Fi:

import ntptime
ntptime.host = "pool.ntp.org"
ntptime.settime()          # sets the RTC to UTC

ntptime.settime() puede lanzar OSError cuando la petición NTP agota el tiempo de espera, así que reinténtalo varias veces en lugar de dejar que bloquee el programa.

Paso 3: el programa completo de Pico W MQTT TLS

Guárdalo como main.py. Para el servidor público de pruebas, las credenciales pueden quedar vacías y sirve cualquier client ID único. Para producción, rellena los valores generados en el portal.

# main.py - Pico W MQTT over TLS with certificate verification
import time
import gc
import ssl
import network
import machine
import ntptime
import ubinascii
from umqtt.simple import MQTTClient

WIFI_SSID = "YOUR_WIFI_SSID"
WIFI_PASSWORD = "YOUR_WIFI_PASSWORD"

BROKER = "broker.connectmq.com"
PORT = 8883
# Public test broker: anonymous, any unique client ID.
# Production: use the values from the broker portal.
MQTT_USER = None                 # "YOUR_USERNAME"
MQTT_PASSWORD = None             # "YOUR_PASSWORD"
CLIENT_ID = "pico-tls-" + ubinascii.hexlify(machine.unique_id()).decode()  # or "YOUR_CLIENT_ID"

TOPIC = "connectmq/demo/tl5v8n/pico1/temperature"
CA_FILE = "isrgrootx1.der"

sensor = machine.ADC(4)


def connect_wifi():
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    if not wlan.isconnected():
        wlan.connect(WIFI_SSID, WIFI_PASSWORD)
        for _ in range(40):
            if wlan.isconnected():
                break
            time.sleep(0.5)
    if not wlan.isconnected():
        raise OSError("Wi-Fi failed, status %d" % wlan.status())
    print("Wi-Fi:", wlan.ifconfig()[0])


def sync_time():
    for attempt in range(5):
        try:
            ntptime.settime()
            print("Time set:", time.gmtime())
            return
        except OSError:
            time.sleep(2)
    print("Warning: NTP failed, clock may be wrong")


def make_ssl_context():
    ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
    ctx.verify_mode = ssl.CERT_REQUIRED
    with open(CA_FILE, "rb") as f:
        ctx.load_verify_locations(cadata=f.read())
    return ctx


def connect_mqtt(ctx):
    gc.collect()
    print("Free RAM before TLS:", gc.mem_free())
    client = MQTTClient(
        CLIENT_ID, BROKER, port=PORT,
        user=MQTT_USER, password=MQTT_PASSWORD,
        keepalive=60, ssl=ctx,
    )
    start = time.ticks_ms()
    client.connect()
    print("TLS + MQTT connect took", time.ticks_diff(time.ticks_ms(), start), "ms")
    print("Free RAM after TLS:", gc.mem_free())
    return client


def read_temperature():
    v = sensor.read_u16() * 3.3 / 65535
    return round(27 - (v - 0.706) / 0.001721, 2)


def main():
    connect_wifi()
    sync_time()
    ctx = make_ssl_context()
    client = connect_mqtt(ctx)
    while True:
        payload = '{"value": %s, "unit": "C"}' % read_temperature()
        client.publish(TOPIC, payload, qos=1)
        print("Published", payload)
        time.sleep(30)           # publishing every 30 s keeps the 60 s keepalive satisfied


while True:
    try:
        main()
    except OSError as e:
        print("Error:", e, "- reconnecting in 10 s")
        time.sleep(10)

umqtt.simple envuelve el socket con el contexto y pasa el nombre del broker como hostname del servidor. Eso es lo que permite a mbedTLS comprobar que el certificado se emitió para broker.connectmq.com, y además envía SNI, que muchos servidores TLS necesitan para elegir el certificado correcto.

Observa la salida desde tu ordenador, también por TLS:

mosquitto_sub -h broker.connectmq.com -p 8883 --cafile isrgrootx1.pem \
  -i laptop-tl5v8n -v -t "connectmq/demo/tl5v8n/#"

Recuerda que el servidor público está pensado para pruebas y prototipos: no envíes allí datos de producción.

Tiempo de handshake y memoria en el RP2040

El RP2040 no tiene aceleración criptográfica por hardware, así que mbedTLS hace todo el trabajo por software en núcleos Cortex-M0+. Espera que el handshake TLS se note claramente, a menudo del orden de un segundo o más, frente a una conexión TCP sin cifrar casi instantánea. El programa imprime la cifra exacta en tu red, que conviene anotar porque depende del intercambio de claves negociado, de la longitud de la cadena de certificados y de la latencia del Wi-Fi.

Lo que esto significa en la práctica:

  1. Conecta una vez y mantente conectado. Con alimentación de red, mantén una sesión TLS de larga duración y envía muchos mensajes por ella. El coste del handshake solo se paga al reconectar.
  2. Ten cuidado con los diseños con deep sleep. Un dispositivo a batería que despierta, conecta, publica y duerme paga el handshake completo en cada ciclo, lo que cuesta tiempo y energía. Nuestra guía de Pico W a batería con MQTT trata los intervalos más largos y la agrupación de lecturas para este caso.
  3. Libera memoria primero. Una sesión TLS necesita decenas de kilobytes de RAM para los búferes. Llama a gc.collect() antes de conectar, evita crear objetos grandes antes del handshake y compara las cifras de memoria libre impresas antes y después.
  4. Considera la Pico 2 W. Su RP2350 usa núcleos Cortex-M33 más rápidos, lo que acorta el handshake y deja más margen. Consulta Pico W vs Pico 2 W.

Solución de errores TLS en la Pico W

Error o síntoma Causa probable Solución
OSError: (-9984, 'MBEDTLS_ERR_X509_CERT_VERIFY_FAILED') Certificado raíz incorrecto o ausente, o hostname incorrecto Carga ISRG Root X1 y conecta por hostname, no por IP
La verificación falla solo tras el encendido Reloj sin ajustar Ejecuta ntptime.settime() antes de conectar
OSError: [Errno 12] ENOMEM No hay suficiente RAM contigua para los búferes TLS gc.collect() antes de conectar, reduce búferes y variables globales
TypeError en MQTTClient(... ssl_params=...) Ejemplo antiguo con un umqtt.simple nuevo Pasa un SSLContext como ssl=
Conecta en el 1883 pero agota el tiempo en el 8883 La red bloquea el 8883 Prueba desde un portátil en la misma red; usa otra red o pide a TI que permita el 8883 saliente
MQTTException: 5 No autorizado Revisa el usuario, la contraseña y el client ID exacto del portal
El handshake tarda muchísimo Señal Wi-Fi débil o presión de RAM Comprueba el RSSI con wlan.status("rssi") y la memoria libre

Una nota sobre CERT_NONE: poner ctx.verify_mode = ssl.CERT_NONE hace desaparecer los errores, y por eso aparece en muchas respuestas de foros. También elimina la autenticación del servidor. Úsalo solo para un diagnóstico puntual, nunca en código desplegado.

Pasar a tu propio broker privado

El servidor público acepta conexiones TLS anónimas en el 8883, lo que es perfecto para probar este código. Para dispositivos reales, crea un broker privado en ConnectMQ y copia las credenciales generadas en los tres ajustes del principio del programa. El broker vincula los client IDs a las credenciales emitidas, así que usa exactamente el client ID del portal. Como el certificado es de Let's Encrypt, el mismo archivo isrgrootx1.der sirve tanto para el servidor público como para tu broker privado, y las renovaciones de certificados en el lado del broker no requieren ningún cambio en el dispositivo.

Un broker MQTT gestionado también bloquea direcciones IP tras intentos de inicio de sesión fallidos repetidos y aplica cuotas por plan, lo que protege tu broker de clientes con mal comportamiento o comprometidos sin ningún trabajo por tu parte. Para saber más sobre brokers en la nube, consulta nuestra guía del broker MQTT en la nube, y para otros proyectos con Raspberry Pi, el tutorial de Raspberry Pi y MQTT.

Protege tu Pico W con ConnectMQ

Crea un broker Serverless gratuito en app.connectmq.com, sin tarjeta de crédito, copia el usuario, la contraseña y el client ID en main.py y tu Pico W publicará mediante TLS verificado en el puerto 8883. El explorador de topics en vivo del portal muestra cómo llegan los mensajes, para que puedas confirmar la ruta segura de extremo a extremo.

Preguntas frecuentes

¿Qué certificado necesita la Pico W para ConnectMQ?

ISRG Root X1, la raíz de Let's Encrypt, en formato DER. Confiar en la raíz significa que las renovaciones rutinarias de certificados en el broker no obligan a actualizar el dispositivo.

¿Por qué mi Pico W rechaza un certificado válido?

Las causas más comunes son un reloj que nunca se ajustó, de modo que el certificado parece todavía no válido, conectar por dirección IP en lugar de por hostname, o cargar un certificado raíz equivocado.

¿Cuánto tarda un handshake TLS en la Pico W?

Es notablemente más lento que en chips con hardware criptográfico, porque el RP2040 hace toda la criptografía por software. Mídelo con time.ticks_ms() alrededor de connect() como se muestra arriba y mantén las conexiones abiertas en lugar de reconectar para cada mensaje.

¿Puedo usar certificados de cliente en la Pico W?

El SSLContext de MicroPython admite load_cert_chain para certificados de cliente. El broker autentica los dispositivos con el usuario, la contraseña y el client ID emitidos en el portal, así que la configuración estándar es una conexión TLS con verificación del servidor y esas credenciales.

Artículos relacionados

Fiabilidad de la Raspberry Pi: cómo usar una Pi 24/7 como gateway IoT

Una guía práctica para mantener un gateway MQTT con Raspberry Pi funcionando durante meses: desgaste del almacenamiento, sistemas de archivos de solo lectura, alimentación, refrigeración, watchdogs de hardware y software y monitorización remota de la salud.

9 min de lectura

Pico W vs Pico 2 W para MQTT: rendimiento, memoria y TLS de RP2040 frente a RP2350

La Pico 2 W mantiene el formato y el chip Wi-Fi de la Pico W, pero sustituye el RP2040 por el RP2350, más rápido. Esta comparativa analiza qué significa para los dispositivos MQTT: CPU, memoria, TLS, funciones de seguridad, soporte de software y qué placa comprar para cada proyecto.

9 min de lectura

Registro de accesos Raspberry Pi RFID MQTT con el lector RC522

Conecta un lector RFID RC522 a una Raspberry Pi, lee etiquetas en Python, compáralas con una lista blanca local y publica cada intento de acceso como un evento MQTT que pueden consumir registradores, dashboards y herramientas de alertas.

8 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.