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



