Practical case: greenhouse vent with Arduino UNO

Practical case: greenhouse vent with Arduino UNO — hero

Objective and use case

What you’ll build: You will build a standalone automated greenhouse vent controller that dynamically adjusts a physical ventilation flap via a servo motor based on ambient temperature. The system incorporates a manual hardware override and hysteresis logic to prevent mechanical oscillation.

Why it matters / Use cases

  • Agricultural Automation: Regulate small-scale greenhouses autonomously, eliminating the need for constant human monitoring to prevent plant stress.
  • Hysteresis Implementation: Utilize distinct upper and lower temperature thresholds (e.g., open flap at 26°C, close at 22°C) to stabilize the system and prevent servo wear from rapid toggling.
  • Safety Overrides: Provide a hardware-based manual bypass (via limit switch) for immediate control during emergencies or routine maintenance.

Expected outcome

  • A closed-loop system that drives a servo from 0° (closed) to 90° (open) with <50ms response latency when temperature thresholds are crossed.
  • Smooth, jitter-free mechanical operation at temperature boundaries due to programmed hysteresis.
  • Immediate mechanical response to the limit switch, preempting all automated sensor logic with near-zero latency.

Audience: Makers, agriculture tech students, and embedded developers; Level: Beginner to Intermediate

Architecture/flow: Temperature Sensor (Analog Input) ➔ Microcontroller (Hysteresis Logic & Hardware Interrupts) ➔ PWM Output ➔ Servo Motor Actuator

Educational validation note

Before publication, this case passed the Prometeo automated validation gate with status PASS. The validator checked the code blocks, article structure, copy/paste-safe commands and consistency with the supported device catalog.

Published validation evidence

  • Automatic result: PASS.
  • Parsed structure: 3 sections, 3 tables and 2 code blocks detected before publication.
  • Checked code: 1 Arduino/arduino-cli compile, 1 Bash/copy-paste checks.
  • Supported catalog: the article text was checked against Prometeo’s validation-capable device profiles, and unsupported stacks block publication.
  • Report findings: no blocking findings.

This validation confirms syntax and tool compatibility for the published material, but it does not replace physical testing on your exact hardware, wiring and runtime environment.

Educational safety note

This project is a low-voltage educational prototype, not a certified product. Before powering the setup, verify the wiring of your Arduino UNO R3, avoid shorting 5 V, GND or digital pins, disconnect power before changing connections, and use proper interface modules for relays, motors or external loads.

Prerequisites

To successfully complete this tutorial, you should have:
* A basic understanding of how to use the command line/terminal on your operating system.
* The Arduino CLI (Command Line Interface) installed on your computer.
* Basic familiarity with breadboarding and jumper wire connections.
* A standard USB Type-B cable to connect the Arduino UNO to your computer.

Materials

For this project, you must use EXACTLY this device model and component list:
* Microcontroller: Arduino UNO R3 (ATmega328P)
* Actuator: SG90 micro servo motor
* Sensor: LM35 analog temperature sensor (TO-92 package)
* Input Device: Limit switch (standard microswitch with a roller lever or basic push button)
* Accessories: 1x Solderless breadboard, assorted male-to-male jumper wires.

Note: No external resistors are required for the limit switch because we will utilize the ATmega328P’s internal pull-up resistors via software.

Setup/Connection

Proper wiring is critical for the stability of analog readings and servo movements. The LM35 provides a linear analog voltage output proportional to the temperature (10mV per degree Celsius). The SG90 servo is controlled via Pulse Width Modulation (PWM), and the limit switch uses a simple digital input.

Wiring Table

ComponentPin / Wire ColorArduino UNO R3 PinFunction / Notes
LM35Pin 1 (Left, flat face up)5VPower supply for the temperature sensor.
LM35Pin 2 (Middle)A0Analog output signal (10mV/°C).
LM35Pin 3 (Right)GNDGround reference.
SG90 ServoRed Wire5VPower supply for the servo motor.
SG90 ServoBrown / Black WireGNDGround reference.
SG90 ServoOrange / Yellow WireD9PWM signal to control servo angle.
Limit SwitchCOM (Common)GNDGround reference for the switch.
Limit SwitchNO (Normally Open)D2Digital input. Pulled HIGH internally; goes LOW when pressed.

