Skip to content

Final project : microalgae bio-photoreactor

Hero shot and video

My final project is to build a bio-photoreactor rack to cultivate micro-algae. The algae will then be used for methanisation or simply to study their growth.

A bio-photoreactor is a tank, with a nutrient solution and specific conditions where light passes through. The algae capture the light and grow (multiplication, they are all roughly the same size, only the number of them increases).

This project will involve specific requirements such as :

  • 10 small reactors (2 to 3 litres): the idea is to carry out a series of independent tests to have several results to compare. It can also be used by several students at the same time.
  • A transparent tank: to let the light through. This also allows you to see what’s going on and monitor reactions.
  • a special shape at the bottom so that there is no dead zone (cone, sphere, etc.). The dead zone is an area where the liquid does not move, which is something we absolutely want to avoid. If the liquid is stagnant, the algae will settle quickly and their growth will be stopped.
  • an air inlet for each reactor: the algae will need oxygen (aerobic reaction) even if they are in the liquid phase. Air must therefore be added to the mixture to provide oxygen.
  • an LED lamp: algae need light! and this light needs to be controlled! Algae have a day/night cycle that we have to respect. But we also want to vary this cycle to see the effects. These light will cover the entire colour spectrum and UV so we can cultivate differents species.
  • an global cabinet: the installation must be easy to move, as must the reactors inside. The facilities also need to be water-resistant, as they can crack during the process. The light also need to not blind people in the room. So I need to put a armoury with a reflect wall at the inside.
  • an box for the pressurised air cables: the air will arrive via a pressure gauge and will be divided into 10. This system must be integrated into the armoury to make it more practical and aesthetically pleasing.
  • a lid with holes and an air outlet with a non-return valve: during the growth period, various measuring instruments can be introduced.
  • an air outlet is also necessary, to evacuate the air injected at the base. However, this outlet must not let in particles (dust, etc.) from the outside.
  • rubber seals: the link between the tank and the cover will be physical. The idea is to make the material easy to dismantle and replace, so chemical welding and seals will be avoided as much as possible.

Build of material and prices

(all prices in euros)

Global project :

Part Part in project quantity Price Total price Supplyer
Screw Screw 180pc 0.10 18.00 RS
Nuts Nuts 180pc 0.10 18.00 RS
Resin Bottom of reactor 10x33 + 10x151ml 0.118 217.12 Formlabs
Resin Non-return valve 10x5.3ml 0.118 6.25 Formlabs
Big seals Big seals 20pc 0.80 16.00 Amazon
bicycle inner tube Small seals 10pc 0 0 Trash
LEDs LEDs 3pc 22.00 66.00 Amazon
Sponge Diffuser 2pc 1.00 2.00 Amazon
PMMA Glue PMMA Glue 100ml 0.13 13.00 Amazon
Marine vernish Wather resistance 750ml 0.02 16.00 Amazon
Wood plank Box 3.12 m2 28.00 85.00 Barillet
PMMA Tubes Tubes 10pc 20.60 206.00 Plasti service

LED circut

Part Part in project quantity Price Total price Supplyer
RP2040 Electronics 1 4.38 4.38 Digikey
Resistances 0 OHM Electronics 8 0.01 0.08 Digikey
Regulator 3.3V Electronics 1 0.30 0.30 Digikey
N mofset Electronics 3 0.40 1.20 Digikey
Capacitor 10uF Electronics 1 0.10 0.10 Digikey
Capacitor 1uF Electronics 1 0.01 0.01 Digikey

Screen and button circut

Part Part in project quantity Price Total price Supplyer
Resistances 0 OHM Electronics 5 0.01 0.05 Digikey
Resistances 499 OHM Electronics 6 0.01 0.06 Digikey
Tactile switch Electronics 6 0.20 1.20 Digikey
ssd1306 screen Electronics 1 3.00 3.00 Digikey

Temperature circut

Part Part in project quantity Price Total price Supplyer
RP2040 Electronics 1 4.38 4.38 Digikey
Resistances 0 OHM Electronics 3 0.01 0.03 Digikey
Resistances 10 OHM Electronics 1 0.01 0.01 Digikey
N mofset Electronics 2 0.20 0.40 Digikey
Thermistor Electronics 1 1.50 1.50 Digikey
Regulator 3.3V Electronics 1 0.30 0.30 Digikey
Diode 1.3A Electronics 2 0.30 0.60 Digikey
Capacitor 10uF Electronics 1 0.10 0.10 Digikey
Capacitor 1uF Electronics 3 0.01 0.03 Digikey
12V connector Electronics 1 0.60 0.60 Digikey

