You dont have javascript enabled! Please enable it!

Caso práctico: Nivel de agua LoRaWAN Arduino MKR WAN 1310

Caso práctico: Nivel de agua LoRaWAN Arduino MKR WAN 1310 — hero

Objetivo y caso de uso

Qué construirás: Un sistema de telemetría para monitorear el nivel de agua utilizando Arduino MKR WAN 1310, JSN-SR04T e INA219 a través de LoRaWAN.

Para qué sirve

  • Monitoreo remoto del nivel de agua en tanques para optimizar el riego agrícola.
  • Detección de inundaciones en áreas propensas mediante alertas en tiempo real.
  • Control del nivel de agua en sistemas de acuicultura para mantener la salud de los peces.
  • Integración con sistemas de gestión de recursos hídricos para análisis de datos históricos.

Resultado esperado

  • Actualizaciones de nivel de agua cada 5 minutos con latencia menor a 2 segundos.
  • Envío de datos a la nube con un máximo de 10 paquetes por hora.
  • Alertas de nivel crítico enviadas a través de MQTT con un tiempo de respuesta de menos de 1 segundo.
  • Consumo de energía promedio de 30 mA en modo de transmisión y 1 µA en modo de sueño.

Público objetivo: Ingenieros y desarrolladores de IoT; Nivel: Avanzado

Arquitectura/flujo: Sensores JSN-SR04T e INA219 conectados al Arduino MKR WAN 1310, transmitiendo datos a través de LoRaWAN a una plataforma en la nube.

Nivel: Avanzado

Prerrequisitos

Sistema operativo y herramientas exactas

  • Sistemas operativos soportados:
  • Ubuntu 22.04 LTS (64‑bit) actualizado a 22.04.4
  • Windows 11 Pro 23H2 (64‑bit)
  • macOS 14.5 (Sonoma)

  • Toolchain (versión exacta):

  • Python 3.11.8
  • pipx 1.4.3
  • PlatformIO Core (CLI) 6.1.14
  • PlatformIO platform atmelsam 8.2.0
  • Arduino Core for SAMD (framework-arduino-samd) 1.8.13
  • GCC ARM Embedded (toolchain-gccarmnoneeabi) 1.90301.200702

  • Librerías Arduino (vía PlatformIO) con versiones exactas:

  • arduino-libraries/MKRWAN@1.3.1
  • adafruit/Adafruit INA219@1.2.1
  • greiman/NewPing@1.9.7
  • arduino-libraries/ArduinoLowPower@1.2.3

  • Drivers/puertos:

  • Arduino MKR WAN 1310 expone USB CDC (no requiere CP210x/CH34x).
  • Windows 10/11: driver CDC integrado.
  • Linux: configurar udev para /dev/ttyACM0 (ver más abajo).
  • macOS: no requiere drivers adicionales.

Requisitos de red LoRaWAN

  • Acceso a una red LoRaWAN (recomendado: The Things Stack (TTS) / The Things Network v3).
  • Identificadores OTAA:
  • JoinEUI/AppEUI (16 hex)
  • AppKey (32 hex)
  • Región/frecuencia (ejemplo: EU868 o US915)
  • Antena conectada a la MKR WAN 1310 antes del primer encendido.

Materiales

  • 1 × Arduino MKR WAN 1310 (modelo exacto).
  • 1 × Sensor ultrasónico impermeable JSN‑SR04T (versión de 4 pines).
  • 1 × Sensor de corriente/tensión INA219 (módulo con shunt de 0.1 Ω).
  • 1 × Convertidor DC‑DC step‑up 3.7 V → 5 V (mín. 1 A).
  • 1 × Batería LiPo 3.7 V (por ejemplo, 2000–5000 mAh) con conector JST para MKR.
  • 1 × Antena LoRa 868/915 MHz compatible con MKR WAN 1310.
  • Resistencias para divisor de nivel en la línea ECHO (5 V a 3.3 V):
  • 1 × 20 kΩ (R2 a GND)
  • 1 × 10 kΩ (R1 en serie con la señal ECHO hacia el pin de la MKR)
  • Cables dupont macho‑hembra.
  • Caja estanca IP65 (opcional, recomendado).
  • Abrazaderas y soporte para instalar el JSN‑SR04T en la parte superior del depósito.
  • PC con USB y cable micro‑USB.

