Mon, 22 Oct 2018 21:43:45 +0200
Updated esp-ide. Removed VNC server corre encoding because no clients would use it. Enabled WiFi error logmessages. Write runtime record is now debug logging. Removed recipe.Record number, not usefull and was wrong too. Removed console print of json log data.
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 |