In total (excluding consumables), this project cost around 700 euros.

1. Reactor

1.1. Lid

The lids were made in PMMA and laser-cut. I first made the design in FreeCad, determining the size of my non-return valve, and using the pre-existing size of my tubes. Each screw had to fit into the pre-existing holes. I then laser-cut a 6mm sheet of PMMA. For ease of use, I add a number to each cover directly on the laser-cutting software.

Here you can find the FreeCad lid file

1.2. Non-return valve

The main aim of this part is to let excess air out of the tube without letting air, particles, etc. in from outside. I made this piece in resin using stereolithography, which is the only affordable, water-resistant printable material. The design was updated from my week 5. I used PMMA glue to adhere the cover and non-return valve.

Here you can find the FreeCad non-return valve file

1.3. Tube

The tubes were ordered from a plastics manufacturer. They are transparent PMMA tubes, 900mm in diameter, 3mm thick and 400mm high. They were cut by the company.

1.4. Bottom curve

This part is divided into two sections, all designed in FreeCad :

Here you can find the FreeCad bottom file

1.4.1. Upper part

The upper part is the part that connects the tube and the air inlet. This part must have a particular shape (conical) to allow a better air flow.

I first thought of making it in a mold, but the size of the mold would be very large, and so would the consumption of material. So I decided to use stereolithography. Using this method instead of PLA will add water-resistant properties, which are essential for my project. The result is really excellent, the precision of the printer is better than FreeCad’s rendering parameters, I can see the tesselation. After printing, I remove the backing each time, and very gently sand the part.

1.4.2. Diffuser part

The diffuser has 3 main roles: - Hold the sponge that will diffuse the air - Receive the air connection - Make a watertight junction with the upper part

To accommodate the hose that will carry the air, I designed a special shape, a sort of long conical tube. This shape allows a hose to connect easily and stay in place simply by the force of friction. There’s also a cavity where a sponge can be placed. For this, simply cut out a piece and place it inside. As before, the result is beautiful and precise. It adapts perfectly to the hose.

1.5. Seals

In my project, there are 2 types of seals: small ones (between the lower part and the diffuser) and large ones (between the tube and the cover or lower part). For the small seals, I laser-cut them from an old bicycle inner tube. The size is the same as the thickness of the diffuser cavity. I chose to make flat seal rather than thoric seal because it’s easier and I can use scrap instead of ordering new parts.

For large seals, I currently use a commercial version, used in the canning industry, such as this one:

2. Cabinet

2.1. Structure

I used FreeCad to design this box. I need to create each part as a separate object, so that I can move it around more easily later.

The reactors need to be stored aligned so that they can be taken out easily, so I created this receptacle, but the box is very long (1.35m) and there’s a risk of bending. To avoid this, I design supports that follow the line of the wood to dissipate the weight and absorb the forces.

Another problem is heat emission. My algae will need a temperature of no more than 40/50 degrees, so I need to provide air circulation. Normally, this design should be calculated and simulated, but I don’t have those skills or the software at the moment, so I’m going to improvise. The air flow will start at the bottom, follow the wall and leave through the vents at the top. The air vents shouldn’t be too biga and they shouldn’t be a source of direct light that would blind users, so they are deeper than wider.

2.2. CNC and assembly

The milling machine we use is a 3-axis CNC measuring 1500 x 2400 mm. It is equipped with an automatic tool change system with 8 different cutters, a cooler for the spindle motor, a particle extraction system and a suction table. For the parameters, I used the parameters we found in the group assignment: 13200 rpm, 750mm/min feed and a 6mm descent step. I didn’t have too many problems with the cutting, I just have a bit of trouble with the origin of my parts. For the moment this problem hasn’t been completely solved, so I’ll have to do a few more tests before I can start cutting.

Once cut, I sand my pieces to remove imperfections and hollow out a few gaps. Overall, my pieces fit together, but for the long, thin one, there’s a bit of flex. The result is fine.

Next, I varnish my cabinet with a boat varnish. The aim is to make the cabinet more resistant to external aggression, and to make the interior waterproof. I also make 2 doors (simple squares) with CNC, and fix them with dowels.

Here you can find the FreeCad box file

2.3. Light

2.3.1 LED system

