main/automation.c

changeset 0
b74b0e4902c3
child 1
ad2c8b13eb88
equal deleted inserted replaced
-1:000000000000 0:b74b0e4902c3
1 /**
2 * @file automation.c
3 * @brief Automation functions.
4 */
5
6 #include "config.h"
7
8 int BoilPower = 100;
9 int LastMashStep = 0;
10 char temp_buf[64];
11 char logline[128];
12 char strftime_buf[64];
13 bool loop;
14 bool CoolBeep = false;
15 bool Resume = false;
16 bool pumpRest = false;
17 bool updateRuntime = false;
18 bool NewMinute = false;
19 bool TempReached = false;
20 uint8_t MashState = MASH_NONE;
21 float temp_MLT;
22 float MinMash = 38.0;
23 float MaxMash = 80.0;
24 uint32_t power_MLT = 0;
25 uint32_t power_HLT = 0;
26 uint32_t counts = 0;
27 float stageTemp = 0.0;
28 uint16_t stageTime = 0;
29 uint16_t TimeWhirlPool = 0;
30 uint32_t TimeLeft = 0;
31 uint32_t TimeSpent = 0;
32 uint32_t SecsCount = 0;
33 uint32_t pumpTime = 0;
34 uint32_t TimeBrewing = 0;
35 uint16_t Steady = 0;
36 bool _NewMinute = false;
37 bool _UseHLT = false;
38 bool _Prompt = false;
39
40 extern bool System_TimeOk;
41 extern sButton Buttons[MAXBUTTONS];
42 extern int Main_Screen;
43 extern DS18B20_State *ds18b20_state;
44 extern DRIVER_State *driver_state;
45 extern SemaphoreHandle_t xSemaphoreDS18B20;
46 extern SemaphoreHandle_t xSemaphoreDriver;
47 extern double Output;
48 extern time_t now;
49 extern struct tm timeinfo;
50
51 #ifdef CONFIG_TEMP_SENSORS_SIMULATOR
52 extern float Fake_MLT;
53 extern float Fake_HLT;
54 #endif
55
56 static const char *TAG = "automation";
57
58
59 /*
60 * Automation init function that only runs once when a
61 * new screen is entered.
62 */
63 bool Automation_Init(void)
64 {
65 switch (Main_Screen) {
66 case MAIN_AUTO_INIT:
67 #ifdef CONFIG_TEMP_SENSORS_SIMULATOR
68 Fake_MLT = recipe.MashStep[0].Temperature - 10;
69 Fake_HLT = recipe.SpargeTemp - 15;
70 if (xSemaphoreTake(xSemaphoreDS18B20, 10) == pdTRUE) {
71 ds18b20_state->mlt_temperature = ((int)(Fake_MLT * 16)) / 16.0;
72 ds18b20_state->hlt_temperature = ((int)(Fake_HLT * 16)) / 16.0;
73 xSemaphoreGive(xSemaphoreDS18B20);
74 }
75 #endif
76 for (int i = 0; i < 7; i++) {
77 if (recipe.MashStep[i].Resttime)
78 LastMashStep = i;
79 }
80 ESP_LOGI(TAG, "Last mash step %d", LastMashStep);
81
82 // Check for a crashed session.
83 if (runtime.AutoModeStarted) {
84 TopMessage("Brouwen hervatten?");
85 Buttons_Add( 40, 100, 80, 40, "Ja", 0);
86 Buttons_Add(200, 100, 80, 40, "Nee", 1);
87 Buttons_Show();
88 SoundPlay(SOUND_Prompt);
89 loop = true;
90 while (loop) {
91 switch (Buttons_Scan()) {
92 case 0: loop = false;
93 Resume = true;
94 Main_Screen = runtime.StageResume;
95 TimeLeft = runtime.StageTimeLeft;
96 TimeBrewing = runtime.TimeBrewing;
97 _UseHLT = runtime.UseHLT;
98 MashState = MASH_NONE;
99 pumpTime = 0;
100 pumpRest = false;
101 if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
102 driver_state->enable = true;
103 if (_UseHLT) {
104 driver_state->hlt_sp = recipe.SpargeTemp;
105 driver_state->hlt_mode = HLT_MODE_BANG;
106 }
107 xSemaphoreGive(xSemaphoreDriver);
108 }
109 ESP_LOGI(TAG, "Resume brew screen %d, time left %d", Main_Screen, TimeLeft);
110 log_begin((time_t)0);
111 update_json();
112 log_annotation(ANNOTATION_SYSTEM, "Resume");
113 return true;
114 break;
115
116 case 1: loop = false;
117 Resume = false;
118 break;
119
120 default:
121 break;
122 }
123 vTaskDelay(50 / portTICK_PERIOD_MS);
124 }
125 Buttons_Clear();
126 TFT_fillScreen(_bg);
127 }
128
129 if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
130 driver_state->enable = true;
131 xSemaphoreGive(xSemaphoreDriver);
132 }
133 runtime.AutoModeStarted = true;
134 runtime.UseHLT = _UseHLT = false;
135 runtime.TimeBrewing = 0;
136 TimeBrewing = 0;
137 runtime.StageResume = MAIN_AUTO_INIT;
138 runtime.StageTimeLeft = 0;
139 runtime.HopAddition = 0;
140 runtime.Logfile[0] = '\0';
141 runtime.PumpCooling = false;
142 write_runtime();
143 power_MLT = power_HLT = counts = 0;
144 log_clean();
145 vTaskDelay(250 / portTICK_PERIOD_MS); // Allow some time
146 break;
147
148 case MAIN_AUTO_DELAYSTART:
149 break;
150
151 case MAIN_AUTO_HEATUP:
152 if (runtime.UseHLT) {
153 if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
154 driver_state->hlt_mode = HLT_MODE_BANG;
155 xSemaphoreGive(xSemaphoreDriver);
156 }
157 TopMessage("Spoelwater opwarmen");
158 MLT_info(71, 26, false);
159 HLT_info(71,150, false, false);
160 }
161 break;
162
163 case MAIN_AUTO_MASH_IN:
164 case MAIN_AUTO_MASH_1:
165 case MAIN_AUTO_MASH_2:
166 case MAIN_AUTO_MASH_3:
167 case MAIN_AUTO_MASH_4:
168 case MAIN_AUTO_MASH_5:
169 case MAIN_AUTO_MASH_6:
170 case MAIN_AUTO_MASH_OUT:
171 if (Main_Screen == MAIN_AUTO_MASH_IN) {
172 MinMash = 38.0;
173 MaxMash = recipe.MashStep[1].Temperature + 10.0;
174 TimeBrewing = 0;
175 runtime.TimeBrewing = 0;
176 if (System_TimeOk) {
177 time(&now);
178 localtime_r(&now, &timeinfo);
179 log_begin(now);
180 runtime.BrewStart = now;
181 } else {
182 log_begin((time_t)0);
183 runtime.BrewStart = (time_t)0;
184 }
185 updateRuntime = true;
186 } else if (Main_Screen == MAIN_AUTO_MASH_OUT) {
187 MinMash = 75.0;
188 MaxMash = 80.0;
189 } else {
190 MinMash = recipe.MashStep[Main_Screen - MAIN_AUTO_MASH_IN - 1].Temperature;
191 if (recipe.MashStep[Main_Screen - MAIN_AUTO_MASH_IN + 1].Resttime) {
192 MaxMash = recipe.MashStep[Main_Screen - MAIN_AUTO_MASH_IN + 1].Temperature;
193 } else {
194 MaxMash = 75.0;
195 }
196 }
197 MashState = MASH_NONE;
198 pumpTime = 0;
199 pumpRest = false;
200 runtime.StageResume = Main_Screen;
201 updateRuntime = true;
202 TopMessage("Maischen");
203 MLT_info(71, 26, false);
204 if (_UseHLT) {
205 HLT_info(71,170, false, true);
206 }
207 break;
208
209 case MAIN_AUTO_TOBOIL:
210 if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
211 driver_state->mlt_sp = stageTemp = config.BoilTemperature;
212 driver_state->mlt_mode = MLT_MODE_EXT;
213 driver_state->hlt_sp = 0.0;
214 driver_state->hlt_mode = HLT_MODE_NONE;
215 xSemaphoreGive(xSemaphoreDriver);
216 }
217
218 runtime.StageResume = Main_Screen;
219 updateRuntime = true;
220 TempReached = false;
221
222 TopMessage("Naar koken");
223 MLT_info(71, 26, false);
224 Buttons_Add( 5, 30, 60, 40, "+sp", 0);
225 Buttons_Add(255, 30, 60, 40, "-sp", 1);
226 Buttons_Show();
227 ESP_LOGI(TAG, "Mash done, going to boil.");
228 break;
229
230 case MAIN_AUTO_BOILING:
231 if (Resume) {
232 TimeLeft = runtime.StageTimeLeft * 60;
233 } else {
234 // +1 minute for flameout and 2 seconds for a smooth transition.
235 runtime.StageTimeLeft = TimeLeft = (recipe.BoilTime * 60) + 60;
236 runtime.StageResume = Main_Screen;
237 runtime.HopAddition = 0;
238 }
239 SecsCount = 0;
240
241 if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
242 driver_state->mlt_sp = stageTemp = config.BoilTemperature;
243 driver_state->mlt_mode = MLT_MODE_EXT;
244 xSemaphoreGive(xSemaphoreDriver);
245 }
246 SoundPlay(SOUND_TempReached);
247 BoilPower = equipment.BoilPower;
248 updateRuntime = true;
249 TopMessage("Koken");
250 MLT_info(71, 26, false);
251 Buttons_Add( 3, 30, 60, 40, "+sp", 0);
252 Buttons_Add(257, 30, 60, 40, "-sp", 1);
253 Buttons_Add( 3, 190, 60, 40, "+1m", 2);
254 Buttons_Add(257, 190, 60, 40, "-1m", 3);
255 Buttons_Show();
256 ESP_LOGI(TAG, "Boil temperature reached, boil %d minutes", (TimeLeft / 60) -1);
257 log_annotation(ANNOTATION_STAGE, "Koken");
258 break;
259
260 case MAIN_AUTO_COOLING_H:
261 case MAIN_AUTO_COOLING_M:
262 case MAIN_AUTO_COOLING_C:
263 TempReached = false;
264 runtime.StageResume = Main_Screen;
265 runtime.StageTimeLeft = 0;
266 updateRuntime = true;
267 if ((Main_Screen == MAIN_AUTO_COOLING_H) && (! recipe.Whirlpool7)) {
268 // Skip cooling before whirlpool 74 degrees
269 Main_Screen = MAIN_AUTO_COOLING_M;
270 return true; //goto startover;
271 }
272 if ((Main_Screen == MAIN_AUTO_COOLING_M) && (! recipe.Whirlpool6)) {
273 // Skip cooling before whirlpool 63 degrees.
274 Main_Screen = MAIN_AUTO_COOLING_C;
275 return true; //goto startover;
276 }
277 TopMessage("Start koelen?");
278 Buttons_Add( 40, 100, 80, 40, "Start", 0);
279 Buttons_Add(200, 100, 80, 40, "Stop", 1);
280 Buttons[1].dark = true;
281 Buttons_Show();
282 SoundPlay(SOUND_Prompt);
283 _Prompt = true;
284 break;
285
286 case MAIN_AUTO_WHIRLPOOL7:
287 case MAIN_AUTO_WHIRLPOOL6:
288 case MAIN_AUTO_WHIRLPOOL2:
289 TempReached = true;
290 runtime.StageResume = Main_Screen;
291 updateRuntime = true;
292 if ((Main_Screen == MAIN_AUTO_WHIRLPOOL9) && (! recipe.Whirlpool9)) {
293 // Skip whirlpool 93 degrees.
294 Main_Screen = MAIN_AUTO_COOLING_H;
295 return true; //goto startover;
296 }
297 if ((Main_Screen == MAIN_AUTO_WHIRLPOOL7) && (! recipe.Whirlpool7)) {
298 // Skip whirlpool 74 degrees.
299 Main_Screen = MAIN_AUTO_COOLING_M;
300 return true; //goto startover;
301 }
302 if ((Main_Screen == MAIN_AUTO_WHIRLPOOL6) && (! recipe.Whirlpool6)) {
303 // Skip whirlpool 63 degrees.
304 Main_Screen = MAIN_AUTO_COOLING_C;
305 return true; //goto startover;
306 }
307 if ((Main_Screen == MAIN_AUTO_WHIRLPOOL2) && (! recipe.Whirlpool2)) {
308 // Skip final whirlpool.
309 Main_Screen = MAIN_AUTO_DONE;
310 return true; //goto startover;
311 }
312
313 TopMessage("Start Whirlpool?");
314 Buttons_Add( 40, 100, 80, 40, "Start", 0);
315 Buttons_Add(200, 100, 80, 40, "Stop", 1);
316 Buttons[1].dark = true;
317 Buttons_Show();
318 SoundPlay(SOUND_Prompt);
319 _Prompt = true;
320 break;
321
322 case MAIN_AUTO_DONE:
323 case MAIN_AUTO_ABORT:
324 if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
325 driver_state->enable = false;
326 driver_state->mlt_mode = MLT_MODE_NONE;
327 driver_state->mlt_sp = 0.0;
328 driver_state->hlt_mode = HLT_MODE_NONE;
329 driver_state->hlt_sp = 0.0;
330 driver_state->pump_run = 0;
331 xSemaphoreGive(xSemaphoreDriver);
332 }
333 _fg = TFT_YELLOW;
334 TFT_setFont(DEJAVU24_FONT, NULL);
335 if (Main_Screen == MAIN_AUTO_DONE) {
336 TFT_print("Brouwen is gereed.", CENTER, CENTER);
337 ESP_LOGI(TAG, "Brew is done");
338 SoundPlay(SOUND_End);
339 } else {
340 TFT_print("Brouwen is afgebroken.", CENTER, CENTER);
341 ESP_LOGI(TAG, "Brew is aborted");
342 SoundPlay(SOUND_Warn);
343 }
344 log_close();
345 runtime.Logfile[0] = '\0';
346 runtime.BrewStart = (time_t)0;
347 runtime.AutoModeStarted = false;
348 runtime.StageResume = MAIN_MODE_FREE;
349 runtime.PumpCooling = false;
350 runtime.HopAddition = 0;
351 runtime.TimeBrewing = 0;
352 runtime.StageTimeLeft = 0;
353 updateRuntime = true;
354 Buttons_Add(130, 200, 60, 40, "Ok", 0);
355 Buttons[0].dark = true;
356 Buttons_Show();
357 break;
358
359 default:
360 break;
361 }
362
363 return false;
364 }
365
366
367
368 /*
369 * Automation loop screens. Mostly non-blocking.
370 */
371 bool Automation_Loop(void)
372 {
373 static bool beeped = false;
374 char tmp[32];
375 uint16_t y;
376
377 switch (Main_Screen) {
378
379 case MAIN_AUTO_INIT:
380 /*
381 * Present selected equipment and recipe.
382 */
383 read_recipe(config.RecipeRec);
384 y = 28;
385 TopMessage("Automaat");
386 TFT_setFont(DEFAULT_FONT, NULL);
387 ShowText(2,y,"Installatie", equipment.Name);
388 y += 16;
389 ShowText(2,y,"Recept", recipe.Name);
390 y += 16;
391 ShowFloat(2, y, "Maisch in", " C", recipe.MashStep[0].Temperature, 2);
392 ShowFloat(162, y, "Spoelwater", " C", recipe.SpargeTemp, 2);
393 y += 16;
394 _fg = TFT_WHITE;
395 TFT_print("Maisch stap", 2, y);
396 TFT_print("Temp.", 200, y);
397 TFT_print("Rust", 260, y);
398 _fg = TFT_YELLOW;
399 y += 16;
400 for (int i = 1; i < 8; i++) {
401 if (recipe.MashStep[i].Resttime) {
402 TFT_print(recipe.MashStep[i].Name, 2, y);
403 sprintf(tmp, "%.2f", recipe.MashStep[i].Temperature);
404 TFT_print(tmp, 200, y);
405 sprintf(tmp, "%2d min", recipe.MashStep[i].Resttime);
406 TFT_print(tmp, 260, y);
407 y += 16;
408 }
409 }
410 ShowInteger(2, y, "Kooktijd", " miniuten", recipe.BoilTime);
411 y += 16;
412 if (recipe.Additions) {
413 _fg = TFT_YELLOW;
414 sprintf(tmp, "%d ", recipe.Additions);
415 TFT_print(tmp, 2, y);
416 _fg = TFT_WHITE;
417 TFT_print("toevoegingen om", LASTX, y);
418 _fg = TFT_YELLOW;
419 for (int i = 1; i <= recipe.Additions; i++) {
420 sprintf(tmp, " %d", recipe.Addition[i-1].Time);
421 TFT_print(tmp, LASTX, y);
422 }
423 _fg = TFT_WHITE;
424 TFT_print(" minuten", LASTX, y);
425 } else {
426 _fg = TFT_WHITE;
427 TFT_print("Geen hop toevoegingen.", 2, y);
428 }
429 y += 16;
430 ShowFloat(2, y, "Koelen tot", " C", recipe.CoolTemp, 2);
431 if (recipe.Whirlpool9) {
432 ShowInteger(2, y, "Whirlpool 88..100 graden", " minuten", recipe.Whirlpool9);
433 y += 16;
434 }
435 if (recipe.Whirlpool7) {
436 ShowInteger(2, y, "Whirlpool 71..77 graden", " minuten", recipe.Whirlpool7);
437 y += 16;
438 }
439 if (recipe.Whirlpool6) {
440 ShowInteger(2, y, "Whirlpool 60..66 graden", " minuten", recipe.Whirlpool6);
441 y += 16;
442 }
443 if (recipe.Whirlpool2) {
444 ShowInteger(2, y, "Whirlpool koud", " minuten", recipe.Whirlpool2);
445 y += 16;
446 }
447 Buttons_Add( 0, 210, 70, 30, "Stop" , 0);
448 Buttons_Add(250, 210, 70, 30, "Start" , 1);
449 Buttons[0].dark = true;
450 Buttons_Show();
451 loop = true;
452 while (loop) {
453 switch (Buttons_Scan()) {
454 case 0: loop = false;
455 Main_Screen = MAIN_AUTO_ABORT;
456 break;
457
458 case 1: loop = false;
459 break;
460
461 default: break;
462 }
463 vTaskDelay(20 / portTICK_PERIOD_MS);
464 }
465 if (Main_Screen == MAIN_AUTO_ABORT)
466 break;
467
468 _UseHLT = false;
469 _bg = TFT_BLACK;
470 TFT_fillScreen(_bg);
471 TopMessage("Maisch water aanwezig?");
472 Buttons_Clear();
473 Buttons_Add( 40, 100, 80, 40, "Ja", 0);
474 Buttons_Add(200, 100, 80, 40, "Nee", 1);
475 Buttons_Show();
476 SoundPlay(SOUND_Prompt);
477 loop = true;
478 while (loop) {
479 switch (Buttons_Scan()) {
480 case 0: loop = false;
481 break;
482
483 case 1: loop = false;
484 Main_Screen = MAIN_AUTO_ABORT;
485 break;
486
487 default: break;
488 }
489 vTaskDelay(20 / portTICK_PERIOD_MS);
490 }
491 if (Main_Screen == MAIN_AUTO_ABORT)
492 break;
493
494 if ((equipment.SSR2 == SSR2_HLT_SHARE) || (equipment.SSR2 == SSR2_HLT_IND)) {
495 TopMessage("Spoelwater aanwezig?");
496 SoundPlay(SOUND_Prompt);
497 loop = true;
498 while (loop) {
499 switch (Buttons_Scan()) {
500 case 0: loop = false;
501 _UseHLT = true;
502 break;
503
504 case 1: loop = false;
505 break;
506
507 default: break;
508 }
509 vTaskDelay(20 / portTICK_PERIOD_MS);
510 }
511 runtime.UseHLT = _UseHLT;
512 } else {
513 runtime.UseHLT = _UseHLT = false;
514 }
515 updateRuntime = true;
516 if (_UseHLT) {
517 /*
518 * Calculate HLT setpoint for pre-heat. Substract the
519 * available Mash rest times, asume 0.5 degrees/minute
520 * heat capacity during mash.
521 */
522 int AvailableTime = 0;
523 for (int i = 1; i < 6; i++) // Only normal Mash steps
524 AvailableTime += recipe.MashStep[i].Resttime;
525 if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
526 driver_state->hlt_sp = recipe.SpargeTemp - ((AvailableTime / 2) + 2);
527 ESP_LOGI(TAG, "HLT preheat set to %4.1f", driver_state->hlt_sp);
528 xSemaphoreGive(xSemaphoreDriver);
529 }
530 }
531 Buttons_Clear();
532 Main_Screen = MAIN_AUTO_DELAYSTART;
533 break;
534
535 case MAIN_AUTO_DELAYSTART:
536 Main_Screen = MAIN_AUTO_HEATUP;
537 break;
538
539 case MAIN_AUTO_HEATUP:
540 if (! runtime.UseHLT) { // Skip if HLT is off
541 Main_Screen = MAIN_AUTO_MASH_IN;
542 break;
543 }
544
545 MLT_info(71, 26, true);
546 HLT_info(71,150, true, false);
547 if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
548 if (driver_state->hlt_pv >= driver_state->hlt_sp) {
549 Main_Screen = MAIN_AUTO_MASH_IN;
550 driver_state->hlt_sp = recipe.SpargeTemp; // Set final setpoint
551 }
552 xSemaphoreGive(xSemaphoreDriver);
553 }
554 break;
555
556 case MAIN_AUTO_MASH_IN:
557 case MAIN_AUTO_MASH_1:
558 case MAIN_AUTO_MASH_2:
559 case MAIN_AUTO_MASH_3:
560 case MAIN_AUTO_MASH_4:
561 case MAIN_AUTO_MASH_5:
562 case MAIN_AUTO_MASH_6:
563 case MAIN_AUTO_MASH_OUT:
564 temp_MLT = 0.0;
565 if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
566 temp_MLT = driver_state->mlt_pv;
567
568 if (MashState == MASH_ADD || MashState == MASH_REMOVE) {
569 driver_state->pump_run = 0;
570 } else if (MashState != MASH_NONE) {
571 if (Main_Screen == MAIN_AUTO_MASH_IN) {
572 driver_state->pump_run = (equipment.PumpPreMash && ! pumpRest) ? 1 : 0;
573 } else if (Main_Screen == MAIN_AUTO_MASH_OUT) {
574 driver_state->pump_run = (equipment.PumpMashOut && ! pumpRest) ? 1 : 0;
575 } else {
576 driver_state->pump_run = (equipment.PumpOnMash && ! pumpRest) ? 1 : 0;
577 }
578 }
579 xSemaphoreGive(xSemaphoreDriver);
580 }
581 if (MashState == MASH_NONE) {
582 stageTemp = recipe.MashStep[Main_Screen - MAIN_AUTO_MASH_IN].Temperature;
583 stageTime = recipe.MashStep[Main_Screen - MAIN_AUTO_MASH_IN].Resttime;
584 TempReached = false;
585 if (stageTime == 0) {
586 ESP_LOGI(TAG, "Mash step %d skipped", Main_Screen - MAIN_AUTO_MASH_IN);
587 Main_Screen++;
588 break;
589 }
590 if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
591 driver_state->mlt_sp = stageTemp;
592 driver_state->mlt_mode = MLT_MODE_PID;
593 xSemaphoreGive(xSemaphoreDriver);
594 }
595 MashState = MASH_WAITTEMP;
596 ESP_LOGI(TAG, "Mash step %d time: %d temp: %4.1f min: %4.1f max: %4.1f",
597 Main_Screen - MAIN_AUTO_MASH_IN, stageTime, stageTemp, MinMash, MaxMash);
598
599 if (Main_Screen > MAIN_AUTO_MASH_IN) {
600 // Do not annotate before the log is open.
601 if (Main_Screen == MAIN_AUTO_MASH_OUT) {
602 log_annotation(ANNOTATION_STAGE, "Uitmaischen");
603 } else {
604 sprintf(logline, "Maisch: %d", Main_Screen - MAIN_AUTO_MASH_IN);
605 log_annotation(ANNOTATION_STAGE, logline);
606 }
607 }
608
609 if (strlen(recipe.MashStep[Main_Screen - MAIN_AUTO_MASH_IN].Name)) {
610 TopMessage(recipe.MashStep[Main_Screen - MAIN_AUTO_MASH_IN].Name);
611 } else {
612 sprintf(temp_buf, "Maisch stap #%d", Main_Screen - MAIN_AUTO_MASH_IN);
613 TopMessage(temp_buf);
614 }
615 Buttons_Add( 5, 30, 60, 40, "+sp", 0);
616 Buttons_Add(255, 30, 60, 40, "-sp", 1);
617 Buttons_Show();
618
619 } else if (MashState == MASH_WAITTEMP) {
620 pumpRest = false;
621 if (temp_MLT < stageTemp) {
622 Steady = 0;
623 }
624 if ((temp_MLT >= stageTemp) && (Steady > 10)) {
625 SoundPlay(SOUND_TempReached);
626 TempReached = true;
627 MashState = MASH_REST;
628 if (Main_Screen == MAIN_AUTO_MASH_IN) {
629 TimerSet(0);
630 } else {
631 if (Resume && (runtime.StageTimeLeft < stageTime))
632 TimerSet(runtime.StageTimeLeft * 60);
633 else
634 TimerSet(stageTime * 60);
635 }
636 Resume = false;
637 runtime.StageTimeLeft = TimeLeft / 60;
638 updateRuntime = true;
639 ESP_LOGI(TAG, "Mash step %d temperature reached, rest time %d", Main_Screen - MAIN_AUTO_MASH_IN, TimeLeft / 60);
640 Buttons_Clear();
641 Buttons_Add( 0, 120, 60, 40, "+1m", 0);
642 Buttons_Add(260, 120, 60, 40, "-1m", 1);
643 Buttons_Show();
644 }
645 switch (Buttons_Scan()) {
646 case 0: if (stageTemp < MaxMash) {
647 stageTemp += 0.25;
648 if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
649 driver_state->mlt_sp = stageTemp;
650 xSemaphoreGive(xSemaphoreDriver);
651 }
652 }
653 break;
654
655 case 1: if (stageTemp > MinMash) {
656 stageTemp -= 0.25;
657 if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
658 driver_state->mlt_sp = stageTemp;
659 xSemaphoreGive(xSemaphoreDriver);
660 }
661 }
662 break;
663
664 default:
665 break;
666 }
667 if (NewMinute)
668 updateRuntime = true;
669
670 } else if (MashState == MASH_REST) {
671 /*
672 * Mash step rest time and pump control
673 */
674 if (((Main_Screen == MAIN_AUTO_MASH_OUT) && equipment.PumpMashOut) ||
675 ((Main_Screen != MAIN_AUTO_MASH_OUT) && equipment.PumpOnMash)) {
676 float DeltaTemp = equipment.PumpRest * stageTemp / 120; // Maximum temperature drop before heating again.
677 if (pumpTime >= (equipment.PumpCycle + equipment.PumpRest) || ((stageTemp - temp_MLT) > DeltaTemp)) {
678 pumpTime = 0;
679 }
680 if (pumpTime >= equipment.PumpCycle) {
681 if (! pumpRest) {
682 pumpRest = true;
683 ESP_LOGI(TAG, "Pump rest");
684 }
685 } else {
686 if (pumpRest) {
687 pumpRest = false;
688 ESP_LOGI(TAG, "Pump start");
689 }
690 }
691 }
692 if (TimeLeft) {
693 switch (Buttons_Scan()) {
694 case 0: TimeLeft += 60;
695 runtime.StageTimeLeft = TimeLeft / 60;
696 updateRuntime = true;
697 break;
698
699 case 1: if (TimeLeft < 60)
700 TimeLeft = 0;
701 else
702 TimeLeft -= 60;
703 runtime.StageTimeLeft = TimeLeft / 60;
704 updateRuntime = true;
705 break;
706
707 default: break;
708 }
709 }
710
711 if (TimeLeft == 0) {
712 runtime.StageTimeLeft = TimeLeft / 60;
713 updateRuntime = true;
714 if ((Main_Screen == MAIN_AUTO_MASH_IN) && config.AskAdd) {
715 /*
716 * Add Mash prompt.
717 */
718 log_annotation(ANNOTATION_EVENT, "Mout storten");
719 Buttons_Clear();
720 Buttons_Add( 5,120, 60, 40, "Halt", 0);
721 Buttons[0].dark = true;
722 Buttons_Add(255,120, 60, 40, "Ok", 1);
723 Buttons_Show();
724 _fg = TFT_WHITE;
725 _bg = TFT_BLACK;
726 TFT_setFont(DEJAVU24_FONT, NULL);
727 TFT_print("Mout storten?", CENTER, 135);
728 SoundPlay(SOUND_Prompt);
729 MashState = MASH_ADD;
730 ESP_LOGI(TAG, "Mash add prompt");
731 break;
732 }
733 if (((Main_Screen - MAIN_AUTO_MASH_IN) == LastMashStep) && config.AskIodine) {
734 /*
735 * Iodone test prompt.
736 */
737 log_annotation(ANNOTATION_EVENT, "Jodium test");
738 TFT_fillRect(0, 120, 320, 50, TFT_BLACK);
739 Buttons_Clear();
740 Buttons_Add( 5,120, 60, 40, "Halt", 0);
741 Buttons[0].dark = true;
742 Buttons_Add(255,120, 60, 40, "Ok", 1);
743 Buttons_Show();
744 _fg = TFT_WHITE;
745 _bg = TFT_BLACK;
746 TFT_setFont(DEJAVU24_FONT, NULL);
747 TFT_print("Jodium test?", CENTER, 127);
748 SoundPlay(SOUND_Prompt);
749 beeped = false;
750 TimerSet(config.IodineTime * 60);
751 MashState = MASH_IODINE;
752 ESP_LOGI(TAG, "Mash iodine test prompt");
753 break;
754 }
755 if ((Main_Screen == MAIN_AUTO_MASH_OUT) && config.AskRemove) {
756 /*
757 * Mash remove prompt.
758 */
759 log_annotation(ANNOTATION_EVENT, "Mout verwijderen");
760 TFT_fillRect(0, 120, 320, 50, TFT_BLACK);
761 Buttons_Clear();
762 Buttons_Add( 5,120, 60, 40, "Halt", 0);
763 Buttons[0].dark = true;
764 Buttons_Add(255,120, 60, 40, "Ok", 1);
765 Buttons_Show();
766 _fg = TFT_WHITE;
767 _bg = TFT_BLACK;
768 TFT_setFont(DEJAVU18_FONT, NULL);
769 TFT_print("Mout verwijderen?", CENTER, 135);
770 SoundPlay(SOUND_Prompt);
771 MashState = MASH_REMOVE;
772 ESP_LOGI(TAG, "Mash remove prompt");
773 break;
774 }
775 if (Main_Screen != MAIN_AUTO_ABORT)
776 Main_Screen++;
777 TempReached = false;
778 } else {
779 TimerShow(TimeLeft, 65, 122);
780 if (NewMinute) {
781 runtime.StageTimeLeft = TimeLeft / 60;
782 updateRuntime = true;
783 }
784 }
785 } else if (MashState == MASH_ADD) {
786 switch (Buttons_Scan()) {
787 case 0: Main_Screen = MAIN_AUTO_ABORT;
788 break;
789 case 1: Main_Screen++;
790 break;
791 default: break;
792 }
793 } else if (MashState == MASH_IODINE) {
794 if (TimeSpent % 45 == 0) {
795 if (! beeped) {
796 SoundPlay(SOUND_Warn);
797 beeped = true;
798 }
799 } else {
800 beeped = false;
801 }
802 switch (Buttons_Scan()) {
803 case 0: Main_Screen = MAIN_AUTO_ABORT;
804 break;
805 case 1: Main_Screen++;
806 break;
807 default: break;
808 }
809 if (TimeLeft == 0) {
810 Main_Screen++;
811 }
812 } else if (MashState == MASH_REMOVE) {
813 switch (Buttons_Scan()) {
814 case 0: Main_Screen = MAIN_AUTO_ABORT;
815 break;
816 case 1: Main_Screen++;
817 break;
818 default: break;
819 }
820 } /* MashState */
821 MLT_info(71, 26, true);
822 if (_UseHLT) {
823 HLT_info(71, 170, true, true);
824 }
825 break;
826
827 case MAIN_AUTO_TOBOIL:
828 Output = 255;
829 /*
830 * Go to the boil temperature and wait until it is steady.
831 */
832 if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
833 if (driver_state->mlt_pv < stageTemp) {
834 Steady = 0;
835 } else {
836 if (Steady > 10) {
837 Main_Screen = MAIN_AUTO_BOILING;
838 TempReached = true;
839 }
840 }
841 driver_state->pump_run = (equipment.PumpOnBoil && (driver_state->mlt_pv < equipment.PumpMaxTemp)) ? 1 : 0;
842 xSemaphoreGive(xSemaphoreDriver);
843 }
844
845 MLT_info(71, 26, true);
846 switch (Buttons_Scan()) {
847 case 0: if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
848 driver_state->mlt_sp += 0.25;
849 xSemaphoreGive(xSemaphoreDriver);
850 }
851 break;
852
853 case 1: if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
854 driver_state->mlt_sp -= 0.25;
855 xSemaphoreGive(xSemaphoreDriver);
856 }
857 break;
858
859 default: break;
860 }
861 if (Resume)
862 Resume = false;
863 break;
864
865 case MAIN_AUTO_BOILING:
866 if (Resume)
867 Resume = false;
868 if (NewMinute) {
869 if ((runtime.HopAddition < recipe.Additions) && (TimeLeft <= ((recipe.Addition[runtime.HopAddition].Time * 60) + 60))) {
870 ESP_LOGI(TAG, "Hop addition %d at %d minutes", runtime.HopAddition + 1, recipe.Addition[runtime.HopAddition].Time);
871 TopMessage(recipe.Addition[runtime.HopAddition].Name);
872 sprintf(logline, "Hopgift %d %s", runtime.HopAddition + 1, recipe.Addition[runtime.HopAddition].Name);
873 log_annotation(ANNOTATION_EVENT, logline);
874 SoundPlay(SOUND_AddHop);
875 runtime.HopAddition++;
876 } else {
877 TopMessage("Koken");
878 }
879 runtime.StageTimeLeft = TimeLeft / 60;
880 updateRuntime = true;
881 }
882 if (TimeLeft < 60) {
883 if (Output) {
884 log_annotation(ANNOTATION_STAGE, "Vlamuit");
885 }
886 // Flameout
887 Output = 0;
888 } else if (driver_state->mlt_pv >= stageTemp) {
889 Output = (int)((BoilPower * 255.0) / 100.0);
890 if (Buttons[4].x == -1) {
891 Buttons_Add( 3,110, 60, 40, "+%", 4);
892 Buttons_Add(257,110, 60, 40, "-%", 5);
893 Buttons_Show();
894 }
895 } else {
896 Output = 255;
897 if (Buttons[4].x != -1) {
898 Buttons[4].x = Buttons[5].x = -1;
899 Buttons_Show();
900 TFT_fillRect( 3,110, 60, 40, TFT_BLACK);
901 TFT_fillRect(257,110, 60, 40, TFT_BLACK);
902 }
903 }
904
905 MLT_info(71, 26, true);
906 TimerShow(TimeLeft, 65, 190);
907
908 switch (Buttons_Scan()) {
909 case 0: if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
910 driver_state->mlt_sp += 0.25;
911 xSemaphoreGive(xSemaphoreDriver);
912 }
913 break;
914
915 case 1: if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
916 driver_state->mlt_sp -= 0.25;
917 xSemaphoreGive(xSemaphoreDriver);
918 }
919 break;
920
921 case 2: TimeLeft += 60;
922 break;
923
924 case 3: if (TimeLeft > 60)
925 TimeLeft -= 60;
926 else
927 TimeLeft = 0;
928 break;
929
930 case 4: if (BoilPower < 100)
931 BoilPower++;
932 break;
933
934 case 5: if (BoilPower > 0)
935 BoilPower--;
936 break;
937
938 default: break;
939 }
940
941 if (TimeLeft == 0) {
942 Main_Screen = MAIN_AUTO_WHIRLPOOL9;
943 ESP_LOGI(TAG, "Boil is ready");
944 }
945 break;
946
947 case MAIN_AUTO_COOLING_H:
948 case MAIN_AUTO_COOLING_M:
949 case MAIN_AUTO_COOLING_C:
950 if (_Prompt) {
951 /*
952 * Prompt mode
953 */
954 switch (Buttons_Scan()) {
955 case 0: _Prompt = false;
956 Buttons_Clear();
957 break;
958 case 1: Main_Screen = MAIN_AUTO_DONE;
959 Buttons_Clear();
960 return true; //goto startover;
961 break;
962 default:
963 break;
964 }
965
966 if (! _Prompt) {
967 /*
968 * Starting cooling, setup the screen.
969 */
970 Buttons_Clear();
971 TFT_fillScreen(_bg);
972 if (Main_Screen == MAIN_AUTO_COOLING_H) {
973 stageTemp = 77.0;
974 } else if (Main_Screen == MAIN_AUTO_COOLING_M) {
975 stageTemp = 66.0;
976 } else {
977 stageTemp = recipe.CoolTemp;
978 }
979 CoolBeep = false;
980 ESP_LOGI(TAG, "Start cooling from %6.2f to %4.1f", ds18b20_state->mlt_temperature, stageTemp);
981 if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
982 driver_state->mlt_mode = MLT_MODE_OFF;
983 driver_state->mlt_sp = stageTemp;
984 xSemaphoreGive(xSemaphoreDriver);
985 }
986 log_annotation(ANNOTATION_STAGE, "Koelen");
987 TopMessage("Koelen");
988 MLT_info(71, 26, false);
989 Buttons_Add( 5, 200, 60, 40, "Stop", 0);
990 Buttons[0].dark = true;
991 Buttons_Add( 5, 26, 60, 40, "+1", 1);
992 Buttons_Add(255, 26, 60, 40, "-1", 2);
993 /*
994 * The next key is not a mistake, but we need a key entry which
995 * will later become the pump key. The keyscan routine will find
996 * the original key if pressed.
997 */
998 Buttons_Add(255, 26, 60, 40, "-1", 3);
999 Buttons_Show();
1000 }
1001 } else {
1002 /*
1003 * Not in prompt mode.
1004 */
1005 #ifdef CONFIG_TEMP_SENSORS_SIMULATOR
1006 if (Fake_MLT > 12.0) {
1007 if (driver_state->pump_run)
1008 Fake_MLT -= 0.00025 * (Fake_MLT - 12.0);
1009 else
1010 Fake_MLT -= 0.00015 * (Fake_MLT - 12.0);
1011 }
1012 if (xSemaphoreTake(xSemaphoreDS18B20, 10) == pdTRUE) {
1013 ds18b20_state->mlt_temperature = ((int)(Fake_MLT * 16)) / 16.0;
1014 xSemaphoreGive(xSemaphoreDS18B20);
1015 }
1016 #endif
1017 MLT_info(71, 26, true);
1018 if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
1019 /*
1020 * If the pump safe temperature is reached, add the control button.
1021 * Redefine key number 3 if it is at the position of key 2.
1022 */
1023 if ((driver_state->mlt_pv < equipment.PumpMaxTemp) && (Buttons[3].x == Buttons[2].x) &&(Buttons[3].y == Buttons[2].y)) {
1024 Buttons_Add(255, 200, 60, 40, "Pomp", 3);
1025 Buttons_Show();
1026 }
1027 xSemaphoreGive(xSemaphoreDriver);
1028 }
1029 switch (Buttons_Scan()) {
1030 case 1: if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
1031 if (Main_Screen == MAIN_AUTO_COOLING_H) {
1032 if (driver_state->mlt_sp < 77.0)
1033 driver_state->mlt_sp += 1.0;
1034 } else if (Main_Screen == MAIN_AUTO_COOLING_M) {
1035 if (driver_state->mlt_sp < 66.0)
1036 driver_state->mlt_sp += 1.0;
1037 } else if (Main_Screen == MAIN_AUTO_COOLING_C) {
1038 if (driver_state->mlt_sp < 30.0)
1039 driver_state->mlt_sp += 1.0;
1040 }
1041 xSemaphoreGive(xSemaphoreDriver);
1042 }
1043 break;
1044
1045 case 0: Buttons_Add( 60, 150, 90, 40, "Stoppen", 4);
1046 Buttons[4].dark = true;
1047 Buttons_Add(170, 150, 90, 40, "Sorry", 5);
1048 Buttons_Show();
1049 break;
1050
1051 case 2: if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
1052 if (Main_Screen == MAIN_AUTO_COOLING_H) {
1053 if (driver_state->mlt_sp > 71.0)
1054 driver_state->mlt_sp -= 1.0;
1055 } else if (Main_Screen == MAIN_AUTO_COOLING_M) {
1056 if (driver_state->mlt_sp > 60.0)
1057 driver_state->mlt_sp -= 1.0;
1058 } else if (Main_Screen == MAIN_AUTO_COOLING_C) {
1059 if (driver_state->mlt_sp > 10.0)
1060 driver_state->mlt_sp -= 1.0;
1061 }
1062 xSemaphoreGive(xSemaphoreDriver);
1063 }
1064 break;
1065
1066 case 3: if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
1067 if (driver_state->mlt_pv < equipment.PumpMaxTemp) {
1068 if (driver_state->pump_run)
1069 driver_state->pump_run = 0;
1070 else
1071 driver_state->pump_run = 1;
1072 } else {
1073 driver_state->pump_run = 0;
1074 }
1075 runtime.PumpCooling = driver_state->pump_run;
1076 updateRuntime = true;
1077 xSemaphoreGive(xSemaphoreDriver);
1078 }
1079 break;
1080
1081 case 4: Main_Screen++;
1082 break;
1083
1084 case 5: Buttons[4].x = Buttons[5].x = -1;
1085 TFT_fillRect(60, 150, 200, 40, TFT_BLACK);
1086 break;
1087
1088 default:
1089 break;
1090 }
1091
1092 if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
1093 if (! CoolBeep && (driver_state->mlt_pv < (driver_state->mlt_sp + 2.0))) {
1094 SoundPlay(SOUND_Warn);
1095 CoolBeep = true;
1096 }
1097 if (driver_state->mlt_pv <= driver_state->mlt_sp) {
1098 SoundPlay(SOUND_TempReached);
1099 Main_Screen++;
1100 }
1101 xSemaphoreGive(xSemaphoreDriver);
1102 }
1103 }
1104 break;
1105
1106 case MAIN_AUTO_WHIRLPOOL9:
1107 case MAIN_AUTO_WHIRLPOOL7:
1108 case MAIN_AUTO_WHIRLPOOL6:
1109 case MAIN_AUTO_WHIRLPOOL2:
1110 if (_Prompt) {
1111
1112 switch (Buttons_Scan()) {
1113 case 0: _Prompt = false;
1114 break;
1115 case 1: if (Main_Screen == MAIN_AUTO_WHIRLPOOL2) {
1116 Main_Screen = MAIN_AUTO_DONE;
1117 } else {
1118 Main_Screen++;
1119 }
1120 Buttons_Clear();
1121 return true; //goto startover;
1122 break;
1123 default:
1124 break;
1125 }
1126
1127 if (! _Prompt) {
1128 /*
1129 * Prepare the screen for the actual whirpool.
1130 */
1131 Buttons_Clear();
1132 TFT_fillScreen(_bg);
1133 if (Main_Screen == MAIN_AUTO_WHIRLPOOL9) {
1134 TimeWhirlPool = recipe.Whirlpool9;
1135 if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
1136 driver_state->mlt_sp = 93.0;
1137 driver_state->mlt_mode = MLT_MODE_PID;
1138 xSemaphoreGive(xSemaphoreDriver);
1139 }
1140 TopMessage("Whirlpool 88..100");
1141 } else if (Main_Screen == MAIN_AUTO_WHIRLPOOL7) {
1142 TimeWhirlPool = recipe.Whirlpool7;
1143 if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
1144 driver_state->mlt_sp = 74.0;
1145 driver_state->mlt_mode = MLT_MODE_PID;
1146 xSemaphoreGive(xSemaphoreDriver);
1147 }
1148 TopMessage("Whirlpool 71..77");
1149 } else if (Main_Screen == MAIN_AUTO_WHIRLPOOL6) {
1150 TimeWhirlPool = recipe.Whirlpool6;
1151 if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
1152 driver_state->mlt_sp = 63.0;
1153 driver_state->mlt_mode = MLT_MODE_PID;
1154 xSemaphoreGive(xSemaphoreDriver);
1155 }
1156 TopMessage("Whirlpool 60..66");
1157 } else {
1158 TimeWhirlPool = recipe.Whirlpool2;
1159 TopMessage("Koude whirlpool");
1160 }
1161 if (Resume) {
1162 TimeWhirlPool = runtime.StageTimeLeft;
1163 }
1164
1165 /*
1166 * If the pump is allowed at the current temperature, turn it on.
1167 */
1168 if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
1169 driver_state->pump_run = (driver_state->mlt_pv < equipment.PumpMaxTemp) ? 1 : 0;
1170 xSemaphoreGive(xSemaphoreDriver);
1171 }
1172 log_annotation(ANNOTATION_STAGE, "Whirlpool");
1173
1174 TimerSet(TimeWhirlPool * 60);
1175 runtime.StageTimeLeft = TimeWhirlPool;
1176 updateRuntime = true;
1177 MLT_info(71, 26, false);
1178 ESP_LOGI(TAG, "Whirlpool %d minutes, sp %4.1f", TimeWhirlPool, driver_state->mlt_sp);
1179 Buttons_Add(255, 120, 60, 40, "+1m", 0);
1180 Buttons_Add( 5, 120, 60, 40, "-1m", 1);
1181 Buttons_Add(130, 200, 60, 40, "Pomp", 2);
1182 Buttons_Show();
1183 }
1184 } else {
1185 /*
1186 * Not running in prompt mode, do the whirlpool.
1187 */
1188 if (TimeLeft == 120) {
1189 /*
1190 * Drop the temperature when whirlpool is almost ready.
1191 * If we are lucky the heater element will cool down so
1192 * the next cooling stage will not wast too much energy.
1193 */
1194 if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
1195 if (Main_Screen == MAIN_AUTO_WHIRLPOOL9)
1196 driver_state->mlt_sp = 88.0;
1197 if (Main_Screen == MAIN_AUTO_WHIRLPOOL7)
1198 driver_state->mlt_sp = 71.0;
1199 if (Main_Screen == MAIN_AUTO_WHIRLPOOL6)
1200 driver_state->mlt_sp = 60.0;
1201 xSemaphoreGive(xSemaphoreDriver);
1202 }
1203 }
1204
1205 MLT_info(71, 26, true);
1206 TimerShow(TimeLeft, 65, 122);
1207 switch (Buttons_Scan()) {
1208 case 0: TimeLeft += 60;
1209 break;
1210 case 1: if (TimeLeft > 60)
1211 TimeLeft -= 60;
1212 else
1213 TimeLeft = 0;
1214 break;
1215 case 2: if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
1216 if (driver_state->mlt_pv < equipment.PumpMaxTemp) {
1217 if (driver_state->pump_run)
1218 driver_state->pump_run = 0;
1219 else
1220 driver_state->pump_run = 1;
1221 } else {
1222 driver_state->pump_run = 0;
1223 }
1224 xSemaphoreGive(xSemaphoreDriver);
1225 }
1226 }
1227
1228 if (NewMinute) {
1229 runtime.StageTimeLeft = TimeLeft / 60;
1230 updateRuntime = true;
1231 }
1232
1233 if ((TimeLeft == 0)) {
1234 if (Main_Screen == MAIN_AUTO_WHIRLPOOL9) {
1235 Main_Screen = MAIN_AUTO_COOLING_H;
1236 if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
1237 driver_state->pump_run = runtime.PumpCooling;
1238 xSemaphoreGive(xSemaphoreDriver);
1239 }
1240 } else if (Main_Screen == MAIN_AUTO_WHIRLPOOL7) {
1241 Main_Screen = MAIN_AUTO_COOLING_M;
1242 if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
1243 driver_state->pump_run = runtime.PumpCooling;
1244 xSemaphoreGive(xSemaphoreDriver);
1245 }
1246 } else if (Main_Screen == MAIN_AUTO_WHIRLPOOL6) {
1247 Main_Screen = MAIN_AUTO_COOLING_C;
1248 if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
1249 driver_state->pump_run = runtime.PumpCooling;
1250 xSemaphoreGive(xSemaphoreDriver);
1251 }
1252 } else if (Main_Screen == MAIN_AUTO_WHIRLPOOL2) {
1253 Main_Screen = MAIN_AUTO_DONE;
1254 }
1255 }
1256 }
1257 break;
1258
1259 case MAIN_AUTO_DONE:
1260 case MAIN_AUTO_ABORT:
1261 if (Buttons_Scan() == 0)
1262 Main_Screen = MAIN_MODE_FREE;
1263 break;
1264
1265 default:
1266 break;
1267 }
1268
1269 return false;
1270 }
1271
1272

mercurial