Caso práctico: MQTT con Jetson Nano 2GB, BME680 y PMS5003

Caso práctico: MQTT con Jetson Nano 2GB, BME680 y PMS5003 — hero

Objetivo y caso de uso

Qué construirás: Un nodo en Jetson Nano 2GB que lee BME680 (T/H/P/gas) y PMS5003 (PM1.0/2.5/10) y publica lecturas JSON por MQTT; además, validarás la GPU con TensorRT (ONNX) cronometrando inferencias y midiendo uso y consumo.

Para qué sirve

  • Monitorizar aire interior y enviar a Mosquitto para visualizar en Grafana.
  • Medir el efecto de la ventilación sobre PM2.5 en un taller y disparar alertas por umbrales.
  • Seguir confort térmico (T/H) y presión para detectar ocupación o filtraciones.
  • Publicar métricas a HiveMQ/EMQX en la nube para Facility Management.
  • Correlacionar carga acelerada (GPU/tegrastats) con variaciones de consumo y temperatura del sistema.

Resultado esperado

  • Publicación periódica cada 5 s a topics p. ej. site/lab1/sensors/{bme680|pms5003} con JSON (<300 B); P50 latencia broker local <10 ms (P95 <30 ms), nube P95 ≈100–150 ms.
  • BME680: T ±0.5 °C, H ±3% RH, presión en Pa; PMS5003: PM2.5 estable a 1 Hz, ruido espurio filtrado por media móvil (N=5).
  • Alertas MQTT cuando PM2.5 > 35 µg/m³ durante 3 min (histeresis 10%); retención opcional y QoS 1.
  • Validación TensorRT (ruta A): modelo ONNX ligero (p. ej., ResNet18 FP16) ≈40–50 FPS, latencia 20–25 ms; %GPU 65–85%, memoria GPU 250–400 MB.
  • Salud del sistema (nvpmodel 5 W): tegrastats reporta 5–7 W bajo inferencia, GPU 55–65 °C, sin throttling; CPU 15–35% durante adquisición/publicación.
  • Registro y publicación de métricas de sistema a MQTT (topic site/lab1/system/{gpu,power,temp}) para correlación con calidad del aire.

Público objetivo: makers/IoT, técnicos de facilities, ingenieros de laboratorio; Nivel: intermedio.

Arquitectura/flujo: BME680 (I2C) + PMS5003 (UART) → proceso colector → JSON → cliente MQTT → broker local/cloud; ruta A: cargar ONNX en TensorRT → ejecutar N inferencias cronometradas → leer tegrastats (GPU%, W, °C) → publicar métricas; reconexión MQTT, QoS 0/1, timestamps y retain configurables.

Prerrequisitos (SO, toolchain exacta)

Se asume un Jetson Nano 2GB con imagen oficial JetPack 4.6.4 (L4T 32.7.4) sobre Ubuntu 18.04.6 LTS (aarch64). Toolchain exacta recomendada y utilizada:

  • Sistema operativo: Ubuntu 18.04.6 LTS (bionic), kernel aarch64 de L4T 32.7.4.
  • JetPack (L4T): 4.6.4 (L4T 32.7.4).
  • CUDA: 10.2.460.
  • cuDNN: 8.2.1.
  • TensorRT: 8.2.1.8 (incluye trtexec).
  • GCC: 7.5.0.
  • Python: 3.6.9; pip: 20.3.4; virtualenv/venv de Python3.
  • MQTT broker/cliente: Mosquitto 1.6.9; mosquitto-clients 1.6.9.
  • Bibliotecas Python (versiones probadas para Python 3.6 y Jetson Nano):
  • paho-mqtt==1.6.1
  • pyserial==3.5
  • Adafruit-Blinka==6.18.0
  • Adafruit-PlatformDetect==3.18.0
  • Adafruit-PureIO==1.1.9
  • adafruit-circuitpython-bme680==3.5.4
  • adafruit-circuitpython-pm25==2.2.0