The LED system will supply the energy needed by the algae to carry out photosynthesis. I based my calculations on the laboratory’s old LED panel, with a power of 95 W. That sounds like a lot to us, but we’ve found an equivalent. It involves making a panel with 5 LED ribbons. To support the strips and make sure they’re evenly distributed, I used a poplar board. I cut the LED strips every 1.2m and soldered each of them.

But the strips seems not to hold perfectly, so I have to glue them to the board. After this I design the circut. This circut will have to manage the ammount of current for each colour (3A). It will also be connected to a button and screen display to receve and send data. To bring power to this board, I recicle a 12V 10A power supply.

When I connected all the parts, I got this:

Here you can find the FreeCad poplar board file

Here you can find the led Board PCB, sketch, project

2.3.2 Screen and buttons

To simplify the use of the project, I implemented a screen and a series of buttons, like a mini console. The aim is twofold: to display the essential information and to modify it easily. The button have been place in a way that is easy to understand : 4 arrows (right, left, up, down) and 2 button “ok” and “cancell/return”.

This part and the display are connected to the same microcontroller (RP2040) as the LEDs. At first, I thought of using a Xiao ESP32, but I had so many problems that I decided to stick with the RP2040, which works much faster and with far fewer errors. For the screen, I use an ssd1306 screen which is small but has a few advantages:

  • it’s very easy to set up (only 4 pins) and easy to code
  • it’s not expensive
  • it consumes less energy

To get a clean interface, I draw with Freecad, then laser-cut the front of a poplar box and CNC-cut a 10mm wooden planck. Then I sand, varnish and glue with wood glue.

To make all this work, I also need to make some code with the Ardwino IDE. This code does several things: at the beginning, the first display is an adafruit screen for one second, then it prints the vital information of the project: the light intensity for each color and the time. The user can navigate between each line, and this position is symbolized by a “”. When the user wants to change the intensity of a color, he enters the line and a “*” is printed. When the value is modified, clicking on ok or cancel validates the value. If the user enters the time line, a new screen appears with the time (hh:mm) of switching on and off. The user can then modify the cycle value.

#include <Wire.h> // Screen
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#include "pico/stdlib.h" // LEDs
#include "hardware/pwm.h"

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET    -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

#define buttonPin1 26
#define buttonPin2 27
#define buttonPin3 28
#define buttonPin4 29
#define buttonPin5 3
#define buttonPin6 4

const uint redPin = D6;    // Broche D6
const uint greenPin = D8;  // Broche D8
const uint bluePin = D7;   // Broche D7

int buttonState1 = 0;
int buttonState2 = 0;
int buttonState3 = 0;
int buttonState4 = 0;
int buttonState5 = 0;
int buttonState6 = 0;

int RedState = 100; //The value foe each LED color
int GreenState = 100;
int BlueState = 100;

int lightHourOn = 11;
int lightHourOff = 12;
int lightMinuteOn = 60;
int lightMinuteOff = 0;
int globalTimeOn = 0;

int hours = 0; // To dispaly the time passed
int minutes = 0;
int seconds = 0;
int timeBuffer = 0;
unsigned long previousMillis = 0;

bool selectL1 = true; //To know at what line we are 
bool selectL2 = false;
bool selectL3 = false;
bool selectL4 = false;

bool engageL1 = false; //To know if the line is engage (we can change the values) 
bool engageL2 = false;
bool engageL3 = false;
bool engageL4 = false;

bool pageRGB = true;
bool pageCycle = false;


void setup() {
  gpio_set_function(redPin, GPIO_FUNC_PWM); // PWM parts
  gpio_set_function(greenPin, GPIO_FUNC_PWM);
  gpio_set_function(bluePin, GPIO_FUNC_PWM);

  uint slice_num_red = pwm_gpio_to_slice_num(redPin); //Get the slices
  uint slice_num_green = pwm_gpio_to_slice_num(greenPin);
  uint slice_num_blue = pwm_gpio_to_slice_num(bluePin);

  pwm_set_wrap(slice_num_red, 255); // Configure each slices
  pwm_set_wrap(slice_num_green, 255);
  pwm_set_wrap(slice_num_blue, 255);

  pwm_set_enabled(slice_num_red, true); //Activate the PWM
  pwm_set_enabled(slice_num_green, true);
  pwm_set_enabled(slice_num_blue, true);


  pinMode(buttonPin1, INPUT); //Initialiset the buttons and the screen
  pinMode(buttonPin2, INPUT);
  pinMode(buttonPin3, INPUT);
  pinMode(buttonPin4, INPUT);
  pinMode(buttonPin5, INPUT);
  pinMode(buttonPin6, INPUT);

  Serial.begin(115200);

  // initialize with the I2C addr 0x3C (for the 128x64)
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }

  // Show initial display buffer contents on the screen -- 
  // the library initializes this with an Adafruit splash screen.
  display.display();
  delay(1000); // Pause for 2 seconds

  // Clear the buffer
  display.clearDisplay();
}


