OC
OceanRemote
Low-code IoT platform
โ† Back to Course

Complete Solar-Powered Soil Sensor

Complete Solar-Powered Soil Sensor

โ˜€๏ธ Complete Solar-Powered Soil Sensor Station

๐ŸŒฑ What You'll Learn in This Lesson:

  • Build a completely off-grid, solar-powered soil sensor
  • Monitor soil moisture AND battery voltage remotely
  • Optimize power consumption for months of operation
  • Track how many times your sensor has reported data
  • Deploy in remote fields without power access

๐ŸŒ Why Solar-Powered IoT for African Farms?

Many farms in Africa lack reliable grid electricity. Solar-powered sensors solve this problem completely!

Challenge Solar Solution
๐Ÿ”Œ No grid electricity Solar panel + battery = unlimited power
๐Ÿ’ฐ High battery replacement cost Solar charges battery, lasts years
๐Ÿ“ก Remote location access Deploy anywhere with GSM/WiFi coverage
๐Ÿ”‹ Battery health unknown Monitor voltage remotely to prevent failure

๐Ÿ› ๏ธ Complete Components List

Component Cost Purpose
ESP32 (ESP32-S2 or S3 for lower power) $6-10 Main controller
Capacitive Soil Moisture Sensor $8-12 Measures soil water content
6V Solar Panel (2W minimum) $10-15 Generates power from sun
18650 Li-ion Battery (2000-3000mAh) $5-8 Stores energy for night/cloudy days
TP4056 Battery Charging Module $2-3 Manages solar charging
Voltage Divider (2x 100kฮฉ resistors) $0.20 Measures battery voltage safely

Total cost: ~$35-50 for a complete solar-powered sensor station!

๐Ÿ’ก Where to Buy (Shipping to Africa):

AliExpress (2-4 weeks, cheapest), Jumia (faster, local), or local electronics shops in major cities.

๐Ÿ”Œ Complete Wiring Diagram

โš ๏ธ IMPORTANT SAFETY NOTE:

Never connect solar panel directly to ESP32! Always use a battery and charging module (TP4056).

โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—
โ•‘                         SOLAR-POWERED SENSOR STATION                       โ•‘
โ• โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ฃ
โ•‘                                                                           โ•‘
โ•‘   โ˜€๏ธ SOLAR PANEL (6V)                                                     โ•‘
โ•‘         โ”‚                                                                 โ•‘
โ•‘         โ”œโ”€ Positive (+) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ TP4056 IN+ (B+ on module)            โ•‘
โ•‘         โ””โ”€ Negative (-) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ TP4056 IN- (B- on module)            โ•‘
โ•‘                                                                           โ•‘
โ•‘   ๐Ÿ”‹ TP4056 CHARGING MODULE                                               โ•‘
โ•‘         โ”‚                                                                 โ•‘
โ•‘         โ”œโ”€ BAT+ (18650 positive) โ”€โ”€โ–บ 18650 Battery (+)                   โ•‘
โ•‘         โ”œโ”€ BAT- (18650 negative) โ”€โ”€โ–บ 18650 Battery (-)                   โ•‘
โ•‘         โ”‚                                                                 โ•‘
โ•‘         โ”œโ”€ OUT+ (4.2V regulated) โ”€โ”€โ–บ ESP32 VIN pin                       โ•‘
โ•‘         โ””โ”€ OUT- (GND) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ ESP32 GND pin                      โ•‘
โ•‘                                                                           โ•‘
โ•‘   ๐Ÿง  ESP32                                                                โ•‘
โ•‘         โ”‚                                                                 โ•‘
โ•‘         โ”œโ”€ GPIO32 (ADC) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Soil Moisture Sensor AO              โ•‘
โ•‘         โ”œโ”€ 3.3V โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Soil Moisture Sensor VCC             โ•‘
โ•‘         โ”‚                                                                 โ•‘
โ•‘         โ”œโ”€ GPIO34 (ADC) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Voltage Divider Center (Battery)     โ•‘
โ•‘         โ”‚                                                                 โ•‘
โ•‘         โ””โ”€ GND โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ All sensor grounds                   โ•‘
โ•‘                                                                           โ•‘
โ•‘   ๐Ÿ”‹ VOLTAGE DIVIDER (for battery monitoring)                            โ•‘
โ•‘         4.2V (Battery+)                                                  โ•‘
โ•‘           โ”‚                                                              โ•‘
โ•‘           โ”œโ”€ 100kฮฉ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ–บ GPIO34 (ADC)                       โ•‘
โ•‘           โ”‚                      โ”‚                                       โ•‘
โ•‘           โ””โ”€ 100kฮฉ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ–บ GND                                โ•‘
โ•‘                                                                           โ•‘
โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

