CONCEPT

PROBLEM : Greenhouses overheat on sunny days and lack light on dark ones. Ventilation and lighting are usually adjusted by hand, often too late.

OUR RESPONSE : A miniature greenhouse that senses its environment and reacts on its own: the roof opens to release heat, and LEDs switch on when daylight fades.

HOW THE GREENHOUSE BEHAVES

Temperature rises →  The servo opens the roof to release warm air

Temperature falls →  The servo closes the roof to keep the heat in

Daylight fades →  The LEDs turn on to light the plants

Daylight returns →  The LEDs turn off to save energy

INPUT PROCESS OUTPUT

BILL OF MATERIALS

SCHEMATIC

 

CODE

// =====================================================================

// ADAPTIVE GREENHOUSE: responsive roof and lighting

// – Temperature (DHT20 sensor) → servo opens/closes the roof

// – Daylight (light sensor) → LEDs turn on/off

// – OLED display shows live data

// – Button re-measures the room temperature (baseline)

// =====================================================================

#include <Servo.h>

#include <U8x8lib.h>

#include “DHT.h”

#include “Wire.h”

// —————- Pins —————-

#define LIGHT_PIN A6 // light sensor

#define BUTTON_PIN 6 // button

#define SERVO_PIN 9 // servo signal wire (orange)

const int LED_PINS[] = { 2, 7, 8, 12 }; // 4 LEDs (pin 6 is used by the button)

const int N_LEDS = 4;

// —————- Roof angles —————-

// Swap the two values if the roof moves the wrong way

#define ROOF_CLOSED 0

#define ROOF_OPEN 90

// —————- Temperature thresholds —————-

// Relative to the room temperature measured at startup (baseline)

#define OPEN_OFFSET 0.5 // roof opens at baseline + 0.5 °C

#define CLOSE_OFFSET 0.2 // roof closes at baseline + 0.2 °C

// —————- Light thresholds —————-

// Check the values in the Serial Monitor and adjust if needed

#define LIGHT_ON_BELOW 200 // darker than this → LEDs on

#define LIGHT_OFF_ABOVE 300 // brighter than this → LEDs off

// —————- Timing (milliseconds) —————-

#define STEP_INTERVAL 20 // time per 1° of roof movement → smooth motion

#define SENSOR_INTERVAL 2000 // how often the temperature is read

#define DISPLAY_INTERVAL 200 // OLED refresh rate

#define DEBOUNCE_TIME 25 // ignore button noise shorter than this

// —————- Objects —————-

Servo roof;

U8X8_SSD1306_128X64_NONAME_HW_I2C oled(U8X8_PIN_NONE);

DHT dht(DHT20); // DHT20 temperature & humidity sensor (I2C)

// —————- State variables —————-

float temperature = 22; // current temperature

float baseline = 22; // room temperature at startup

float openTemp, closeTemp; // thresholds calculated from the baseline

bool roofOpen = false; // should the roof be open?

int currentAngle = ROOF_CLOSED;

float smoothLight = 0; // smoothed light reading

bool ledsOn = false;

bool buttonState = LOW;

bool lastReading = LOW;

unsigned long lastDebounceTime = 0;

unsigned long lastStepTime = 0;

unsigned long lastSensorTime = 0;

unsigned long lastDisplayTime = 0;

// ———————————————————————

// Measure the current room temperature and use it as the new reference

// ———————————————————————

void calibrate() {

float t = dht.readTemperature();

if (!isnan(t)) {

temperature = t;

baseline = t;

}

openTemp = baseline + OPEN_OFFSET;

closeTemp = baseline + CLOSE_OFFSET;

Serial.print(“Baseline: “);

Serial.println(baseline);

}

// ———————————————————————

// SETUP: runs once at power-up

// ———————————————————————

void setup() {

Serial.begin(115200);

pinMode(BUTTON_PIN, INPUT);

for (int i = 0; i < N_LEDS; i++) pinMode(LED_PINS[i], OUTPUT);

// Start with the roof closed

roof.attach(SERVO_PIN);

roof.write(currentAngle);

// Start the OLED

oled.begin();

oled.setFont(u8x8_font_chroma48medium8_r);

oled.setCursor(0, 3);

oled.print(“Calibrating…”);

// Start the temperature sensor

Wire.begin();

dht.begin();

delay(2000); // the sensor needs time after power-up

calibrate();

smoothLight = analogRead(LIGHT_PIN);

oled.clear();

}

// ———————————————————————

// LOOP: runs thousands of times per second, no delay() inside

// ———————————————————————

