Practical case: Agri LoRaWAN MKR WAN 1310+BME680+DS18B20

Practical case: Agri LoRaWAN MKR WAN 1310+BME680+DS18B20 — hero

Objective and use case

What you’ll build: A LoRaWAN-enabled microclimate node for agriculture using the Arduino MKR WAN 1310, BME680, and DS18B20 to measure and transmit environmental data.

Why it matters / Use cases

  • Monitor air quality and temperature in greenhouses to optimize plant growth.
  • Track soil moisture levels in remote fields to improve irrigation efficiency.
  • Provide farmers with real-time data on microclimate conditions to make informed decisions.
  • Utilize low-power LoRaWAN communication for long-range data transmission in rural areas.

Expected outcome

  • Achieve reliable LoRaWAN OTAA join with periodic uplinks every 15 minutes.
  • Measure air temperature with a precision of ±0.5°C and humidity with ±3% accuracy.
  • Transmit data payloads of 11 bytes efficiently over LoRaWAN.
  • Maintain a low-power duty cycle to extend battery life beyond 1 year.

Audience: Agricultural engineers; Level: Advanced

Architecture/flow: Arduino MKR WAN 1310 collects data from BME680 and DS18B20, encodes it, and sends it via LoRaWAN to The Things Stack.

Advanced Hands‑On: LoRa Agro Microclimate Node with Arduino MKR WAN 1310 + BME680 + DS18B20 (lora-agro-microclima-node)

This practical case guides you through building an advanced LoRaWAN-enabled microclimate node tailored for agricultural monitoring, using the exact device model: Arduino MKR WAN 1310 + BME680 + DS18B20. You will deploy a battery-friendly node that measures air temperature/humidity/pressure/gas (VOC proxy) and soil/liquid temperature, encodes the data into a compact binary payload, sends it over LoRaWAN (OTAA) to The Things Stack (TTN v3), and supports configurable sampling intervals via downlink.

Key goals:
– Reliable LoRaWAN OTAA join and periodic uplinks.
– Accurate sensor sampling with proper oversampling and stabilization for BME680.
– DS18B20 on 1‑Wire with a 4.7 kΩ pull‑up resistor.
– Compact binary payload (11 bytes) with a documented uplink format and a TTN payload formatter.
– Low‑power duty cycle and watchdog considerations for field reliability.

Important note on toolchain: family defaults to Arduino UNO + Arduino CLI; however, this project uses a different board (Arduino MKR WAN 1310). Therefore we use PlatformIO (CLI) for build/flash/monitor commands. No GUI is required.


Prerequisites

  • Operating system:
  • Windows 10/11, macOS 12+ (Monterey or newer), or Ubuntu 22.04 LTS.
  • PlatformIO Core (CLI) installed via pip:
  • Python 3.10+ recommended.
  • Verified with PlatformIO Core 6.1.13.
  • The Things Stack (TTN v3) account:
  • Application created in the desired cluster.
  • Frequency plan matching your region (e.g., EU868, US915, AU915, AS923, KR920, IN865).
  • A device registered with OTAA:
    • Device EUI (generated by TTN or read from the MKR).
    • App EUI (Join EUI).
    • App Key (16 bytes).
  • USB cable (USB‑A to Micro‑B) for Arduino MKR WAN 1310.
  • Antenna attached to the MKR WAN 1310’s u.FL connector (mandatory before any RF transmission).
  • Basic familiarity with:
  • LoRaWAN OTAA workflow.
  • 1‑Wire topology and pull‑up resistors.
  • I2C bus concepts for sensor addressing and pull‑ups.
  • Serial port permissions:
  • Linux: add your user to the dialout group and re‑login.
    • Command: sudo usermod -aG dialout $USER

Driver notes:
– Arduino MKR WAN 1310 is a native USB CDC device; on Windows 10/11 and macOS, no third‑party drivers (CP210x/CH34x) are required. It appears as “Arduino MKR WAN 1310 (COMx)” or a tty device on Unix-like systems.