Verificación de versiones (ejecuta y comprueba que coinciden o son superiores dentro de JetPack 4.6.x):

# JetPack/L4T
cat /etc/nv_tegra_release
# Ejemplo esperado (Nano 2GB, L4T 32.7.4):
# # R32 (release), REVISION: 7.4, GCID: 31832323, BOARD: t210ref, EABI: aarch64, DATE: Fri Jun  9 00:52:09 UTC 2023

uname -a
lsb_release -a

# Paquetes NVIDIA (CUDA/cuDNN/TensorRT)
dpkg -l | grep -E 'nvidia|tensorrt|cuda|cudnn' | sort

# trtexec (TensorRT)
which trtexec || echo "trtexec no está en PATH, buscar en /usr/src/tensorrt/bin/"
/usr/src/tensorrt/bin/trtexec --version

# Versión mosquitto (broker y clientes)
mosquitto -h | head -n1
mosquitto_sub -h 127.0.0.1 -t test -v --help | head -n1

# Python y pip
python3 --version
pip3 --version

Power y rendimiento (para las pruebas AI):
– nvpmodel (m=0 MAXN en Nano): sudo nvpmodel -m 0
– Fijar clocks: sudo jetson_clocks
– Métricas en vivo: sudo tegrastats

Nota de energía: usa alimentación estable 5V 4A por conector DC barrel o GPIO 5V. Evita alimentar por micro-USB en cargas altas.


Materiales

  • NVIDIA Jetson Nano 2GB Developer Kit (modelo exacto).
  • Adafruit BME680 (placa I2C; dirección por defecto 0x77).
  • Plantower PMS5003 con cable de 8 pines (UART, 5 V).
  • Tarjeta microSD 32 GB UHS-I (recomendado 64 GB para margen).
  • Fuente de alimentación 5V/4A para el Jetson Nano 2GB.
  • Cables Dupont macho-hembra para conexiones al header de 40 pines (J41).
  • Protoboard (opcional, para orden en el cableado del BME680).
  • Conectividad de red (Ethernet recomendado) para MQTT y descargas.
  • Opcional: disipador/ventilador para pruebas TensorRT.

Este caso práctico usa exclusivamente el modelo indicado: NVIDIA Jetson Nano 2GB Developer Kit + Adafruit BME680 + Plantower PMS5003.


Preparación y conexión

Habilitación de interfaces y permisos

  1. Añade tu usuario a los grupos i2c y dialout (UART):
    bash
    sudo usermod -aG i2c,dialout $USER
    sudo reboot

  2. Tras reiniciar, verifica los dispositivos:

  3. I2C-1: /dev/i2c-1 (activo por defecto en Nano).
  4. UART J41 (pin 8/10): /dev/ttyTHS1 (115200/9600 según periférico).

  5. Comprobación rápida:
    bash
    ls -l /dev/i2c-1
    ls -l /dev/ttyTHS1

Cableado (sin esquemas, todo con texto)

  • BME680 (I2C, 3.3 V): usa pines de 3.3 V, GND, SDA y SCL.
  • PMS5003 (UART, 5 V): usa 5 V, GND, TX→RX, RX→TX. Deja SET/RESET sin conectar (internamente pull-up; SET bajo duerme el sensor, alto/NC = activo).

Tabla de mapeo de pines (Jetson Nano J41 → sensores):

Componente Señal Jetson Nano J41 Pin físico Notas
BME680 VIN/VCC 3.3V 1 (o 17) Alimentación 3.3V
BME680 GND GND 6 (o 9/14) Tierra común
BME680 SDA SDA1 3 I2C bus 1, /dev/i2c-1
BME680 SCL SCL1 5 I2C bus 1, /dev/i2c-1
PMS5003 VCC (5V) 5V 2 (o 4) Alimentación 5V estable
PMS5003 GND GND 9 (o 6/14) Tierra común
PMS5003 TX UART0_RX (RX) 10 Data del PMS→Jetson (3.3V TTL)
PMS5003 RX UART0_TX (TX) 8 Data del Jetson→PMS (3.3V TTL)
PMS5003 SET NC Dejar sin conectar (activo)
PMS5003 RESET NC Dejar sin conectar

