Build · Sketch · v1.0.0

The LoRa Gateway.

What it does
Hears every LoRa Field Node on the property and pushes their readings to your endpoint
Boards
Heltec WiFi LoRa 32 V3 · V2
License
Open: copy it, change it, own it

One board at the house, where the Wi-Fi is, listening on a private 915 MHz channel for every LoRa Field Node on the property. Each frame it hears becomes ordinary oat-ods at your endpoint: one stream per field sensor, named by that sensor's own serial number, with the node's signal strength and battery beside it. It is the BLE Listener with a different radio, and it behaves like every other OAT gateway: the same setup page, the same console, the same push engine, heartbeat and signature. One gateway serves every field node on the farm.

What it does.

Field nodes broadcast. This listens. When a frame arrives it checks it, files each reading under the sensor it came from, averages the readings over the window you set, and pushes a batch to the endpoint you own: your own webhook, an MQTT broker, Home Assistant, or the Open Agriculture Technology Test Endpoint while you prove the chain works. Every 60 seconds it sends its own heartbeat, so your endpoint can tell a quiet farm from an unreachable one.

It also keeps the farm's radio honest. It measures the signal and noise margin of every node on every frame, counts lost frames from their sequence numbers, and when a node falls silent for three of its own cycles it releases that node's streams, so a dead node shows up at your endpoint as an absence rather than a frozen last value.

The gateway names nothing. Streams carry the sensors' hardware ids, and the map from hardware to place lives at your endpoint, where it survives a reflash and can be edited without a walk to the field. That is the same rule every OAT sketch keeps.

See it in action: the Open Agriculture Technology Test Endpoint is live right now, and a gateway pointed at it appears within a minute, with its field nodes' sensors listed beneath it.

Flash it.

Plug your board in, find it below, and press its button. The image is built for the exact board, and the installer checks the chip matches before it writes anything.

Your browser can't flash devices, use Chrome or Edge on a desktop. This page must be served over HTTPS to flash. SX1262 radio · native USB · the recommended gateway · beta
SX1276 radio · CP2102 USB, may need a driver · beta

The V3 is the better gateway: more memory for a big farm's sensor table, a better receiver, native USB.

If the board won't take the flash, or it restarts over and over

Hold the BOOT button down while you plug the USB cable in. Let go once it is plugged in, then press the install button above. That is the whole fix, and it is worth trying first any time a board is being stubborn.

Here is what is going on. A board with no working firmware on it restarts several times a second, forever, because it looks for a program to run and finds nothing. The installer needs the board to hold still long enough to write a megabyte and a half, and it cannot get hold of one that keeps restarting. Holding BOOT as the power arrives puts the chip into the loading mode it was built with, where no program runs at all, so there is nothing to restart and the installer gets a still target.

If you have a serial monitor open, you will know it worked because the scrolling stops. Quiet is what you want.

You may also see invalid header: 0xffffffff repeating in the monitor. That reads like damage and it is not: it is what an empty chip looks like, which is exactly where a board lands if a write was interrupted. The board is fine. Flash it again with BOOT held and it will come back.

If it flashes and runs fine on its own but fails the moment the sensor is connected, suspect whatever sits between them before you suspect either one. A screw-terminal carrier, an adapter board or a breakout is a component too, and a bridged or cracked terminal on one will stop the board booting while the sensor and the ESP32 are both perfectly good. Detaching the sensor is the quickest test: if the board comes back to life, neither it nor the sensor is at fault, and the thing in the middle is where to look. That exact fault cost us most of a night.

Two other things are worth checking before you decide a board is bad. Use a data USB cable, since a charge-only cable carries power but no signal and gives you the same symptoms. And plug straight into the computer rather than through a hub, because a write that browns out part way through leaves the board empty in just this way.

Attach the antenna before you power the board. For the gateway, put a real antenna on the pigtail and get it up high. The gateway's antenna is the single biggest range decision on the farm.

What you need.

How it works.

  1. A field node reads its sensors and broadcasts one small frame on the shared channel.
  2. The gateway's radio, in continuous receive, hears it and checks the frame's magic, version and CRC. Anything foreign on the channel is dropped and counted.
  3. A roster frame tells the gateway which one-byte tag belongs to which sensor's full hardware address. A data frame carries the readings by tag. The gateway files each reading under its sensor's stream, using the same id the wired sketch for that part would have used.
  4. On the gateway's own timer, it pushes one batch of averaged readings to your endpoint, signed, exactly as any OAT gateway does.

There is no acknowledgement and no downlink. The channel is a private radio LAN with the manners of UDP, and the gateway is the bridge from that LAN to your network. Loss is visible, not hidden: each node's lost-frame count sits on the status page.

Set it up.

Setup is the same one form as every OAT gateway, served by the device itself.

  1. Power the gateway from any USB charger. Attach the antenna first.
  2. Join the Wi-Fi network OAT-Setup-XXXXXX (open, no password). A setup page opens by itself; if not, visit http://192.168.4.1.
  3. Sign in, fill in the gateway's name, your Farm ID, the location, your Wi-Fi, and where the readings should go, then press Save & Connect.
  4. Leave the four radio plan fields at their defaults unless you changed them on the nodes: 915.0 MHz, 125 kHz, spreading factor 7, sync word 12. They must match the field nodes exactly or nothing is heard.
  5. Watch Field nodes heard on the same page. A node appears on its next cycle, three minutes by default, with its sensor count, signal strength, battery and cadence. Type tx on a node's console to hurry it along.
  6. Open the Status page and press Push now. A green “OK” means the field readings just reached your endpoint.

