|
1 /** |
|
2 * @file recipes.c |
|
3 * @brief Recipes management. |
|
4 */ |
|
5 |
|
6 #include "config.h" |
|
7 |
|
8 |
|
9 extern sButton Buttons[MAXBUTTONS]; |
|
10 extern int Main_Screen; |
|
11 |
|
12 bool r_UpdateRec = false; |
|
13 int r_CurrentRec = 1; |
|
14 int r_Records = 1; |
|
15 int r_Imported = 0; |
|
16 char _xml_element[10][32]; |
|
17 int _xml_depth; |
|
18 int _xml_mashsteps; |
|
19 char _xml_add_name[64]; |
|
20 int _xml_add_type; |
|
21 int _xml_add_time; |
|
22 int _xml_add_ramp; |
|
23 float _xml_add_temp; |
|
24 float _xml_tun_temp; |
|
25 bool _xml_add_valid; |
|
26 |
|
27 static const char *TAG = "recipes"; |
|
28 |
|
29 char char_data_buffer[1024]; |
|
30 size_t offs; |
|
31 bool overflow; |
|
32 |
|
33 |
|
34 |
|
35 void Addition_Add(char *Name, uint8_t Type, uint16_t Time) |
|
36 { |
|
37 printf("Addition_Add(%s, %d, %d)\n", Name, Type, Time); |
|
38 if (! recipe.Additions) { |
|
39 // No entries yet, add the first one. |
|
40 sprintf(recipe.Addition[recipe.Additions].Name, "%s", Name); |
|
41 recipe.Addition[recipe.Additions].Type = Type; |
|
42 recipe.Addition[recipe.Additions].Time = Time; |
|
43 recipe.Additions++; |
|
44 return; |
|
45 } |
|
46 |
|
47 // See if we already got one with the same time. |
|
48 for (int i = 0; i < recipe.Additions; i++) { |
|
49 if (recipe.Addition[i].Time == Time) { |
|
50 // Yes, update the name. |
|
51 snprintf(recipe.Addition[i].Name, 63, "%s, %s", recipe.Addition[i].Name, Name); |
|
52 return; |
|
53 } |
|
54 } |
|
55 |
|
56 // A new entry and we already have some. Add it and keep the list sorted. |
|
57 for (int i = 0; i < recipe.Additions; i++) { |
|
58 if (Time > recipe.Addition[i].Time) { |
|
59 printf("Insert at %d\n", i); |
|
60 // Nake room |
|
61 for (int j = i; j < recipe.Additions; j++) { |
|
62 sprintf(recipe.Addition[j+1].Name, "%s", recipe.Addition[j].Name); |
|
63 recipe.Addition[j+1].Type = recipe.Addition[j].Type; |
|
64 recipe.Addition[j+1].Time = recipe.Addition[j].Time; |
|
65 } |
|
66 sprintf(recipe.Addition[i].Name, "%s", Name); |
|
67 recipe.Addition[i].Type = Type; |
|
68 recipe.Addition[i].Time = Time; |
|
69 recipe.Additions++; |
|
70 return; |
|
71 } |
|
72 } |
|
73 |
|
74 // Just append. |
|
75 sprintf(recipe.Addition[recipe.Additions].Name, "%s", Name); |
|
76 recipe.Addition[recipe.Additions].Type = Type; |
|
77 recipe.Addition[recipe.Additions].Time = Time; |
|
78 recipe.Additions++; |
|
79 } |
|
80 |
|
81 |
|
82 |
|
83 void reset_char_data_buffer (void) |
|
84 { |
|
85 offs = 0; |
|
86 overflow = false; |
|
87 } |
|
88 |
|
89 |
|
90 |
|
91 void char_data (void *userData, const XML_Char *s, int len) |
|
92 { |
|
93 if (!overflow) { |
|
94 if (len + offs >= sizeof(char_data_buffer) ) { |
|
95 overflow = true; |
|
96 } else { |
|
97 memcpy(char_data_buffer + offs, s, len); |
|
98 offs += len; |
|
99 } |
|
100 } |
|
101 } |
|
102 |
|
103 |
|
104 |
|
105 void process_char_data_buffer (void) |
|
106 { |
|
107 bool allspace = true; |
|
108 |
|
109 if (offs > 0) { |
|
110 char_data_buffer[ offs ] = '\0'; |
|
111 |
|
112 /* |
|
113 * If all spaces and control characters, the data is invalid. |
|
114 */ |
|
115 for (int i = 0; i < strlen(char_data_buffer); i++) { |
|
116 if (char_data_buffer[i] > ' ') |
|
117 allspace = false; |
|
118 } |
|
119 if (allspace) |
|
120 return; |
|
121 |
|
122 if ((_xml_depth > 2) && (strcmp(_xml_element[0], "RECIPES") == 0) && (strcmp(_xml_element[1], "RECIPE") == 0)) { |
|
123 /* |
|
124 * We are in a recipe |
|
125 */ |
|
126 if ((_xml_depth == 3) && (strcmp("NAME", _xml_element[2]) == 0)) { |
|
127 snprintf(recipe.Name, 127, "%s", char_data_buffer); |
|
128 } else if ((_xml_depth == 3) && (strcmp("BOIL_TIME", _xml_element[2]) == 0)) { |
|
129 recipe.BoilTime = atoi(char_data_buffer); |
|
130 } else if ((_xml_depth == 5) && (strcmp("HOPS", _xml_element[2]) == 0) && (strcmp("HOP", _xml_element[3]) == 0)) { |
|
131 /* |
|
132 * Hops that are added during the boil. |
|
133 * But check for whirlpool hops too. |
|
134 */ |
|
135 if (strcmp("NAME", _xml_element[4]) == 0) { |
|
136 snprintf(_xml_add_name, 63, "%s", char_data_buffer); |
|
137 } else if (strcmp("USE", _xml_element[4]) == 0) { |
|
138 _xml_add_valid = (strcmp("Boil", char_data_buffer) == 0); // Only "Boil" is a valid hop |
|
139 if (strcmp("Aroma", char_data_buffer) == 0) { |
|
140 recipe.Whirlpool7 = 30; |
|
141 } |
|
142 } else if (strcmp("TIME", _xml_element[4]) == 0) { |
|
143 _xml_add_time = atoi(char_data_buffer); |
|
144 } |
|
145 } else if ((_xml_depth >= 3) && (strcmp("STYLE", _xml_element[2]) == 0)) { |
|
146 // Ignore |
|
147 } else if ((_xml_depth >= 3) && (strcmp("EQUIPMENT", _xml_element[2]) == 0)) { |
|
148 // Ignora |
|
149 } else if ((_xml_depth >= 3) && (strcmp("YEASTS", _xml_element[2]) == 0)) { |
|
150 // Ignore |
|
151 } else if ((_xml_depth >= 3) && (strcmp("WATERS", _xml_element[2]) == 0)) { |
|
152 // Ignore |
|
153 } else if ((_xml_depth == 5) && (strcmp("FERMENTABLES", _xml_element[2]) == 0) && (strcmp("FERMENTABLE", _xml_element[3]) == 0)) { |
|
154 /* |
|
155 * Fermentabes that must be added during the boil. |
|
156 */ |
|
157 if (strcmp("NAME", _xml_element[4]) == 0) { |
|
158 snprintf(_xml_add_name, 63, "%s", char_data_buffer); |
|
159 } else if (strcmp("TYPE", _xml_element[4]) == 0) { |
|
160 if ((strcmp("Sugar", char_data_buffer) == 0)/* || (strcmp("Adjunct", char_data_buffer) == 0)*/) { |
|
161 _xml_add_type = ADDITION_FERMENTABLE; |
|
162 _xml_add_time = 10; |
|
163 } else { |
|
164 _xml_add_type = -1; |
|
165 } |
|
166 } else if (strcmp("ADD_AFTER_BOIL", _xml_element[4]) == 0) { |
|
167 _xml_add_valid = (strcmp("FALSE", char_data_buffer) == 0); |
|
168 } |
|
169 } else if ((_xml_depth == 5) && (strcmp("MISCS", _xml_element[2]) == 0) && (strcmp("MISC", _xml_element[3]) == 0)) { |
|
170 /* |
|
171 * Check for Misc additions to add during the boil. |
|
172 */ |
|
173 if (strcmp("NAME", _xml_element[4]) == 0) { |
|
174 snprintf(_xml_add_name, 63, "%s", char_data_buffer); |
|
175 } else if (strcmp("USE", _xml_element[4]) == 0) { |
|
176 _xml_add_valid = (strcmp("Boil", char_data_buffer) == 0); // Only "Boil" is a valid hop. |
|
177 } else if (strcmp("TYPE", _xml_element[4]) == 0) { |
|
178 if (strcmp("Spice", char_data_buffer) == 0) |
|
179 _xml_add_type = ADDITION_SPICE; |
|
180 if (strcmp("Fining", char_data_buffer) == 0) |
|
181 _xml_add_type = ADDITION_FINING; |
|
182 if (strcmp("Herb", char_data_buffer) == 0) |
|
183 _xml_add_type = ADDITION_HERB; |
|
184 if (strcmp("Flavor", char_data_buffer) == 0) |
|
185 _xml_add_type = ADDITION_FLAVOR; |
|
186 if (strcmp("Other", char_data_buffer) == 0) |
|
187 _xml_add_type = ADDITION_OTHER; |
|
188 } else if (strcmp("TIME", _xml_element[4]) == 0) { |
|
189 _xml_add_time = atoi(char_data_buffer); |
|
190 } |
|
191 } else if ((_xml_depth >= 4) && (strcmp("MASH", _xml_element[2]) == 0)) { |
|
192 if ((_xml_depth >= 6) && (strcmp("MASH_STEP", _xml_element[4]) == 0)) { |
|
193 if (strcmp("NAME", _xml_element[5]) == 0) { |
|
194 snprintf(_xml_add_name, 31, "%s", char_data_buffer); |
|
195 } else if (strcmp("TYPE", _xml_element[5]) == 0) { |
|
196 // Temperature Infusion Decoction |
|
197 _xml_add_valid = (strcmp("Temperature", char_data_buffer) == 0); |
|
198 } else if (strcmp("STEP_TEMP", _xml_element[5]) == 0) { |
|
199 _xml_add_temp = atof(char_data_buffer); |
|
200 } else if (strcmp("STEP_TIME", _xml_element[5]) == 0) { |
|
201 _xml_add_time = atoi(char_data_buffer); |
|
202 } else if (strcmp("RAMP_TIME", _xml_element[5]) == 0) { |
|
203 _xml_add_ramp = atoi(char_data_buffer); |
|
204 } |
|
205 } else if ((_xml_depth >= 4) && (strcmp("TUN_TEMP", _xml_element[3]) == 0)) { |
|
206 // Save this and check later if this is the strike temperature. |
|
207 _xml_tun_temp = atof(char_data_buffer); |
|
208 } else if ((_xml_depth >= 4) && (strcmp("SPARGE_TEMP", _xml_element[3]) == 0)) { |
|
209 recipe.SpargeTemp = atof(char_data_buffer); |
|
210 } |
|
211 } |
|
212 } |
|
213 } |
|
214 } |
|
215 |
|
216 |
|
217 |
|
218 void startElement(void *userData, const char *name, const char **attr) |
|
219 { |
|
220 sprintf(_xml_element[_xml_depth], "%s", name); |
|
221 _xml_depth++; |
|
222 process_char_data_buffer(); |
|
223 reset_char_data_buffer(); |
|
224 } |
|
225 |
|
226 |
|
227 |
|
228 void endElement(void *userData, const char *name) |
|
229 { |
|
230 process_char_data_buffer(); |
|
231 reset_char_data_buffer(); |
|
232 if ((_xml_depth == 4) && (strcmp("HOP", _xml_element[3]) == 0)) { |
|
233 if (_xml_add_valid) { |
|
234 Addition_Add(_xml_add_name, ADDITION_HOP, _xml_add_time); |
|
235 } |
|
236 } |
|
237 if ((_xml_depth == 4) && (strcmp("FERMENTABLE", _xml_element[3]) == 0)) { |
|
238 if (_xml_add_valid && (_xml_add_type == ADDITION_FERMENTABLE)) { |
|
239 Addition_Add(_xml_add_name, _xml_add_type, _xml_add_time); |
|
240 } |
|
241 } |
|
242 if ((_xml_depth == 4) && (strcmp("MISC", _xml_element[3]) == 0)) { |
|
243 if (_xml_add_valid) { |
|
244 Addition_Add(_xml_add_name, _xml_add_type, _xml_add_time); |
|
245 } |
|
246 } |
|
247 if ((_xml_depth == 5) && (strcmp("MASH_STEP", _xml_element[4]) == 0)) { |
|
248 // printf("Flush End MASH_STEP %d %s\n", _xml_depth, _xml_add_name); |
|
249 _xml_mashsteps++; |
|
250 sprintf(recipe.MashStep[_xml_mashsteps].Name, "%s", _xml_add_name); |
|
251 recipe.MashStep[_xml_mashsteps].Temperature = _xml_add_temp; |
|
252 recipe.MashStep[_xml_mashsteps].Resttime = _xml_add_time; |
|
253 recipe.MashStep[_xml_mashsteps].Steptime = _xml_add_ramp; |
|
254 } |
|
255 _xml_depth--; |
|
256 } |
|
257 |
|
258 |
|
259 |
|
260 int ParseRecipe(char *fn, char *code) |
|
261 { |
|
262 char buf[512]; |
|
263 |
|
264 memset(&recipe, 0, sizeof(recipe)); |
|
265 XML_Parser parser = XML_ParserCreate(NULL); |
|
266 |
|
267 int done; |
|
268 _xml_depth = 0; |
|
269 _xml_mashsteps = 0; |
|
270 _xml_tun_temp = 0.0; |
|
271 |
|
272 XML_SetElementHandler(parser, startElement, endElement); |
|
273 XML_SetCharacterDataHandler(parser, char_data); |
|
274 |
|
275 FILE *fp = fopen(fn, "r"); |
|
276 |
|
277 do { |
|
278 int len = (int)fread(buf, 1, sizeof(buf), fp); |
|
279 done = len < sizeof(buf); |
|
280 if (XML_Parse(parser, buf, len, done) == XML_STATUS_ERROR) { |
|
281 printf( "%s at line %5lu\n", XML_ErrorString(XML_GetErrorCode(parser)), XML_GetCurrentLineNumber(parser)); |
|
282 return 1; |
|
283 } |
|
284 vTaskDelay(2 / portTICK_PERIOD_MS); |
|
285 } while (!done); |
|
286 |
|
287 XML_ParserFree(parser); |
|
288 fclose(fp); |
|
289 |
|
290 /* |
|
291 * Fix missing things. |
|
292 */ |
|
293 snprintf(recipe.Code, 31, "%s", code); |
|
294 // The code is created from the recipe filename. |
|
295 for (int i = 0; i < strlen(recipe.Code); i++) { |
|
296 if ((recipe.Code[i] == ' ') || (recipe.Code[i] == '.')) { |
|
297 recipe.Code[i] = '\0'; |
|
298 break; |
|
299 } |
|
300 } |
|
301 recipe.CoolTemp = 20.0; |
|
302 sprintf(recipe.MashStep[0].Name, "Mash-in"); |
|
303 if (_xml_tun_temp > recipe.MashStep[1].Temperature) { |
|
304 recipe.MashStep[0].Temperature = _xml_tun_temp; |
|
305 } else { |
|
306 recipe.MashStep[0].Temperature = recipe.MashStep[1].Temperature + 1.25; |
|
307 } |
|
308 recipe.MashStep[0].Resttime = 1; |
|
309 |
|
310 if (! recipe.MashStep[7].Resttime) { |
|
311 // Move last mash step to position 7. |
|
312 for (int i = 6; i > 1; i--) { |
|
313 if (recipe.MashStep[i].Resttime) { |
|
314 // Got it, move. |
|
315 sprintf(recipe.MashStep[7].Name, "%s", recipe.MashStep[i].Name); |
|
316 recipe.MashStep[i].Name[0] = '\0'; |
|
317 recipe.MashStep[7].Temperature = recipe.MashStep[i].Temperature; |
|
318 recipe.MashStep[i].Temperature = 0.0; |
|
319 recipe.MashStep[7].Resttime = recipe.MashStep[i].Resttime; |
|
320 recipe.MashStep[i].Resttime = 0; |
|
321 recipe.MashStep[7].Steptime = recipe.MashStep[i].Steptime; |
|
322 recipe.MashStep[i].Steptime = 0; |
|
323 break; |
|
324 } |
|
325 } |
|
326 } |
|
327 |
|
328 printf("Recipe: %s\n", recipe.Name); |
|
329 printf("Code : %s\n", recipe.Code); |
|
330 printf("Boil time %d minutes\n", recipe.BoilTime); |
|
331 printf("n Stepname temp time ramp\n"); |
|
332 printf("- ------------------------------ ----- ---- ----\n"); |
|
333 for (int i = 0; i < 8; i++) { |
|
334 if (recipe.MashStep[i].Resttime) { |
|
335 printf("%d %-31s %5.2f %4d %4d\n", i, recipe.MashStep[i].Name, recipe.MashStep[i].Temperature, |
|
336 recipe.MashStep[i].Resttime, recipe.MashStep[i].Steptime); |
|
337 } |
|
338 } |
|
339 printf("%d additions\n", recipe.Additions); |
|
340 printf("n Addition name t Tim\n"); |
|
341 printf("- --------------------------------------------------------------- - ---\n"); |
|
342 for (int i = 0; i < recipe.Additions; i++) { |
|
343 printf("%d %-63s %d %3d\n", i, recipe.Addition[i].Name, recipe.Addition[i].Type, recipe.Addition[i].Time); |
|
344 } |
|
345 printf("Cooltemp %5.2f\n", recipe.CoolTemp); |
|
346 if (recipe.Whirlpool9) |
|
347 printf("Whirlpool9 %d\n", recipe.Whirlpool9); |
|
348 if (recipe.Whirlpool7) |
|
349 printf("Whirlpool7 %d\n", recipe.Whirlpool7); |
|
350 if (recipe.Whirlpool6) |
|
351 printf("Whirlpool6 %d\n", recipe.Whirlpool6); |
|
352 if (recipe.Whirlpool2) |
|
353 printf("Whirlpool2 %d\n", recipe.Whirlpool2); |
|
354 printf("SpargeTemp %5.2f\n", recipe.SpargeTemp); |
|
355 |
|
356 return 0; |
|
357 } |
|
358 |
|
359 |
|
360 |
|
361 /* |
|
362 * Recipes init function, only runs once a new screen is entered. |
|
363 */ |
|
364 void Recipes_Init(void) |
|
365 { |
|
366 FILE *f; |
|
367 DIR *dir; |
|
368 struct dirent *de; |
|
369 size_t bytes; |
|
370 uint8_t *dst; |
|
371 uint16_t y; |
|
372 char filename[256], newname[256]; |
|
373 int rc; |
|
374 |
|
375 switch (Main_Screen) { |
|
376 case MAIN_TOOLS_RECIPE: |
|
377 TopMessage("Recepten importeren"); |
|
378 r_Imported = 0; |
|
379 TFT_setFont(DEFAULT_FONT, NULL); |
|
380 y = 28; |
|
381 if ((dir = opendir("/sdcard/recipe"))) { |
|
382 de = readdir(dir); |
|
383 while (de) { |
|
384 if (strstr(de->d_name, ".xml") || strstr(de->d_name, ".XML")) { |
|
385 _fg = TFT_YELLOW; |
|
386 TFT_print(de->d_name, 2, y); |
|
387 snprintf(filename, 255, "/sdcard/recipe/%s", de->d_name); |
|
388 snprintf(newname, 255, "/sdcard/recipe/%s", de->d_name); |
|
389 newname[strlen(newname) -2] = 'o'; |
|
390 newname[strlen(newname) -1] = 'k'; |
|
391 rc = ParseRecipe(filename, de->d_name); |
|
392 ESP_LOGI(TAG, "Recipe %s parsed, rc=%d", filename, rc); |
|
393 if (rc == 0) { |
|
394 append_recipe(); |
|
395 rename(filename, newname); |
|
396 _fg = TFT_GREEN; |
|
397 TFT_print(" Ok", LASTX, y); |
|
398 } else { |
|
399 _fg = TFT_RED; |
|
400 TFT_print(" Fout", LASTX, y); |
|
401 } |
|
402 r_Imported++; // Count them all |
|
403 y += 16; |
|
404 } |
|
405 de = readdir(dir); |
|
406 } |
|
407 closedir(dir); |
|
408 } |
|
409 |
|
410 f = fopen("/spiffs/etc/recipe.conf", "r"); |
|
411 dst = (uint8_t*)&recipe; |
|
412 r_Records = 0; |
|
413 while ((bytes = fread(dst, 1, sizeof(recipe), f))) { |
|
414 r_Records++; |
|
415 } |
|
416 fclose(f); |
|
417 // Load the default record. |
|
418 r_CurrentRec = config.RecipeRec; |
|
419 r_UpdateRec = true; |
|
420 break; |
|
421 |
|
422 case MAIN_TOOLS_RECIPE_EDIT: |
|
423 break; |
|
424 |
|
425 default: break; |
|
426 } |
|
427 } |
|
428 |
|
429 |
|
430 |
|
431 /* |
|
432 * Recipes management loop, non-blocking. |
|
433 */ |
|
434 void Recipes_Loop(void) |
|
435 { |
|
436 uint32_t crc1, crc2; |
|
437 uint8_t *dst; |
|
438 int mashsteps; |
|
439 uint16_t y; |
|
440 char tmp[32]; |
|
441 float mintemp; |
|
442 |
|
443 switch (Main_Screen) { |
|
444 case MAIN_TOOLS_RECIPE: |
|
445 if (r_Imported) { |
|
446 Buttons_Clear(); |
|
447 Buttons_Add(135, 210, 50, 30, "Ok" , 0); |
|
448 Buttons_Show(); |
|
449 |
|
450 while (true) { |
|
451 if (Buttons_Scan() == 0) |
|
452 break; |
|
453 vTaskDelay(20 / portTICK_PERIOD_MS); |
|
454 } |
|
455 Buttons_Clear(); |
|
456 r_Imported = 0; |
|
457 } |
|
458 if (r_UpdateRec) { |
|
459 _bg = TFT_BLACK; |
|
460 TFT_fillScreen(_bg); |
|
461 TFT_resetclipwin(); |
|
462 TopMessage("Recepten"); |
|
463 r_UpdateRec = false; |
|
464 read_recipe(r_CurrentRec); |
|
465 TFT_setFont(DEFAULT_FONT, NULL); |
|
466 ShowText(2, 28, "Naam", recipe.Name); |
|
467 ShowText(2, 44, "Code", recipe.Code); |
|
468 ShowInteger(162, 44, "Record", NULL, recipe.Record); |
|
469 ShowInteger(2, 60, "Kooktijd", " min", recipe.BoilTime); |
|
470 ShowFloat(162, 60, "Koel tot", " C", recipe.CoolTemp, 2); |
|
471 ShowFloat(2, 76, "Maisch in", " C", recipe.MashStep[0].Temperature, 2); |
|
472 ShowFloat(162, 76, "Spoelwater", " C", recipe.SpargeTemp, 2); |
|
473 y = 92; |
|
474 _fg = TFT_WHITE; |
|
475 TFT_print("Maisch stap", 2, y); |
|
476 TFT_print("Temp.", 200, y); |
|
477 TFT_print("Rust", 260, y); |
|
478 _fg = TFT_YELLOW; |
|
479 y += 16; |
|
480 for (int i = 1; i < 8; i++) { |
|
481 if (recipe.MashStep[i].Resttime) { |
|
482 TFT_print(recipe.MashStep[i].Name, 2, y); |
|
483 sprintf(tmp, "%.2f", recipe.MashStep[i].Temperature); |
|
484 TFT_print(tmp, 200, y); |
|
485 sprintf(tmp, "%2d min", recipe.MashStep[i].Resttime); |
|
486 TFT_print(tmp, 260, y); |
|
487 y += 16; |
|
488 } |
|
489 } |
|
490 if (recipe.Additions) { |
|
491 _fg = TFT_YELLOW; |
|
492 sprintf(tmp, "%d ", recipe.Additions); |
|
493 TFT_print(tmp, 2, y); |
|
494 _fg = TFT_WHITE; |
|
495 TFT_print("toevoegingen om", LASTX, y); |
|
496 _fg = TFT_YELLOW; |
|
497 for (int i = 1; i <= recipe.Additions; i++) { |
|
498 sprintf(tmp, " %d", recipe.Addition[i-1].Time); |
|
499 TFT_print(tmp, LASTX, y); |
|
500 } |
|
501 _fg = TFT_WHITE; |
|
502 TFT_print(" minuten", LASTX, y); |
|
503 } else { |
|
504 _fg = TFT_WHITE; |
|
505 TFT_print("Geen hop toevoegingen.", 2, y); |
|
506 } |
|
507 y += 16; |
|
508 if (recipe.Whirlpool9 || recipe.Whirlpool7 || recipe.Whirlpool6 || recipe.Whirlpool2) { |
|
509 _fg = TFT_WHITE; |
|
510 TFT_print("Whirlpool ", 2, y); |
|
511 _fg = TFT_YELLOW; |
|
512 if (recipe.Whirlpool9) |
|
513 TFT_print("88+ ", LASTX, y); |
|
514 if (recipe.Whirlpool7) |
|
515 TFT_print("71+ ", LASTX, y); |
|
516 if (recipe.Whirlpool6) |
|
517 TFT_print("60+ ", LASTX, y); |
|
518 if (recipe.Whirlpool2) |
|
519 TFT_print("koud", LASTX, y); |
|
520 } |
|
521 |
|
522 Buttons_Clear(); |
|
523 Buttons_Add( 0, 210, 45, 30, "Ok" , 0); |
|
524 Buttons_Add( 46, 210, 45, 30, "+" , 1); |
|
525 if (r_CurrentRec != config.RecipeRec) |
|
526 Buttons_Add( 92, 210, 45, 30, "-", 2); |
|
527 else |
|
528 Buttons_Add( 92, 210, 45, 30, "" , 2); |
|
529 if (r_CurrentRec > 1) |
|
530 Buttons_Add(138, 210, 45, 30, "<", 3); |
|
531 else |
|
532 Buttons_Add(138, 210, 45, 30, "", 3); |
|
533 if (r_CurrentRec < r_Records) |
|
534 Buttons_Add(184, 210, 45, 30, ">", 4); |
|
535 else |
|
536 Buttons_Add(184, 210, 45, 30, "", 4); |
|
537 if (r_CurrentRec != config.RecipeRec) |
|
538 Buttons_Add(230, 210, 45, 30, "Std", 5); |
|
539 else |
|
540 Buttons_Add(230, 210, 45, 30, "", 5); |
|
541 Buttons_Add(276, 210, 45, 30, "Ed" , 6); |
|
542 Buttons[0].dark = true; |
|
543 Buttons_Show(); |
|
544 } /* if (r_UpdateRec) */ |
|
545 switch (Buttons_Scan()) { |
|
546 case 0: Main_Screen = MAIN_TOOLS; |
|
547 break; |
|
548 |
|
549 case 1: memset(&recipe, 0, sizeof(recipe)); |
|
550 recipe.Version = 1; |
|
551 recipe.Record = r_Records + 1; |
|
552 sprintf(recipe.Name, "Recipe %d", r_Records + 1); |
|
553 sprintf(recipe.Code, "00%d", r_Records + 1); |
|
554 sprintf(recipe.MashStep[0].Name, "Mash-in"); |
|
555 recipe.MashStep[0].Temperature = 67.5; |
|
556 recipe.MashStep[0].Resttime = 1; |
|
557 sprintf(recipe.MashStep[1].Name, "Mash"); |
|
558 recipe.MashStep[1].Temperature = 67.0; |
|
559 recipe.MashStep[1].Resttime = 75; |
|
560 sprintf(recipe.MashStep[7].Name, "Mash-out"); |
|
561 recipe.MashStep[7].Temperature = 78.0; |
|
562 recipe.MashStep[7].Resttime = 5; |
|
563 recipe.BoilTime = 60; |
|
564 recipe.Additions = 2; |
|
565 sprintf(recipe.Addition[0].Name, "Hop"); |
|
566 recipe.Addition[0].Time = 60; |
|
567 recipe.Addition[0].Type = ADDITION_HOP; |
|
568 sprintf(recipe.Addition[1].Name, "Hop"); |
|
569 recipe.Addition[1].Time = 10; |
|
570 recipe.Addition[1].Type = ADDITION_HOP; |
|
571 recipe.CoolTemp = 20.0; |
|
572 recipe.Whirlpool9 = 0; |
|
573 recipe.Whirlpool7 = 0; |
|
574 recipe.Whirlpool6 = 0; |
|
575 recipe.Whirlpool2 = 0; |
|
576 recipe.SpargeTemp = 85.0; |
|
577 append_recipe(); |
|
578 r_Records++; |
|
579 r_CurrentRec = r_Records; |
|
580 r_UpdateRec = true; |
|
581 ESP_LOGI(TAG, "New recipe record %d", recipe.Record); |
|
582 break; |
|
583 |
|
584 case 2: if ((r_CurrentRec != config.RecipeRec) && (r_Records > 1)) { |
|
585 delete_recipe(r_CurrentRec); |
|
586 r_Records--; |
|
587 if (r_CurrentRec > r_Records) |
|
588 r_CurrentRec = r_Records; |
|
589 r_UpdateRec = true; |
|
590 } |
|
591 break; |
|
592 |
|
593 case 3: if (r_CurrentRec > 1) { |
|
594 r_CurrentRec--; |
|
595 r_UpdateRec = true; |
|
596 } |
|
597 break; |
|
598 |
|
599 case 4: if (r_CurrentRec < r_Records) { |
|
600 r_CurrentRec++; |
|
601 r_UpdateRec = true; |
|
602 } |
|
603 break; |
|
604 |
|
605 case 5: if (r_CurrentRec != config.RecipeRec) { |
|
606 config.RecipeRec = r_CurrentRec; |
|
607 write_config(); |
|
608 r_UpdateRec = true; |
|
609 ESP_LOGI(TAG, "Recipe %d `%s' set as default", r_CurrentRec, recipe.Name); |
|
610 } |
|
611 break; |
|
612 |
|
613 case 6: Main_Screen = MAIN_TOOLS_RECIPE_EDIT; |
|
614 break; |
|
615 |
|
616 default: break; |
|
617 } |
|
618 break; |
|
619 |
|
620 case MAIN_TOOLS_RECIPE_EDIT: |
|
621 dst = (uint8_t*)&recipe; |
|
622 crc1 = crc32_le(0, dst, sizeof(recipe)); |
|
623 |
|
624 EditText("Naam", recipe.Name, 31); |
|
625 EditText("Code", recipe.Code, 31); |
|
626 mashsteps = 0; |
|
627 for (int i = 1; i < 7; i++) { |
|
628 if (recipe.MashStep[i].Resttime) |
|
629 mashsteps++; |
|
630 } |
|
631 EditInt("Maisch stappen", &mashsteps, 1, 6); |
|
632 EditFloat("Inmaisch temperatuur", &recipe.MashStep[0].Temperature, 38, 74, 2); |
|
633 // Round to 0.25 values |
|
634 recipe.MashStep[0].Temperature = ((int)(recipe.MashStep[0].Temperature * 4)) / 4.0; |
|
635 for (int i = 1; i <= mashsteps; i++) { |
|
636 sprintf(tmp, "Maisch stap %d naam", i); |
|
637 EditText(tmp, recipe.MashStep[i].Name, 31); |
|
638 sprintf(tmp, "Maisch stap %d temperatuur", i); |
|
639 if (i == 1) |
|
640 mintemp = recipe.MashStep[0].Temperature - 5; |
|
641 else |
|
642 mintemp = recipe.MashStep[i - 1].Temperature; |
|
643 EditFloat("Inmaisch temperatuur", &recipe.MashStep[i].Temperature, mintemp, 74, 2); |
|
644 recipe.MashStep[i].Temperature = ((int)(recipe.MashStep[i].Temperature * 4)) / 4.0; |
|
645 sprintf(tmp, "Maisch stap %d rusttijd in minuten", i); |
|
646 EditUint16(tmp, &recipe.MashStep[i].Resttime, 1, 480); |
|
647 if (i == 1) { |
|
648 recipe.MashStep[i].Steptime = 1; |
|
649 } else { |
|
650 recipe.MashStep[i].Steptime = (int)(recipe.MashStep[i].Temperature - recipe.MashStep[i - 1].Temperature); |
|
651 } |
|
652 } |
|
653 mintemp = recipe.MashStep[mashsteps].Temperature; |
|
654 EditFloat("Uitmaischen temperatuur", &recipe.MashStep[7].Temperature, mintemp, 80, 2); |
|
655 recipe.MashStep[7].Temperature = ((int)(recipe.MashStep[7].Temperature * 4)) / 4.0; |
|
656 EditUint16("Uitmaischen tijd in minuten", &recipe.MashStep[7].Resttime, 1, 15); |
|
657 recipe.MashStep[7].Steptime = (int)(recipe.MashStep[7].Temperature - recipe.MashStep[mashsteps].Temperature); |
|
658 // Zero any higher steps to diable them. |
|
659 for (int i = (mashsteps + 1); i < 7; i++) |
|
660 recipe.MashStep[i].Resttime = 0; |
|
661 |
|
662 EditUint16("Kook tijd in minuten", &recipe.BoilTime, 3, 480); |
|
663 EditUint8("Hop/kruiden toevoegingen", &recipe.Additions, 1, 10); |
|
664 for (uint8_t i = 0; i < recipe.Additions; i++) { |
|
665 sprintf(tmp, "Toevoeging %d maam", i+1); |
|
666 if (strlen(recipe.Addition[i].Name) == 00) { |
|
667 sprintf(recipe.Addition[i].Name, "Hop %d", (int)i+1); |
|
668 } |
|
669 EditText(tmp, recipe.Addition[i].Name, 40); |
|
670 sprintf(tmp, "Toevoeging %d tijd", i+1); |
|
671 if (i == 0) { |
|
672 EditUint16(tmp, &recipe.Addition[i].Time, 0, recipe.BoilTime); |
|
673 } else { |
|
674 EditUint16(tmp, &recipe.Addition[i].Time, 0, recipe.Addition[i-1].Time - 1); |
|
675 } |
|
676 recipe.Addition[i].Type = ADDITION_HOP; |
|
677 } |
|
678 |
|
679 EditFloat("Koel temperatuur", &recipe.CoolTemp, 10, 30, 2); |
|
680 recipe.CoolTemp = ((int)(recipe.CoolTemp * 4)) / 4.0; |
|
681 EditFloat("Spoelwater temperatuur", &recipe.SpargeTemp, 75, 98, 2); |
|
682 recipe.SpargeTemp = ((int)(recipe.SpargeTemp * 4)) / 4.0; |
|
683 EditUint16("Whirlpool 88..100 graden, 0 = niet", &recipe.Whirlpool9, 0, 120); |
|
684 EditUint16("Whirlpool 71..77 graden, 0 = niet", &recipe.Whirlpool7, 0, 120); |
|
685 EditUint16("Whirlpool 60..66 graden, 0 = niet", &recipe.Whirlpool6, 0, 120); |
|
686 EditUint16("Whirlpool koud, 0 = niet", &recipe.Whirlpool2, 0, 120); |
|
687 |
|
688 crc2 = crc32_le(0, dst, sizeof(recipe)); |
|
689 if ((crc1 != crc2) && Confirm("Gewijzigd, opslaan?", "Ja", "Nee")) { |
|
690 write_recipe(recipe.Record); |
|
691 } |
|
692 Main_Screen = MAIN_TOOLS_RECIPE; |
|
693 break; |
|
694 |
|
695 default: break; |
|
696 } |
|
697 } |
|
698 |
|
699 |