├── README.md ├── bm_pgrm └── bm_pgrm.ino └── bm_recorder └── bm_recorder.ino /README.md: -------------------------------------------------------------------------------- 1 | Arduino Bread 2 | ======================= 3 | 4 | This project replaces the control circuit of a Sanyo breadmaker "The Bread Factory Plus" (Model SBM-20) 5 | (many other models are similar) with a circuit based on an Arduino. 6 | 7 | The aim is to separate out the different stages in the breadmaker's cycle so I can make good sourdough bread. In general the sourdough takes longer to rise than dough made using store bought dried yeast granules. The rise time can also vary depending on how the sourdough starter is on any day. Also the longer the rise time the stronger the sour flavour of the bread. 8 | 9 | This project is ongoing. Currently I have made a breadmaker with an Arduino brain where I can set up programs with my own times for any part of the breadmaker cycle. To make a loaf of sourdough, I put the ingredients in the pan and select a program that mixes and kneads the dough then keeps the pan at rise temperature indefinetly. When I've checked the bread is sufficiently risen, I stop that program and run a bake only program. Eventually, I'd like some way of measuring the height of the dough so the arduino can track when the bread is risen and then switch to the bake cycle. The current rise temperature is the same as for the original breadmaker cycle (~37 °C). I'll probably add an option to rise the bread at a lower temperature for longer to get a stronger sour flavour. 10 | 11 | The model of breadmaker I'm using is a fairly old Sanyo "The Bread Factory Plus" (Model SBM-20). From other people's projects I suspect many breadmakers are quite similar internally and these notes could be applicable to many other models and brands. 12 | 13 | The circuit and documentation can be found at http://elfnor.com/Arduino%20Bread%20Details.html 14 | 15 | This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. 16 | 17 | elfnor 18 | 19 | 20 | -------------------------------------------------------------------------------- /bm_pgrm/bm_pgrm.ino: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | New brain for a Sanyo breadmaker "The Bread Factory Plus" (Model SBM-20) 4 | (many other models are similar) 5 | 6 | circuit and documentation on www.elfnor.com/Arduino Bread Details.html 7 | code https://github.com/elfnor/arduino-bread 8 | 9 | This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. 10 | 11 | elfnor 12 | */ 13 | 14 | // consts and structure to define a set of times for loaf programs 15 | // if a value is set to zero that part is skipped. 16 | // the heat cycle time is only used to estimate completion time 17 | // actually heat cycle continues until bake temperature is reached 18 | 19 | 20 | 21 | struct PGRMS 22 | { 23 | int min_pulseMix; 24 | int min_contMix; 25 | int min_rise1; 26 | int min_punchDown; 27 | int min_rise2; 28 | int min_heat; 29 | int min_bake; 30 | }; 31 | 32 | 33 | const int N_PGRMS = 6; 34 | 35 | const PGRMS pgrms[N_PGRMS] = 36 | { 37 | {6, 29, 30, 1, 53, 8, 42}, // 0 : normal loaf 38 | {6, 29, 0, 0, 0, 0, 0}, // 1 : mix only 39 | {0, 0, 1000, 0, 0, 0, 0}, // 2 : very long rise 40 | {6, 29, 1000, 0, 0, 0, 0}, // 3 : mix plus long rise 41 | {0, 0, 0, 0, 0, 8, 42}, // 4 : bake only 42 | {3, 3, 10, 0, 0, 0, 0}, // 5 : test nonsense 43 | }; 44 | 45 | 46 | 47 | #include 48 | #include 49 | #include 50 | 51 | #define UP_BUTTON_PIN 0 52 | #define DOWN_BUTTON_PIN 1 53 | #define SELECT_BUTTON_PIN 2 54 | #define START_BUTTON_PIN 3 55 | #define STOP_BUTTON_PIN 4 56 | 57 | #define heater 6 58 | #define motor 7 59 | #define buzzer 8 60 | #define DIO 9 //TM1637 61 | #define CLK 10 //TM1637 4 digit display 62 | 63 | #define LED_PIN 13 64 | 65 | const int thermistor = A0; // Analog input pin that the thermistor is connected to 66 | 67 | //instantiate a TM1637 object 68 | TM1637Display display(CLK, DIO); 69 | 70 | // Instantiate a Bounce object 71 | Bounce start_deb = Bounce(); 72 | Bounce stop_deb = Bounce(); 73 | Bounce up_deb = Bounce(); 74 | Bounce down_deb = Bounce(); 75 | Bounce select_deb = Bounce(); 76 | 77 | 78 | boolean stop_pushed = false; 79 | 80 | long min_start; 81 | long min_total; 82 | long min_delay = 0; 83 | 84 | int n = 0; //selected program 85 | 86 | 87 | void setup() { 88 | pinMode(motor, OUTPUT); 89 | pinMode(heater, OUTPUT); 90 | pinMode(buzzer, OUTPUT); 91 | 92 | // Setup the button with an internal pull-up : 93 | pinMode(START_BUTTON_PIN,INPUT_PULLUP); 94 | pinMode(STOP_BUTTON_PIN, INPUT_PULLUP); 95 | pinMode(UP_BUTTON_PIN, INPUT_PULLUP); 96 | pinMode(DOWN_BUTTON_PIN, INPUT_PULLUP); 97 | pinMode(SELECT_BUTTON_PIN, INPUT_PULLUP); 98 | 99 | // After setting up the button, setup the Bounce instance : 100 | start_deb.attach(START_BUTTON_PIN); 101 | start_deb.interval(5); // interval in ms 102 | 103 | stop_deb.attach(STOP_BUTTON_PIN); 104 | stop_deb.interval(5); // interval in ms 105 | 106 | up_deb.attach(UP_BUTTON_PIN); 107 | up_deb.interval(5); // interval in ms 108 | 109 | down_deb.attach(DOWN_BUTTON_PIN); 110 | down_deb.interval(5); // interval in ms 111 | 112 | select_deb.attach(SELECT_BUTTON_PIN); 113 | select_deb.interval(5); // interval in ms 114 | 115 | //Setup the LED : 116 | pinMode(LED_PIN,OUTPUT); 117 | digitalWrite(motor, 0); 118 | digitalWrite(heater,0); 119 | 120 | //TM1637 4 digit display 121 | display.setBrightness(0xF); 122 | uint8_t data[] = { 0x0, 0x0, 0x0, 0x0 }; 123 | display.setSegments(data); 124 | setDisplay(n); 125 | 126 | // 10 second delay to make it easier to upload new programs 127 | //delay(10000); 128 | // initialize serial communications at 9600 bps: 129 | Serial.begin(9600); 130 | // three beeps to show its turned on 131 | tone(buzzer, 262, 200); 132 | delay(200); 133 | tone(buzzer, 262, 200); 134 | delay(200); 135 | tone(buzzer, 262, 200); 136 | 137 | } 138 | 139 | void loop() 140 | { 141 | setDisplay(n); 142 | // Update the Bounce instances : 143 | start_deb.update(); 144 | select_deb.update(); 145 | up_deb.update(); 146 | down_deb.update(); 147 | 148 | 149 | if ( select_deb.fell() ) 150 | { 151 | tone(buzzer, 262, 500); 152 | n = n + 1; 153 | if ( n >= N_PGRMS) { n = 0; } 154 | setDisplay(n); 155 | min_total = pgrms[n].min_pulseMix + pgrms[n].min_contMix + pgrms[n].min_rise1 + pgrms[n].min_rise2 + + pgrms[n].min_heat + pgrms[n].min_bake; 156 | } 157 | 158 | if ( up_deb.fell() ) 159 | { 160 | int min_display; 161 | min_display = min_total + min_delay; 162 | displayMins(min_display); 163 | min_delay = min_delay + 1; 164 | min_display = min_total + min_delay; 165 | displayMins(min_display); 166 | } 167 | 168 | if ( down_deb.fell() ) 169 | { 170 | int min_display; 171 | min_display = min_total + min_delay; 172 | displayMins(min_display); 173 | min_delay = min_delay - 1; 174 | if ( min_delay <= 0) { min_delay = 0; } 175 | min_display = min_total + min_delay; 176 | displayMins(min_display); 177 | } 178 | 179 | if ( start_deb.fell() ) 180 | { 181 | tone(buzzer, 262, 500); 182 | make_loaf(n); 183 | } 184 | } 185 | 186 | 187 | void make_loaf(int n) 188 | { 189 | stop_pushed = false; 190 | min_start = millis()/1000; 191 | min_start = min_start/60; 192 | 193 | min_total = min_total + min_delay; 194 | 195 | delayUpdate(min_delay); 196 | if (stop_pushed){return;} 197 | pulseMix(pgrms[n].min_pulseMix); 198 | if (stop_pushed){return;} 199 | contMix(pgrms[n].min_contMix); 200 | if (stop_pushed){return;} 201 | rise(pgrms[n].min_rise1); 202 | if (stop_pushed){return;} 203 | punchDown(pgrms[n].min_punchDown); 204 | if (stop_pushed){return;} 205 | rise(pgrms[n].min_rise2); 206 | if (stop_pushed){return;} 207 | heat(pgrms[n].min_heat); 208 | if (stop_pushed){return;} 209 | bake(pgrms[n].min_bake); 210 | if (stop_pushed){return;} 211 | setDisplay(0); 212 | } 213 | 214 | 215 | 216 | boolean checkStop() 217 | { 218 | //check if stop button pressed 219 | // this is called every 1000 mills 220 | 221 | updateDisplayTime(); 222 | 223 | stop_deb.update(); 224 | if ( stop_deb.fell() ) 225 | { 226 | digitalWrite(LED_PIN, LOW); 227 | digitalWrite(motor, LOW); 228 | digitalWrite(heater, LOW); 229 | stop_pushed = true; 230 | tone(buzzer, 196, 500); 231 | setDisplay(0); 232 | return true; 233 | } 234 | return false; 235 | } 236 | 237 | void setDisplay(int n) 238 | { 239 | //write n to display 240 | display.showNumberDec(n, false); 241 | } 242 | 243 | void updateDisplayTime() 244 | { 245 | int min_display; 246 | // set display to show time until loaf completed. 247 | 248 | long min_now = millis()/1000; 249 | min_now = min_now/60; 250 | min_display = min_total - min_now - min_start; 251 | displayMins(min_display); 252 | } 253 | 254 | void displayMins(int min_display) 255 | // set clock display to hh:mm 256 | { 257 | uint8_t segto; 258 | int hours = int(min_display/60); 259 | int cont = hours*100 + min_display-hours*60; 260 | 261 | display.showNumberDec(cont, true); 262 | 263 | cont++; 264 | 265 | segto = 0x80 | display.encodeDigit((cont / 100)%10); 266 | display.setSegments(&segto, 1, 1); 267 | } 268 | 269 | 270 | void delayUpdate(long minutes) 271 | { 272 | if (minutes != 0) 273 | { 274 | // check stop button every second 275 | long n = 60 * minutes; 276 | for (int i=0; i thigh) 347 | { 348 | t_off = 59; 349 | t_on = 1; 350 | } 351 | if (thermistorValue < tlow) 352 | { 353 | t_off = 50; 354 | t_on = 10; 355 | } 356 | if (thermistorValue <= thigh && thermistorValue >= tlow) 357 | { 358 | t_off = 54; 359 | t_on = 6; 360 | } 361 | digitalWrite(heater, HIGH); 362 | for (int j=0; j thigh) 443 | { 444 | t_off = 10; 445 | t_on = 5; 446 | } 447 | if (thermistorValue < tlow) 448 | { 449 | t_off = 0; 450 | t_on = 15; 451 | } 452 | if (thermistorValue <= thigh && thermistorValue >= tlow) 453 | { 454 | t_off = 6; // 10 0.4*15 455 | t_on = 9; //15 0.6*15 456 | } 457 | digitalWrite(heater, HIGH); 458 | for (int j=0; j