How to build an Arduino based sourdough incubator

on

Why build an Arduino based sourdough incubator?

Living in a foreign country usually leads to some challenges and some things you miss from your home country. For me this is good sourdough bread, which is pretty hard to get in Switzerland and kind of expensive. So since I’m cheap ;-), I thought why not make some sourdough by myself. There are like a hundred tutorials online (but only a handful are usefully), so I tried one myself. The first couple of breads I made had some potential for improvement. They could rather be used to knock out burglars. But I got better over time. What I learned quickly, is that managing the right temperature is key to a good sourdough, which is the key to good bread. So I came up with the idea to build an incubator for the dough, leaving it up to the Arduino to manage the temperature.

What should my incubator be able to do?

  • Increase temperature to up to 40C (105F) quickly.
  • Cool down temperature; if temperature is too high (cool down to room temperature as max).
  • Enough room to work with at least 1 kg (2.2 lbs) of dough.
  • Use different (programmable) approaches for sour dough growing (3-step approach, 1-step approach, dough reactivation)
  • Give feedback on the current status and time left for each phase
  • Signal; if the user has to do something with the dough (add ingredients or the process is finished)
  • It should look nice (so my wife will allow it in the kitchen)

Hardware setup

List of parts

  • Angled steel made from aluminum (for the frame)
  • Aluminium foil (for isolation)
  • Plexiglas
  • lamp holder/mounting device
  • Light bulb (heat source)
  • PC van (cooler)
  • 2 Relays (turning on heater and cooler)
  • Temp sensor
  • Wires
  • 9v Battery (to support the van)
  • A small project case for the Arduino, the relays, and the circuit board

Electronics

First I setup a prototype for the electronics to make sure that all components work together before I start soldering.

To make things easier I used Grove System components, because they are easy to connect.

I hooked up 2 relays (one to turn on the PC fan, one for the light), a temperature sensor and the LCD monitor. In addition I connected the Fan with a 9v battery and connected this to the relay. The Arduino board will have its own power supply for now, since I want to protocol the data collected.
Look at the picture for a basic overview on the setup.

Sourdough incubator test setup
Sourdough incubator detaild setup

Connection to Arduino board

  • Relay one (fan) is connected to: Pin 3
  • Relay two (light bulb) is connected to: Pin 6
  • LCD: Pin 11, Pin 12
  • Temp Sensor: Analog 0

Case

When I assembled the incubator I did not have in mind that I wanted to write a tutorial someday. So I did not take any photos during the process, but I will include some detailed shots of each component.
First I created 2 basic frames using the angled steel. So I cut out 4 pieces of the angled steel for each frame with a length of 30 cm (12 inches) and welded them together. Screwing the parts together works also fine.

I also made some holes to attach the Plexiglas to the frames (to for each side panel and 8 for each of the top and bottom panels).

Then I cut out the Plexiglas to fit in the frames, leaving me with 6 parts – 4 for each side 30cm x 25cm (12 inches x 10 inches), 1 for the bottom 30cm x 30cm (12 inches x 12 inches), and 1 for the top 30cm x 30cm (12 inches x 12 inches).

Sourdough incubator top cover
Sourdough incubator top cover

1 of the side panels is used for the electronics. I cut some holes to mount the electronics:

  • 1 big hole for the fan 6 cm (2,4 inches) in diameter (this is strongly depending on the fan you use)
  • 4 small holes to mount the fan using screws
  • 1 hole for the lamp holder 3 cm (1,2 inches)
  • 1 hole for wires (fan and temp sensor) – the diameter depends on the number of wires, this is a bit trial and error.
  • 2 small holes to attach a project case with screws (this contains the Arduino, the relays and the circuit board the connect the sensors and relays)

The next step is to attach the Plexiglas parts to the frames. I started by putting the bottom and the top Plexiglas parts in each frame and screwed them together.

I had to try a couple times and sand the parts a bit to make them fit.

Sourdough incubator assembled
Sourdough incubator assembled

After the basic case is assembles I added the lamp holder and attached the Fan.

Sourdough incubator fan and lightbulb
Sourdough incubator fan and lightbulb

Then I soldered up the circuit board (basically adding connectors for the sensor, the relays, and the LCD).

I attached the circuit board the Arduino connected the parts with wires and put it all in the project case.

Sourdough incubator electronics
Sourdough incubator electronics

I also connected the lamp and the fan with battery to the corresponding relays and added the temperature sensor.

Sourdough incubator temp sensor
Sourdough incubator temp sensor

After the project case is closed and everything is wired up (and the code is on the Arduino) the setup is complete.

Sourdough incubator full setup
Sourdough incubator full setup

However I had some problems increasing the temperature to the level I wanted.
The solution is, to cover the inside with aluminum foil – this will make the heating device much more efficient.

So this is the hardware – now to the code.

Code