๐Ÿ“– Complete Code with Line-by-Line Explanation

/*
 * โ˜€๏ธ Complete Solar-Powered Soil Sensor Station
 * 
 * Monitors soil moisture and battery voltage
 * Sleeps for 15 minutes between readings
 * Runs for months on a single charge (solar recharges!)
 * 
 * Perfect for remote fields, greenhouses, and off-grid farms
 * 
 * Author: OceanRemote Education
 * Version: 1.0
 */

#include        // For network connectivity
#include  // For sending data to cloud

// ========== WIFI CONFIGURATION ==========
const char* ssid = "YOUR_WIFI";          // Replace with your WiFi name
const char* password = "YOUR_PASSWORD";  // Replace with your WiFi password

// ========== OCEANREMOTE CONFIGURATION ==========
const char* token = "YOUR_OCEANREMOTE_TOKEN";  // Get this from OceanRemote dashboard

// ========== PIN DEFINITIONS ==========
#define SOIL_PIN 32       // GPIO32 - Soil moisture sensor (ADC)
#define BATTERY_PIN 34    // GPIO34 - Battery voltage monitoring (ADC)

// ========== SOIL MOISTURE CALIBRATION ==========
const int DRY_VALUE = 3800;   // Reading when sensor is in dry air
const int WET_VALUE = 1500;   // Reading when sensor is completely submerged in water

// ========== VOLTAGE DIVIDER SETUP ==========
// Using 100kฮฉ + 100kฮฉ divider = 2x reduction
// Actual battery voltage = ADC reading ร— 2
const float VOLTAGE_DIVIDER_RATIO = 2.0;

// ========== RTC DATA (Survives deep sleep) ==========
// This variable keeps its value even when ESP32 sleeps
RTC_DATA_ATTR int bootCount = 0;

void setup() {
    // ---------- STEP 1: INITIALIZE ----------
    Serial.begin(115200);
    bootCount++;  // Increment boot counter (tracks how many readings)
    
    Serial.println("========================================");
    Serial.print("๐ŸŒฑ Solar Soil Sensor v1.0 - Boot #");
    Serial.println(bootCount);
    Serial.println("========================================");
    
    // ---------- STEP 2: READ SOIL MOISTURE ----------
    int raw = analogRead(SOIL_PIN);
    Serial.print("๐Ÿ“Š Raw soil reading: ");
    Serial.println(raw);
    
    // Convert raw ADC value (0-4095) to percentage (0-100%)
    int moisture = map(raw, DRY_VALUE, WET_VALUE, 0, 100);
    
    // Keep value within 0-100% range
    moisture = constrain(moisture, 0, 100);
    
    Serial.print("๐Ÿ’ง Soil moisture: ");
    Serial.print(moisture);
    Serial.println("%");
    
    // ---------- STEP 3: READ BATTERY VOLTAGE ----------
    int batteryRaw = analogRead(BATTERY_PIN);
    Serial.print("๐Ÿ”‹ Raw battery reading: ");
    Serial.println(batteryRaw);
    
    // Convert ADC reading to voltage (3.3V reference / 4095 steps)
    float voltage = batteryRaw * (3.3 / 4095.0);
    
    // Apply voltage divider factor (2.0x) to get actual battery voltage
    float batteryVoltage = voltage * VOLTAGE_DIVIDER_RATIO;
    
    Serial.print("๐Ÿ”‹ Battery voltage: ");
    Serial.print(batteryVoltage);
    Serial.println("V");
    
    // ---------- STEP 4: BATTERY HEALTH CHECK ----------
    if (batteryVoltage < 3.3) {
        Serial.println("โš ๏ธ BATTERY LOW! Voltage below 3.3V - Replace or recharge soon!");
    } else if (batteryVoltage < 3.5) {
        Serial.println("๐ŸŸก Battery medium - Consider recharging soon");
    } else if (batteryVoltage > 4.1) {
        Serial.println("โœ… Battery fully charged!");
    } else {
        Serial.println("โœ… Battery healthy");
    }
    
    // ---------- STEP 5: IRRIGATION RECOMMENDATION ----------
    if (moisture < 30) {
        Serial.println("โš ๏ธ SOIL DRY! Time to water your crops!");
    } else if (moisture < 50) {
        Serial.println("๐ŸŸก Soil is drying - Monitor closely");
    } else if (moisture > 80) {
        Serial.println("โœ… Soil wet - No watering needed");
    } else {
        Serial.println("โœ… Soil moisture adequate");
    }
    
    // ---------- STEP 6: CONNECT TO WIFI (with timeout) ----------
    Serial.print("๐Ÿ“ก Connecting to WiFi");
    WiFi.begin(ssid, password);
    
    int attempts = 0;
    while (WiFi.status() != WL_CONNECTED && attempts < 20) {
        delay(200);
        Serial.print(".");
        attempts++;
    }
    
    if (WiFi.status() == WL_CONNECTED) {
        Serial.println("\nโœ… WiFi connected!");
        Serial.print("๐Ÿ“ถ IP address: ");
        Serial.println(WiFi.localIP());
        
        // ---------- STEP 7: SEND DATA TO OCEANREMOTE ----------
        HTTPClient http;
        http.begin("https://api.oceanremote.net/device/state");
        http.addHeader("Content-Type", "application/x-www-form-urlencoded");
        
        // Build the data string
        String data = "token=" + String(token);
        data += "&soil_moisture=" + String(moisture);
        data += "&battery_voltage=" + String(batteryVoltage);
        data += "&boot_count=" + String(bootCount);
        
        // Optional: Add irrigation recommendation
        if (moisture < 30) {
            data += "&status=needs_water";
        } else {
            data += "&status=ok";
        }
        
        Serial.println("๐Ÿ“ค Sending data to OceanRemote...");
        Serial.println(data);
        
        int httpCode = http.POST(data);
        
        if (httpCode > 0) {
            Serial.print("โœ… Data sent! HTTP response: ");
            Serial.println(httpCode);
        } else {
            Serial.print("โŒ Failed to send data. Error: ");
            Serial.println(httpCode);
        }
        
        http.end();  // Close connection
        
        // Disconnect WiFi to save power
        WiFi.disconnect(true);
        WiFi.mode(WIFI_OFF);
        Serial.println("๐Ÿ“ก WiFi disconnected to save power");
        
    } else {
        Serial.println("\nโŒ WiFi connection failed! Skipping data send.");
        Serial.println("   (Will retry on next wake-up)");
    }
    
    // ---------- STEP 8: CALCULATE NEXT WAKE-UP TIME ----------
    // Get current time for logging
    unsigned long currentTime = millis() / 1000;  // seconds since boot
    
    Serial.println("========================================");
    Serial.println("๐Ÿ’ค Entering deep sleep mode...");
    Serial.print("   This sensor will wake up in 15 minutes (");
    Serial.print(15 * 60);
    Serial.println(" seconds)");
    Serial.println("========================================\n");
    
    // ---------- STEP 9: DEEP SLEEP (15 MINUTES) ----------
    // Sleep for 15 minutes (15 ร— 60 ร— 1,000,000 microseconds)
    esp_sleep_enable_timer_wakeup(15 * 60 * 1000000ULL);
    
    // Go to sleep
    esp_deep_sleep_start();
}