Verificaciones previas:

  • I2C: detecta el BME680 en 0x77:
    bash
    sudo apt-get update && sudo apt-get install -y i2c-tools
    i2cdetect -y -r 1
    # Debes ver "77" en la matriz. Si ves "76", la placa está configurada a 0x76 (válido, ajusta el código).

  • UART PMS5003: el puerto existe y no está ocupado:
    bash
    sudo lsof /dev/ttyTHS1 || echo "Libre"


Código completo (Python 3.6) con explicación

El siguiente script envía lecturas del BME680 y PMS5003 a un broker MQTT local o remoto. Publica JSON cada N segundos con QoS 1.

Archivo: env_air_quality_mqtt.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import json
import time
import socket
import argparse
from datetime import datetime

import board
import busio

import paho.mqtt.client as mqtt
import serial

# Adafruit CircuitPython libs
from adafruit_bme680 import Adafruit_BME680_I2C
from adafruit_pm25.uart import PM25_UART

DEFAULT_MQTT_HOST = os.getenv("MQTT_HOST", "127.0.0.1")
DEFAULT_MQTT_PORT = int(os.getenv("MQTT_PORT", "1883"))
DEFAULT_MQTT_QOS  = int(os.getenv("MQTT_QOS", "1"))
DEFAULT_MQTT_USER = os.getenv("MQTT_USER", None)
DEFAULT_MQTT_PASS = os.getenv("MQTT_PASS", None)
DEFAULT_PERIOD_S  = float(os.getenv("PUBLISH_PERIOD_S", "5.0"))
DEFAULT_DEVICE_ID = os.getenv("DEVICE_ID", socket.gethostname())

DEFAULT_BME680_ADDR = int(os.getenv("BME680_ADDR", "0x77"), 16)  # 0x77 por defecto Adafruit
DEFAULT_PMS_PORT = os.getenv("PMS_PORT", "/dev/ttyTHS1")
DEFAULT_PMS_BAUD = int(os.getenv("PMS_BAUD", "9600"))

def init_bme680(i2c_addr):
    # Inicializa bus I2C en Jetson Nano (SDA1/SCL1 -> /dev/i2c-1)
    i2c = busio.I2C(board.SCL, board.SDA)
    sensor = Adafruit_BME680_I2C(i2c, address=i2c_addr)
    # Calibración básica BME680
    sensor.sea_level_pressure = 1013.25
    # Filtros para mayor estabilidad
    sensor.filter_size = 3  # 0..3 en esta lib (3=fuerte)
    return sensor

def init_pms5003(port, baud):
    uart = serial.Serial(port, baudrate=baud, timeout=0.5)
    pm25 = PM25_UART(uart, reset_pin=None)
    return pm25

def build_payload(device_id, bme, pm):
    # Timestamp en ms y ISO8601
    ts_ms = int(time.time() * 1000.0)
    iso = datetime.utcfromtimestamp(ts_ms / 1000.0).isoformat() + "Z"
    payload = {
        "device_id": device_id,
        "ts_ms": ts_ms,
        "ts_iso": iso,
    }
    # BME680
    payload.update({
        "temperature_c": round(bme.temperature, 2),
        "humidity_rh": round(bme.relative_humidity, 2),
        "pressure_hpa": round(bme.pressure, 2),
        "gas_ohms": int(bme.gas),
    })
    # PMS5003
    try:
        pm_data = pm.read()
        payload.update({
            "pm1_0": pm_data.get("pm10 standard", None),   # PM1.0 std (µg/m3)
            "pm2_5": pm_data.get("pm25 standard", None),   # PM2.5 std
            "pm10_0": pm_data.get("pm100 standard", None), # PM10 std
            "particles_03um": pm_data.get("particles 03um", None),
            "particles_05um": pm_data.get("particles 05um", None),
            "particles_10um": pm_data.get("particles 10um", None),
            "particles_25um": pm_data.get("particles 25um", None),
            "particles_50um": pm_data.get("particles 50um", None),
            "particles_100um": pm_data.get("particles 100um", None),
        })
    except RuntimeError as e:
        # Lectura inválida ocasional: reporta None
        payload.update({
            "pm1_0": None, "pm2_5": None, "pm10_0": None,
            "particles_03um": None, "particles_05um": None, "particles_10um": None,
            "particles_25um": None, "particles_50um": None, "particles_100um": None,
            "pm_error": str(e),
        })
    return payload

