Back to IoT Blog
Agriculture IoT 28 min read

Automated Greenhouse Climate Control

Build a complete greenhouse automation system. Monitor temperature, humidity, CO2, soil moisture, and automatically control vents, fans, irrigation, and grow lights for optimal plant growth.

System Overview

Greenhouse automation maintains optimal growing conditions 24/7. By monitoring environmental parameters and automating climate control equipment, you can increase yields by 20-40% while reducing water and energy consumption.

Controlled Parameters:
  • Temperature: 18-28°C for most crops
  • Humidity: 50-70% RH
  • CO2: 400-1000 ppm (enrichment up to 1500 ppm)
  • Soil Moisture: Crop-specific thresholds
  • Light: Duration and intensity control
  • pH/EC: Nutrient solution monitoring

System Architecture

[Sensors] → [ESP32 Controller] → [WiFi/LoRa] → [Home Assistant]
     ↓              ↓                    ↓              ↓
DHT22/BME280   Relay Module        MQTT Broker    Dashboard
Soil Sensors   Motor Drivers                      Mobile App
CO2 Sensor     Water Valves                       Alerts

Hardware Components

Controller

Sensors

Actuators

Sensor Network

Deploy sensors strategically throughout the greenhouse:

Sensor Placement

Wiring Diagram

ESP32
│
├── BME280 (I2C)
│   ├── VCC → 3.3V
│   ├── GND → GND
│   ├── SDA → GPIO21
│   └── SCL → GPIO22
│
├── Soil Moisture x4 (Analog)
│   ├── VCC → 3.3V
│   ├── GND → GND
│   └── SIG → GPIO34,35,32,33
│
├── Relay Module
│   ├── VCC → 5V
│   ├── GND → GND
│   └── IN1-4 → GPIO25,26,27,14
│
└── Optional: LoRa Module
    ├── VCC → 3.3V
    ├── GND → GND
    ├── NSS → GPIO5
    ├── MOSI → GPIO23
    ├── MISO → GPIO19
    └── SCK → GPIO18

Control System

Complete ESP32 firmware for greenhouse automation:

#include <WiFi.h>
#include <PubSubClient.h>
#include <Adafruit_BME280.h>
#include <ArduinoJson.h>

// WiFi & MQTT
const char* ssid = "GREENHOUSE_WIFI";
const char* password = "your_password";
const char* mqtt_server = "192.168.1.100";

// Sensor pins
#define SOIL_MOISTURE_1 34
#define SOIL_MOISTURE_2 35
#define SOIL_MOISTURE_3 32
#define SOIL_MOISTURE_4 33

// Relay pins
#define RELAY_FAN 25
#define RELAY_PUMP 26
#define RELAY_LIGHTS 27
#define RELAY_HEATER 14

// Setpoints
float TEMP_HIGH = 28.0;
float TEMP_LOW = 18.0;
float HUMIDITY_HIGH = 70.0;
float SOIL_MOISTURE_LOW = 30.0; // Percentage

Adafruit_BME280 bme;
WiFiClient espClient;
PubSubClient client(espClient);

void setup() {
  Serial.begin(115200);
  
  // Initialize BME280
  if (!bme.begin(0x76)) {
    Serial.println("BME280 not found!");
  }
  
  // Initialize relay pins
  pinMode(RELAY_FAN, OUTPUT);
  pinMode(RELAY_PUMP, OUTPUT);
  pinMode(RELAY_LIGHTS, OUTPUT);
  pinMode(RELAY_HEATER, OUTPUT);
  
  // All relays OFF (HIGH for active-low modules)
  digitalWrite(RELAY_FAN, HIGH);
  digitalWrite(RELAY_PUMP, HIGH);
  digitalWrite(RELAY_LIGHTS, HIGH);
  digitalWrite(RELAY_HEATER, HIGH);
  
  // WiFi & MQTT setup
  setupWiFi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
}

void setupWiFi() {
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("WiFi connected");
}

void reconnect() {
  while (!client.connected()) {
    if (client.connect("GreenhouseController")) {
      client.subscribe("greenhouse/setpoints");
    } else {
      delay(5000);
    }
  }
}

void callback(char* topic, byte* payload, unsigned int length) {
  // Handle setpoint updates
  StaticJsonDocument<256> doc;
  deserializeJson(doc, payload, length);
  
  if (doc.containsKey("temp_high")) {
    TEMP_HIGH = doc["temp_high"];
  }
  if (doc.containsKey("temp_low")) {
    TEMP_LOW = doc["temp_low"];
  }
}

