ESP32 y Arduino

Arduino Nano 33 IoT MQTT con WiFiNINA: envía datos del IMU por SSL

Publica lecturas del acelerómetro y el giroscopio del Arduino Nano 33 IoT en un broker MQTT con WiFiNINA, WiFiSSLClient y ArduinoMqttClient, y refuérzalo con credenciales y un Last Will.

Placa Arduino Nano 33 IoT enviando datos del sensor de movimiento en una configuración Arduino Nano 33 IoT MQTT por SSL
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

El Nano 33 IoT reúne Wi-Fi, un IMU de 6 ejes y un elemento seguro en una placa diminuta, lo que hace que los proyectos Arduino Nano 33 IoT MQTT sean ideales para monitorizar movimiento. En esta guía enviarás datos del acelerómetro y del giroscopio por una conexión SSL cifrada con WiFiNINA y ArduinoMqttClient. También añadirás un Last Will, mantendrás la tasa de mensajes dentro de los límites del broker y corregirás los errores de certificado más habituales.

La arquitectura del Nano 33 IoT en un minuto

La placa combina tres chips relevantes para este proyecto:

Componente Papel en un proyecto MQTT
SAMD21 (Arm Cortex-M0+) Ejecuta tu sketch
u-blox NINA-W102 Coprocesador Wi-Fi; gestiona TCP y el handshake SSL/TLS
LSM6DS3 Acelerómetro de 3 ejes y giroscopio de 3 ejes
ATECC608A Elemento criptográfico, útil para autenticación avanzada basada en certificados

Tu sketch se comunica con el módulo NINA mediante la librería WiFiNINA. El firmware del NINA guarda una lista de certificados raíz, así que WiFiSSLClient puede verificar la mayoría de las autoridades de certificación conocidas sin cambiar el código. Esa es la razón principal de que esta placa gestione las conexiones SSL con más facilidad que un ESP8266: el trabajo pesado lo hace el coprocesador.

Requisitos previos

  1. Arduino IDE 2.x con Arduino SAMD Boards (32-bits ARM Cortex-M0+) instalado desde el Gestor de tarjetas.
  2. Librerías del Gestor de librerías: WiFiNINA, ArduinoMqttClient y Arduino_LSM6DS3.
  3. Una red Wi-Fi de 2,4 GHz.
  4. Las herramientas de línea de comandos de Mosquitto en tu PC para hacer pruebas.
  5. Firmware del NINA actualizado. Sube Archivo → Ejemplos → WiFiNINA → Tools → CheckWiFiNINAFirmware y actualiza con Herramientas → WiFi101 / WiFiNINA Firmware Updater si hace falta. El firmware antiguo es la causa principal de los fallos de SSL.

Paso 1: lee el IMU

Antes de añadir la red, confirma que el sensor funciona:

#include <Arduino_LSM6DS3.h>

void setup() {
  Serial.begin(115200);
  while (!Serial) {}
  if (!IMU.begin()) {
    Serial.println("IMU not found");
    while (true) {}
  }
  Serial.print("Accelerometer sample rate: ");
  Serial.println(IMU.accelerationSampleRate());
}

void loop() {
  float x, y, z;
  if (IMU.accelerationAvailable()) {
    IMU.readAcceleration(x, y, z);
    Serial.print(x); Serial.print('\t');
    Serial.print(y); Serial.print('\t');
    Serial.println(z);
  }
  delay(200);
}

Con la placa plana sobre la mesa, z debería estar cerca de 1,0 (una g). Inclina la placa y observa cómo cambian los valores.

Paso 2: Arduino Nano 33 IoT MQTT sobre SSL

Te conectarás a broker.connectmq.com en el puerto 8883. El certificado del broker lo emite Let's Encrypt, cuya raíz está incluida en el firmware actual del NINA, así que no hace falta pegar ningún certificado en el sketch. Para la primera prueba puedes usar el servidor MQTT público gratuito de forma anónima; solo exige un client ID único y no vacío. Recuerda que cualquiera puede leer los topics públicos: sirve para pruebas y prototipos, no para datos de producción.

Crea arduino_secrets.h:

