Sat, 20 Oct 2018 17:09:48 +0200
Updated lots of doxygen comments
0 | 1 | /** |
2 | * @file automation.c | |
3 | * @brief Automation functions. | |
4 | */ | |
5 | ||
6 | #include "config.h" | |
7 | ||
1
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
8 | int BoilPower = 100; ///< Boil power 0..100% |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
9 | int LastMashStep = 0; ///< Last valid mash step |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
10 | char temp_buf[64]; ///< Temporary buffer |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
11 | char logline[128]; ///< Log line buffer |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
12 | char strftime_buf[64]; ///< Time buffer |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
13 | bool loop; ///< Loop flag |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
14 | bool CoolBeep = false; ///< Did beep during cooling |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
15 | bool Resume = false; ///< Resume brew flag |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
16 | bool pumpRest = false; ///< Pump is resting |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
17 | bool updateRuntime = false; ///< Update runtime record |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
18 | bool NewMinute = false; ///< We have a new minute |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
19 | bool TempReached = false; ///< Temperature is reached |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
20 | uint8_t MashState = MASH_NONE; ///< Mash states |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
21 | float temp_MLT; ///< MLT temperature |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
22 | float MinMash = 38.0; ///< Minimum edit value |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
23 | float MaxMash = 80.0; ///< Maximum edit value |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
24 | uint32_t power_MLT = 0; ///< MLT power |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
25 | uint32_t power_HLT = 0; ///< HLT power |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
26 | uint32_t counts = 0; ///< Counter for power average |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
27 | float stageTemp = 0.0; ///< Current stage temperature |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
28 | uint16_t stageTime = 0; ///< Current stage timer |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
29 | uint16_t TimeWhirlPool = 0; ///< Whirlpool timer |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
30 | uint32_t TimeLeft = 0; ///< Tie left in this stage |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
31 | uint32_t TimeSpent = 0; ///< Tota time spent |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
32 | uint32_t SecsCount = 0; ///< Seconds counter |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
33 | uint32_t pumpTime = 0; ///< Pump running time |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
34 | uint32_t TimeBrewing = 0; ///< Brewing time elapsed |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
35 | uint16_t Steady = 0; ///< Temperature is steady |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
36 | bool _NewMinute = false; ///< New minute slave flag |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
37 | bool _UseHLT = false; ///< Use HLT slave flag |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
38 | bool _Prompt = false; ///< Prompt display flag |
0 | 39 | |
1
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
40 | extern bool System_TimeOk; ///< System time is valid |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
41 | extern sButton Buttons[MAXBUTTONS]; ///< Buttons definitions |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
42 | extern int Main_Screen; ///< Current screen |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
43 | extern DS18B20_State *ds18b20_state; ///< DS18B20 state |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
44 | extern DRIVER_State *driver_state; ///< Relays driver state |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
45 | extern SemaphoreHandle_t xSemaphoreDS18B20; ///< DS18B20 lock semaphore |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
46 | extern SemaphoreHandle_t xSemaphoreDriver; ///< Relays driver lock semaphore |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
47 | extern double Output; ///< Cakculated outpout power |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
48 | extern time_t now; ///< Current time |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
49 | extern struct tm timeinfo; ///< Current time structure |
0 | 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 |