void controlClimate(float temp, float humidity, float soilMoisture) {
  // Temperature control
  if (temp > TEMP_HIGH) {
    digitalWrite(RELAY_FAN, LOW); // Turn ON extraction fan
  } else if (temp < TEMP_LOW) {
    digitalWrite(RELAY_HEATER, LOW); // Turn ON heater
  } else {
    digitalWrite(RELAY_FAN, HIGH);
    digitalWrite(RELAY_HEATER, HIGH);
  }
  
  // Humidity control
  if (humidity > HUMIDITY_HIGH) {
    digitalWrite(RELAY_FAN, LOW); // Ventilate to reduce humidity
  }
  
  // Irrigation control
  if (soilMoisture < SOIL_MOISTURE_LOW) {
    digitalWrite(RELAY_PUMP, LOW); // Turn ON water pump
    delay(5000); // Water for 5 seconds
    digitalWrite(RELAY_PUMP, HIGH);
  }
}

void loop() {
  if (!client.connected()) reconnect();
  client.loop();
  
  // Read sensors
  float temp = bme.readTemperature();
  float humidity = bme.readHumidity();
  float pressure = bme.readPressure() / 100.0;
  int soil1 = analogRead(SOIL_MOISTURE_1);
  int soil2 = analogRead(SOIL_MOISTURE_2);
  
  // Convert soil moisture to percentage (calibrate for your sensor)
  float soilPercent = map(soil1, 4095, 1500, 0, 100);
  soilPercent = constrain(soilPercent, 0, 100);
  
  // Control climate
  controlClimate(temp, humidity, soilPercent);
  
  // Publish to MQTT
  StaticJsonDocument<512> doc;
  doc["temperature"] = temp;
  doc["humidity"] = humidity;
  doc["pressure"] = pressure;
  doc["soil_moisture"] = soilPercent;
  doc["fan_state"] = (digitalRead(RELAY_FAN) == LOW);
  doc["pump_state"] = (digitalRead(RELAY_PUMP) == LOW);
  doc["timestamp"] = millis();
  
  char jsonBuffer[512];
  serializeJson(doc, jsonBuffer);
  client.publish("greenhouse/climate", jsonBuffer);
  
  delay(10000); // Read every 10 seconds
}

Automation Logic

Temperature Control Strategy

Condition Action Priority
Temp > 32°C Open roof vents + extraction fan High
Temp > 28°C Open roof vents only Medium
Temp < 15°C Close vents + activate heater High
15°C < Temp < 18°C Close vents, no heating Low

Irrigation Schedule

Morning (6:00 AM):
  - Check soil moisture
  - If < 40%, irrigate for 10 minutes
  
Midday (12:00 PM):
  - Check soil moisture
  - If < 30%, irrigate for 5 minutes
  
Evening (6:00 PM):
  - Check soil moisture
  - If < 35%, irrigate for 8 minutes
  
Night:
  - No irrigation (prevent fungal growth)
Seasonal Adjustments:
  • Summer: Increase ventilation, add shade cloth, more frequent irrigation
  • Winter: Reduce ventilation, activate heating, less irrigation
  • Spring/Fall: Moderate settings, transition gradually

Mobile App Integration

Use Home Assistant for dashboard and mobile control:

Home Assistant Configuration

# configuration.yaml
mqtt:
  sensor:
    - name: "Greenhouse Temperature"
      state_topic: "greenhouse/climate"
      value_template: "{{ value_json.temperature }}"
      unit_of_measurement: "°C"
    
    - name: "Greenhouse Humidity"
      state_topic: "greenhouse/climate"
      value_template: "{{ value_json.humidity }}"
      unit_of_measurement: "%"
    
    - name: "Greenhouse Soil Moisture"
      state_topic: "greenhouse/climate"
      value_template: "{{ value_json.soil_moisture }}"
      unit_of_measurement: "%"
  
  switch:
    - name: "Greenhouse Fan"
      state_topic: "greenhouse/climate"
      command_topic: "greenhouse/fan/command"
      value_template: "{{ value_json.fan_state }}"
    
    - name: "Greenhouse Pump"
      state_topic: "greenhouse/climate"
      command_topic: "greenhouse/pump/command"
      value_template: "{{ value_json.pump_state }}"

# Automations
automation:
  - alias: "High Temperature Alert"
    trigger:
      platform: numeric_state
      entity_id: sensor.greenhouse_temperature
      above: 30
    action:
      service: notify.mobile_app
      data:
        message: "Greenhouse temperature is high!"

Optimization Tips

Energy Efficiency

Crop-Specific Settings

Crop Temp Range Humidity Light Hours
Tomatoes 21-27°C 60-70% 14-16h
Lettuce 16-22°C 50-60% 10-12h
Peppers 24-29°C 60-70% 14-16h
Cucumbers 22-28°C 70-80% 12-14h

Next Steps

Expand your greenhouse automation:

  • Add nutrient dosing system for hydroponics
  • Implement computer vision for plant health monitoring
  • Integrate weather forecasts for predictive control
  • Add automated harvesting system
  • Connect to market pricing for optimal harvest timing