void loop() {

unsigned long now = millis();

// ———- INPUT: button → recalibrate ———-

bool reading = digitalRead(BUTTON_PIN);

if (reading != lastReading) lastDebounceTime = now;

if (now – lastDebounceTime > DEBOUNCE_TIME && reading != buttonState) {

buttonState = reading;

if (buttonState == HIGH) calibrate(); // new press → new baseline

}

lastReading = reading;

// ———- INPUT + PROCESS: temperature → roof ———-

if (now – lastSensorTime >= SENSOR_INTERVAL) {

lastSensorTime = now;

float t = dht.readTemperature();

if (!isnan(t)) temperature = t;

if (temperature >= openTemp) roofOpen = true; // too warm → open

else if (temperature <= closeTemp) roofOpen = false; // cooled down → close

// in between: keep the current state (hysteresis)

// Serial Plotter output

Serial.print(“Temp:”); Serial.print(temperature);

Serial.print(“,Open:”); Serial.print(openTemp);

Serial.print(“,Close:”); Serial.print(closeTemp);

Serial.print(“,Roof:”); Serial.print(roofOpen ? openTemp + 0.2 : closeTemp – 0.2);

Serial.print(“,Light:”); Serial.println((int)smoothLight);

}

// ———- INPUT + PROCESS: light → LEDs ———-

// Smoothing: 90% old value + 10% new value, so shadows don’t cause flicker

smoothLight = smoothLight * 0.9 + analogRead(LIGHT_PIN) * 0.1;

if (smoothLight < LIGHT_ON_BELOW) ledsOn = true;

else if (smoothLight > LIGHT_OFF_ABOVE) ledsOn = false;

// ———- OUTPUT: LEDs ———-

for (int i = 0; i < N_LEDS; i++) digitalWrite(LED_PINS[i], ledsOn);

// ———- OUTPUT: smooth roof movement ———-

// The servo moves 1° at a time toward its target angle

int targetAngle = roofOpen ? ROOF_OPEN : ROOF_CLOSED;

if (now – lastStepTime >= STEP_INTERVAL) {

lastStepTime = now;

if (currentAngle < targetAngle) currentAngle++;

else if (currentAngle > targetAngle) currentAngle–;

roof.write(currentAngle);

}

// ———- OUTPUT: OLED display ———-

if (now – lastDisplayTime >= DISPLAY_INTERVAL) {

lastDisplayTime = now;

// Roof state text

bool moving = (currentAngle != targetAngle);

const char* state;

if (moving) state = roofOpen ? “OPENING ” : “CLOSING “;

else state = roofOpen ? “OPEN ” : “CLOSED “;

// How many degrees the roof is open, and how long the bar should be

int openAngle = abs(currentAngle – ROOF_CLOSED);

int filled = map(openAngle, 0, abs(ROOF_OPEN – ROOF_CLOSED), 0, 14);

oled.setCursor(0, 0); oled.print(” GREENHOUSE”);

// Temperature and thresholds with one decimal place

oled.setCursor(0, 1); oled.print(“Temp: “); oled.print(temperature, 1); oled.print(” C “);

oled.setCursor(0, 2); oled.print(“Op:”); oled.print(openTemp, 1);

oled.print(” Cl:”); oled.print(closeTemp, 1); oled.print(” “);

oled.setCursor(0, 3); oled.print(“Roof: “); oled.print(state);

oled.setCursor(0, 4); oled.print(“Angle: “); oled.print(openAngle); oled.print(” deg “);

// Progress bar: [#######…….]

oled.setCursor(0, 5); oled.print(“[“);

for (int i = 0; i < 14; i++) oled.print(i < filled ? “#” : “.”);

oled.print(“]”);

oled.setCursor(0, 6); oled.print(“Light: “); oled.print((int)smoothLight); oled.print(” “);

oled.setCursor(0, 7); oled.print(“LEDs: “); oled.print(ledsOn ? “ON ” : “OFF”);

}

}

HOW THE CODE RUNS

TEMPERATURE TRESHOLDS

LIGHT TRESHOLDS

LIVE FEEDBACK ON THE OLED

PHYSICAL PROTOTYPE

RENDERS

DEMO AND NEXT STEPS

How we demonstrate it

  1. A halogen desk lamp acts as the sun: the greenhouse warms up and the roof opens
  2. A warm palm or breath on the sensor works in about 20 seconds
  3. Covering the light sensor turns the LEDs on
  4. The Serial Plotter shows temperature crossing the thresholds

Next steps

  1. More accurate sensor (e.g. SHT31, ±0.2 °C)
  2. Soil moisture sensor and a small water pump
  3. Ventilation driven by humidity as well
  4. Several roof panels opening in sequence

FUTURE VISION

ANIMATION