Build a Solar-Powered LoRa Long-Range Agriculture Sensor Node with ESP32 & SX1276
Comprehensive engineering tutorial for building an off-grid, solar-powered LoRa agricultural telemetry node using ESP32, SX1276 transceiver, and deep sleep power gating.
- Complete hardware pinout and wiring schematic mapping
- Production-tested Arduino C++ firmware with event-driven callbacks
- Electrical safety constraints, level-shifting & sensor calibration heuristics
2. Overview & Engineering Objectives
Cellular networks like 4G/5G suffer from patchy coverage across rural acreage and impose prohibitive recurring SIM card monthly fees per sensor node. This project builds an autonomous, off-grid telemetry station capable of transmitting soil moisture, temperature, and battery health across 3 to 10 kilometers using Semtech's chirp spread spectrum (CSS) LoRa technology.
Core Engineering Capabilities:
Long-Range Link Budget: Transmits over several kilometers line-of-sight using Spreading Factor 7 (SF7) up to SF12 with +20dBm transmit power.
Micro-Power Deep Sleep Architecture: The ESP32 enters deep sleep between transmission bursts, consuming less than 15µA quiescent current.
Sensor Power Gating: Eliminates passive parasitic drain from the soil moisture probe during sleep by switching sensor power through a high-side P-channel MOSFET or dedicated GPIO power pin.
Sub-1GHz Antenna Matching: Tuned to the 865–867 MHz band, compliant with India's Wireless Planning & Coordination (WPC) license-free regulatory spectrum.
3. Bill of Materials (BOM)
Component
Quantity
Specification / Rating
Est. Cost (INR)
Sourcing / Affiliate Link
ESP32 DevKit V1 (or ESP32-WROOM)
1
30-Pin Dual-Core Xtensa LX6 (Deep-Sleep Capable)
₹380
Buy on Mentora Store
SX1276 / RFM95W LoRa Breakout
1
868MHz / 915MHz Semtech SX1276 Transceiver
₹580
Buy on Mentora Store
868MHz Omnidirectional Antenna
1
2dBi–3dBi SMA Rubber Duck Antenna
₹150
Buy on Mentora Store
Capacitive Soil Moisture Probe
1
Corrosion-resistant v1.2 analog probe
₹140
Buy on Mentora Store
TP4056 Solar Charger + Protection
1
5V 1A Li-ion charger with DW01A protection IC
₹45
Buy on Mentora Store
6V 2W Monocrystalline Solar Panel
1
Waterproof outdoor epoxy solar panel
₹220
Buy on Mentora Store
18650 Li-ion Battery (2600mAh)
1
Protected 3.7V cell
₹160
Buy on Mentora Store
IP65 Weatherproof Enclosure
1
Waterproof outdoor junction box with cable glands
₹190
Buy on Mentora Store
Total Hardware Cost
~₹1,865
4. Hardware Architecture & Critical RF Safety Rules
1. FATAL RULE: Never Power On Without an Antenna!
The SX1276 power amplifier emits up to +20dBm (100mW) of RF energy.
CRITICAL WARNING: If the module transmits without an antenna connected, the RF power has nowhere to radiate. It reflects directly back into the transmitter output stage (voltage standing wave ratio VSWR to ∞), causing an instantaneous thermal breakdown of the internal output transistors. Always screw on the antenna before powering up the board!
2. Sensor Power Gating to Prevent Battery Drainage
The capacitive soil sensor draws ~5mA continuously when powered. Over a 24-hour period, this idle current would drain 120mAh from your battery, even if the ESP32 is asleep!
Solution: Power the sensor's VCC from GPIO 25.
In firmware, drive GPIO 25 HIGH only 50ms before sampling the analog sensor on GPIO 34 (ADC1), read the analog voltage, and immediately drive GPIO 25 LOW before entering deep sleep.
5. RF Modulation & Frequency Regulation in India
In India, the Wireless Planning & Coordination (WPC) wing specifies that license-exempt low-power devices in the sub-1GHz range must operate within 865 MHz to 867 MHz:
Carrier Frequency (f_c): 866.0E6 (866.0 MHz default center frequency).
Bandwidth (BW): 125 kHz (Standard LoRa channel bandwidth).
Spreading Factor (SF): SF7 (for high data rate, low power, <2km) to SF10 (for extended reach, dense foliage, up to 8km).
Coding Rate (CR): 4/5 (Includes forward error correction).
Transmit Power: 17 dBm (Typical) up to 20 dBm (with PA_BOOST).
6. Real-World Troubleshooting FAQs
Q1: Why does LoRa.begin() return 0 or fail during startup?
Root Cause 1: SPI Pin Mapping Mismatch. The SX1276 uses hardware SPI. Verify NSS, RST, and DIO0 assignments:
LoRa.setPins(5, 14, 2); // NSS = GPIO 5, RESET = GPIO 14, DIO0 = GPIO 2
Root Cause 2: 3.3V Power Rail Sag. The SX1276 draws up to 120mA during TX. Power it from the ESP32's clean 3.3V regulator with a 100µF decoupling capacitor across VCC and GND.
Q2: Why does the receiver miss packets or report CRC errors?
Root Cause: Parameter mismatch. Both transmitter and receiver must have identical frequency, spreading factor, coding rate, bandwidth, and sync word.
LoRa.setSyncWord(0xF3); // Private network sync word
LoRa.enableCrc(); // Cyclic redundancy check
Q3: Why does the ESP32 fail to wake up from deep sleep?
Root Cause: Incorrect RTC timer configuration. Remember that espsleepenabletimerwakeup() takes time in microseconds (µs), not milliseconds:
const uint64t SLEEPDURATION_SEC = 900; // 15 minutes
espsleepenabletimerwakeup(SLEEPDURATIONSEC * 1000000ULL);
7. Mentora Monetization & Student Support Hooks
🛒 Official Mentora LoRa Agriculture Kit: ESP32 DevKit, HopeRF RFM95W (868MHz), tuned SMA dipole antenna, capacitive sensor, TP4056 solar charger, 6V solar panel, and IP65 enclosure for ₹2,499.
⚡ ₹49 Mentora Token Vault: Instantly unlock the companion LoRa-to-WiFi Gateway firmware, The Things Network (TTN) LoRaWAN v3 payload decoders, and solar MPPT battery management schematic.
🛠️ ₹299 1-on-1 Mentora Live Video Debugging: 30-minute session with an RF embedded engineer to tune antenna impedance, debug SPI bus logic analyzer traces, and optimize battery sleep budgets for multi-year field deployment.
Hardware Pinout & Wiring Connections
Hardware Pinout & Wiring Connection Guide
Project 7: LoRa Long-Range Agricultural Sensor Node
Microcontroller: ESP32 DevKit V1 (30-pin / 36-pin)LoRa Transceiver: Semtech SX1276 / HopeRF RFM95W Module (866.0 MHz Band)Soil Moisture Sensor: Capacitive Soil Moisture Sensor v1.2Power Stage: 3.7V 18650 Li-ion Battery + TP4056 Solar Charger + 6V 2W Solar Panel
1. Complete Fritzing Pin Connection Mapping Table
Table A: SX1276 LoRa Transceiver to ESP32 Hardware SPI Bus
SX1276 / RFM95W Pin
ESP32 DevKit Pin
Wire Color
Bus Signal / Function
Electrical Constraints & Notes
3.3V / VCC
3V3 Rail
Red
3.3V DC Power Input
Power only from 3.3V. Applying 5V will burn the SX1276 RF silicon!
GND
GND Rail
Black
Ground Reference
Common ground with ESP32 and battery negative.
SCK
GPIO 18
Yellow
SPI Hardware Clock
Primary SPI Clock line.
MISO
GPIO 19
Blue
SPI Master In Slave Out
Master receive line.
MOSI
GPIO 23
Green
SPI Master Out Slave In
Master transmit line.
NSS / CS
GPIO 5
Orange
SPI Chip Select (Active LOW)
Driven LOW during SPI register read/write.
RST / RESET
GPIO 14
Brown
Hardware Reset Line
Pulsed LOW by driver during initialization.
DIO0
GPIO 2
White
TX / RX Done Interrupt
Triggers when LoRa packet transmission finishes.
ANT
SMA Connector Center
Thick Lead
50Ω Coaxial RF Feed
MANDATORY: Screw on 868MHz antenna BEFORE powering up!
Table B: Sensors, Power Gating & Battery Voltage Divider
Component / Sub-circuit
Component Pin
ESP32 Pin
Wire Color
Purpose / Operation
Capacitive Soil Sensor
VCC
GPIO 25
Red
Switched Power Rail: Energized only for 60ms during wake-up.
Capacitive Soil Sensor
GND
GND Rail
Black
Common Ground.
Capacitive Soil Sensor
AOUT
GPIO 34
Blue
Analog Moisture Voltage into ADC1 Channel 6.
Battery Divider (Top 100kΩ)
Terminal 1
Battery (+3.7V–4.2V)
Red
Connects directly to Li-ion cell positive terminal.
Battery Divider (Midpoint)
Node
GPIO 35
Yellow
Halved voltage (0–2.1V) into ADC1 Channel 7.
Battery Divider (Bottom 100kΩ)
Terminal 2
GND Rail
Black
Ground return for divider.
2. Circuit Connection Schematic Diagram (ASCII Art)
+-------------------------------------------------------+
| ESP32 DevKit V1 |
| |
| [ 3V3 ] [ GND ] [GPIO 18] [GPIO 19] [GPIO 23] [G5] [G14] [G2]
+----+-------+---------+---------+---------+----+---+---+--+
| | | | | | | |+3.3V | GND | SCK | MISO | MOSI | NSS|RST|DIO0|
Rail | Rail | | | | | | |
=======+=======+=========+=========+=========+====+===+===+==+
| | | | | | | || | | | | | | |+--+-------+---------+---------+---------+----+---+---+--+
| VCC GND SCK MISO MOSI NSS RST DIO0|
| SX1276 / RFM95W LoRa MODULE | ( ( ( 868MHz| [ ANTENNA ]----------------------+==== Ant ) ) )
+-----------------------------------------------------+
| |
| | [GPIO 25] (Switched Power Gate)| | || | +-------------------+| | || +-------------+ || | || +--+-------------------+---+
| | GND VCC |
| | CAPACITIVE SOIL SENSOR |
| | [ AOUT ] || +------------+-------------+
| |
| v
| [GPIO 34] (ADC1)
|
| BATTERY DIVIDER
| Battery (+) ---> [100k] ---+---> [GPIO 35] (ADC1)
| |
+----------------- GND <--- [100k]
3. Golden Deployment Rules for Field Reliability
Antenna VSWR Protection:
Operating the SX1276 without an antenna causes extreme Voltage Standing Wave Ratio (VSWR), reflecting RF power straight back into the output transistor and permanently destroying the chip. Always attach an authentic 868MHz tuned antenna.
Sensor Power Gating:
Powering the capacitive sensor from GPIO 25 instead of 3V3 saves ~5mA during deep sleep, extending 18650 battery endurance from 3 weeks to over 14 months on a single charge.
ADC1 Only for Battery & Soil Sensing:
Both GPIO 34 and GPIO 35 reside on ADC1. This ensures full analog compatibility even if you later enable Wi-Fi or Bluetooth for local field servicing.
Complete Tested Arduino C++ Firmware
``cpp
/*
- ==============================================================================
- Project: LoRa Long-Range Agricultural Sensor Node (Deep-Sleep Enabled)
- Microcontroller: ESP32 DevKit V1 (Xtensa 32-bit Dual-Core)
- Author: Mentora Maker Lab (Spark Autonomous Content Intelligence)
- License: MIT
*
- Hardware Peripherals:
- - LoRa Transceiver: Semtech SX1276 / RFM95W (866.0 MHz India WPC ISM Band)
- SPI Interface: SCK: GPIO 18, MISO: GPIO 19, MOSI: GPIO 23, NSS: GPIO 5
- Control Pins : RESET: GPIO 14, DIO0: GPIO 2
- - Capacitive Soil Sensor: Signal -> GPIO 34 (ADC1 Channel 6)
- - Sensor Power Gating : VCC -> GPIO 25 (Switched output to save battery)
- - Battery Voltage Divider: Signal -> GPIO 35 (ADC1 Channel 7)
- ==============================================================================
*/
#include <SPI.h>
#include <LoRa.h>
// -----------------------------------------------------------------------------
// USER CONFIGURATION & RF CONSTANTS
// -----------------------------------------------------------------------------
// Legal License-Exempt Frequency in India: 865 - 867 MHz
#define LORAFREQUENCYHZ 866E6
// Hardware Pin Definitions for SX1276
#define LORANSSPIN 5
#define LORARSTPIN 14
#define LORADIO0PIN 2
// Peripheral Pins
#define SENSORPOWERPIN 25 // Drives sensor VCC only during sampling
#define SOILADCPIN 34 // ADC1 pin for soil moisture
#define BATTERYADCPIN 35 // ADC1 pin for 2:1 battery divider (0-4.2V -> 0-2.1V)
// Deep Sleep Parameters
#define SLEEPDURATIONSEC 900ULL // 15-minute sleep interval between transmissions
// Calibration Constants for Capacitive Soil Probe
const int ADCAIRDRY = 3200;
const int ADCWATERWET = 1450;
// RTC Persistent Memory across Deep Sleep cycles
RTCDATAATTR int packetCounter = 0;
// -----------------------------------------------------------------------------
// SENSOR SAMPLING WITH POWER GATING
// -----------------------------------------------------------------------------
int readGatedSoilMoisture() {
// 1. Energize Sensor Rail
digitalWrite(SENSORPOWERPIN, HIGH);
delay(60); // Allow sensor oscillator circuit to stabilize
// 2. Sample ADC with multi-sample noise averaging
long sum = 0;
for (int i = 0; i < 16; i++) {
sum += analogRead(SOILADCPIN);
delayMicroseconds(200);
}
int rawADC = sum / 16;
// 3. De-energize Sensor Rail immediately to conserve battery
digitalWrite(SENSORPOWERPIN, LOW);
// Map to percentage
int clamped = constrain(rawADC, ADCWATERWET, ADCAIRDRY);
int percent = map(clamped, ADCAIRDRY, ADCWATERWET, 0, 100);
return constrain(percent, 0, 100);
}
float readBatteryVoltage() {
// Read voltage divider: 100k + 100k divider halves the battery voltage
int rawADC = analogRead(BATTERYADCPIN);
float pinVoltage = (rawADC / 4095.0) * 3.3;
float batteryVoltage = pinVoltage * 2.0; // Account for 1:2 divider
return batteryVoltage;
}
// -----------------------------------------------------------------------------
// LORA INITIALIZATION & PACKET DISPATCH
// -----------------------------------------------------------------------------
bool initializeAndSendLoRaPacket(int moisturePercent, float batteryVolts) {
// Configure SPI pins
LoRa.setPins(LORANSSPIN, LORARSTPIN, LORADIO0PIN);
Serial.println("[LORA] Initializing SX1276 at 866.0 MHz...");
if (!LoRa.begin(LORAFREQUENCYHZ)) {
Serial.println("[ERROR] Starting LoRa failed! Check wiring and antenna connection.");
return false;
}
// RF Configuration for Robust Farm Coverage
LoRa.setSpreadingFactor(8); // SF8 balances airtime and sensitivity (-127 dBm)
LoRa.setSignalBandwidth(125E3); // 125 kHz bandwidth
LoRa.setCodingRate4(5); // 4/5 coding rate
LoRa.setSyncWord(0xF3); // Private Mentora network sync byte
LoRa.enableCrc(); // Hardware cyclic redundancy check
LoRa.setTxPower(17); // 17 dBm transmit power (~50mW)
// Assemble Telemetry Payload (CSV format for easy gateway parsing)
String payload = "NODE:01,PKT:" + String(packetCounter);
payload += ",SOIL:" + String(moisturePercent);
payload += ",BAT:" + String(batteryVolts, 2);
Serial.printf("[LORA] Broadcasting packet #%d: %s\n", packetCounter, payload.c_str());
// Transmit packet
LoRa.beginPacket();
LoRa.print(payload);
LoRa.endPacket();
// Put radio into sleep mode to save energy
LoRa.sleep();
Serial.println("[LORA] Packet dispatched. Radio placed into ultra-low-power sleep.");
return true;
}
// -----------------------------------------------------------------------------
// ARDUINO SETUP (WAKE -> SAMPLE -> TX -> DEEP SLEEP)
// -----------------------------------------------------------------------------
void setup() {
Serial.begin(115200);
delay(100);
packetCounter++;
Serial.println("\n========================================================");
Serial.println(" MENTORA ESP32 LORA AGRICULTURAL TELEMETRY NODE (V1.2) ");
Serial.printf(" Wake Cycle: #%d | Indian 866 MHz ISM Band\n", packetCounter);
Serial.println("========================================================");
// Configure Pins
pinMode(SENSORPOWERPIN, OUTPUT);
digitalWrite(SENSORPOWERPIN, LOW); // Start with sensor powered OFF
pinMode(SOILADCPIN, INPUT);
pinMode(BATTERYADCPIN, INPUT);
analogReadResolution(12);
analogSetAttenuation(ADC_11db);
// 1. Acquire telemetry
int moisture = readGatedSoilMoisture();
float battery = readBatteryVoltage();
Serial.printf("[SENSOR] Soil Moisture: %d%% | Battery: %.2fV\n", moisture, battery);
// 2. Transmit via LoRa
initializeAndSendLoRaPacket(moisture, battery);
// 3. Configure and Enter Deep Sleep
Serial.printf("[SYSTEM] Entering deep sleep for %llu seconds...\n\n", SLEEPDURATIONSEC);
espsleepenabletimerwakeup(SLEEPDURATIONSEC * 1000000ULL);
// Power down peripherals and enter deep sleep
espdeepsleep_start();
}
// Loop is never reached because ESP32 reboots from setup() upon timer wake-up
void loop() {
}
``
Build a Solar-Powered LoRa Long-Range Agriculture Sensor Node with ESP32 & SX1276 Firmware & Schematics Vault
Download verified Arduino C++ firmware, pinout table, and BOM for Build a Solar-Powered LoRa Long-Range Agriculture Sensor Node with ESP32 & SX1276.
Free Tutorial vs. Project Vault: The tutorial and educational theory above are 100% free. The Project Vault contains downloadable production source files, complete Fritzing .fzz circuits, laser vector files, and 1-on-1 mentor debugging sessions.
#include<Adafruit_Sensor.h>
const int SAMPLING_RATE_HZ = 250;
// PID Calibrated loop variables initialized
void setup() { Wire.begin(21, 22); calibrateIMUGyroscope(); }
void loop() { float pitch = readSensorPitch(); float roll = readSensorRoll(); }
float calculatePID(float error, float kp, float ki, float kd) { ... }
void transmitTelemetryPackage(uint8_t *packet, size_t length) { ... }
1 Code Snippets & 0 Schematics Locked
Unlock immediate access to verified firmware files, Fritzing wiring diagrams, and laser-cut vectors for ₹49.
Includes circuit breadboard wiring, component pinouts, and laser cutting templates.
Screen-share or show your breadboard to M. F. Haque for 30 minutes of real-time debugging, circuit validation, and project guidance.
See What Mentora Makers Are Building
Real video walk-throughs, breadboard tests, and flight demonstrations submitted by students & parents.
Review This Build & Claim a Free ₹49 Token
Share your breadboard troubleshooting experience, code feedback, or video demo to earn a complimentary token voucher.
Have a Reward Voucher?
Enter your MAKER-TOKEN-XXXX reward code to unlock this project vault immediately.
Earn ₹50 Per Friend You Refer
Get your custom referral link. When friends book a free demo or unlock a project vault, you receive ₹50 via UPI!
Why does LoRa.begin() return 0 or fail during startup?
Root Cause 1: SPI Pin Mapping Mismatch. The SX1276 uses hardware SPI. Verify NSS, RST, and DIO0 assignments: LoRa.setPins(5, 14, 2); // NSS = GPIO 5, RESET = GPIO 14, DIO0 = GPIO 2 Root Cause 2: 3.3V Power Rail Sag. The SX1276 draws up to 120mA during TX. Power it from the ESP32's clean 3.3V regulator with a 100µF decoupling capacitor across VCC and GND.
Why does the receiver miss packets or report CRC errors?
Root Cause: Parameter mismatch. Both transmitter and receiver must have identical frequency, spreading factor, coding rate, bandwidth, and sync word. LoRa.setSyncWord(0xF3); // Private network sync word LoRa.enableCrc(); // Cyclic redundancy check
Why does the ESP32 fail to wake up from deep sleep?
Root Cause: Incorrect RTC timer configuration. Remember that esp_sleep_enable_timer_wakeup() takes time in microseconds ($\mu\text{s}$), not milliseconds: const uint64_t SLEEP_DURATION_SEC = 900; // 15 minutes esp_sleep_enable_timer_wakeup(SLEEP_DURATION_SEC * 1000000ULL);
Learn this in our Junior Tech: Game Design & Scratch
Master computational thinking, sprite animations, coordinate systems, and interactive 2D arcade games using drag-and-drop logic blocks.