Materials (Exact Models)

  • Arduino MKR WAN 1310 (ABX00029) with LoRa antenna (included in kit).
  • Environmental sensor: Bosch BME680 breakout (I2C). Example: Adafruit BME680 (Product ID 3660) or equivalent, configured for I2C address 0x76 or 0x77.
  • 1‑Wire temperature sensor: DS18B20 (waterproof probe version is ideal for soil/liquid), TO‑92 or encapsulated probe.
  • 4.7 kΩ resistor (±5% or better) for 1‑Wire data line pull‑up.
  • Jumper wires (male/female as needed).
  • Power:
  • USB power during development.
  • Optional: 3.7 V LiPo cell for field deployment (connect to MKR BAT connector).
  • Optional environmental protection:
  • Enclosure with breathable membrane for BME680 (to avoid condensation and allow gas diffusion).
  • Cable glands and waterproofing for DS18B20 probe and enclosure.

Setup/Connection

The MKR WAN 1310 is a 3.3 V logic board. Do not connect 5 V signals to its pins.

  1. Antenna
  2. Carefully attach the u.FL antenna to the MKR WAN 1310 connector before powering. Never transmit without an antenna.

  3. BME680 (I2C)

  4. Power: VCC → 3.3 V; GND → GND.
  5. I2C: SDA → MKR pin marked SDA; SCL → MKR pin marked SCL.
  6. Address:

    • Most boards default to 0x76; some to 0x77. Check your breakout’s solder jumper.
    • We will default to 0x76 in code and log a warning if detection fails.
  7. DS18B20 (1‑Wire)

  8. If using a waterproof 3‑wire probe (typical color code):
    • Red → 3.3 V
    • Black → GND
    • Yellow/White → Data
  9. Use a 4.7 kΩ resistor between Data and 3.3 V at the MKR side.
  10. Data pin on MKR: D4 (configurable in code).
  11. Do not power DS18B20 from 5 V. Use 3.3 V to match logic level.

  12. Power / USB

  13. Connect the MKR to your PC via USB.
  14. For field operation, attach a LiPo to BAT (observe polarity) and keep the antenna connected.

Table summary of connections:

Module/Signal MKR WAN 1310 Pin Notes
Antenna u.FL RF connector Mandatory before RF TX
BME680 VCC 3.3 V Power 3.3 V only
BME680 GND GND Common ground
BME680 SDA SDA I2C data
BME680 SCL SCL I2C clock
DS18B20 VCC 3.3 V Power 3.3 V only
DS18B20 GND GND Common ground
DS18B20 DATA D4 1‑Wire data line
1‑Wire Pull‑up D4 ↔ 3.3 V (via 4.7 kΩ) Required pull‑up resistor

Full Code

We’ll use PlatformIO with the Arduino framework and the official MKRWAN library. The payload is a compact custom binary:
– Byte 0: Protocol version (0x01)
– Bytes 1–2: DS18B20 temperature (centi‑C, signed int16)
– Bytes 3–4: BME680 temperature (centi‑C, signed int16)
– Bytes 5–6: BME680 humidity (centi‑%, unsigned uint16)
– Bytes 7–8: BME680 pressure (deci‑hPa, unsigned uint16)
– Bytes 9–10: BME680 gas resistance (kΩ, unsigned uint16; clamped)

Create platformio.ini and src/main.cpp as shown.

platformio.ini:

; lora-agro-microclima-node/platformio.ini
[env:mkrwan1310]
platform = atmelsam
board = mkrwan1310
framework = arduino
monitor_speed = 115200
build_flags =
  -DLOG_LEVEL=1
lib_deps =
  arduino-libraries/MKRWAN@^1.1.7
  adafruit/Adafruit BME680 Library@^2.0.3
  adafruit/Adafruit BusIO@^1.14.5
  paulstoffregen/OneWire@^2.3.7
  milesburton/DallasTemperature@^3.11.0
  arduino-libraries/ArduinoLowPower@^1.2.2

src/main.cpp:

#include <Arduino.h>
#include <MKRWAN.h>
#include <Wire.h>
#include <Adafruit_BME680.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <ArduinoLowPower.h>

// ---------------------- Configuration ----------------------