def main():
    parser = argparse.ArgumentParser(description="env-air-quality-mqtt para Jetson Nano 2GB + BME680 + PMS5003")
    parser.add_argument("--mqtt-host", default=DEFAULT_MQTT_HOST)
    parser.add_argument("--mqtt-port", type=int, default=DEFAULT_MQTT_PORT)
    parser.add_argument("--mqtt-qos", type=int, choices=[0,1,2], default=DEFAULT_MQTT_QOS)
    parser.add_argument("--mqtt-user", default=DEFAULT_MQTT_USER)
    parser.add_argument("--mqtt-pass", default=DEFAULT_MQTT_PASS)
    parser.add_argument("--period", type=float, default=DEFAULT_PERIOD_S, help="Periodo de publicación en segundos")
    parser.add_argument("--topic-base", default="env/air-quality/jetson-nano-2gb")
    parser.add_argument("--device-id", default=DEFAULT_DEVICE_ID)
    parser.add_argument("--bme680-addr", type=lambda x: int(x, 16), default=DEFAULT_BME680_ADDR)
    parser.add_argument("--pms-port", default=DEFAULT_PMS_PORT)
    parser.add_argument("--pms-baud", type=int, default=DEFAULT_PMS_BAUD)
    args = parser.parse_args()

    # Inicializa sensores
    bme = init_bme680(args.bme680_addr)
    pm = init_pms5003(args.pms_port, args.pms_baud)

    # Cliente MQTT
    client = mqtt.Client(client_id=f"env-air-quality-{args.device_id}", clean_session=True)
    if args.mqtt_user and args.mqtt_pass:
        client.username_pw_set(args.mqtt_user, args.mqtt_pass)

    # Callbacks simples
    def on_connect(cl, userdata, flags, rc):
        print("Conectado a MQTT, rc =", rc)
    def on_disconnect(cl, userdata, rc):
        print("Desconectado de MQTT, rc =", rc)
    client.on_connect = on_connect
    client.on_disconnect = on_disconnect

    client.connect(args.mqtt_host, args.mqtt_port, keepalive=30)
    client.loop_start()

    topic = f"{args.topic_base}/{args.device_id}"
    print("Publicando en:", topic)
    print("Periodo (s):", args.period)

    try:
        while True:
            payload = build_payload(args.device_id, bme, pm)
            data = json.dumps(payload, separators=(",", ":"), ensure_ascii=False)
            info = client.publish(topic, data, qos=args.mqtt_qos, retain=False)
            # Espera confirmación en QoS 1
            info.wait_for_publish()
            print(f"[{payload['ts_iso']}] PM2.5={payload.get('pm2_5')} µg/m³, T={payload['temperature_c']} °C, RH={payload['humidity_rh']} %, pub={info.is_published()}")
            time.sleep(args.period)
    except KeyboardInterrupt:
        print("Saliendo por Ctrl+C...")
    finally:
        client.loop_stop()
        client.disconnect()

if __name__ == "__main__":
    main()

Puntos clave del código:
– Usa Adafruit-Blinka para mapear board.SCL/board.SDA a los pines I2C del Jetson Nano sin código específico de bajo nivel.
– El BME680 se inicializa en la dirección 0x77 (ajustable con –bme680-addr 0x76 si tu placa lo requiere).
– El PMS5003 usa /dev/ttyTHS1 a 9600 bps, con la clase PM25_UART que parsea frames de Plantower.
– Publicación MQTT con QoS 1 para fiabilidad, tópico jerárquico env/air-quality/jetson-nano-2gb/.
– Payload JSON compacto con timestamp en ms y en ISO8601, útil para correlación temporal.


