Garden133 Radio: Designing a Low-Power LoRa Protocol for ESP32 Soil Sensors

Posted on July 3, 2025 • 5 min read • 977 words
Share via
Designing a custom, low-power LoRa radio protocol using Protocol Buffers for the Garden133 ESP32 soil moisture monitoring system.
Garden133 Radio: Designing a Low-Power LoRa Protocol for ESP32 Soil Sensors

Garden133 project sensor devices sample soil moisture levels in the yard and garden. They communicate this information to our home automation system, where it can be used to avoid unnecessary watering.

Introducing Garden133 gives a project overview, and I’ve also written about the project hardware and firmware. This post describes how the sensor units transmit data through obstacles to a server inside the home using a custom protocol built on LoRa1 radios and Protocol Buffers2.

The Challenge: Range and Power Constraints  

The sensor units live in the garden or yard and are battery-powered. This presents two primary engineering requirements:

  1. Range: Standard WiFi often fails to reach the far corners of a yard reliably. The signal is easily attenuated by vegetation, exterior walls, and other structures.
  2. Power: WiFi is relatively power-intensive. For a device that must operate for months on a small solar-charged battery, the energy cost of maintaining a WiFi connection is too high.

Since soil moisture levels change slowly, we only need to transmit a few bytes of data every few minutes. LoRa is well-suited for this, as it allows trading bandwidth for increased range and power efficiency.

Why Choose LoRa for Garden Sensors?  

LoRa (Long Range) is a physical radio modulation specialized for low-power, long-distance communication. Unlike WiFi, which prioritizes throughput, LoRa is designed to ensure small packets are received reliably even in the presence of interference.

In the words of Vit Prajzler:

LoRa is a digital spread spectrum modulation, more specifically, a chirp spread spectrum modulation.

By using chirp signals that sweep in frequency, LoRa can be decoded even when the signal strength is below the noise floor. This makes it possible to maintain a link through obstacles that would block a WiFi signal.

I used the HopeRF RFM95W modules for this project. They are inexpensive and interface easily with an ESP32 via SPI, as nicely documented in this post from Random Nerd Tutorials.

HopeRF RFM95W LoRa module mounted on custom ESP32 PCB
The HopeRF RFM95W module used to bridge the distance between the garden and the house.

Optimizing for Range and Regulatory Compliance  

I am in the US, so I use the 915MHz band and must comply with FCC dwell time regulations. A single transmission cannot occupy a frequency for more than 400ms. This constraint dictates how much data can be sent in a single packet.

LoRa settings require balancing several factors:

  • Spreading Factor (7-12): Higher factors increase range but slow down the data rate.
  • Bandwidth (125-500kHz): Higher bandwidth increases the data rate but reduces sensitivity.

I eventually selected a Spreading Factor of 11 and a Bandwidth of 500kHz. This allows ~100-byte packets to be transmitted in under 400ms while maintaining a reliable connection throughout my property. An informal test in my semi-urban neighborhood, with many homes and trees, yielded a maximum range of around 150m.

Designing a Self-Describing Protocol  

A primary goal was to avoid the need for manual registration of sensors. I wanted to be able to deploy a new sensor and have it recognized by Home Assistant automatically without manually entering device IDs or configuration.

Packet Structure  

To achieve this, I designed a protocol where the satellite unit describes itself to the base station. A packet starts with a 4-byte magic sequence (og3p), followed by a protocol version and sequence ID. The message content is divided into two parts:

  1. Identity: A unique 32-bit ID (hashed from the WiFi MAC), the device name, and hardware/software versions.
  2. Data: A list of sensors (e.g., moisture, battery voltage, temperature) and their current readings.

Because a full description of 10 sensors exceeds the 400ms transmission window, I use a rolling broadcast strategy. At boot, the sensor transmits its full description across four separate packets. Once initialized, it switches to shorter, data-only packets.

Data Serialization with Protocol Buffers  

To minimize packet size, I used Protocol Buffers (Protobuf). Protobuf allows defining data structures in a .proto file and compiling them into a compact binary format.

Using the Nanopb implementation for C++, I can serialize sensor data into a few bytes, which is necessary given LoRa’s limited throughput.

message Packet {
  uint32 device_id = 1;
  Device device = 2; // Sent during initialization or periodic updates
  repeated FloatSensorReading reading = 3;
  repeated Sensor sensor = 5; // Sent during initialization
}

Resilience and Synchronization  

One-way communication (Satellite → Base Station) means the satellite does not know if the base station has received its identity packets. If the base station is reset or a packet is dropped, the base station might lose the sensor’s configuration.

To handle this, starting every hour, the satellite unit appends one sensor description to each regular data packet until the entire device configuration has been retransmitted. This ensures that the base station will eventually recover the full configuration of any sensor in the yard without manual intervention.

Garden133 communication cycle

Conclusion  

The Garden133 radio design demonstrates how LoRa can be used with a custom protocol to provide reliable communication on a strict power budget. By employing a rolling broadcast strategy, I was able to build a system that requires no manual configuration after deployment.

Resources  

The firmware for the devices is available on GitHub for:

Code to read and write the packet format is part of the og33 library. Satellite auto discovery code and Protobuf message definitions are part of the og3x-satellite library.

References  


  1. LoRa (the name stands for “long range”) is a physical proprietary radio communication technique ( Wikipedia, Semtech). It is specialized for long range, low bandwidth, low power radio communication for Internet of Things (IoT) applications. ↩︎

  2. Protocol Buffer or “Protobuf”, is Google’s language-neutral, platform-neutral, extensible mechanism for serializing structured data. Compared to json or XML, it is much more compact, but it is not human-readable in the same way. ↩︎

  3. og3 is my C++ utility library for ESP microprocessors, published on GitHub↩︎