Nota: Mantenemos la coherencia con el modelo “Arduino MKR WAN 1310 + JSN‑SR04T + INA219”. El step‑up 5 V y las resistencias son auxiliares para poder alimentar el JSN‑SR04T a 5 V y adaptar niveles de 5 V a 3.3 V (la MKR es 3.3 V).

Preparación y conexión

Consideraciones eléctricas y de señal

  • MKR WAN 1310 trabaja a 3.3 V en GPIO. JSN‑SR04T requiere 5 V de alimentación:
  • Trigger (TRIG) tolera 3.3 V como “alto” en la mayoría de unidades (funciona en campo).
  • Echo (ECHO) devuelve 5 V: imprescindibles resistencias para dividir a ~3.3 V.
  • INA219 alimentado a 3.3 V, pero puede medir la línea de 5 V del step‑up (bus hasta 26 V).
  • Recomendado: poner el INA219 “en serie” con la línea de 5 V que alimenta el JSN‑SR04T para telemetría de consumo del sensor (salud del sistema).

Tabla de cableado (puertos/pines)

Componente Pin/Terminal Arduino MKR WAN 1310 Notas
JSN‑SR04T VCC (5 V) Salida step‑up 5 V El step‑up toma la LiPo y genera 5 V
JSN‑SR04T GND GND GND común al step‑up y la MKR
JSN‑SR04T TRIG D6 3.3 V es suficiente como “alto”
JSN‑SR04T ECHO D7 (vía divisor) R1=10 kΩ en serie desde ECHO; R2=20 kΩ de D7 a GND (≈3.3 V en alto)
INA219 V+ Salida step‑up 5 V Entrada del bus a medir (hacia sensor)
INA219 V- VCC de JSN‑SR04T La salida del INA219 alimenta al sensor
INA219 GND GND común Común con MKR y step‑up
INA219 SDA SDA I2C (3.3 V)
INA219 SCL SCL I2C (3.3 V)
INA219 VCC 3V3 Alimentación lógica del INA219
Batería LiPo JST Conector BAT MKR gestiona carga/uso de LiPo
Step‑up 5 V (entrada) VIN/GND BAT/GND Alimentado por la LiPo de la MKR
Antena LoRa Conector u.FL/SMA MKR WAN 1310 Conectar antes de energizar
USB Micro‑USB MKR WAN 1310 Solo para programación y monitor serie

Notas de montaje:
– Mantén cables de TRIG/ECHO separados de la antena LoRa para evitar acoplamientos.
– El JSN‑SR04T debe colocarse rígido en la tapa del depósito, apuntando perpendicularmente al agua.
– Evita espuma/turbulencia en la vertical del sensor; si es inevitable, usa filtro de mediana y límite de tiempo.

Reglas de udev (Linux)

Crea el archivo /etc/udev/rules.d/99-arduino.rules con:

SUBSYSTEM=="tty", ATTRS{idVendor}=="2341", MODE:="0666", GROUP:="dialout"
KERNEL=="ttyACM*", MODE:="0666", GROUP:="dialout"

Luego:

sudo udevadm control --reload-rules
sudo udevadm trigger

Código completo

El proyecto consta de:
– src/main.cpp (código principal)
– include/secrets.h (claves OTAA y región)
– platformio.ini (configuración de PlatformIO, ver más adelante)

A continuación, el código completo de la aplicación y el archivo de secretos.

src/main.cpp

#include <Arduino.h>
#include <MKRWAN.h>
#include <Adafruit_INA219.h>
#include <ArduinoLowPower.h>
#include <NewPing.h>
#include "secrets.h"

// ------------------------ Configuración de pines y constantes ------------------------
#define PIN_TRIG 6
#define PIN_ECHO 7

// Distancia máxima relevante (en cm) para el depósito.
// El JSN-SR04T soporta hasta ~600 cm, ajusta según tu geometría.
#define MAX_DISTANCE_CM 600