Connection Instructions

  1. Power Distribution: Connect the 5V pin from the Arduino to the positive rail of your breadboard. Connect the GND pin from the Arduino to the negative rail.
  2. LM35 Sensor: Insert the LM35 into the breadboard. With the flat side facing you, connect the left pin to the 5V rail, the right pin to the GND rail, and the center pin directly to Arduino analog pin A0.
  3. SG90 Servo: Connect the servo’s power wires (Red to 5V, Brown/Black to GND) to the breadboard rails. Connect the signal wire (Orange/Yellow) to Arduino digital pin D9.
  4. Limit Switch: Connect the Common (COM) terminal of the limit switch to the GND rail. Connect the Normally Open (NO) terminal to Arduino digital pin D2.

Validated Code

The following section contains the complete, compilable Arduino sketch and a Bash shell script used to automate the build and upload process using Arduino CLI.

Arduino Sketch: greenhouse_vent.ino

Create a directory named greenhouse_vent and save this code inside it as greenhouse_vent.ino.

Public preview of the validated file. The complete source is shown to members and in PDF/Print.

/*
 * Greenhouse Vent Servo Controller
 * Device: Arduino UNO R3 (ATmega328P) + SG90 servo + LM35 temperature sensor + limit switch
 * 
 * Description: Reads temperature from LM35. Opens vent (servo 90 deg) if temp >= 28C.
 * Closes vent (servo 0 deg) if temp <= 25C. 
 * A limit switch on D2 acts as a manual override to force the vent open.
 */

#include <Servo.h>

// Pin Definitions
const int lm35Pin = A0;
const int limitSwitchPin = 2;
const int servoPin = 9;

// Servo Object
Servo ventServo;

// Configuration Constants
const float TEMP_OPEN_THRESHOLD = 28.0;
const float TEMP_CLOSE_THRESHOLD = 25.0;
const int ANGLE_CLOSED = 0;
const int ANGLE_OPEN = 90;

// Timing Variables for non-blocking execution
unsigned long lastUpdateMillis = 0;
const unsigned long UPDATE_INTERVAL_MS = 1000;

// State Tracking
bool ventIsOpen = false;

void setup() {
  // Initialize Serial Monitor
  Serial.begin(9600);
  while (!Serial) {
    ; // Wait for serial port to connect
  }

  // Configure Pins
  // Internal pull-up ensures the pin reads HIGH when the switch is unpressed.
  // When pressed, the switch connects the pin to GND, reading LOW.
  pinMode(limitSwitchPin, INPUT_PULLUP);

  // Attach and initialize servo to closed position
  ventServo.attach(servoPin);
  ventServo.write(ANGLE_CLOSED);

  Serial.println("========================================");
  Serial.println("Greenhouse Vent Controller Initialized");
  Serial.println("========================================");
}
// ...

🔒 This content is premium. With the monthly subscription (7-day free trial) you can unlock the full didactic material and the print-ready PDF pack.🔓 Unlock it — 7-day free trial
/*
 * Greenhouse Vent Servo Controller
 * Device: Arduino UNO R3 (ATmega328P) + SG90 servo + LM35 temperature sensor + limit switch
 * 
 * Description: Reads temperature from LM35. Opens vent (servo 90 deg) if temp >= 28C.
 * Closes vent (servo 0 deg) if temp <= 25C. 
 * A limit switch on D2 acts as a manual override to force the vent open.
 */

#include <Servo.h>

// Pin Definitions
const int lm35Pin = A0;
const int limitSwitchPin = 2;
const int servoPin = 9;

// Servo Object
Servo ventServo;

// Configuration Constants
const float TEMP_OPEN_THRESHOLD = 28.0;
const float TEMP_CLOSE_THRESHOLD = 25.0;
const int ANGLE_CLOSED = 0;
const int ANGLE_OPEN = 90;

// Timing Variables for non-blocking execution
unsigned long lastUpdateMillis = 0;
const unsigned long UPDATE_INTERVAL_MS = 1000;

// State Tracking
bool ventIsOpen = false;