// Select LoRaWAN region: EU868, US915, AS923, AU915, KR920, IN865
// Example: EU868
#define LORA_REGION EU868

// Replace with your real credentials from The Things Stack (TTN v3)
String appEui = "70B3D57ED0000000";  // JoinEUI (16 hex, no spaces)
String appKey = "00112233445566778899AABBCCDDEEFF";  // AppKey (32 hex)

// FPort to use for uplinks (and to listen for downlinks)
static const uint8_t LORA_FPORT = 10;

// Confirmed uplink every N transmissions (set 0 to disable)
static const uint8_t CONFIRM_EVERY = 4;

// Initial uplink period (seconds); can be reconfigured by downlink (FPort 10)
static uint32_t uplinkPeriodSec = 300;

// DS18B20 1-Wire pin
static const uint8_t ONEWIRE_PIN = 4;

// BME680 I2C address; try 0x76, fallback to 0x77
static const uint8_t BME680_ADDR_1 = 0x76;
static const uint8_t BME680_ADDR_2 = 0x77;

// ---------------------- Globals ----------------------

LoRaModem modem;
Adafruit_BME680 bme;
OneWire oneWire(ONEWIRE_PIN);
DallasTemperature ds18(&oneWire);

bool bmePresent = false;
bool dsPresent = false;
uint32_t frameCounter = 0;

// ---------------------- Utilities ----------------------

static void logf(const char* fmt, ...) {
#if LOG_LEVEL
  static char buf[256];
  va_list ap;
  va_start(ap, fmt);
  vsnprintf(buf, sizeof(buf), fmt, ap);
  va_end(ap);
  Serial.println(buf);
#endif
}

static int16_t toCentiC(float c) {
  if (isnan(c)) return INT16_MIN;
  long v = lroundf(c * 100.0f);
  if (v > INT16_MAX) v = INT16_MAX;
  if (v < INT16_MIN) v = INT16_MIN;
  return (int16_t)v;
}

static uint16_t toCentiPct(float rh) {
  if (isnan(rh)) return 0;
  long v = lroundf(rh * 100.0f);
  if (v < 0) v = 0;
  if (v > 10000) v = 10000;
  return (uint16_t)v;
}

static uint16_t toDeciHpa(float hpa) {
  if (isnan(hpa)) return 0;
  long v = lroundf(hpa * 10.0f);
  if (v < 0) v = 0;
  if (v > 65535) v = 65535;
  return (uint16_t)v;
}

static uint16_t toKiloOhm(float ohm) {
  if (isnan(ohm) || ohm < 0) return 0;
  long v = lroundf(ohm / 1000.0f);
  if (v > 65535) v = 65535;
  return (uint16_t)v;
}

// Downlink command: payload on FPort 10
// If payload = 0xA0 <uint16_t seconds>, set uplinkPeriodSec
static void handleDownlink(uint8_t* buf, int len) {
  if (len < 3) return;
  if (buf[0] == 0xA0) {
    uint16_t sec = (uint16_t)buf[1] << 8 | buf[2];
    if (sec >= 30 && sec <= 86400) {
      uplinkPeriodSec = sec;
      logf("[DN] Set uplink period to %us", uplinkPeriodSec);
    } else {
      logf("[DN] Ignored invalid period %u", sec);
    }
  }
}

// ---------------------- Setup ----------------------