#define SECRET_SSID "YOUR_WIFI"
#define SECRET_PASS "YOUR_WIFI_PASSWORD"
// Déjalo vacío para el broker público; rellénalo para tu broker privado de ConnectMQ
#define SECRET_MQTT_CLIENT_ID "nano33-c7e2a4"
#define SECRET_MQTT_USER ""
#define SECRET_MQTT_PASS ""

El sketch principal publica un mensaje JSON compacto con la aceleración y la rotación cinco veces por segundo, muy por debajo del límite del servidor público de 10 mensajes por segundo por cliente:

#include <WiFiNINA.h>
#include <ArduinoMqttClient.h>
#include <Arduino_LSM6DS3.h>
#include "arduino_secrets.h"

const char broker[] = "broker.connectmq.com";
const int  port     = 8883;

const char topicImu[]    = "connectmq/demo/nano33-c7e2a4/imu";
const char topicStatus[] = "connectmq/demo/nano33-c7e2a4/status";
const char topicRate[]   = "connectmq/demo/nano33-c7e2a4/rate/set";

WiFiSSLClient sslClient;
MqttClient mqttClient(sslClient);

unsigned long publishIntervalMs = 200;   // 5 mensajes por segundo
unsigned long lastPublish = 0;

void connectWifi() {
  if (WiFi.status() == WL_NO_MODULE) {
    Serial.println("WiFi module not found");
    while (true) {}
  }
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print("Connecting to ");
    Serial.println(SECRET_SSID);
    WiFi.begin(SECRET_SSID, SECRET_PASS);
    delay(5000);
  }
  Serial.print("IP: ");
  Serial.println(WiFi.localIP());
}

void onMqttMessage(int size) {
  char buf[16];
  int i = 0;
  while (mqttClient.available() && i < (int)sizeof(buf) - 1) {
    buf[i++] = (char)mqttClient.read();
  }
  buf[i] = '\0';
  unsigned long ms = strtoul(buf, nullptr, 10);
  if (ms >= 100 && ms <= 60000) {          // nunca más de 10 msg/s
    publishIntervalMs = ms;
    Serial.print("New interval: ");
    Serial.println(publishIntervalMs);
  }
}

bool connectMqtt() {
  mqttClient.setId(SECRET_MQTT_CLIENT_ID);
  if (strlen(SECRET_MQTT_USER) > 0) {
    mqttClient.setUsernamePassword(SECRET_MQTT_USER, SECRET_MQTT_PASS);
  }
  mqttClient.setKeepAliveInterval(30 * 1000L);
  mqttClient.setConnectionTimeout(10 * 1000L);

  mqttClient.beginWill(topicStatus, true, 1);
  mqttClient.print("offline");
  mqttClient.endWill();

  Serial.print("MQTT over SSL... ");
  if (!mqttClient.connect(broker, port)) {
    Serial.print("failed, error ");
    Serial.println(mqttClient.connectError());
    return false;
  }
  Serial.println("connected");

  mqttClient.onMessage(onMqttMessage);
  mqttClient.subscribe(topicRate, 1);

  mqttClient.beginMessage(topicStatus, true, 1);
  mqttClient.print("online");
  mqttClient.endMessage();
  return true;
}

void setup() {
  Serial.begin(115200);
  unsigned long start = millis();
  while (!Serial && millis() - start < 3000) {}

  if (!IMU.begin()) {
    Serial.println("IMU init failed");
    while (true) {}
  }
  connectWifi();
  while (!connectMqtt()) delay(5000);
}