// Intervalo de transmisión (segundos)
#ifndef TX_INTERVAL_SECONDS
#define TX_INTERVAL_SECONDS 300
#endif

// Calibración del depósito (distancias en milímetros)
#ifndef EMPTY_DISTANCE_MM
#define EMPTY_DISTANCE_MM 1500 // distancia sensor->agua cuando el tanque está vacío
#endif

#ifndef FULL_DISTANCE_MM
#define FULL_DISTANCE_MM 100   // distancia sensor->agua cuando el tanque está lleno (tope)
#endif

// Filtro de lecturas ultrasónicas
#define N_PINGS 9

// Estructuras y objetos globales
LoRaModem modem;
Adafruit_INA219 ina219; // dirección por defecto 0x40
NewPing sonar(PIN_TRIG, PIN_ECHO, MAX_DISTANCE_CM);

// ------------------------ Utilidades ------------------------

static uint16_t clamp_u16(int v) {
  if (v < 0) return 0;
  if (v > 65535) return 65535;
  return (uint16_t)v;
}

static int16_t clamp_i16(int v) {
  if (v < -32768) return -32768;
  if (v >  32767) return  32767;
  return (int16_t)v;
}

static float medianDistanceCm(uint8_t n) {
  // NewPing provee ping_median directamente
  unsigned int us = sonar.ping_median(n);
  if (us == 0) return NAN; // No eco
  // NewPing define conversiones: US_ROUNDTRIP_CM = 57 (aprox)
  float cm = us / US_ROUNDTRIP_CM;
  return cm;
}

static int16_t distanceMmFiltered() {
  float cm = medianDistanceCm(N_PINGS);
  if (isnan(cm) || cm <= 0.0f) return -1;
  int mm = (int)(cm * 10.0f + 0.5f); // redondeo a mm
  return clamp_i16(mm);
}

static int computeLevelPercent(int distance_mm) {
  // Convierte distancia a porcentaje de llenado en base a calibración
  // Más cerca (menor distancia) => mayor nivel.
  int span = EMPTY_DISTANCE_MM - FULL_DISTANCE_MM;
  if (span <= 0) return -1;
  int pct = (int)roundf((float)(EMPTY_DISTANCE_MM - distance_mm) * 100.0f / (float)span);
  if (pct < 0) pct = 0;
  if (pct > 100) pct = 100;
  return pct;
}

static void printDevEUI() {
  String devEui = modem.deviceEUI();
  Serial.print("DevEUI: ");
  Serial.println(devEui);
}

// ------------------------ LoRaWAN ------------------------

static bool loraBeginAndJoin() {
  // Inicializa el módem en la región especificada en secrets.h
  if (!modem.begin(LORAWAN_REGION)) {
    Serial.println("Error: no se pudo inicializar el módem LoRa.");
    return false;
  }

  // Opciones recomendadas
  modem.setADR(true);   // Adaptive Data Rate
  modem.dataRate(DEFAULT_DATARATE); // ver secrets.h
  modem.txPower(DEFAULT_TXPOWER_DBM);

  printDevEUI();

  // Claves OTAA
  String appEui = SECRET_APP_EUI;
  String appKey = SECRET_APP_KEY;

  Serial.print("Uniendo a la red (OTAA)...");
  int connected = modem.joinOTAA(appEui, appKey);
  if (!connected) {
    Serial.println(" fallo en join.");
    return false;
  }
  Serial.println(" OK");

  // Confirmar parámetros tras join
  Serial.print("DR actual: ");
  Serial.println(modem.dataRate());
  return true;
}

static bool loraSendUplink(const uint8_t* payload, size_t len, uint8_t fport = 2, bool confirmed = false) {
  modem.beginPacket();
  modem.setPort(fport);
  modem.write(payload, len);
  int err = modem.endPacket(confirmed);
  if (err > 0) {
    Serial.print("Uplink enviado (bytes=");
    Serial.print(len);
    Serial.println(").");
    return true;
  } else {
    Serial.print("Fallo uplink, err=");
    Serial.println(err);
    return false;
  }
}

// ------------------------ Setup y bucle ------------------------

