Build · Sketch · v1.3.0

The LoRa Field Node.

What it does
Reads the probes wired to it and broadcasts them over 915 MHz to your LoRa Gateway
Boards
Heltec WiFi LoRa 32 V4 · V3 · V2
License
Open: copy it, change it, own it

Put every sensor a greenhouse needs on one board, set it in the far corner of the property where there is no Wi-Fi and never will be, and have the readings arrive at the house anyway. This node reads the probes wired to it and, every few minutes, broadcasts one small radio frame on a private 915 MHz channel. It never expects a reply. The LoRa Gateway at the house hears it and does the rest, exactly the way the BLE Listener hears a Bluetooth thermometer. A field node is a thermometer with a longer antenna.

What it does.

Wire what you have to it. It finds what is there and reports it: DS18B20 temperature probes on one wire (up to sixteen), an SHT-30 air temperature and humidity sensor (one or two), a photoresistor for light level, and up to eight capacitive soil-moisture probes. It also reports its own battery if one is fitted, and it carries anything plugged into its pod port: a Radio Sensor Listener, an Apogee meter, another OAT sketch. Nothing is configured per bus sensor: a probe added today shows up at your endpoint on the next cycle under its own factory serial number. Analog inputs are the one exception, and the wiring section says why.

On its cadence, three minutes by default, it reads everything, packs the readings into a frame of a few dozen bytes, listens for a moment to make sure nobody else is talking, and transmits. Then it waits. There is no Wi-Fi radio running, no internet, no account, no pairing, and no acknowledgement. If a frame is lost, the next one three minutes later carries fresh readings, and the gateway reports the gap.

It is the Collect layer for the places Wi-Fi cannot reach: the back field, the pump house, the greenhouse at the end of the row. The LoRa page explains why this radio carries a reading a kilometre on almost no power.

Flash it.

Plug your board in, find it below, and press its button. The LoRa radio is soldered onto these boards, so 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 + RF front end (PA/LNA) · native USB · the bench board · tested
SX1262 radio · native USB · beta
SX1276 radio · CP2102 USB, may need a driver · beta

Which one is yours matters more than usual. The V3 and the V4 are the same chip, so the installer cannot tell them apart, and a V4 flashed with the V3 image boots, says “radio ok,” and hears and sends nothing: the V4 has a radio front-end amplifier between the chip and the antenna that the V4 image powers and the V3 image does not. The version is printed on the board. The V2 has micro-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. A LoRa radio transmitting into nothing can damage its own output stage. The little stub antenna in the box is enough for the bench; a real antenna is where range comes from.

Prefer the source? Browse it on GitHub: the full project, openly licensed, ready to build yourself.

What you need.

How it works.

  1. On its cadence the node reads every sensor it found.
  2. It packs the readings into one small frame: for each sensor, a one-byte tag, a measurement code, and a scaled value. Ten sensors fit in about sixty bytes.
  3. It listens for a moment, and if the channel is quiet, transmits. A random pause of up to three seconds keeps a dozen nodes from ever falling into lock-step.
  4. The gateway hears the frame, files each reading under the sensor's own serial number, averages the window, and pushes oat-ods to your endpoint.

Every fifth cycle, and at boot, the node also sends a roster: the map from each one-byte tag to the sensor's full hardware address. That is how a DS18B20's 64-bit factory serial rides the air as a single byte, and why a probe moved from a Wi-Fi node to a LoRa node keeps its history at the endpoint.

It is a private radio LAN with the manners of UDP. No handshake, no acknowledgement, no routing, no network server. The gateway is the only thing that needs an internet connection, and it needs it for the same reason every OAT gateway does: to push your readings to a place you keep.

Wiring.

The defaults below are for the pins each board leaves free once its radio and screen are spoken for. Every pin is a setting, so if your board or your sensor wants a different one, change it on the console rather than on the bench.