//Import libraries
 #include 
 #include <SerialLCD.h>
 #include 
 //Define serial ports for the LCD
 SerialLCD slcd(11,12);
 //Define Pins for the relays
 #define RelayLight 6
 #define RelayFan 3
 //Temperature variables for 3-phases approach in Degrees Celsius
 int Step1min=22;
 int Step1max=26;
 int Step2min=27;
 int Step2max=29;
 int Step3min=28;
 int Step3max=30;
 int durStep1=360;
 int durStep2=240;
 int durStep3=360;
 int durPause=15;
 //Calculation duration of 3-phases
 long intervala=durStep1*60000;
 long intervalb=durStep2*60000;
 long intervalc=durStep3*60000;
 long intervalPause=durPause*60000;
 int tolerance_3=2;
 //Temperature variables for 1-Phase approach in Degrees Celsius
 int Step1_1min=23;
 int Step1_1max=33;
 int dur1Phase=960;//16 std
 long interval_1=dur1Phase*60000;
 int tolerance_1=1;
 int abstiegsrate;
 //Temperature variables for dough reactivation in Degrees Celsius
 int TRmin=25;
 int TRmax=27;
 int tolerance_TR=1;
 int durTR=360;//360
 long intervalTR=durTR*60000;
 //globale variables
 int currentStep;
 long interval;
 long Updatedelay=10000;
 long UpdateDelayPause=5000;
 long displaycounter;
 unsigned long currentMillis = millis();
 //B value of the thermistor
 int B=3975;
 //Definition LED output pin
 int ledpin=13;
 //Definition button pin to select menu entries
 int buttonpin=7;