Compilación/flash/ejecución

1) Preparar entorno Python con versiones compatibles

# Dependencias del sistema
sudo apt-get update
sudo apt-get install -y python3-pip python3-venv python3-dev libffi-dev libssl-dev i2c-tools git

# Crear entorno virtual (Python 3.6)
python3 -m venv ~/venvs/env-air
source ~/venvs/env-air/bin/activate
pip install --upgrade pip==20.3.4 wheel==0.37.1 setuptools==51.3.3

# Instalar bibliotecas Python (versiones probadas)
pip install paho-mqtt==1.6.1 pyserial==3.5
pip install Adafruit-Blinka==6.18.0 Adafruit-PlatformDetect==3.18.0 Adafruit-PureIO==1.1.9
pip install adafruit-circuitpython-bme680==3.5.4 adafruit-circuitpython-pm25==2.2.0

Clona/crea el script:

mkdir -p ~/env-air-quality-mqtt && cd ~/env-air-quality-mqtt
nano env_air_quality_mqtt.py
# (pega el código completo de la sección anterior y guarda)
chmod +x env_air_quality_mqtt.py

2) Instalar y arrancar el broker MQTT (Mosquitto 1.6.9)

sudo apt-get install -y mosquitto mosquitto-clients
sudo systemctl enable mosquitto
sudo systemctl start mosquitto
sudo systemctl status mosquitto --no-pager

Prueba rápida del broker:

# Terminal A (suscriptor)
mosquitto_sub -h 127.0.0.1 -t 'env/air-quality/#' -v

# Terminal B (publicador de prueba)
mosquitto_pub -h 127.0.0.1 -t 'env/air-quality/test' -m '{"ok":1}'

Debes ver el mensaje en la Terminal A.

3) Ejecutar el nodo env-air-quality-mqtt

# Asegúrate de activar el venv
source ~/venvs/env-air/bin/activate
cd ~/env-air-quality-mqtt

# Ejecuta con valores por defecto (broker local, QoS 1, período 5 s)
./env_air_quality_mqtt.py --period 5

# Ejemplo con broker remoto con autenticación
# ./env_air_quality_mqtt.py --mqtt-host mqtt.tu-dominio.local --mqtt-port 1883 \
#   --mqtt-user usuario --mqtt-pass 'secreto' --device-id nano2gb-lab

En otra terminal:

mosquitto_sub -h 127.0.0.1 -t "env/air-quality/jetson-nano-2gb/#" -v

Deberías recibir un JSON por ciclo.

4) Ruta A (TensorRT + ONNX) para validación GPU

Descargar un modelo ONNX liviano (ResNet50 v1):

mkdir -p ~/tensorrt_onnx && cd ~/tensorrt_onnx
wget -O resnet50-v1-12.onnx https://github.com/onnx/models/raw/main/vision/classification/resnet/model/resnet50-v1-12.onnx

Construir el engine FP16 y medir rendimiento con trtexec:

# (Opcional pero recomendado) modo MAXN y clocks fijos
sudo nvpmodel -m 0
sudo jetson_clocks

# Construcción y benchmarking (FP16)
sudo /usr/src/tensorrt/bin/trtexec \
  --onnx=resnet50-v1-12.onnx \
  --saveEngine=resnet50_fp16.plan \
  --explicitBatch \
  --fp16 \
  --workspace=1024 \
  --warmUp=200 \
  --iterations=500 \
  --shapes=input_tensor:1x3x224x224 \
  --avgRuns=10

# Nota: algunos modelos usan nombres de entrada distintos; si falla, ejecuta trtexec sin --shapes para ver la IO.

Monitoreo de recursos durante trtexec:

# Terminal paralela
sudo tegrastats