Heltec V4 (ESP32-S3 + front end) defaults

  • DS18B20 data on GPIO 33, 4.7k pull-up to 3V3
  • SHT-30 SDA 17, SCL 18 (the screen's bus; they share it)
  • Photoresistor divider to GPIO 4, then set lightpin 4
  • Soil probe GPIO 6, then set soilpins 6. Only 4 and 6 are free analog pins on a V4; past two probes use an ADS1115 on the I2C bus.
  • Pod port RX GPIO 47, TX GPIO 48
  • Battery on-board divider, GPIO 1
  • Never touch GPIO 2, 5, 7 and 46: they drive the radio front end.

Heltec V3 (ESP32-S3) defaults

  • DS18B20 data on GPIO 7, 4.7k pull-up to 3V3
  • SHT-30 SDA 17, SCL 18 (the screen's bus; they share it)
  • Photoresistor divider to GPIO 2, then set lightpin 2
  • Soil probes GPIO 3, 4, 5, 6, then set soilpins 3,4,5,6
  • Pod port RX GPIO 40, TX GPIO 41
  • Battery on-board divider, GPIO 1

Heltec V2 (classic ESP32) defaults

  • DS18B20 data on GPIO 13, 4.7k pull-up to 3V3
  • SHT-30 SDA 4, SCL 15 (the screen's bus)
  • Photoresistor divider to GPIO 36, then set lightpin 36
  • Soil probes GPIO 38, 39, 32, 33, then set soilpins 38,39,32,33
  • Pod port RX GPIO 17, TX GPIO 23
  • Battery on-board divider, GPIO 37

Transmit power

  • The V2 and V3 default to 17 dBm at the chip, which is the antenna power.
  • The V4’s front end adds about 10 dB, so its default is 10 dBm at the chip, about 20 dBm at the antenna. set power 17 reaches roughly 27 dBm for a genuinely far node. Keep it modest on a campus: airtime and neighbours.

Buses are discovered; analog is declared. A DS18B20 announces its serial and an SHT-30 answers at its address, so the node finds them by itself. An analog pin cannot be discovered: a pin with nothing on it floats at a few hundred millivolts and reads exactly like a wet probe. So analog pins ship off, and you declare the ones you wired with one console line. A declared pin reading under 150 mV is reported as raw millivolts only, never as a percentage, because that is a wire off rather than saturated soil.

Power the sensors from the board's Vext pin, which the firmware switches on at boot, or from 3V3. A photoresistor needs a fixed resistor of about 10k to ground to make a divider. A capacitive soil probe outputs a voltage on its own; feed it 3V3, not 5 V, so its output stays inside what the chip can read. The DS18B20 and SHT-30 pages carry the pin-by-pin hookup and the reasons behind each choice.

Set it up.

This node has no Wi-Fi, so it has no setup web page. Setup is the USB console: plug it in, open a serial terminal at 115200 (the browser console on this site works), and type help.

  1. Flash the board and leave it plugged in. It boots, finds its sensors, and prints what it found.
  2. Type status. Every probe is listed with its serial number and its first reading. If a sensor is missing, check its wiring, then type scan.
  3. Leave the radio plan alone unless you changed it on the gateway. The default plan on both sketches is the same: 915.0 MHz, 125 kHz, spreading factor 7, sync word 12.
  4. Set the cadence if three minutes is not what you want: set cadence 120. Sixty seconds is the floor.
  5. Type tx to send a frame right now, then look at the gateway's setup page. Your node appears under Field nodes heard with its signal strength.

The screen. The board’s small display shows what a person standing next to the node needs: its id, the radio plan and whether the radio started, how many probes of each kind it found, the last transmission with its sequence number and how long ago, when the next one is due, and the latest reading with the battery voltage. A node cannot know it was heard, since nothing answers it, so the screen says sent, honestly, and the gateway’s screen says heard. A blank screen on a powered board means the firmware did not boot, which is a flashing problem rather than a radio problem.

Settings survive reboots. A fresh flash wipes them, on purpose, so a re-used board never carries the last owner's radio plan. Calibrating the soil probes is a bench step before the node goes out, one probe at a time, over the same console: hold the probe in dry air and type set cal 3 dry, stand it in a glass of water to the line and type set cal 3 wet, using the probe's own GPIO. Each probe keeps its own pair, because two probes from one bag read hundreds of millivolts apart in the same glass. A probe with no calibration sends its raw millivolts only and no percentage; the raw value is always sent, so you can recalibrate at the endpoint later without visiting the node. show prints the table, and set cal 3 2876 1232 types a pair back in after a reflash. The Soil-Moisture Node shares this exact code and explains the calibration at length.

Open the printable setup card (PDF)

A three-page field card: what to have on hand, the flash and setup steps, blanks for this node’s settings and ids, the console commands, and the fixes for the usual snags. Print one per node.

What it sends.

Over the air, not oat-ods. A LoRa frame carries about 200 bytes and one oat-ods reading is 330 bytes of JSON, so the node sends a compact binary frame instead and the gateway expands it. Your endpoint sees ordinary oat-ods, tagged oat-lora-gateway, with each field sensor as its own stream and the node's signal strength, noise margin and battery as gauges beside it.

The whole over-air contract is one header file, oat_lora_frame.h, shared by both sketches. Adding a measurement is one line in its code table, on both sides. Codes are never renumbered.

The pod port.

Some sensors are not a probe on a wire. A weather station is a radio broadcast that needs decoding. An Apogee meter speaks serial. A neighbour has an ESP32 running something clever. The node cannot know about all of them, and it does not need to: it has a pod port, a second serial port at 115200 baud, and anything that prints readings on it in one small text grammar becomes a cluster of sensors on this node. The thing on the other end of the cable is a pod. It does one job on its own board, and the node carries what it says.

Every OAT sketch prints its readings in this grammar by default, so the Radio Sensor Listener cabled to this port carries an AcuRite’s wind and rain to the house over LoRa under the station’s own id, with no Wi-Fi anywhere in the field. Three wires: the pod’s TX to this node’s pod RX, ground to ground, and optionally the pod’s RX to this node’s pod TX.

The grammar: oat-line/1.

Plain text, one record per line, space-separated, newline-terminated, under 120 characters. Four record types, one letter each. Everything else on the line is ignored and counted, so a pod can print debug text without breaking the reader.

M <stream-id> <measurement> <value> [unit|-] [c|g|k|s|e]    a reading
S <stream-id> brand=<..> model=<..>                        who made it, sticky
L <stream-id> [rssi=<dBm>] [battery=<pct>]                  link health, latest wins
H <pod-id> <firmware> [uptime=<s>]                         the pod's own heartbeat

An AcuRite station, as a Radio Sensor Listener prints it:

S acurite-5n1:2716 brand=AcuRite model=Acurite-5n1
M acurite-5n1:2716 wind_speed 3.4 m/s c
M acurite-5n1:2716 wind_direction 225 deg g
M acurite-5n1:2716 rain_total 128.5 mm k
M acurite-5n1:2716 temperature 21.4 Cel c
M acurite-5n1:2716 humidity 63 %RH c
M acurite-5n1:2716 battery_low 0 - s

This node prints its own readings the same way on its USB port, so a field node plugged into a gateway by USB is itself a pod. The listener page shows the wiring from its side; the developer reference says what a pod’s streams look like to an endpoint, which is: exactly like every other stream.

Writing a pod. Read your sensor however it wants to be read. Print one M line per reading with the vocabulary name and unit, an S line once for brand and model, and an H line now and then. That is the entire contract, and it works from any board in any language.

Range, airtime and power.

The default plan, spreading factor 7 at 125 kHz, is the campus setting: a 60-byte frame costs about a tenth of a second of airtime, which is why a hundred nodes on a three-minute cadence share one channel with room to spare. Line of sight with the gateway antenna up high, it reaches hundreds of metres to a kilometre or two. Metal-framed greenhouses eat signal; a node whose antenna pokes outside the frame hears and is heard far better than one on the bench inside.

For a node genuinely far away, step the spreading factor up on both sides: 9 reaches a few kilometres at four times the airtime. The US 400 ms dwell rule makes 10 the practical ceiling at 125 kHz. The LoRa page walks the trade-off.

Power. This node stays awake. It does not sleep between readings, on purpose: an awake node can listen before it talks, take a reading on demand, and keep its console live. The cost is about half a watt. On mains, that is nothing. Off grid, size for December rather than June: a 20 W panel and a 12 V 10 Ah LiFePO4 battery carry it through a week of overcast. The solar page has the sizing. Oversize the panel and skip the firmware complexity; that is the appropriate trade here.

For makers & trainers.

Frequently asked questions.

Does a LoRa sensor node need Wi-Fi or internet?

No. A LoRa field node has no Wi-Fi radio running and never touches the internet. It broadcasts a small radio frame on a private 915 MHz channel, and a gateway at the house, the one place with a network connection, hears it and forwards the readings. The node can sit anywhere the gateway can hear it, typically hundreds of metres to a few kilometres with antennas in the clear.

Is this LoRaWAN?

No. LoRaWAN is a network protocol with join keys, a network server and regional channel plans, built for public and carrier networks. This is private point-to-point LoRa: the same radio, no network server, no subscription, nothing between your field and your gateway. It is the simpler tool for one farm with one gateway. LoRaWAN earns its complexity when many farms share a public network or when a site runs hundreds of devices behind a multi-channel concentrator.

How many sensors can one field node carry?

Sixteen DS18B20 temperature probes on one wire, two SHT-30 air sensors, one photoresistor, eight soil probes and up to 32 pod streams in this firmware, which is well over 40 readings per cycle. The radio is not the limit: forty readings are three small frames. The practical limit is cable length and the number of wires running into one enclosure. Past about thirty sensors, a second board in the same greenhouse is the cleaner build.

Why does the node not sleep to save battery?

Because an awake node can listen before it transmits, answer its console, and take a reading on demand, and because the field sketch stays identical to every other OAT sketch when it stays awake. The cost is about half a watt, which a modestly oversized solar panel and a LiFePO4 battery cover through winter. That is a parts-list problem rather than a firmware problem, and on a farm parts are the cheaper of the two.

What happens when a frame is lost?

The next frame, three minutes later by default, carries fresh readings, and the gateway reports the gap from the frame's sequence number. There are no retries, because for periodic monitoring a missed sample is information rather than a loss, and retries would double the airtime on a shared channel. If a node goes silent for three cycles, the gateway releases its streams so the endpoint sees an absence rather than a frozen last value.

Can I use a different LoRa board?

The compiled images are for the two Heltec WiFi LoRa 32 boards, because the radio wiring is fixed on those and the images can be flashed from the browser. Any ESP32 with an SX1276 or SX1262 radio can run the source: change the pin map in the shared radio header and build with PlatformIO. An ESP32 with an Ebyte E220 serial modem is a future variant of the same sketch.