void loop() {
  if (WiFi.status() != WL_CONNECTED) connectWifi();
  if (!mqttClient.connected()) {
    delay(3000);
    connectMqtt();
    return;
  }
  mqttClient.poll();

  if (millis() - lastPublish >= publishIntervalMs &&
      IMU.accelerationAvailable() && IMU.gyroscopeAvailable()) {
    lastPublish = millis();
    float ax, ay, az, gx, gy, gz;
    IMU.readAcceleration(ax, ay, az);   // g
    IMU.readGyroscope(gx, gy, gz);      // grados por segundo

    // Envía el JSON en streaming; print(float, digits) evita problemas de soporte de floats en printf
    mqttClient.beginMessage(topicImu);
    mqttClient.print("{\"ax\":"); mqttClient.print(ax, 3);
    mqttClient.print(",\"ay\":"); mqttClient.print(ay, 3);
    mqttClient.print(",\"az\":"); mqttClient.print(az, 3);
    mqttClient.print(",\"gx\":"); mqttClient.print(gx, 1);
    mqttClient.print(",\"gy\":"); mqttClient.print(gy, 1);
    mqttClient.print(",\"gz\":"); mqttClient.print(gz, 1);
    mqttClient.print("}");
    mqttClient.endMessage();
  }
}

Observa el flujo de datos y ralentízalo desde tu PC:

mosquitto_sub -h broker.connectmq.com -p 1883 -i pc-c7e2a4 -t "connectmq/demo/nano33-c7e2a4/#" -v
mosquitto_pub -h broker.connectmq.com -p 1883 -i ctl-c7e2a4 -t "connectmq/demo/nano33-c7e2a4/rate/set" -m 1000

Fíjate en que tu PC usa el puerto 1883 sin cifrar mientras que la placa usa SSL en el 8883. Ambos llegan a los mismos topics en el mismo broker.

El payload se envía en streaming con print(value, digits) en lugar de snprintf. El soporte de floats en las funciones de estilo printf varía entre núcleos de Arduino, mientras que print() formatea los floats igual en todas partes, y el streaming evita tener el mensaje completo en RAM.

Paso 3: pasa a credenciales privadas

El área pública está bien para una demo, pero los datos de movimiento de máquinas o personas reales no deberían ser públicos. 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 arduino_secrets.h, mantén la conexión en el 8883 y sustituye el prefijo connectmq/demo/ por tu propia estructura de topics. Los client IDs están vinculados a las credenciales emitidas, así que el client ID debe coincidir exactamente.

Para orientarte sobre el diseño de topics, la rotación de credenciales y el mínimo privilegio, lee buenas prácticas de seguridad MQTT.

Elegir QoS y frecuencia para los datos del IMU

Los datos de sensores de alta frecuencia y los mensajes de comandos tienen necesidades distintas:

  • Telemetría a 5 Hz o más: QoS 0. Perder una muestra entre cientos no importa, y QoS 0 evita el tráfico extra de confirmaciones por el enlace del NINA.
  • Cambios de frecuencia y otros comandos: suscríbete con QoS 1 para que el broker reintente la entrega.
  • Estado: QoS 1 retenido con un Last Will equivalente, para que los dashboards sepan siempre si la placa está en línea.

Si necesitas funciones como el análisis de vibraciones, plantéate calcular valores resumen en el dispositivo (por ejemplo, la aceleración RMS durante un segundo) y publicarlos en lugar de las muestras en bruto. Reduce el ancho de banda y te mantiene holgadamente dentro de los límites de mensajes de cualquier plan. Los niveles de QoS de MQTT explicados profundiza en estas decisiones.

Calcular resúmenes de vibración en la placa

Las muestras en bruto del IMU son útiles para experimentar, pero para monitorizar un ventilador, una bomba de una planta embotelladora o una lavadora normalmente te interesa cuánto vibra el equipo a lo largo del tiempo. Calcular la media cuadrática (RMS) de la magnitud de la aceleración durante un segundo convierte decenas de muestras en un único número significativo. Si antes eliminas la componente de gravedad de 1 g, el valor queda cerca de cero cuando la máquina está parada.

float sumSq = 0;
int samples = 0;
unsigned long windowStart = 0;

void sampleVibration() {
  float x, y, z;
  if (IMU.accelerationAvailable()) {
    IMU.readAcceleration(x, y, z);
    float mag = sqrt(x * x + y * y + z * z) - 1.0;   // elimina la gravedad
    sumSq += mag * mag;
    samples++;
  }
  if (millis() - windowStart >= 1000 && samples > 0) {
    float rms = sqrt(sumSq / samples);
    mqttClient.beginMessage("connectmq/demo/nano33-c7e2a4/vibration");
    mqttClient.print("{\"rms_g\":");
    mqttClient.print(rms, 4);
    mqttClient.print(",\"n\":");
    mqttClient.print(samples);
    mqttClient.print("}");
    mqttClient.endMessage();
    sumSq = 0;
    samples = 0;
    windowStart = millis();
  }
}