void loop() {
    // This function never runs due to deep sleep
    // The ESP32 wakes up, runs setup(), then sleeps again
}
    

๐Ÿ“Š Code Breakdown: Understanding Each Critical Section

๐Ÿ’พ 1. RTC_DATA_ATTR - Memory That Survives Sleep

RTC_DATA_ATTR int bootCount = 0;
  • What it does: Stores variables in RTC (Real-Time Clock) memory
  • Why important: Regular variables reset when ESP32 sleeps. RTC variables keep their value!
  • Use case: Track how many times your sensor has reported data over days/weeks

๐Ÿ”‹ 2. Battery Voltage Measurement with Voltage Divider

int batteryRaw = analogRead(BATTERY_PIN);
float voltage = batteryRaw * (3.3 / 4095.0);
float batteryVoltage = voltage * 2.0;  // Because 100kฮฉ + 100kฮฉ divider
  • Why voltage divider? ESP32 ADC can only read 0-3.3V. Battery is 3.7-4.2V (too high).
  • How it works: Two 100kฮฉ resistors cut voltage in half, making it safe for ESP32
  • Formula: Actual voltage = ADC reading ร— 2.0
  • Battery levels: 4.2V = full, 3.7V = nominal, 3.3V = low battery warning

๐Ÿ“ก 3. WiFi Connection with Timeout (Prevents Lockups)

int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
    delay(200);
    attempts++;
}
  • Standard WiFi.begin() can loop forever if WiFi is down
  • With timeout: Tries for 4 seconds (20 attempts ร— 200ms), then gives up
  • Result: Sensor still sleeps even if WiFi fails. Retries next time!

๐Ÿ’ค 4. Power Optimization - WiFi Disconnect

WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
  • WiFi.disconnect(true) - Disconnects from network, frees memory
  • WiFi.mode(WIFI_OFF) - Completely turns off WiFi radio
  • Power saving: Reduces current from ~70mA to ~10mA before deep sleep

๐Ÿ“ˆ Battery Life & Solar Sizing Guide

Power Consumption Calculation

