|
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 |