Ready to test it? Enter https://iot-test.openagriculturetechnology.com/ingest as the endpoint URL, then open the Open Agriculture Technology Test Endpoint and pick your farm. The gateway and every field sensor it has heard show up live. It keeps about an hour of readings, enough to prove the chain before you point the gateway at an endpoint you keep.

What it sends.

Ordinary oat-ods, tagged oat-lora-gateway. Each field sensor is its own stream. The frame's link quality becomes gauges on the node's own stream, so your endpoint can chart a node's signal over the seasons and see the antenna that needs attention before the node goes quiet.

See a batch with one field node's readings
{
  "schema": "oat-ods/0.3",
  "msg_type": "batch",
  "sent_at": "2026-09-05T14:30:00Z",
  "seq": 2000041,
  "source": { "tier": "oat-lora-gateway", "gateway_id": "house-gw",
              "farm_id": "willow-oaks", "fw": "OAT-LoRa-Gateway/1.0.0" },
  "messages": [
    { "observed_at": "2026-09-05T14:30:00Z",
      "stream": { "id": "ds18b20:28ff641e8d3c0a12" },
      "measurement": "temperature", "value": 21.37, "unit": "Cel",
      "agg": { "window_s": 180, "samples": 1, "method": "mean" },
      "source": { "physical_id": "ds18b20:28ff641e8d3c0a12",
                  "brand": "Analog Devices", "model": "DS18B20", "rssi": -71, "battery": 87 } },
    { "observed_at": "2026-09-05T14:30:00Z",
      "stream": { "id": "sht30:0a1b2c3d" },
      "measurement": "humidity", "value": 63.2, "unit": "%RH",
      "agg": { "window_s": 180, "samples": 1, "method": "mean" },
      "source": { "physical_id": "sht30:0a1b2c3d", "brand": "Sensirion", "model": "SHT30", "rssi": -71, "battery": 87 } },
    { "observed_at": "2026-09-05T14:30:00Z",
      "stream": { "id": "3c71bf94:a3" },
      "measurement": "soil_moisture", "value": 41.5, "unit": "%",
      "agg": { "window_s": 180, "samples": 1, "method": "mean" },
      "source": { "physical_id": "3c71bf94:a3", "model": "ADC", "rssi": -71, "battery": 87 } },
    { "observed_at": "2026-09-05T14:30:00Z",
      "stream": { "id": "3c71bf94" },
      "measurement": "rssi", "value": -71, "unit": "dBm",
      "agg": { "window_s": 180, "samples": 1, "method": "last" },
      "source": { "physical_id": "3c71bf94", "brand": "Heltec", "model": "LoRa field node" } }
  ]
}

Want the whole format, both message types, every field, and the full measurement vocabulary? See the developer reference, or read the machine-readable JSON Schema. The over-air frame between node and gateway is documented on the field node page.

Placement, range and how many.

High and central. The gateway antenna's height matters more than any setting in the firmware. A mast on the house or the barn roof with the antenna in clear air will hear a node a kilometre away at the default plan; the same board on a windowsill may not hear the far greenhouse. Line of sight, or close to it, is the goal.

How many nodes. This build tracks 32 field nodes and 96 sensors. At the default plan a node's frame costs about a tenth of a second of airtime, so a hundred nodes on a three-minute cadence would still use under a tenth of the channel. Need more than the table holds? Two compile-time constants, or a second gateway on a second sync word for the other half of the farm.

Farther nodes. A slower spreading factor reaches farther and costs more airtime, and every node on a gateway must share the gateway's plan. For a mix of near and far, run a second gateway on the far plan. Each gateway hears one plan at a time.

A gateway is one place the farm can fail. Put it on the same small battery back-up as the router, so a power blip does not make every field node look dead at once. Its own 60-second heartbeat tells your endpoint whether the gateway or the farm is the thing that went quiet.

For makers & trainers.

Frequently asked questions.

What is a LoRa gateway?

A device with a LoRa radio that receives the broadcasts of field sensors and forwards them onto a network the sensors themselves cannot reach. In this design it is one small ESP32 board at the house with Wi-Fi, listening on a private 915 MHz channel and pushing every reading it hears to an endpoint the grower owns. It is a bridge between the farm's radio LAN and the internet.

How many field nodes can one gateway serve?

This firmware tracks 32 nodes and 96 sensors, and the radio channel itself carries a hundred nodes on a three-minute cadence with most of its capacity unused. A larger farm adds a second gateway on its own sync word rather than a bigger table, and a mix of near and far nodes runs two gateways on two spreading factors, because a gateway hears one plan at a time.

Why does every node have to match the gateway's radio plan?

Because LoRa receivers decode only one combination of frequency, bandwidth, spreading factor and sync word at a time. A frame sent on a different spreading factor is nearly invisible to a receiver tuned for another, which is a useful property for keeping two networks apart and an unhelpful one when a node was set up with the wrong number. The four settings live on the gateway's setup page and on each node's console, and the defaults on both sketches are identical.

Is the radio link encrypted?

No. The over-air frame is a short private-format packet with a CRC, on a private sync word, carrying temperature and soil readings. The signed, authenticated boundary is the gateway's push to your endpoint, exactly as it is for a Bluetooth thermometer heard by the BLE Listener. If a reading crossing a farm's own airspace in the clear is a concern, the frame has room for a message authentication code, and it is a small addition to both sketches.

What does the gateway do when a field node stops transmitting?

Each frame carries the node's cadence, so the gateway knows when to expect the next one. After three missed cycles it releases that node's streams, and the next batch to your endpoint simply does not contain them. That is the honest signal: an absence your endpoint can alert on, rather than a stale value repeated forever. When the node returns, its streams come back under the same ids.