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