main/recipes.c

changeset 0
b74b0e4902c3
child 1
ad2c8b13eb88
equal deleted inserted replaced
-1:000000000000 0:b74b0e4902c3
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

mercurial