void setup() {
  // Initialize Serial Monitor
  Serial.begin(9600);
  while (!Serial) {
    ; // Wait for serial port to connect
  }

  // Configure Pins
  // Internal pull-up ensures the pin reads HIGH when the switch is unpressed.
  // When pressed, the switch connects the pin to GND, reading LOW.
  pinMode(limitSwitchPin, INPUT_PULLUP);

  // Attach and initialize servo to closed position
  ventServo.attach(servoPin);
  ventServo.write(ANGLE_CLOSED);

  Serial.println("========================================");
  Serial.println("Greenhouse Vent Controller Initialized");
  Serial.println("========================================");
}

void loop() {
  unsigned long currentMillis = millis();

  // Execute control logic at defined intervals
  if (currentMillis - lastUpdateMillis >= UPDATE_INTERVAL_MS) {
    lastUpdateMillis = currentMillis;

    // 1. Read Manual Override Limit Switch
    // LOW means the switch is pressed (override active)
    bool overrideActive = (digitalRead(limitSwitchPin) == LOW);

    // 2. Read and Calculate Temperature from LM35
    int rawADC = analogRead(lm35Pin);

    // The Arduino UNO has a 10-bit ADC (0-1023) and operates at 5.0V.
    // Voltage = (ADC Value / 1024.0) * 5.0
    float voltage = rawADC * (5.0 / 1024.0);

    // LM35 outputs 10mV per degree Celsius (0.01V/C)
    // Temperature (C) = Voltage / 0.01 = Voltage * 100.0
    float temperatureC = voltage * 100.0;

    // 3. Determine Target Vent State
    if (overrideActive) {
      // Manual override forces the vent open
      ventIsOpen = true;
    } else {
      // Temperature-based hysteresis control
      if (temperatureC >= TEMP_OPEN_THRESHOLD) {
        ventIsOpen = true;
      } else if (temperatureC <= TEMP_CLOSE_THRESHOLD) {
        ventIsOpen = false;
      }
      // If temperature is between 25.0 and 28.0, ventIsOpen remains unchanged.
    }

    // 4. Actuate Servo
    if (ventIsOpen) {
      ventServo.write(ANGLE_OPEN);
    } else {
      ventServo.write(ANGLE_CLOSED);
    }

    // 5. Log System State
    Serial.print("Temp: ");
    Serial.print(temperatureC, 1);
    Serial.print(" C | Override: ");
    Serial.print(overrideActive ? "ACTIVE " : "STANDBY");
    Serial.print(" | Vent State: ");
    Serial.println(ventIsOpen ? "OPEN  (90 deg)" : "CLOSED (0 deg)");
  }
}

Automation Script: build_and_upload.sh

Save this file in the parent directory of greenhouse_vent (or adjust paths accordingly). This script ensures the Arduino AVR core is installed, compiles the code, and uploads it to the board.

#!/bin/bash

# Define the FQBN for Arduino UNO R3
FQBN="arduino:avr:uno"

# Define the serial port (Change this to match your system, e.g., /dev/ttyACM0 or COM3)
PORT="/dev/ttyACM0"

# Define the sketch directory
SKETCH_DIR="greenhouse_vent"

echo "Updating Arduino CLI core index..."
arduino-cli core update-index

echo "Installing Arduino AVR core..."
arduino-cli core install arduino:avr

echo "Compiling sketch..."
arduino-cli compile --fqbn $FQBN $SKETCH_DIR

if [ $? -eq 0 ]; then
    echo "Compilation successful. Uploading to $PORT..."
    arduino-cli upload --fqbn $FQBN --port $PORT $SKETCH_DIR

    if [ $? -eq 0 ]; then
        echo "Upload complete! Open serial monitor at 9600 baud."
    else
        echo "Upload failed. Please check the PORT and connection."
    fi
else
    echo "Compilation failed. Please check the source code."
fi

Build/Flash/Run commands

To deploy the code to your Arduino UNO R3, you will use the Arduino CLI. Below are the exact commands used by the automation script, which you can also run manually.

Command Reference Table

ActionCommand
Update core indexarduino-cli core update-index
Install AVR corearduino-cli core install arduino:avr
Compile sketcharduino-cli compile --fqbn arduino:avr:uno greenhouse_vent
Upload to boardarduino-cli upload --fqbn arduino:avr:uno --port <PORT> greenhouse_vent
Monitor outputarduino-cli monitor --port <PORT> --config baudrate=9600