State Current Duration Energy per Cycle
Sensor Reading 10mA 1 second 0.0028mAh
WiFi Connecting 120mA 4 seconds 0.133mAh
Data Transmission 150mA 2 seconds 0.083mAh
Deep Sleep 0.0025mA 14 minutes 53 sec 0.0006mAh
Total per 15 minutes - - ~0.22mAh
๐Ÿ”‹ Battery Life Calculation:
  • Daily consumption: 0.22mAh ร— 96 cycles = 21.1mAh per day
  • 2000mAh battery: 2000 รท 21.1 = 94 DAYS without solar!
  • With 2W solar panel: Unlimited runtime (recharges faster than consumption)

โ˜€๏ธ Solar Panel Sizing Formula

Solar Panel Wattage = (Daily Consumption) รท (Sun Hours) ร— 1.5

For our sensor:
Daily Consumption: 0.022Ah ร— 3.7V = 0.081Wh
Sun Hours (Africa average): 5 hours
Required Panel: 0.081Wh รท 5h ร— 1.5 = 0.024W

Recommend: 2W panel (80x overkill for cloudy days!)
๐Ÿ’ก Minimum Recommendation:

Use a 6V 2W solar panel - works in cloudy conditions, charges battery fully, and costs only $10-15!

๐Ÿ“– Real-World Deployment - Kenyan Tomato Farmer:

A smallholder farmer deployed 3 solar-powered sensors across 5 acres:

  • ๐Ÿ’ง 40% water savings (sensors triggered irrigation only when needed)
  • ๐Ÿ“ˆ 35% yield increase (consistent soil moisture)
  • ๐Ÿ’ฐ $80 saved per month on water and electricity
  • ๐Ÿ”‹ Zero battery changes in 4 months (solar recharge only)

"I check my soil moisture from my phone while in town. It's changed everything!" - Farmer, Kenya

๐ŸŽฏ Customization Options

Adjust Reading Interval

// 15 minutes (current)
esp_sleep_enable_timer_wakeup(15 * 60 * 1000000ULL);

// 1 hour (more battery saving)
esp_sleep_enable_timer_wakeup(60 * 60 * 1000000ULL);

// 5 minutes (more frequent updates)
esp_sleep_enable_timer_wakeup(5 * 60 * 1000000ULL);

Customize Soil Moisture Thresholds by Crop

// Tomatoes: Water below 50%
const int WATER_THRESHOLD = 50;

// Maize/Corn: Water below 40%
const int WATER_THRESHOLD = 40;

// Cacti/Succulents: Water below 20%
const int WATER_THRESHOLD = 20;

Add Multiple Sensors (Expandable)

#define SOIL_PIN_1 32
#define SOIL_PIN_2 33
#define SOIL_PIN_3 34

int moisture1 = map(analogRead(SOIL_PIN_1), DRY_VALUE, WET_VALUE, 0, 100);
int moisture2 = map(analogRead(SOIL_PIN_2), DRY_VALUE, WET_VALUE, 0, 100);
int moisture3 = map(analogRead(SOIL_PIN_3), DRY_VALUE, WET_VALUE, 0, 100);
โš ๏ธ Common Issues & Troubleshooting:
  • โŒ ESP32 won't wake from deep sleep: Connect GPIO16 to RST pin!
  • โŒ Battery voltage reads incorrectly: Calibrate voltage divider with multimeter
  • โŒ Solar panel not charging: TP4056 needs 5V minimum; 6V panel works great
  • โŒ WiFi won't connect: ESP32 only supports 2.4GHz (not 5GHz)
  • โŒ Soil readings inaccurate: Re-calibrate DRY_VALUE/WET_VALUE for YOUR sensor
  • โŒ Battery drains too fast: Check if ESP32 is sleeping (measure current with multimeter)

๐Ÿš€ Next Steps: Solar Sensor Enhancements

  • Add temperature sensor (DS18B20): Monitor soil temperature for planting decisions
  • Add LoRa module: Send data 10km+ without WiFi (perfect for remote fields)
  • Add cellular module (SIM800L): Send data via SMS if no WiFi
  • Add external antenna: Improve WiFi range to cover entire farm
  • Deploy multiple sensors: Create a mesh network across your fields
  • Add SD card logging: Backup data locally in case of network failure
๐ŸŽ‰ Congratulations!

You've built a complete, solar-powered soil sensor station! This system can run for years in remote fields with zero maintenance.

๐Ÿ“Š Monitor your farm's soil moisture and battery levels from anywhere using OceanRemote dashboard.

๐ŸŒ You're now part of the precision agriculture revolution - saving water, increasing yields, and protecting the environment!

๐Ÿ’ก Key Takeaways:
  • Apply these concepts directly to your farm or project.
  • Take notes on important details for the quiz.
  • Use the button below to track your progress.