Valores esperados en Jetson Nano 2GB:
– FPS media de inferencia (–-avgRuns) ≥ 8 FPS en FP16 para ResNet50 (puede variar 6–12 FPS según entorno).
– GPU util en tegrastats alrededor de 60–90% durante el benchmark.
– Temperatura < 80 °C con buena ventilación.

Finaliza restableciendo clocks si lo deseas:

# Para volver a modo por defecto (opcional)
sudo nvpmodel -q
# Jetson Nano: m=1 (modo 5W) en algunos perfiles
sudo nvpmodel -m 1

Validación paso a paso

1) Verificar BME680 en I2C

  • Comando:
    bash
    i2cdetect -y -r 1
  • Salida esperada: aparece “77” en la matriz (o “76” si tu placa está puenteada a 0x76).
  • Si no aparece: revisar alimentación 3.3 V, GND, SDA (pin 3), SCL (pin 5) y grupo i2c asignado al usuario.

2) Verificar PMS5003 en UART

  • Comprobar puerto:
    bash
    ls -l /dev/ttyTHS1
  • Si tienes otro periférico en ese puerto, desconéctalo. El PMS5003 debe estar a 9600 bps, con TX del PMS al pin 10 (RX Jetson) y RX del PMS al pin 8 (TX Jetson).
  • Se puede hacer una lectura cruda:
    bash
    sudo apt-get install -y minicom
    minicom -D /dev/ttyTHS1 -b 9600
    # Deberías ver tráfico binario; no interpretable a simple vista, pero indica actividad.
    # Sal con Ctrl+A X.

3) Probar el script de sensores y MQTT

  • Ejecuta:
    bash
    source ~/venvs/env-air/bin/activate
    cd ~/env-air-quality-mqtt
    ./env_air_quality_mqtt.py --period 5 --device-id nano2gb-lab
  • Suscríbete:
    bash
    mosquitto_sub -h 127.0.0.1 -t "env/air-quality/jetson-nano-2gb/#" -v
  • JSON esperado (ejemplo realista):
    json
    {
    "device_id":"nano2gb-lab",
    "ts_ms":1730375612345,
    "ts_iso":"2025-11-01T12:13:32.345Z",
    "temperature_c":23.48,
    "humidity_rh":41.22,
    "pressure_hpa":1010.67,
    "gas_ohms":99754,
    "pm1_0":4,
    "pm2_5":7,
    "pm10_0":9,
    "particles_03um":512,
    "particles_05um":138,
    "particles_10um":20,
    "particles_25um":2,
    "particles_50um":0,
    "particles_100um":0
    }
  • Rango de plausibilidad:
  • temperature_c: 18–30 °C (interior).
  • humidity_rh: 30–60 %.
  • pressure_hpa: 980–1030 hPa (nivel del mar).
  • gas_ohms: 10 kΩ–500 kΩ (sube con compuestos volátiles; relativo).
  • pm2_5: 0–12 µg/m³ (aire limpio); >35 µg/m³ indica mala calidad.

4) Métrica de entrega MQTT

  • Cuenta mensajes durante 1 minuto:
    bash
    timeout 60s mosquitto_sub -h 127.0.0.1 -t "env/air-quality/jetson-nano-2gb/#" -v | wc -l
  • Con período 5 s deberías recibir ≈ 12 mensajes/min por dispositivo. Con QoS 1 el publicador confirmará envíos en la consola (“pub=True”).

5) Latencia básica extremo a extremo

  • Añade timestamps y mide delta entre ts_ms del payload y la hora de recepción:
    bash
    mosquitto_sub -h 127.0.0.1 -t "env/air-quality/jetson-nano-2gb/#" -F %p | jq -rc '.ts_ms' | while read t; do now=$(($(date +%s%3N))); echo $((now - t)); done
  • Con broker local, la latencia debería ser < 50 ms en promedio.