void setup() {
  Serial.begin(115200);
  while (!Serial && millis() < 5000) {
    ; // wait for serial if connected
  }
  logf("lora-agro-microclima-node starting...");
  logf("Board: Arduino MKR WAN 1310 | Region: %d", (int)LORA_REGION);

  // Ensure RF module is ready
  if (!modem.begin(LORA_REGION)) {
    logf("Failed to start LoRa modem. Check region and antenna.");
    while (1) { delay(1000); }
  }
  logf("Modem: %s", modem.version().c_str());
  logf("DevEUI (modem): %s", modem.deviceEUI().c_str());

  // Configure ADR and port
  modem.setADR(true);
  modem.setPort(LORA_FPORT);

  // OTAA join
  logf("Joining (OTAA)...");
  int connected = modem.joinOTAA(appEui, appKey);
  if (!connected) {
    logf("Join failed. Will retry every 30s.");
    for (;;) {
      delay(30000);
      if (modem.joinOTAA(appEui, appKey)) break;
      logf("Join retry failed...");
    }
  }
  logf("Joined network.");

  // I2C init
  Wire.begin();

  // BME680 detection and configuration
  if (bme.begin(BME680_ADDR_1)) {
    bmePresent = true;
    logf("BME680 detected at 0x%02X", BME680_ADDR_1);
  } else if (bme.begin(BME680_ADDR_2)) {
    bmePresent = true;
    logf("BME680 detected at 0x%02X", BME680_ADDR_2);
  } else {
    logf("BME680 not detected at 0x76/0x77. Check wiring/address.");
  }

  if (bmePresent) {
    bme.setTemperatureOversampling(BME680_OS_8X);
    bme.setHumidityOversampling(BME680_OS_2X);
    bme.setPressureOversampling(BME680_OS_4X);
    bme.setIIRFilterSize(BME680_FILTER_SIZE_3);
    bme.setGasHeater(320, 150);
  }

  // DS18B20 init
  ds18.begin();
  dsPresent = (ds18.getDeviceCount() > 0);
  logf("DS18B20 devices: %d", ds18.getDeviceCount());
  if (!dsPresent) {
    logf("Warning: No DS18B20 detected on D4. Check 4.7k pull-up and wiring.");
  }

  // Put modem to sleep between TX; we’ll wake explicitly.
  modem.sleep();
}

// ---------------------- Main Loop ----------------------

void loop() {
  float ds_temp_c = NAN;
  float bme_temp_c = NAN, bme_rh = NAN, bme_hpa = NAN, bme_gas = NAN;

  // Read DS18B20
  if (dsPresent) {
    ds18.requestTemperatures();
    ds_temp_c = ds18.getTempCByIndex(0);
  }

  // Read BME680 (blocking performReading handles gas heater timing)
  if (bmePresent) {
    if (bme.performReading()) {
      bme_temp_c = bme.temperature;
      bme_rh = bme.humidity;
      bme_hpa = bme.pressure / 100.0f;
      bme_gas = bme.gas_resistance; // Ohms
    } else {
      logf("BME680 reading failed");
    }
  }

  // Build payload
  uint8_t payload[16];
  size_t len = 0;
  payload[len++] = 0x01; // protocol version

  int16_t ds_cC = toCentiC(ds_temp_c);
  int16_t bme_cC = toCentiC(bme_temp_c);
  uint16_t rh_cP = toCentiPct(bme_rh);
  uint16_t p_dhPa = toDeciHpa(bme_hpa);
  uint16_t gas_kohm = toKiloOhm(bme_gas);

  // Pack big-endian
  payload[len++] = (uint8_t)(ds_cC >> 8);
  payload[len++] = (uint8_t)(ds_cC & 0xFF);
  payload[len++] = (uint8_t)(bme_cC >> 8);
  payload[len++] = (uint8_t)(bme_cC & 0xFF);
  payload[len++] = (uint8_t)(rh_cP >> 8);
  payload[len++] = (uint8_t)(rh_cP & 0xFF);
  payload[len++] = (uint8_t)(p_dhPa >> 8);
  payload[len++] = (uint8_t)(p_dhPa & 0xFF);
  payload[len++] = (uint8_t)(gas_kohm >> 8);
  payload[len++] = (uint8_t)(gas_kohm & 0xFF);

  // Log
  logf("Frame %lu | DS18: %.2f C | BME: %.2f C, %.2f %%RH, %.2f hPa, %.0f ohm | period=%us",
       (unsigned long)frameCounter,
       ds_temp_c, bme_temp_c, bme_rh, bme_hpa, bme_gas, uplinkPeriodSec);

  // Wake modem and send
  modem.wake();

  bool confirmed = (CONFIRM_EVERY != 0) && ((frameCounter % CONFIRM_EVERY) == 0);

  int err = modem.beginPacket();
  if (err == 0) {
    modem.write(payload, len);
    int res = modem.endPacket(confirmed);
    if (res > 0) {
      logf("Uplink sent (%s). Bytes=%u",
           confirmed ? "confirmed" : "unconfirmed", (unsigned)len);
    } else {
      logf("Uplink failed (res=%d).", res);
    }
  } else {
    logf("beginPacket error: %d", err);
  }

  // Check for downlink (after a confirmed uplink there may be RX windows)
  int packetSize = modem.parsePacket();
  if (packetSize > 0) {
    uint8_t dn[64];
    int i = 0;
    while (modem.available() && i < (int)sizeof(dn)) {
      dn[i++] = modem.read();
    }
    logf("Downlink: port=%d bytes=%d", modem.getDownlinkPort(), i);
    handleDownlink(dn, i);
  }

  // Sleep modem and MCU
  modem.sleep();
  frameCounter++;

  // Low power sleep (approx)
  LowPower.sleep(uplinkPeriodSec * 1000UL);
}