void setup() {
  pinMode(PIN_TRIG, OUTPUT);
  pinMode(PIN_ECHO, INPUT); // Recuerda: hay un divisor resistivo hacia D7
  digitalWrite(PIN_TRIG, LOW);

  Serial.begin(115200);
  while (!Serial && millis() < 4000) {
    ; // Espera breve a monitor serie
  }
  Serial.println("\n[Inicio] lora-water-level-telemetry (MKR WAN 1310 + JSN-SR04T + INA219)");

  // INA219
  if (!ina219.begin()) {
    Serial.println("Error: INA219 no encontrado en I2C. Verifica cableado.");
  } else {
    // Calibración típica: 32V, 2A (depende de tu módulo/shunt)
    ina219.setCalibration_32V_2A();
    Serial.println("INA219 OK (calibrado 32V/2A).");
  }

  // LoRaWAN: iniciar y unirse
  if (!loraBeginAndJoin()) {
    Serial.println("No se pudo unir a LoRaWAN en setup. Se reintentará en el loop.");
  }
}

void loop() {
  // (Re)intento de join si no hay sesión
  if (!modem.connected()) {
    Serial.println("Reconectando a LoRaWAN...");
    if (!loraBeginAndJoin()) {
      Serial.println("Join fallido. Espera y reintento.");
      LowPower.sleep(15000);
      return;
    }
  }

  // 1) Medición ultrasónica
  int16_t d_mm = distanceMmFiltered();
  if (d_mm < 0) {
    Serial.println("Medición ultrasónica inválida (sin eco).");
  } else {
    Serial.print("Distancia: ");
    Serial.print(d_mm);
    Serial.println(" mm");
  }

  int level_pct = (d_mm > 0) ? computeLevelPercent(d_mm) : -1;
  if (level_pct >= 0) {
    Serial.print("Nivel estimado: ");
    Serial.print(level_pct);
    Serial.println(" %");
  }

  // 2) Telemetría de alimentación del sensor (en la línea 5V del step-up a través del INA219)
  float bus_v = ina219.getBusVoltage_V();       // Voltaje del bus ~5 V
  float shunt_v = ina219.getShuntVoltage_mV();  // mV en la resistencia shunt
  float cur_mA = ina219.getCurrent_mA();        // Corriente hacia el sensor
  float pwr_mW = ina219.getPower_mW();

  Serial.print("INA219: Vbus=");
  Serial.print(bus_v, 3);
  Serial.print(" V, Ishunt=");
  Serial.print(cur_mA, 1);
  Serial.print(" mA, P=");
  Serial.print(pwr_mW, 1);
  Serial.println(" mW");

  // 3) Empaquetado de payload (6 bytes)
  // Formato (puerto 2):
  // [0..1]: distancia_mm (uint16, big-endian)
  // [2..3]: vbus_mV (uint16, big-endian) = bus_v * 1000
  // [4..5]: current_mA * 10 (int16, big-endian), con saturación
  uint8_t payload[6];
  uint16_t dist_u16 = clamp_u16(d_mm);
  uint16_t vbus_mv = clamp_u16((int)(bus_v * 1000.0f + 0.5f));
  int16_t cur_mA_x10 = clamp_i16((int)(cur_mA * 10.0f));

  payload[0] = (uint8_t)((dist_u16 >> 8) & 0xFF);
  payload[1] = (uint8_t)(dist_u16 & 0xFF);
  payload[2] = (uint8_t)((vbus_mv >> 8) & 0xFF);
  payload[3] = (uint8_t)(vbus_mv & 0xFF);
  payload[4] = (uint8_t)((cur_mA_x10 >> 8) & 0xFF);
  payload[5] = (uint8_t)(cur_mA_x10 & 0xFF);

  // 4) Envío LoRaWAN
  bool ok = loraSendUplink(payload, sizeof(payload), 2 /*FPort*/, false /*unconfirmed*/);
  if (!ok) {
    Serial.println("Reintento de uplink en 10 s...");
    LowPower.sleep(10000);
  }

  // 5) Dormir para ahorrar energía
  // Nota: en SAMD21, sleep conserva estado, pero algunos esquemas de bajo consumo
  // requieren reconfigurar periféricos tras standby. Probado con ArduinoLowPower.
  uint32_t sleep_ms = (uint32_t)TX_INTERVAL_SECONDS * 1000UL;
  Serial.print("Sleep ");
  Serial.print(TX_INTERVAL_SECONDS);
  Serial.println(" s.");
  LowPower.sleep(sleep_ms);
}

