THE PROBLEM

Wild pollinators are declining in Catalonia, and the regional plan PIPOL aimed to reverse this loss by 2030. In May 2025, the Barcelona City Council began installing 100 nests for wild bees and wasps across 25 parks (Barcelona City Council, 2025). But a nest is only a shelter. It is fixed, while a solitary bee’s needs change with every life stage.

Temperature is the major threat identified. In a field study of red mason bees, offspring survival fell as nest temperature rose, mostly because eggs failed to develop (Ostap-Chec et al., 2021). In the summer, female bees avoided nests that were hotter than 28 °C and in the winter, warm spells increase weight loss in overwintering bees (Apidologie, 2025). A static nest cannot answer this.

Our Question: Can the nest regulate its internal temperatures to adapt to the life stage of the bee?

THE PROPOSAL

We built a shade for these existing nest designs that chooses its position from three inputs in order to passively control internal conditions:

  • Rain
  • Internal Temperature
  • External Temperature

The code equates a manually inputted month into a season and thereafter, life stage. The stage defines a temperature range, and the range determines the servo shade angle.

THE LOGIC

Rain overrides everything. If it is dry, the system cross-checks inside against outside, so that it regulates the nest using comparative data.

THE CODE

#include <Servo.h>

#include <Wire.h>

// #include <RTClib.h>

#include <DHT.h>

#include <DHT20.h>

#include <U8x8lib.h>

#define TEMP_OUT_PIN 2

#define WATER_PIN A1

#define LIGHT_OUT_PIN A6

#define myservo1_PIN 3

#define myservo2_PIN 6

Servo myservo1;

Servo myservo2;

// RTC_DS1307 rtc;

DHT exteriorSensor(TEMP_OUT_PIN, DHT11);

DHT20 interiorSensor;

U8X8_SSD1306_128X64_NONAME_HW_I2C screen(U8X8_PIN_NONE);

int target1 = 0;            

int target2 = 0;

int myservo1_angle = 0;           //Starting Angle of Servos

int myservo2_angle = 0;

float temp_in = 0.0;

float temp_out = 0.0;

int light_out = 0;

bool water = false;

unsigned long lastServoMove = 0;  // Servo Movement Time

const unsigned long SERVO_INTERVAL = 45;

unsigned long lastSensorRead = 0;  // Servo Reading Timing

const unsigned long SENSOR_INTERVAL = 3000;

float lower_limit;  // Temperature Limits for Each Season

float upper_limit;

int month;

const char* getBeeLifeStage(int month) {

  if (month == 3 || month == 4 || month == 5) {

    return “Nesting”;                           //The organism prepares a nest, lays eggs, or establishes a place for reproduction.

  }

  else if (month == 6 || month == 7 || month == 8) {

    return “Larva Dev”;                           //The young organism develops and grows.

  }

  else if (month == 9 || month == 10 || month == 11) {

    return “Dormant”;                           //A period of reduced activity, growth, or metabolis

  }

  else {

    return “Overwintering”;                           //The organism survives winter conditions, often by reducing activity and conserving energy.

  }

}

void moveServos() {

  if (millis() – lastServoMove >= SERVO_INTERVAL) {

    lastServoMove = millis();

    if (myservo1_angle < target1) {                                            // Servo 1: move gradually towards target

      myservo1_angle++;

    }

    else if (myservo1_angle > target1) {

      myservo1_angle–;

    }

    if (myservo2_angle < target2) {                                           // Servo 2: move gradually towards target

      myservo2_angle++;

    }

    else if (myservo2_angle > target2) {

      myservo2_angle–;

    }

    myservo1_angle = constrain(myservo1_angle, 0, 180);                      // Keep angles within safe limits

    myservo2_angle = constrain(myservo2_angle, 0, 180);

    myservo1.write(constrain(myservo1_angle, 5, 175));                                    // Send positions to servos

    myservo2.write(constrain(180 – myservo2_angle, 5, 175));

  }

}