void printRGB(){ // The RGB screen
  display.print(F(" Vert = "));display.print(RedState);display.print(F("%       "));
  if(selectL1 == true){
    display.println(F(" *"));
  }
  if(engageL1 == true){
    display.println(F("**"));
  }
  if(selectL1 == false && engageL1 == false){
    display.println(F("  "));
  }

  display.print(F(" Bleu = "));display.print(GreenState);display.print(F("%       "));
  if(selectL2 == true){
    display.println(F(" *"));
  }
  if(engageL2 == true){
    display.println(F("**"));
  }
  if(selectL2 == false && engageL2 == false){
    display.println(F("  "));
  }

  display.print(F(" Rouge = "));display.print(BlueState);display.print(F("%      "));
  if(selectL3 == true){
    display.println(F(" *"));
  }
  if(engageL3 == true){
    display.println(F("**"));
  }
  if(selectL3 == false && engageL3 == false){
    display.println(F("  "));
  }

 display.print(F(" "));display.print(hours);display.print(F(":"));display.print(minutes);display.print(F(":"));display.print(seconds);display.print(F("          "));
  if(selectL4 == true){
    display.println(F(" *"));
  }
  else {
    display.println(F("  "));
  }
}

void printLightCycle(){ //The light cycle screen
  display.print(F(" Alumme : "));
  display.print(lightHourOn);
  display.print(F(" h    "));
  if(selectL1 == true){
    display.println(F(" *"));
  }
  if(engageL1 == true){
    display.println(F("**"));
  }
  if(selectL1 == false && engageL1 == false){
    display.println(F("  "));
  }

  display.print(F("          "));
  display.print(lightMinuteOn);
  display.print(F(" min   "));
  if(selectL2 == true){
    display.println(F(" *"));
  }
  if(engageL2 == true){
    display.println(F("**"));
  }
  if(selectL2 == false && engageL2 == false){
    display.println(F("  "));
  }

  display.print(F(" Eteint : "));
  display.print(lightHourOff);
  display.print(F(" h    "));
  if(selectL3 == true){
    display.println(F("  *"));
  }
  if(engageL3 == true){
    display.println(F("**"));
  }
  if(selectL3 == false && engageL3 == false){
    display.println(F("  "));
  }

  display.print(F("          "));
  display.print(lightMinuteOff);
  display.print(F(" min  "));
  if(selectL4 == true){
    display.println(F("  *"));
  }
  if(engageL4 == true){
    display.println(F("**"));
  }
  if(selectL4 == false && engageL4 == false){
    display.println(F("  "));
  }
}


void haut(){
  Serial.println("Haut");
    if(selectL2 == true){
      selectL2 = false;
      selectL1 = true;
    }
    else if(selectL3 == true){
      selectL3 = false;
      selectL2 = true;
    }
    else if(selectL4 == true){
      selectL4 = false;
      selectL3 = true;
    }
}

void bas(){
  Serial.println("Bas");
    if(selectL3 == true){
      selectL3 = false;
      selectL4 = true;
    }
    else if(selectL2 == true){
      selectL2 = false;
      selectL3 = true;
    }
    else if(selectL1 == true){
      selectL1 = false;
      selectL2 = true;
    }
}

void gauche(){
  Serial.println("Gauche");
    if(pageRGB == true){
      if(engageL1 == true && RedState > 0)
        RedState --;
      if(engageL2 == true && GreenState > 0)
        GreenState --;
      if(engageL3 == true && BlueState > 0)
        BlueState --;
    }
    if(pageCycle == true){
      if(engageL1 == true && lightHourOn > 0){
        lightHourOn --;
        lightHourOff ++;
      } 
      if(engageL2 == true && lightMinuteOn > 0){
        lightMinuteOn --;
        lightMinuteOff = 60 - lightMinuteOn % 60;
      }
      if(engageL3 == true && lightHourOff > 0){
        lightHourOn ++;
        lightHourOff --;
      } 
      if(engageL4 == true && lightMinuteOff > 0){
        lightMinuteOn ++;
        lightMinuteOff = 60 - lightMinuteOn % 60;
        if(lightMinuteOn == 60) //Exeption (60 an 0, not 60 and 60)
          lightMinuteOff = 0;
      }
    }
}