include/secrets.h

#pragma once
// Región LoRaWAN: EU868, US915, AU915, AS923, KR920, IN865
#define LORAWAN_REGION EU868

// Data rate por defecto (depende de la región):
// EU868: 5 (SF7BW125), US915: 3 (SF7BW125), ajusta si es necesario
#define DEFAULT_DATARATE 5

// Potencia TX (dBm), sujeta a regulaciones de la región
#define DEFAULT_TXPOWER_DBM 14

// Claves OTAA (The Things Stack / TTN v3)
// Reemplaza con tus valores hexadecimales (en mayúsculas, sin espacios)
static const char SECRET_APP_EUI[] = "70B3D57ED0039BFF"; // JoinEUI/AppEUI
static const char SECRET_APP_KEY[] = "00112233445566778899AABBCCDDEEFF"; // AppKey

// Calibraciones del tanque (puedes sobreescribir por build_flags en PlatformIO)
#ifndef TX_INTERVAL_SECONDS
#define TX_INTERVAL_SECONDS 300
#endif

#ifndef EMPTY_DISTANCE_MM
#define EMPTY_DISTANCE_MM 1500
#endif

#ifndef FULL_DISTANCE_MM
#define FULL_DISTANCE_MM 100
#endif

Breve explicación de las partes clave:
– MKRWAN: abstrae el módulo Murata CMWX1ZZABZ de la MKR WAN 1310. begin(región), joinOTAA(), beginPacket()/endPacket().
– NewPing: gestiona tiempos de eco; ping_median(n) reduce outliers por espuma o salpicaduras.
– INA219: calibración 32V/2A es común en módulos con shunt de 0.1 Ω. Nos da corriente y voltaje de la línea 5 V del sensor.
– Empaquetado binario: payload compacto de 6 bytes, apto para LoRaWAN y sencillo de decodificar.
– LowPower.sleep(): standby del SAMD21 para reducir consumo entre mediciones.

Compilación/flash/ejecución

A continuación, el flujo completo con comandos exactos y ordenados usando PlatformIO CLI (versión 6.1.14). Se asume un directorio de trabajo vacío.

1) Instalar PlatformIO Core 6.1.14 (recomendado con pipx)
– Linux/macOS:

python3 --version
pipx --version
pipx install "platformio==6.1.14"
pio --version
  • Windows (PowerShell):
py -3.11 -m pip install --user pipx
py -3.11 -m pipx ensurepath
# Cierra y reabre PowerShell
pipx install "platformio==6.1.14"
pio --version

2) Inicializar proyecto para Arduino MKR WAN 1310

mkdir lora-water-level-telemetry
cd lora-water-level-telemetry
pio project init --board mkrwan1310 --project-option "platform=atmelsam@8.2.0" --project-option "framework=arduino"

3) Especificar versiones exactas de framework y librerías (platformio.ini)
Crea/edita platformio.ini con:

[env:mkrwan1310]
platform = atmelsam@8.2.0
framework = arduino
board = mkrwan1310
platform_packages =
  framework-arduino-samd@1.8.13
  toolchain-gccarmnoneeabi@1.90301.200702
lib_deps =
  arduino-libraries/MKRWAN@1.3.1
  adafruit/Adafruit INA219@1.2.1
  greiman/NewPing@1.9.7
  arduino-libraries/ArduinoLowPower@1.2.3
monitor_speed = 115200
build_flags =
  -DTX_INTERVAL_SECONDS=300
  -DEMPTY_DISTANCE_MM=1500
  -DFULL_DISTANCE_MM=100

4) Añadir el código fuente
– Crear include/secrets.h y src/main.cpp con el contenido mostrado arriba.

En Linux/macOS (ejemplo):

mkdir -p include src
$EDITOR include/secrets.h
$EDITOR src/main.cpp
$EDITOR platformio.ini

5) Compilar