void setup()
 {
 //pin layout definition
 pinMode(RelayLight, OUTPUT);
 pinMode(RelayFan, OUTPUT);
 pinMode(ledpin, OUTPUT);
 pinMode(buttonpin,INPUT);
 digitalWrite(RelayFan,LOW);
 digitalWrite(RelayLight,LOW);
 Serial.begin(9600);
 }
 //initialize the LCD
 void init_Display() {
 slcd.begin();

}
 //Define basic variables
 unsigned char speed;
 unsigned int lowLuefter = 10;
 unsigned int highLuefter = 10;
 unsigned int baseTime = 10;
 //run the code by calling the menu selection
 void loop()
 {
 int selection=getSelection();
 if(selection==0)
 onephase(interval_1);
 else if (selection == 1)
 threephases();
 else if(selection == 2) {
 //slcd.clear();
 //slcd.println("not supported ...");
 //delay(3000);
 doughreactivation();
 }
 }
 //function for the 3 step approach - there is a break of 15 minutes after each phase to add flour and water
 void threephases()
 {
 //Step execution
 breed("Dough activation 1",intervala,Step1min,Step1max,tolerance_3);
 pausePhase(intervalPause);
 breed("Dough activation 2",intervalb,Step2min,Step2max,tolerance_3);
 pausePhase(intervalPause);
 breed("Dough activation 3",intervalc,Step3min,Step3max,tolerance_3);
 digitalWrite(ledpin, digitalRead(ledpin) ^ 1);
 slcd.clear();
 slcd.setCursor(0,0);
 slcd.print("Done!!!");
 while(true);
 }
 //function for the 1 step approach - based on a linear function, the temperature will be reduced evenly
 void onephase(long interval)
 {
 long starttime=millis();
 //Step execution
 while((millis()-starttime) {
 //wie lang lief es schon
 long timerun=(millis()-starttime)/60000;
 breed("1-Step",900000,calcTemp(timerun),calcTemp(timerun)+tolerance_1,tolerance_1);
 }
 slcd.clear();
 slcd.setCursor(0,0);
 slcd.print("Done!!!");
 while(true);
 }
 int calcTemp(long timerun)
 {
 //Serial.println(Step1_1max-(((float)(Step1_1max-Step1_1min)/dur1Phase)*timerun));
 return Step1_1max-(((float)(Step1_1max-Step1_1min)/dur1Phase)*timerun);
 }
 //function switches the relay to turn on heating device
 void heaton()
 {
 digitalWrite(RelayLight, HIGH);
 digitalWrite(RelayFan,LOW);
 //Serial.println("- Flame on -");
 //Serial.println("- Fan off -");
 }
 //function turns off fan and heating device
 void alloff()
 {
 digitalWrite(RelayLight, LOW);
 digitalWrite(RelayFan,LOW);
 //Serial.println("- Flame on -");
 //Serial.println("- Fan off -");
 }
 //function turns on the fan by switching a relay
 void fanon()
 {
 digitalWrite(RelayLight, LOW);
 digitalWrite(RelayFan,HIGH);
 //Serial.println("- Flame on -");
 //Serial.println("- Fan off -");
 }
 //function dough reactivation - the temperature will be steady
 void doughreactivation()
 {
 breed("doughreactivation",intervalTR,TRmin,TRmax,tolerance_TR);
 digitalWrite(ledpin, digitalRead(ledpin) ^ 1);
 slcd.clear();
 slcd.setCursor(0,0);
 slcd.print("Done!!!");
 while(true);
 }
 //function ready temperature from the sensor
 int readingtemperature()
 {
 int readvalue;
 float temp;
 readvalue=analogRead(0);
 //resistance
 temp=(float)(1023-readvalue)*10000/readvalue; //get the resistance of the sensor;
 temp=1/(log(temp/10000)/B+1/298.15)-273.15;//convert to temperature via datasheet ;
 return temp;
 }
 //Breeding the dough
 void breed(const char* text,long interval,int mintemp, int maxtemp,int tolerance) {
 init_Display();
 long previousMillis=millis();
 int heaton=0;
 int fanon=0;
 debug(text,previousMillis,interval,maxtemp,mintemp);
 while( (millis() - previousMillis) < interval)
 {
 displaycounter=(interval - (millis() - previousMillis)) / 60000 + 1;
 printCrap(text);
 debug(text,previousMillis,interval,maxtemp,mintemp);
 if (!fanon && (readingtemperature() < mintemp)) { heaton(); heaton=1; } else if (!heaton && (readingtemperature() > maxtemp))
 {
 fanon();
 fanon=1;
 }
 else {
 Serial.println("hier");
 if (heaton){
 if(readingtemperature() > mintemp+tolerance) {
 alloff();
 heaton=0;
 }
 }
 else if (fanon){
 if(readingtemperature() < maxtemp-tolerance) {
 alloff();
 fanon=0;
 }
 }
 }
 delay(Updatedelay);
 }
 //timer off
 alloff();
 slcd.setCursor(5,1);
 slcd.print("Ende: work!");
 delay(2000);
 }
 //function for pause between phases when dough is growing
 void pausePhase(long interval) {
 //init_Display();
 slcd.noPower();
 slcd.noDisplay();
 //led an
 digitalWrite(ledpin, digitalRead(ledpin) ^ 1);
 int previousMillis=millis();
 while(millis() - previousMillis < interval)
 {
 debug("pause",previousMillis,interval,0,0);
 delay(UpdateDelayPause);
 }
 //led aus
 digitalWrite(ledpin, digitalRead(ledpin) ^ 1);
 }
 //Reporting functions
 void printCrap(const char* text) {
 slcd.clear();
 slcd.setCursor(0,0);
 slcd.print(text);
 slcd.setCursor(0,1);
 slcd.print("T:");
 slcd.setCursor(2,1);
 slcd.print(readingtemperature(),DEC);
 slcd.setCursor(5,1);
 slcd.print("Noch:");
 slcd.setCursor(10,1);
 slcd.print(displaycounter,DEC);
 }
 //Debugging functions
 void debug(const char* text, long prevMillis, long interval,int maxtemp,int mintemp) {
 Serial.print("Current temperature is: ");
 Serial.println(readingtemperature());
 Serial.print("This is step: ");
 Serial.println(text);
 Serial.print("Current Millis: ");
 Serial.println(millis());
 Serial.print("previousMillis: ");
 Serial.println(prevMillis);
 Serial.print("Time run ");
 Serial.println(millis()-prevMillis);
 Serial.print("Interval: ");
 Serial.println(interval);
 Serial.print("MaxTemp:");
 Serial.println(maxtemp);
 Serial.print("MinTemp:");
 Serial.println(mintemp);
 }
 int selection=1;
 long holdtime=2000;
 int getSelection() {
 init_Display();
 slcd.println("Breeder 2000");
 delay(2000);
 //attachInterrupt(0,buttonPressed,HIGH);
 //interrupts();
 slcd.clear();
 slcd.setCursor(0,0);
 slcd.print("Select Program:");
 slcd.setCursor(0,1);
 slcd.print("1P ");
 slcd.setCursor(5,1);
 slcd.print("3P ");
 slcd.setCursor(9,1);
 slcd.print("TR ");
 int buttonstate=0;
 long starttime=0;
 while(true) {
 buttonstate=digitalRead(buttonpin);
 if(buttonstate==HIGH) {
 if(starttime==0)
 starttime=millis();
 else
 if (millis()-starttime > holdtime)
 break;
 }
 else if(starttime!=0) {
 toggleSelection();
 starttime=0;
 }
 delay(100);
 }
 return selection;
 }
 void toggleSelection() {
 selection = (selection+1)%3;
 if(selection==0) {
 slcd.setCursor(3,1);
 slcd.print("X");
 slcd.setCursor(8,1);
 slcd.print(" ");
 slcd.setCursor(12,1);
 slcd.print(" ");
 }
 else if(selection==1) {
 slcd.setCursor(3,1);
 slcd.print(" ");
 slcd.setCursor(8,1);
 slcd.print("X");
 slcd.setCursor(12,1);
 slcd.print(" ");
 }
 else if(selection==2) {
 slcd.setCursor(3,1);
 slcd.print(" ");
 slcd.setCursor(8,1);
 slcd.print(" ");
 slcd.setCursor(12,1);
 slcd.print("X");
 }
 delay(200);
 }
 //function to record button press
 void buttonPressed() {
 //digitalWrite(ledpin, digitalRead(ledpin) ^ 1);
 }

2 Comments Add yours

  1. of says:

    This is great. I have a very similar design work in progress but I am using cork panels instead of foil.

Leave a Reply

Your email address will not be published. Required fields are marked *