Llama a sampleVibration() desde loop() junto a mqttClient.poll(). El resultado es un mensaje por segundo en lugar de un flujo continuo, y el campo n indica en cuántas muestras se basa el valor. En el lado receptor, un umbral sobre rms_g es una forma sencilla y eficaz de detectar vibraciones anómalas. Calíbralo grabando antes unos minutos de funcionamiento normal, porque cada máquina y cada posición de montaje se comportan de forma diferente.

Mantener sana la conexión

Un registrador de movimiento suele funcionar sin supervisión durante días, así que prepárate para los cortes de red. La lógica de reconexión del sketch reintenta cada pocos segundos, pero el reinicio de un router puede tardar más, así que plantéate aumentar la espera tras fallos repetidos para no saturar la red. Publica el topic de estado retenido en cada reconexión correcta y registra WiFi.RSSI() junto con tus datos: una señal débil es la razón más habitual de que una placa dentro de una caja metálica se desconecte una y otra vez. Si las lecturas se detienen mientras el estado sigue indicando online, comprueba que se llame a poll() en cada pasada por loop().

Lista de comprobación para solucionar problemas

  1. connect() falla en el 8883 pero funciona en el 1883 con WiFiClient: actualiza el firmware del NINA. Si usas un broker con una CA poco común, carga su certificado raíz con la herramienta de certificados del actualizador de firmware.
  2. "WiFi module not found": placa seleccionada incorrecta o librería WiFiNINA desactualizada.
  3. Error 2 (identificador rechazado): client ID vacío.
  4. Error 4 o 5: las credenciales o el client ID no coinciden con los emitidos en el portal.
  5. Desconexiones al cabo de unos minutos: algo en loop() bloquea poll(), o dos placas comparten client ID.
  6. Valores del IMU congelados: comprueba que IMU.begin() tuvo éxito y que solo lees cuando accelerationAvailable() es true.

Guías relacionadas

La guía de la librería ArduinoMqttClient profundiza en la API usada aquí, y el tutorial de Arduino UNO R4 WiFi MQTT muestra el mismo patrón sobre la pila WiFiS3. Para una visión más amplia de los proyectos MQTT embebidos, consulta el tutorial de IoT con ESP32.

Envía los datos de tu Nano 33 IoT a ConnectMQ

ConnectMQ es un broker MQTT en la nube con SSL en el puerto 8883, un certificado de Let's Encrypt en el que el módulo NINA confía de serie y un portal con explorador de topics en vivo y reglas de alerta por umbral, ideal para alertas de movimiento y vibración. Crea un broker Serverless gratuito en app.connectmq.com, sin tarjeta de crédito.

Preguntas frecuentes

¿Admite el Arduino Nano 33 IoT MQTT sobre SSL?

Sí. El módulo Wi-Fi NINA-W102 realiza el handshake SSL/TLS, y WiFiSSLClient combinado con ArduinoMqttClient se conecta al puerto 8883 con muy poco código.

¿Tengo que añadir un certificado raíz para ConnectMQ?

Normalmente no. ConnectMQ usa un certificado de Let's Encrypt, y su raíz forma parte del firmware actual del NINA. Actualiza el firmware si la verificación falla.

¿A qué velocidad puedo publicar los datos del IMU?

Mantén el dispositivo dentro de los límites de tu broker. En el servidor público son 10 mensajes por segundo por cliente; para frecuencias de muestreo mayores, agrupa varias lecturas en un mensaje o publica resúmenes calculados.

¿Puedo usar el ATECC608A para la autenticación MQTT?

El elemento criptográfico puede guardar claves para autenticación basada en certificados con brokers que admiten certificados de cliente. En la configuración de esta guía te autenticas con el usuario, la contraseña y el client ID emitidos en el portal.

Artículos relacionados

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

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

9 min de lectura

Todos los artículos →

Tu broker MQTT privado, a unos pocos clics

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