pio run

6) Conectar la MKR WAN 1310 por USB
– Identificar el puerto:
– Linux: ls /dev/ttyACM
– macOS: ls /dev/tty.usbmodem

– Windows: revisar Administrador de Dispositivos (COMx)

7) Subir el firmware
– Linux/macOS:

pio run -t upload --upload-port /dev/ttyACM0
  • Windows (ejemplo COM6):
pio run -t upload --upload-port COM6

8) Monitor serie
– Linux/macOS:

pio device monitor -b 115200 --port /dev/ttyACM0
  • Windows:
pio device monitor -b 115200 --port COM6

Verás el DevEUI, el proceso de join y las lecturas/telemetrías periódicas.

Validación paso a paso

1) Preparación en The Things Stack (TTN v3):
– Crear Application (p. ej. app: lora-water-level-telemetry).
– Registrar un dispositivo OTAA:
– DevEUI: puedes leerlo desde el monitor serie (modem.deviceEUI()) y copiarlo al TTS.
– JoinEUI/AppEUI: define uno y consérvalo en secrets.h.
– AppKey: genera una clave segura y transpórtala a secrets.h.
– Seleccionar la región/frecuencia (ej. EU868). Debe coincidir con LORAWAN_REGION en secrets.h.

2) Primer arranque:
– Conectar antena LoRa a la MKR WAN 1310.
– Energizar la placa (USB + LiPo conectada).
– Abrir monitor serie: verificar salida similar a:
– “DevEUI: AABBCCDDEEFF1122”
– “Uniendo a la red (OTAA)… OK”
– Lecturas: “Distancia: 1234 mm”, “Nivel estimado: 30 %”
– “INA219: Vbus=5.002 V, Ishunt=37.5 mA, P=187.5 mW”
– “Uplink enviado (bytes=6). Sleep 300 s.”

3) Validación de uplink en TTS:
– En la consola, abre el dispositivo y la pestaña “Live data”/“Uplinks”.
– Debes ver un uplink cada TX_INTERVAL_SECONDS con port=2 y payload de 6 bytes.

4) Decodificador de payload (TTS > Payload formatter > Uplink > Javascript)
– Usa este decodificador de ejemplo:

function decodeUplink(input) {
  const bytes = input.bytes;
  if (bytes.length < 6) {
    return { data: { error: "payload too short" } };
  }
  const dist_mm = (bytes[0] << 8) | bytes[1];
  const vbus_mv = (bytes[2] << 8) | bytes[3];
  let cur_mA_x10 = (bytes[4] << 8) | bytes[5];
  if (cur_mA_x10 & 0x8000) cur_mA_x10 = cur_mA_x10 - 0x10000; // int16
  const cur_mA = cur_mA_x10 / 10.0;

  // Ejemplo de nivel estimado si conoces EMPTY/FULL (sincronizar con firmware si cambian)
  const EMPTY = 1500;
  const FULL = 100;
  const span = EMPTY - FULL;
  let level_pct = null;
  if (span > 0) {
    level_pct = Math.max(0, Math.min(100, Math.round(((EMPTY - dist_mm) * 100.0) / span)));
  }

  return {
    data: {
      distance_mm: dist_mm,
      level_percent: level_pct,
      vbus_mv: vbus_mv,
      current_mA: cur_mA
    }
  };
}
  • Guarda y prueba con un uplink recibido.

5) Verificación física:
– Mide con una regla la distancia del sensor al agua y compárala con “Distancia: … mm” en el monitor serie.
– Cambia el nivel del depósito (p. ej., añade agua) y confirma que:
– La distancia disminuye y el nivel (%) aumenta coherentemente.
– En TTN, la gráfica (si la configuras en un dashboard externo) refleja la evolución.

6) Telemetría de consumo:
– Cubre temporalmente el sensor para ver si la lectura cambia (el JSN‑SR04T puede cambiar su consumo según medición).
– Verifica que Vbus ~ 5 V y la corriente esté en el rango típico (20–70 mA para JSN‑SR04T, varía por versión).