Build/Flash/Run Commands (PlatformIO CLI)

  • Verify PlatformIO Core version:
pio --version
  • Create project structure (if starting from scratch):
mkdir -p lora-agro-microclima-node
cd lora-agro-microclima-node
pio project init --board mkrwan1310 --project-option "framework=arduino"
  • Place the provided platformio.ini at project root and main.cpp in src/.

  • Build:

pio run
  • Put the MKR WAN 1310 in a normal USB connected state. If needed, double-tap the reset button to enter bootloader mode (port may change to a “bootloader” COM/tty).

  • Upload:

pio run -t upload
  • Serial monitor (adjust port if needed):
pio device list
pio device monitor --baud 115200
  • If you need to set a specific port:
pio run -t upload --upload-port COM7     # Windows example
pio run -t upload --upload-port /dev/ttyACM0  # Linux example

Step‑by‑Step Validation

  1. Pre-flight checks
  2. Antenna securely attached to the MKR WAN 1310.
  3. BME680 wired to 3.3 V, GND, SDA, SCL; address known (0x76 or 0x77).
  4. DS18B20 wired to 3.3 V, GND, D4, with 4.7 kΩ pull‑up to 3.3 V on the D4 data line.
  5. TTN device registered (OTAA), frequency plan matches your region.

  6. Provisioning in The Things Stack (TTN v3)

  7. Create an application and register a device.
  8. Note the Device EUI, Join EUI (App EUI), and App Key.
  9. Paste JoinEUI and AppKey into main.cpp.
  10. For US915/AU915, ensure sub-band configuration on your gateway matches network plan (MKRWAN abstracts this, but gateway must match network).

  11. First boot and join

  12. Open serial monitor:
    • pio device monitor –baud 115200
  13. Expected logs:
    • Modem version and DevEUI
    • “Joining (OTAA)…” then “Joined network.”
    • Sensor presence logs (BME680 detected at 0x76/0x77, DS18B20 devices: N)
  14. If join fails, the code automatically retries every 30 seconds.

  15. Sensor validation

  16. Observe serial logs for numeric readings:
    • DS18: e.g., 21.56 C
    • BME: e.g., 22.14 C, 48.32 %RH, 1008.5 hPa, 12100 ohm
  17. Touch DS18B20 probe: temperature should rise within a few seconds.
  18. BME680 gas resistance responds slowly to VOC changes; do not expect immediate large swings. Humidity/temperature/pressure should look reasonable for your environment.

  19. Uplink validation on TTN

  20. In TTN Console, go to the device “Live data.”
  21. After join, you should see periodic uplinks on FPort 10 with 11‑byte payloads.
  22. Example payload (hex): 01 07 D0 08 04 13 88 27 23 00 96

    • Interpreted as:
    • ver=1
    • ds18=0x07D0=2000 => 20.00 C
    • bme_t=0x0804=2052 => 20.52 C
    • rh=0x1388=5000 => 50.00 %
    • p=0x2723=10019 => 1001.9 hPa
    • gas=0x0096=150 => 150 kΩ
  23. Payload formatter in TTN v3

  24. Application → Payload formatters → Uplink → Formatter type “Javascript.”
  25. Use this decoder to parse the custom binary:
function decodeUplink(input) {
  const bytes = input.bytes;
  if (!bytes || bytes.length < 11) {
    return { errors: ["invalid length"] };
  }
  const ver = bytes[0];
  const s16 = (hi, lo) => {
    let v = (hi << 8) | lo;
    if (v & 0x8000) v = v - 0x10000;
    return v;
  };
  const u16 = (hi, lo) => ((hi << 8) | lo) & 0xFFFF;

  const ds_cC = s16(bytes[1], bytes[2]);
  const bme_cC = s16(bytes[3], bytes[4]);
  const rh_cP = u16(bytes[5], bytes[6]);
  const p_dhPa = u16(bytes[7], bytes[8]);
  const gas_kohm = u16(bytes[9], bytes[10]);

  return {
    data: {
      version: ver,
      ds18_c: ds_cC / 100.0,
      bme_temp_c: bme_cC / 100.0,
      bme_rh_pct: rh_cP / 100.0,
      bme_hpa: p_dhPa / 10.0,
      bme_gas_kohm: gas_kohm
    },
    warnings: [],
    errors: []
  };
}
  • Save and return to Live data. You should now see decoded fields.

  • Downlink test to change sampling period

  • Construct a downlink with FPort 10 and payload format “Hex.”
  • Format: A0 00 78 sets period to 0x0078 = 120 seconds.
  • In TTN console: end device → Messaging → Downlink → FPort 10, Hex payload A00078 → Schedule downlink.
  • Watch serial logs for “[DN] Set uplink period to 120s.”
  • Confirm next uplinks occur every ~120 seconds.

  • Duty cycle and confirmed uplinks

  • The code uses confirmed uplinks every 4th frame by default (CONFIRM_EVERY=4).
  • Reduce confirmation frequency or disable (set 0) for production to conserve airtime and battery.

  • Low‑power behavior

  • Observe that the device sleeps between transmissions; current draw should drop significantly when running on battery (use a power meter if available).
  • Ensure BME680 heater usage is acceptable for your energy budget; our profile is moderate.

Troubleshooting

  • No COM/tty port appears:
  • Try a different USB cable/port.
  • Double‑tap reset to access bootloader; upload then try normal mode again.
  • Windows: Device Manager → Ports (COM & LPT) should show “Arduino MKR WAN 1310.”
  • Linux: adduser to dialout group and re‑login; check dmesg for ttyACM device.

  • Join fails repeatedly:

  • Verify AppEUI/JoinEUI and AppKey exact hex values, no spaces.
  • Check frequency plan and region constant (LORA_REGION) matches your TTN application’s plan and local regulations.
  • Gateway coverage and correct channel plan (especially for US915/AU915 sub‑bands).
  • Antenna firmly connected; never transmit without it.
  • If you re‑flashed many times and frame counters cause MIC failures, in TTN v3 device’s “Advanced MAC settings,” consider disabling frame counter checks for testing or re‑provision device session (then re‑enable for production).

  • Uplinks seen, but decoding fails:

  • Ensure the uplink formatter JS is installed at application or device level and set to Javascript.
  • Confirm payload length is 11 bytes (if length differs, you may have edited code; update decoder accordingly).

  • BME680 not detected:

  • Confirm wiring to SDA/SCL and 3.3 V, GND.
  • Check address: if your module is strapped to 0x77, either change the code or solder jumper.
  • Avoid long I2C runs; for field setups, keep wires short or use shielded cable.
  • Ensure pull‑ups on the breakout are present; most BME680 boards include them.

  • DS18B20 not detected:

  • Verify 4.7 kΩ pull‑up from D4 to 3.3 V is installed.
  • Confirm the sensor lead colors; some probes swap yellow/white. Identify with a multimeter or documentation.
  • Power with 3.3 V, not 5 V.
  • Parasite power mode is not used in this code; ensure 3‑wire mode.

  • Downlinks not received:

  • Only confirmed uplinks open RX windows that are more deterministic on some networks; schedule your downlink shortly after an uplink or use confirmed uplink events.
  • Check that the downlink FPort is 10 (matches code).
  • Keep downlink payload length small and timing near the next RX window (TTN handles scheduling).

  • Excessive gas resistance fluctuations:

  • Allow a burn‑in time for BME680 (several minutes) after power‑up.
  • Avoid enclosing the BME680 in airtight housings; use a vented enclosure with a hydrophobic membrane.

  • Build failures on libraries:

  • Ensure platformio.ini contains the exact lib_deps lines provided.
  • Run pio pkg update to refresh packages.