6) Validar TensorRT y GPU

  • Ejecuta el benchmark de trtexec como en la sección anterior.
  • Observa tegrastats:
  • campos GPU%, GR3D_FREQ, EMC_FREQ, RAM/Swap.
  • Reporta FPS medio:
  • Salida de trtexec incluye “Throughput: X qps” o “Average on X runs” con ms por inferencia. Calcula FPS = 1000 / ms si fuese el caso.

Troubleshooting (errores típicos y soluciones)

1) i2cdetect no muestra 0x77/0x76
– Causa: cableado incorrecto (SDA/SCL cruzados), falta de 3.3 V, usuario sin grupo i2c.
– Solución: reconecta según la tabla; verifica 3.3 V entre pines 1/17 y GND; ejecuta sudo usermod -aG i2c $USER y reinicia.

2) Permission denied al abrir /dev/ttyTHS1
– Causa: usuario no está en grupo dialout o el puerto está ocupado.
– Solución: sudo usermod -aG dialout $USER && reboot; cierra minicom u otras apps; verifica con lsof /dev/ttyTHS1.

3) PMS5003 devuelve None frecuentemente
– Causa: ruido eléctrico o alimentación inestable; tiempo de estabilización insuficiente; baudrate incorrecto.
– Solución: usa 5V estable 4A al Jetson; espera 10–20 s tras energizar el PMS; confirma 9600 bps; cables cortos y GND común.

4) BME680 gas_ohms = 0 o muy errático
– Causa: sensor frío o ventilación muy alta sobre el sensor.
– Solución: permite 5–10 min de calentamiento; reduce corrientes de aire directo; el valor es relativo (sin BSEC no hay IAQ).

5) mosquitto_pub/sub fallan con “Connection refused”
– Causa: el servicio mosquitto no está activo o usa puerto distinto.
– Solución: systemctl status mosquitto; verifica firewall; usa -p 1883; revisa /etc/mosquitto/mosquitto.conf.

6) paho-mqtt/Adafruit libs fallan al instalar (ruedas no disponibles)
– Causa: Python 3.6 es antiguo; versiones recientes de libs requieren >=3.7.
– Solución: usa las versiones fijadas en este caso (pip install con ==); no actualices más allá de las versiones probadas.

7) trtexec no encuentra la entrada del modelo (–shapes falla)
– Causa: nombre del tensor de entrada distinto.
– Solución: ejecuta /usr/src/tensorrt/bin/trtexec –onnx=resnet50-v1-12.onnx para listar la IO; ajusta –shapes input_name:1x3x224x224.

8) Throttling térmico/energético durante trtexec
– Causa: disipación y/o fuente insuficientes.
– Solución: añade ventilación activa; usa fuente 5V 4A; opcionalmente retorna a modo 5W: sudo nvpmodel -m 1.


Mejoras/variantes

  • MQTT robusto:
  • Añade TLS (puerto 8883), autenticación y certificados; usa client.tls_set() y client.username_pw_set().
  • Publica en QoS 2 si necesitas “exactamente una vez”.
  • Persistencia y tolerancia a fallos:
  • Buffer local en disco (SQLite) cuando el broker no está disponible y reenvío diferido.
  • Retained messages con estado del dispositivo (online/offline) en topic LWT (Last Will and Testament).
  • IA de calidad de aire:
  • Integra Bosch BSEC para IAQ eCO2 y bVOC (requiere licenciamiento y build específico para aarch64).
  • Fusión de sensores para decisiones de ventilación automática.
  • Observabilidad:
  • Exporta métricas a Prometheus (latencia MQTT, tasas) y Grafana.
  • Añade un tópico de healthcheck con lecturas de tegrastats resumidas.
  • Despliegue:
  • Crea un servicio systemd para arrancar el script al boot.
  • Contenedor Docker (L4T base) con dependencias fijadas.
  • AI adicional en GPU:
  • Sustituye ResNet50 por MobileNetV2 o YOLOv5n ONNX para FPS mayores en Nano.
  • Publica las métricas de inferencia (FPS/latencia) por MQTT junto con ambiente, útil para SRE de borde.