7) Validación de duty-cycle y DR:
– En EU868, el duty-cycle limita la cadencia efectiva. Con TX_INTERVAL_SECONDS=300 no deberías tener problemas.
– Confirma data rate (DR) en el log y ajusta si la cobertura es pobre.

Troubleshooting

1) Join OTAA falla continuamente
– Causas:
– AppEUI/AppKey mal introducidos (orden o mayúsculas/minúsculas).
– Región incorrecta (LORAWAN_REGION no coincide con TTS).
– Antena no conectada o mala calidad de enlace.
– Soluciones:
– Duplica/pega de nuevo AppEUI/AppKey verificando longitud (16/32 hex).
– Ajusta región en secrets.h y recompila.
– Asegura antena y prueba cerca de una gateway conocida.
– Baja el data rate (EU868: DEFAULT_DATARATE=3 → SF9) para mayor alcance.

2) Lecturas del JSN‑SR04T a cero o “sin eco”
– Causas:
– Alimentación insuficiente (step‑up no da corriente suficiente).
– Divisor resistivo incorrecto (ECHO sigue a 5 V y la MKR no lee).
– Objetivo demasiado cercano o espuma intensa.
– Soluciones:
– Verifica Vbus~5 V e Ishunt con INA219; si cae la tensión, sube la capacidad del step‑up.
– Comprueba resistencias (10 kΩ en serie desde ECHO, 20 kΩ de pin a GND).
– Aumenta N_PINGS, reubica el sensor, o añade un tubo tranquilizador.

3) INA219 siempre lee 0 mA o valores erráticos
– Causas:
– Cableado V+/V- invertido (sensor no alimentado a través del shunt).
– Calibración inadecuada para el shunt real.
– Soluciones:
– Asegúrate de que la alimentación del JSN‑SR04T pasa por V+ → INA219 → V-.
– Cambia a setCalibration_32V_1A si trabajas con corrientes < 1 A y shunt de 0.1 Ω.

4) Uplinks no aparecen en TTS (pero el firmware dice “enviado”)
– Causas:
– Port o formato bloqueado por el decodificador.
– Gateway saturada o fuera de servicio.
– Soluciones:
– Asegura que FPort=2 (o el que uses), y que el payload no excede el tamaño máximo para ese DR.
– Prueba con payload más pequeño; acércate a la gateway y revisa Live Data.

5) Bloqueos al entrar en sleep o pérdida de sesión tras dormir
– Causas:
– Standby reconfigura periféricos; el módem puede requerir re‑sync.
– Soluciones:
– Mantén la lógica de re‑join en loop() como en el ejemplo (modem.connected()).
– Evita deep sleep excesivo antes de confirmar join inicial.

6) “Error: INA219 no encontrado”
– Causas:
– SDA/SCL invertidos, soldaduras flojas, alimentación a 5 V en VCC INA219 (usa 3.3 V).
– Soluciones:
– Verifica continuidad de SDA/SCL, que 3V3 alimenta VCC del INA219, y GND común.

7) Potencia de transmisión fuera de normativa
– Causa:
– DEFAULT_TXPOWER_DBM demasiado alta para tu región.
– Solución:
– Ajusta a 14 dBm (EU868) o según norma local; usa ADR cuando sea posible.

8) Distancia y nivel inconsistentes
– Causas:
– Calibración (EMPTY/FULL) no corresponde a la instalación real.
– Condensación en el sensor.
– Soluciones:
– Recalibra EMPTY_DISTANCE_MM y FULL_DISTANCE_MM sobre el terreno.
– Agrega un pequeño capuchón o protección anti‑condensación sin obstruir el haz.