void droite(){
  Serial.println("Droite");
    if(pageRGB == true){
      if(engageL1 == true && RedState < 100)
        RedState ++;
      if(engageL2 == true && GreenState < 100)
        GreenState ++;
      if(engageL3 == true && BlueState < 100)
        BlueState ++;
    }
    if(pageCycle == true){
      if(engageL1 == true && lightHourOn < 23){
        lightHourOn ++;
        lightHourOff --;
      } 
      if(engageL2 == true && lightMinuteOn < 60){
        lightMinuteOn ++;
        lightMinuteOff = 60 - lightMinuteOn % 60;
        if(lightMinuteOn == 60) //Exeption (60 an 0, not 60 and 60)
          lightMinuteOff = 0;
      }
      if(engageL3 == true && lightHourOff < 23){
        lightHourOn --;
        lightHourOff ++;
      } 
      if(engageL4 == true && lightMinuteOff < 60){
        lightMinuteOn --;
        lightMinuteOff = 60 - lightMinuteOn % 60;
      }
    }
}

void okEntrer(){
  Serial.println("2 Ok");
    if(engageL1 == true || engageL2 == true || engageL3 == true || engageL4 == true){ // If already engage, disengage (like a validation)
      if(engageL1 == true){
        engageL1 = false;
        selectL1 = true;
      }
      if(engageL2 == true){
        engageL2 = false;
        selectL2 = true;
      }
      if(engageL3 == true){
        engageL3 = false;
        selectL3 = true;
      }
      if(engageL4 == true){
        engageL4 = false;
        selectL4 = true;
      }
    }
    else{
      if(selectL1 == true){ //Engege the line if not already engage 
        engageL1 = true;
        selectL1 = false;
      }
      if(selectL2 == true){
        engageL2 = true;
        selectL2 = false;
      }
      if(selectL3 == true){
        engageL3 = true;
        selectL3 = false;
      }
      if(selectL4 == true && pageRGB == true){
        pageRGB = false;
        pageCycle = true;
        selectL1 = true; 
        selectL2 = false;
        selectL3 = false;
        selectL4 = false;
      }
      if(selectL4 == true && pageCycle == true){
        engageL4 = true;
        selectL4 = false;
      }
    }
}

void annulerRetour(){
  Serial.println("1 Annuler/retour");
  if(pageCycle == true && (selectL1 == true || selectL2 == true || selectL3 == true || selectL4 == true)){
      pageRGB = true;
      pageCycle = false;
      selectL1 = true; 
      selectL2 = false;
      selectL3 = false;
      selectL4 = false;
    }
  if(engageL1 == true){
    engageL1 = false;
    selectL1 = true;
  }
  if(engageL2 == true){
    engageL2 = false;
    selectL2 = true;
  }
  if(engageL3 == true){
    engageL3 = false;
    selectL3 = true;
  }
  if(engageL4 == true){
    engageL4 = false;
    selectL4 = true;
  }
}


void setColor(int red, int green, int blue) { //Apply the PWM
    pwm_set_gpio_level(redPin, red);
    pwm_set_gpio_level(greenPin, green);
    pwm_set_gpio_level(bluePin, blue);
}

void passOneSecond(long currentMillis){
  unsigned long totalSeconds = currentMillis / 1000; // Convertir en secondes
  seconds = totalSeconds % 60; // Obtenir les secondes
  unsigned int totalMinutes = totalSeconds / 60; // Convertir en minutes
  minutes = totalMinutes % 60; // Obtenir les minutes
  unsigned int totalHours = totalMinutes / 60; // Convertir en heures
  hours = totalHours % 24; // Obtenir les heures
}