Improvements

  • Payload standardization:
  • Use CayenneLPP or a full SenML CBOR/JSON pipeline for broader interoperability. The custom binary is efficient but bespoke.

  • BSEC integration for IAQ:

  • Replace Adafruit BME680 library with Bosch Sensortec BSEC 2.x to compute IAQ, sIAQ, CO2eq, bVOC. Requires licensing terms and more flash/RAM.

  • Battery voltage measurement:

  • Implement VBAT read via internal divider if exposed on MKR WAN 1310 (consult board schematic). Expose in payload to monitor energy.

  • Event‑driven sampling:

  • Change interval based on diurnal profile or soil temperature dynamics. Implement hysteresis and backoff logic for network duty cycle.

  • Robustness:

  • Add a watchdog timer, brown‑out detection, and persistent storage (EEPROM emulation) for uplinkPeriodSec so downlink settings survive resets.

  • Security and compliance:

  • Enforce CFList/sub‑band settings as required by local regulations.
  • Avoid confirmed uplinks as default in production; use them sparingly for diagnostics.

  • Calibration:

  • Compare DS18B20 and BME680 temperature against a reference; apply per‑sensor offsets stored in flash.

  • Mechanical:

  • Thermally isolate BME680 from MCU heat sources; use a standoff and vented location.
  • Waterproof the DS18B20 cable ingress with proper glands and potting where needed.

Checklist

  • Antenna connected to MKR WAN 1310 u.FL.
  • PlatformIO Core installed and working (pio –version).
  • platformio.ini configured for mkrwan1310 with required lib_deps.
  • appEui and appKey entered correctly in main.cpp.
  • Region constant (LORA_REGION) matches TTN frequency plan.
  • BME680 wired to 3.3 V, GND, SDA/SCL; address known (0x76/0x77).
  • DS18B20 wired to 3.3 V, GND, D4 with 4.7 kΩ pull‑up to 3.3 V on D4.
  • Build succeeds (pio run).
  • Upload succeeds (pio run -t upload).
  • Serial monitor shows join success and sensible sensor readings.
  • TTN Console shows uplinks; payload formatter installed and decoding fields.
  • Optional: Downlink A0 00 78 received, sampling period updated to 120 s.
  • Confirmed uplink duty minimized; production interval aligned with regional duty cycle limits.

With these steps, you have a fully functional lora-agro-microclima-node on the Arduino MKR WAN 1310 using BME680 and DS18B20. It’s ready for field deployment, telemetry logging, and integration with dashboards or alerting pipelines.

Find this product and/or books on this topic on Amazon

Go to Amazon

As an Amazon Associate, I earn from qualifying purchases. If you buy through this link, you help keep this project running.

Quick Quiz

Question 1: What device model is used for the LoRaWAN-enabled microclimate node?




Question 2: Which sensor is used for measuring air temperature, humidity, and pressure?




Question 3: What type of resistor is used with the DS18B20 sensor?




Question 4: What is the maximum payload size for uplink data in this project?




Question 5: Which operating systems are supported for this project?




Question 6: What is the recommended version of Python for installing PlatformIO Core?




Question 7: What is the purpose of the watchdog in this project?




Question 8: What type of downlink does the project support?




Question 9: Which toolchain is recommended for building and flashing the project?




Question 10: What is required to create a device registered with OTAA?




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

Telecommunications Electronics Engineer and Computer Engineer (official degrees in Spain).

Follow me:
error: Contenido Protegido / Content is protected !!
Scroll to Top