IoT for Agriculture: Building a Smart Farm Monitoring System
How I designed an IoT system for smallholder farmers — soil sensors, weather alerts, automated irrigation, and a mobile dashboard using ESP32, MQTT, and Python.
IoT for Agriculture: Building a Smart Farm Monitoring System
Soil sensors, weather data, irrigation control, and crop analytics — how I designed a complete IoT system for smallholder farmers using affordable hardware and open-source software.
Why Agriculture Needs IoT
A smallholder farmer who knows the soil moisture level in each plot can irrigate precisely rather than wastefully. Real-time weather data triggers frost alerts that save crops. Automated irrigation runs at the right time rather than on a fixed schedule that ignores actual conditions.
The hardware is now cheap enough that cost is no longer the barrier. A complete field monitoring node costs under $15. The barrier is software expertise to build reliable, maintainable systems.
Sensor Selection for Agriculture
Soil moisture: Capacitive sensors (not resistive — they corrode). The STEMMA Soil Sensor (I2C) is reliable for shallow monitoring. For deeper soil, tensiometers measure actual soil water tension.
Soil temperature: DS18B20 waterproof probe (1-Wire protocol, excellent for burial in soil).
NPK sensors: RS-485 Modbus NPK sensors give actual nitrogen, phosphorus, and potassium readings. They require a MAX485 RS-485 to TTL adapter to connect to the ESP32.
Ambient: BME280 (I2C) gives temperature, humidity, and atmospheric pressure.
Irrigation control: 12V solenoid valves controlled by a relay module. YF-S201 flow sensor measures actual water usage.
Irrigation Decision Logic
MOISTURE_LOW = 35 # irrigate below this %
MOISTURE_HIGH = 65 # stop irrigation at this %
MAX_DAILY_MIN = 60 # cap daily irrigation at 60 minutesclass IrrigationController:
def evaluate(self, field_id, reading):
moisture = reading'soil_moisture']
if moisture < MOISTURE_LOW and self.can_irrigate_today(field_id):
duration = self.calculate_duration(moisture)
self.start_irrigation(field_id, duration)
def calculate_duration(self, moisture):
deficit = MOISTURE_HIGH - moisture # how far below target