void loop() {

  display.setTextSize(1);      // Normal 1:1 pixel scale
  display.fillScreen(SSD1306_BLACK);
  display.setTextColor(SSD1306_WHITE); // Draw white text
  display.setCursor(0, 0);     // Start at top-left corner

  if(pageRGB == true) //print the needed page
    printRGB();
  if(pageCycle == true)
    printLightCycle();

  // Show the display buffer on the screen
  display.display();

  buttonState1 = digitalRead(buttonPin1); //read if the button in engage
  buttonState2 = digitalRead(buttonPin2); //read if the button in engage
  buttonState3 = digitalRead(buttonPin3); //read if the button in engage
  buttonState4 = digitalRead(buttonPin4); //read if the button in engage
  buttonState5 = digitalRead(buttonPin5); //read if the button in engage
  buttonState6 = digitalRead(buttonPin6); //read if the button in engage

  if(buttonState1 == 1) // If "up" button is pressed
    haut();

  if(buttonState2 == 1) //If possible the value change
    gauche();

  if(buttonState3 == 1) // If "down" button is pressed
    bas();

  if(buttonState4 == 1) //If possible the value change
    droite();

  if(buttonState5 == 1){ //Disengage the line
    annulerRetour();
    //saveVariables();
    delay(200);
  }

  if(buttonState6 == 1){ //Engage the line
    okEntrer();
    //saveVariables();
    delay(200);
  }

  globalTimeOn = (lightHourOn * 60) + lightMinuteOn; // Calculate if we are in the dark or the light phase

  if(((hours * 60) + minutes) >= globalTimeOn) // Dark phase 
    setColor(0,0,0);
  else
    setColor(250*(static_cast<float>(RedState)/100), 250*(static_cast<float>(GreenState)/100), 250*(static_cast<float>(BlueState)/100)); // Light phase

  delay(100);

  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= 1000) {
    previousMillis = currentMillis;
    passOneSecond(currentMillis);
  }
}

Here you can find the Board PCB, sketch, project

Here you can find the code file

Here you can find the FreeCad button box file

2.4 Temperature control

Because of the large amount of heat produce by the LEDs, I have to install a system of active heat dissipation with the passive system. This system is verry simple: when a define temperature is cross, the 2 vents activate. I create this board :

This board have tow main part : a temperature sensor and a fan regulator. All the calculation will be manage by the RP2040 and when 25 degrees are atteint, the fans activate slowly and more and mmore until 35 degrees. At this point wathever the temperature, the fans are at maximum speed. It give this code :

const int thermistorPin = A3; // Define the thermistor pin
const float refResistance = 10000.0; // Set the resistance reference
const float refVoltage = 3.3; // Set the tension reference
const float refTemperature = 25.0; // Set the temperature reference
const float betaValue = 3750.0; // Set de beta value of the transistor

int pwmValue = 0; // Default value for the MOSFETs

const int mosfetPin1 = D0; // Define MOSFET 1 pin
const int mosfetPin2 = D1; // Define MOSFET 2 pin
const int pwmFrequency = 15000; // Define PWM frequency in Hz

void setup() {
  Serial.begin(9600); // Initialise serial communication

  pinMode(mosfetPin1, OUTPUT); // Define MOSFET 1 as an outpout
  pinMode(mosfetPin2, OUTPUT); // Define MOSFET 2 as an outpout
}

void loop() {
  int rawValue = analogRead(thermistorPin); // Read analogic value
  float voltage = rawValue * refVoltage / 1023.0; // Convert in tension
  float resistance = refResistance * (refVoltage / voltage - 1.0); // Calculate resistance of thermistor
  float temperature = 1.0 / ((log(resistance / refResistance) / betaValue) + (1.0 / (refTemperature + 273.15))) - 273.15; // Calculate temperature in celcius
  Serial.print("Temperature : "); // Print tmperature
  Serial.print(temperature);
  Serial.println(" C");

    if (temperature <= 25) {
        pwmValue = 0; // Fans off below 25 degrees
    } else if (temperature >= 35) {
        pwmValue = 250; // Fans at max above 35 degrees
    } else {
        // Linear interpolation between 25 and 35 degrees 
        pwmValue = map(temperature, 25, 35, 100, 250);
    }

    analogWrite(mosfetPin1, pwmValue); // Send the value to the fan 1 
    analogWrite(mosfetPin2, pwmValue); // Send the value to the fan 2

    delay(1000); // Wait 1 second   
}

With this configuration, temperature management is fully automatic: all the user has to do is plug in the power and the system is up and running!

Here you can find the Board PCB, sketch, project

Here you can find the code file

3. Sensors

Due to the short timeframe, sensors will not be added as part of this project. But this also means that there’s room for improvement, later or with another person (perhaps another Fabacademy project?).

4. The files

Here you can find the FreeCad box file

Here you can find the FreeCad bottom file

Here you can find the temperature Board PCB, sketch, project

Here you can find the code file

Here you can find the FreeCad lid file

Here you can find the FreeCad non-return valve file

Here you can find the FreeCad poplar board file

Here you can find the led Board PCB, sketch, project

Here you can find the Board PCB, sketch, project

Here you can find the code file

Here you can find the FreeCad button box file