Mejoras/variantes

  • Downlinks para configuración remota:
  • Implementa recepción de downlinks (puerto 10) para ajustar TX_INTERVAL_SECONDS, DR o límites EMPTY/FULL sin reprogramar.
  • Confirmed uplinks bajo evento:
  • Enviar uplink confirmado solo cuando el nivel cambia más de X %, manteniendo no confirmados para el resto.
  • Codificación CayenneLPP o JSON CBOR:
  • Si integras más sensores, un esquema estándar puede simplificar la decodificación en plataformas IoT.
  • Ahorro energético avanzado:
  • Añade un MOSFET P‑channel de alta‑lado para cortar la alimentación del JSN‑SR04T entre mediciones.
  • Usa temporizadores RTC y revisa “LowPower.deepSleep()” con re‑join programado.
  • Autodiagnóstico:
  • Incluye en el payload un “status byte” con bits de error (eco ausente, Vbus bajo, join fallido anterior).
  • Geometría del tanque:
  • Calcula volumen real (litros) a partir de la distancia y la forma (cilindro, prisma, irregular con tabla de calibración).
  • Seguridad:
  • Rota AppKey y usa device‑specific keys por fabricación.
  • Redundancia de medición:
  • Filtrado Kalman/MAD, o doble lectura con diferentes ventanas para mitigar espuma/salpicaduras.

Checklist de verificación

  • [ ] Antena LoRa conectada a la MKR WAN 1310 antes de energizar.
  • [ ] Step‑up 5 V conectado a LiPo (BAT) y GND común con MKR.
  • [ ] JSN‑SR04T alimentado desde INA219 (V+ del INA219 a 5 V del step‑up; V- a VCC del sensor).
  • [ ] Divisor resistivo en ECHO correcto (10 kΩ en serie desde ECHO, 20 kΩ de pin a GND).
  • [ ] TRIG conectado a D6, ECHO (dividido) a D7; SDA/SCL del INA219 a SDA/SCL de la MKR.
  • [ ] platformio.ini con las versiones exactas indicadas (atmelsam@8.2.0, framework-arduino-samd@1.8.13, etc.).
  • [ ] secrets.h con LORAWAN_REGION, AppEUI y AppKey correctos.
  • [ ] Compilación exitosa: pio run sin errores.
  • [ ] Carga exitosa en el puerto correcto (upload).
  • [ ] Join OTAA OK y uplinks visibles en TTS.
  • [ ] Decodificador funcionando (distancia_mm, level_percent, vbus_mv, current_mA).
  • [ ] Validación física del nivel: distancia coherente con medición manual.
  • [ ] Intervalo de transmisión y consumo dentro de expectativas.

Apéndice: Notas adicionales sobre precisión del JSN‑SR04T

  • El JSN‑SR04T está optimizado para exteriores y ambientes húmedos. Su lóbulo de haz es relativamente estrecho, pero la reflexión en superficies turbulentas puede variar.
  • Para depósitos con mucha espuma, un tubo tranquilizador (tubo vertical perforado) bajo el sensor reduce ruido.
  • Ajusta MAX_DISTANCE_CM al rango real para minimizar ecos espurios lejanos.
  • Si detectas lecturas “0 cm” es posible saturación o ningún eco; el filtro de mediana ayuda, pero revisa también la alimentación.

Apéndice: Consideraciones regulatorias

  • EU868: potencia máx. 14 dBm en la mayoría de sub‑bandas y duty‑cycle 1% típico. Respeta duty‑cycle mediante el intervalo de transmisión.
  • US915: no hay duty‑cycle pero sí limitación por hop y dwell time. Ajusta DR y sub‑bandas según la red.

Con este caso práctico, dispones de una cadena completa y reproducible para “lora‑water‑level‑telemetry” con el modelo exacto “Arduino MKR WAN 1310 + JSN‑SR04T + INA219”, cubriendo materiales, conexión, código, toolchain y validación integral.

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 la versión exacta de Python requerida?




Pregunta 2: ¿Qué sistema operativo no es soportado según el artículo?




Pregunta 3: ¿Cuál es la versión de PlatformIO Core requerida?




Pregunta 4: ¿Qué librería de Arduino tiene la versión 1.3.1?




Pregunta 5: ¿Qué tipo de sensor se requiere para el proyecto?




Pregunta 6: ¿Qué batería es recomendada para el proyecto?




Pregunta 7: ¿Cuál es la frecuencia recomendada para la antena LoRa?




Pregunta 8: ¿Qué resistor se usa para el divisor de nivel de 5 V a 3.3 V?




Pregunta 9: ¿Qué herramienta se usa para gestionar las librerías de Arduino?




Pregunta 10: ¿Qué driver se requiere en Windows para el Arduino MKR WAN 1310?




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 al inicio