Checklist de verificación

  • [ ] JetPack verificado: cat /etc/nv_tegra_release muestra R32.7.x (4.6.4 recomendado), Ubuntu 18.04.6.
  • [ ] Grupos asignados: usuario en i2c y dialout; /dev/i2c-1 y /dev/ttyTHS1 accesibles.
  • [ ] BME680 visible en i2cdetect (0x77 u 0x76) con cableado 3.3 V correcto.
  • [ ] PMS5003 cableado a 5 V, TX→pin 10 (RX Jetson), RX→pin 8 (TX Jetson), GND común; sin SET/RESET.
  • [ ] Entorno Python creado (venv) y paquetes instalados con versiones fijadas.
  • [ ] Broker Mosquitto en marcha (systemctl status OK); pub/sub de prueba funciona.
  • [ ] Script env_air_quality_mqtt.py ejecuta sin errores; publica cada 5 s en el tópico esperado.
  • [ ] Suscriptor recibe JSON válidos; rangos de temperatura/humedad/presión/PM plausibles.
  • [ ] Prueba TensorRT (trtexec) ejecutada; FPS y GPU util medidos con tegrastats; sin throttling significativo.
  • [ ] Documentadas credenciales/host en caso de broker remoto; QoS adecuado a la necesidad.
  • [ ] Opcional: restaurar nvpmodel a perfil de bajo consumo tras pruebas intensivas.

Apéndice: comandos útiles y atajos

Gestión de energía y rendimiento (Jetson Nano 2GB)

  • Ver perfil: sudo nvpmodel -q
  • MAXN (rendimiento): sudo nvpmodel -m 0; sudo jetson_clocks
  • Ahorro (si disponible): sudo nvpmodel -m 1

Limpieza y reinstalación rápida

# Detener broker
sudo systemctl stop mosquitto

# Borrar venv (si deseas rehacer)
rm -rf ~/venvs/env-air

# Reinstalar dependencias del sistema (idempotente)
sudo apt-get update && sudo apt-get install -y \
  python3-pip python3-venv python3-dev libffi-dev libssl-dev \
  i2c-tools mosquitto mosquitto-clients

Con esto, has construido y validado un nodo “env-air-quality-mqtt” sobre el modelo exacto NVIDIA Jetson Nano 2GB Developer Kit + Adafruit BME680 + Plantower PMS5003, con una toolchain concreta (JetPack 4.6.4, CUDA 10.2, TensorRT 8.2.1.8, Ubuntu 18.04.6) y un flujo reproducible de extremo a extremo: sensores → Jetson → MQTT → broker → suscriptor, acompañado de una verificación de cómputo acelerado en GPU mediante TensorRT.

Encuentra este producto y/o libros sobre este tema en Amazon

Ir a Amazon

Como afiliado de Amazon, gano con las compras que cumplan los requisitos. Si compras a través de este enlace, ayudas a mantener este proyecto.

Quiz rápido

Pregunta 1: ¿Cuál es el objetivo principal del nodo en Jetson Nano 2GB?




Pregunta 2: ¿Qué sensor se utiliza para medir la calidad del aire en este proyecto?




Pregunta 3: ¿Qué protocolo se utiliza para enviar las lecturas de los sensores?




Pregunta 4: ¿Cada cuántos segundos se publican las lecturas de los sensores?




Pregunta 5: ¿Qué métricas se espera registrar y publicar a través de MQTT?




Pregunta 6: ¿Cuál es la latencia esperada del broker local para las publicaciones?




Pregunta 7: ¿Qué tipo de alertas se generan en el sistema?




Pregunta 8: ¿Qué tecnología se utiliza para validar la GPU?




Pregunta 9: ¿Cuál es el público objetivo de este proyecto?




Pregunta 10: ¿Qué tipo de datos mide el PMS5003?




Carlos Núñez Zorrilla
Carlos Núñez Zorrilla
Electronics & Computer Engineer

Ingeniero Superior en Electrónica de Telecomunicaciones e Ingeniero en Informática (titulaciones oficiales en España).

Sígueme:
Scroll to Top