Numbered Workflow

  1. Connect your Arduino UNO R3 to your computer via the USB cable.
  2. Identify your serial port. On Linux, this is typically /dev/ttyACM0 or /dev/ttyUSB0. On Windows, it will be a COM port like COM3. You can find it by running arduino-cli board list.
  3. Open your terminal and navigate to the directory containing your greenhouse_vent folder.
  4. Run the compilation command: arduino-cli compile --fqbn arduino:avr:uno greenhouse_vent.
  5. Run the upload command, replacing <PORT> with your actual port: arduino-cli upload --fqbn arduino:avr:uno --port /dev/ttyACM0 greenhouse_vent.
  6. Start the serial monitor to view the logs: arduino-cli monitor --port /dev/ttyACM0 --config baudrate=9600.

Step-by-step Validation

Once the code is uploaded and the serial monitor is running, perform the following validation checkpoints to ensure the logic and hardware are functioning correctly.

  • Checkpoint 1: Initialization and Idle State

    • Action: Observe the serial monitor immediately after uploading, with the room temperature below 25°C.
    • Expected Observation: The serial monitor prints the initialization banner. The servo moves to the 0-degree position. Logs indicate a temperature below 25.0°C, Override: STANDBY, and Vent State: CLOSED.
    • Pass Condition: Servo is physically at the zero position and logs reflect the closed state accurately.
  • Checkpoint 2: Heating Up (Crossing Upper Threshold)

    • Action: Gently pinch the LM35 sensor between your fingers to raise its temperature. Watch the serial monitor.
    • Expected Observation: The logged temperature will steadily rise. Once it hits 28.0°C or higher, the Vent State changes to OPEN.
    • Pass Condition: The SG90 servo physically rotates 90 degrees immediately when the log shows a temperature $\ge$ 28.0°C.
  • Checkpoint 3: Hysteresis Verification (Cooling Down)

    • Action: Let go of the LM35 and allow it to cool. Observe the logs as the temperature falls between 27.9°C and 25.1°C.
    • Expected Observation: The temperature drops, but the Vent State remains OPEN. The servo does not move.
    • Pass Condition: The system maintains its current state while in the hysteresis deadband, preventing rapid mechanical oscillation.
  • Checkpoint 4: Crossing Lower Threshold

    • Action: Continue letting the sensor cool (or blow gently on it) until the temperature drops to 25.0°C or below.
    • Expected Observation: The log updates the Vent State to CLOSED.
    • Pass Condition: The servo physically rotates back to the 0-degree position.
  • Checkpoint 5: Manual Override Activation

    • Action: While the temperature is below 25°C (vent normally closed), press and hold the limit switch.
    • Expected Observation: The next serial log (within 1 second) shows Override: ACTIVE and Vent State: OPEN.
    • Pass Condition: The servo immediately moves to 90 degrees despite the cold temperature. Releasing the switch should return the servo to 0 degrees on the next cycle.

Troubleshooting

If your prototype is not behaving as expected, consult the table below for common issues and their solutions.

SymptomLikely CauseFix
LM35 reads ~0°C or ~500°C constantlySensor wired backwards or floating ground.Disconnect power immediately. Check the flat face of the LM35. Left is 5V, Right is GND, Center is A0. Re-seat the jumper wires.
Temperature readings fluctuate wildly (±5°C)

Find this product and/or books on this topic on Amazon

Go to Amazon

As an Amazon Associate, I earn from qualifying purchases. If you buy through this link, you help keep this project running.

Quick Quiz

Question 1: What is the primary function of the automated greenhouse vent controller?




Question 2: Which component is used as the actuator to physically move the ventilation flap?




Question 3: Why is hysteresis logic incorporated into the system?




Question 4: How does the hysteresis implementation stabilize the system?




Question 5: What component provides the hardware-based manual bypass for emergencies or maintenance?




Question 6: What is the expected response latency when temperature thresholds are crossed?




Question 7: What is the expected range of motion for the servo driving the ventilation flap?




Question 8: What is an example of the distinct temperature thresholds used for hysteresis in this system?




Question 9: What type of control system is the automated greenhouse vent controller described as?




Question 10: What is the main benefit of agricultural automation in this context?




Carlos Núñez Zorrilla
Carlos Núñez Zorrilla
Electronics & Computer Engineer

Telecommunications Electronics Engineer and Computer Engineer (official degrees in Spain).

Follow me:
Scroll to Top