void setup() {

  Serial.begin(115200);

  delay(1000);

  screen.setBusClock(100000);                       //Set up the Display Screen

  screen.begin();

  screen.setFlipMode(1);

  screen.setFont(u8x8_font_chroma48medium8_r);

  screen.clear();

  Serial.println(“Screen setup done”);

  // rtc.adjust(DateTime(2026, 10, 23, 12, 5, 0));  // Set RTC to the date and time when this sketch was compiled… must be disabled after setting it the first time

  exteriorSensor.begin();

  interiorSensor.begin();

  pinMode(WATER_PIN, INPUT);

  myservo1.attach(myservo1_PIN);

  myservo2.attach(myservo2_PIN);

  myservo1.write(myservo1_angle);

  myservo2.write(myservo2_angle);

}

void setSeasonLimits(int month) {

  if (month >= 3 && month <= 5) {  // Spring

    lower_limit = 10;

    upper_limit = 22;

  } else if (month >= 6 && month <= 8) {  // Summer

    lower_limit = 18;

    upper_limit = 28;

  } else if (month >= 9 && month <= 11) {  // Autumn

    lower_limit = 7;

    upper_limit = 15;

  } else {  // Winter

    lower_limit = 1;

    upper_limit = 5;

  }

}

void loop() {

  interiorSensor.read();  // Read interior temperature from DHT20

  temp_in = interiorSensor.getTemperature();

  temp_out = exteriorSensor.readTemperature();  // Read exterior temperature from DHT11

  float temp_difference = temp_in – temp_out;

  light_out = analogRead(LIGHT_OUT_PIN);

  water = digitalRead(WATER_PIN);

// rtc.now().month();

  month = ;             // Manually Input the Month

  const char* monthNames[] = {

    “January”, “February”, “March”, “April”, “May”, “June”, “July”, “August”, “September”, “October”, “November”, “December”

  };

  screen.drawString(0, 0, “Month:”);

  screen.drawString(6, 0, monthNames[month – 1]);

  screen.drawString(0, 1, getBeeLifeStage(month));

  setSeasonLimits(month);

  if (water == HIGH) {                                                    //  //Rain Detection

     target1 = 110;

     target2 = 110;

    screen.drawString(0, 3, “Rain Detected”);

  }

  else {

    screen.drawString(0, 3, “No Rain Detected”);

    setSeasonLimits(month);

  if (temp_in > upper_limit && temp_difference > 0) {                     // Nest is too hot and warmer than outside

    target1 = 90;

    target2 = 90;

  }

  else if (temp_in < lower_limit && temp_difference < 0) {                // Nest is too cold and colder than outside

    target1 = 45;

    target2 = 45;

  }

  else {                                                                 // Neither temperature comparison supports intervention

    target1 = 0;

    target2 = 0;

  }

}

  moveServos();

  screen.drawString(0, 4, “Temp In:”);

  screen.setCursor(8, 4);

  screen.print(temp_in);

  screen.drawString(0, 5, “Temp Out:”);

  screen.setCursor(9, 5);

  screen.print(temp_out);

  screen.drawString(0, 6, “Light:”);

  screen.setCursor(6, 6);

  screen.print(light_out);

}

SCHEMATIC DIAGRAM

THE PROTOTYPE

CHALLENGES

  1. We initially planned to calibrate a real-time clock so that the system would read the month automatically. However, there were difficulties in coding it. As a quick alternative, we made the month a manual input.
  2. While writing the code, we realised that the shade angle was being informed by several inputs simultaneously. In order to prevent conflicting outputs, we had to define a priority order.

FUTURE EXPLORATIONS

  1. Fix the real-time clock in the code so that the month, stage and limits update automatically in order to optimise the system.
  2. Simulate sun paths and shade for different park positions in order to determine shade angles.
  3. The shade is a passive method as it only changes how much sun and rain reach the nest. The next step would be to add an active strategy, so the nest can be warmed or cooled towards the range for the bees’ life stage.

THE DEMONSTRATION