diff -r 000000000000 -r b74b0e4902c3 main/recipes.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main/recipes.c Sat Oct 20 13:23:15 2018 +0200 @@ -0,0 +1,699 @@ +/** + * @file recipes.c + * @brief Recipes management. + */ + +#include "config.h" + + +extern sButton Buttons[MAXBUTTONS]; +extern int Main_Screen; + +bool r_UpdateRec = false; +int r_CurrentRec = 1; +int r_Records = 1; +int r_Imported = 0; +char _xml_element[10][32]; +int _xml_depth; +int _xml_mashsteps; +char _xml_add_name[64]; +int _xml_add_type; +int _xml_add_time; +int _xml_add_ramp; +float _xml_add_temp; +float _xml_tun_temp; +bool _xml_add_valid; + +static const char *TAG = "recipes"; + +char char_data_buffer[1024]; +size_t offs; +bool overflow; + + + +void Addition_Add(char *Name, uint8_t Type, uint16_t Time) +{ + printf("Addition_Add(%s, %d, %d)\n", Name, Type, Time); + if (! recipe.Additions) { + // No entries yet, add the first one. + sprintf(recipe.Addition[recipe.Additions].Name, "%s", Name); + recipe.Addition[recipe.Additions].Type = Type; + recipe.Addition[recipe.Additions].Time = Time; + recipe.Additions++; + return; + } + + // See if we already got one with the same time. + for (int i = 0; i < recipe.Additions; i++) { + if (recipe.Addition[i].Time == Time) { + // Yes, update the name. + snprintf(recipe.Addition[i].Name, 63, "%s, %s", recipe.Addition[i].Name, Name); + return; + } + } + + // A new entry and we already have some. Add it and keep the list sorted. + for (int i = 0; i < recipe.Additions; i++) { + if (Time > recipe.Addition[i].Time) { + printf("Insert at %d\n", i); + // Nake room + for (int j = i; j < recipe.Additions; j++) { + sprintf(recipe.Addition[j+1].Name, "%s", recipe.Addition[j].Name); + recipe.Addition[j+1].Type = recipe.Addition[j].Type; + recipe.Addition[j+1].Time = recipe.Addition[j].Time; + } + sprintf(recipe.Addition[i].Name, "%s", Name); + recipe.Addition[i].Type = Type; + recipe.Addition[i].Time = Time; + recipe.Additions++; + return; + } + } + + // Just append. + sprintf(recipe.Addition[recipe.Additions].Name, "%s", Name); + recipe.Addition[recipe.Additions].Type = Type; + recipe.Addition[recipe.Additions].Time = Time; + recipe.Additions++; +} + + + +void reset_char_data_buffer (void) +{ + offs = 0; + overflow = false; +} + + + +void char_data (void *userData, const XML_Char *s, int len) +{ + if (!overflow) { + if (len + offs >= sizeof(char_data_buffer) ) { + overflow = true; + } else { + memcpy(char_data_buffer + offs, s, len); + offs += len; + } + } +} + + + +void process_char_data_buffer (void) +{ + bool allspace = true; + + if (offs > 0) { + char_data_buffer[ offs ] = '\0'; + + /* + * If all spaces and control characters, the data is invalid. + */ + for (int i = 0; i < strlen(char_data_buffer); i++) { + if (char_data_buffer[i] > ' ') + allspace = false; + } + if (allspace) + return; + + if ((_xml_depth > 2) && (strcmp(_xml_element[0], "RECIPES") == 0) && (strcmp(_xml_element[1], "RECIPE") == 0)) { + /* + * We are in a recipe + */ + if ((_xml_depth == 3) && (strcmp("NAME", _xml_element[2]) == 0)) { + snprintf(recipe.Name, 127, "%s", char_data_buffer); + } else if ((_xml_depth == 3) && (strcmp("BOIL_TIME", _xml_element[2]) == 0)) { + recipe.BoilTime = atoi(char_data_buffer); + } else if ((_xml_depth == 5) && (strcmp("HOPS", _xml_element[2]) == 0) && (strcmp("HOP", _xml_element[3]) == 0)) { + /* + * Hops that are added during the boil. + * But check for whirlpool hops too. + */ + if (strcmp("NAME", _xml_element[4]) == 0) { + snprintf(_xml_add_name, 63, "%s", char_data_buffer); + } else if (strcmp("USE", _xml_element[4]) == 0) { + _xml_add_valid = (strcmp("Boil", char_data_buffer) == 0); // Only "Boil" is a valid hop + if (strcmp("Aroma", char_data_buffer) == 0) { + recipe.Whirlpool7 = 30; + } + } else if (strcmp("TIME", _xml_element[4]) == 0) { + _xml_add_time = atoi(char_data_buffer); + } + } else if ((_xml_depth >= 3) && (strcmp("STYLE", _xml_element[2]) == 0)) { + // Ignore + } else if ((_xml_depth >= 3) && (strcmp("EQUIPMENT", _xml_element[2]) == 0)) { + // Ignora + } else if ((_xml_depth >= 3) && (strcmp("YEASTS", _xml_element[2]) == 0)) { + // Ignore + } else if ((_xml_depth >= 3) && (strcmp("WATERS", _xml_element[2]) == 0)) { + // Ignore + } else if ((_xml_depth == 5) && (strcmp("FERMENTABLES", _xml_element[2]) == 0) && (strcmp("FERMENTABLE", _xml_element[3]) == 0)) { + /* + * Fermentabes that must be added during the boil. + */ + if (strcmp("NAME", _xml_element[4]) == 0) { + snprintf(_xml_add_name, 63, "%s", char_data_buffer); + } else if (strcmp("TYPE", _xml_element[4]) == 0) { + if ((strcmp("Sugar", char_data_buffer) == 0)/* || (strcmp("Adjunct", char_data_buffer) == 0)*/) { + _xml_add_type = ADDITION_FERMENTABLE; + _xml_add_time = 10; + } else { + _xml_add_type = -1; + } + } else if (strcmp("ADD_AFTER_BOIL", _xml_element[4]) == 0) { + _xml_add_valid = (strcmp("FALSE", char_data_buffer) == 0); + } + } else if ((_xml_depth == 5) && (strcmp("MISCS", _xml_element[2]) == 0) && (strcmp("MISC", _xml_element[3]) == 0)) { + /* + * Check for Misc additions to add during the boil. + */ + if (strcmp("NAME", _xml_element[4]) == 0) { + snprintf(_xml_add_name, 63, "%s", char_data_buffer); + } else if (strcmp("USE", _xml_element[4]) == 0) { + _xml_add_valid = (strcmp("Boil", char_data_buffer) == 0); // Only "Boil" is a valid hop. + } else if (strcmp("TYPE", _xml_element[4]) == 0) { + if (strcmp("Spice", char_data_buffer) == 0) + _xml_add_type = ADDITION_SPICE; + if (strcmp("Fining", char_data_buffer) == 0) + _xml_add_type = ADDITION_FINING; + if (strcmp("Herb", char_data_buffer) == 0) + _xml_add_type = ADDITION_HERB; + if (strcmp("Flavor", char_data_buffer) == 0) + _xml_add_type = ADDITION_FLAVOR; + if (strcmp("Other", char_data_buffer) == 0) + _xml_add_type = ADDITION_OTHER; + } else if (strcmp("TIME", _xml_element[4]) == 0) { + _xml_add_time = atoi(char_data_buffer); + } + } else if ((_xml_depth >= 4) && (strcmp("MASH", _xml_element[2]) == 0)) { + if ((_xml_depth >= 6) && (strcmp("MASH_STEP", _xml_element[4]) == 0)) { + if (strcmp("NAME", _xml_element[5]) == 0) { + snprintf(_xml_add_name, 31, "%s", char_data_buffer); + } else if (strcmp("TYPE", _xml_element[5]) == 0) { + // Temperature Infusion Decoction + _xml_add_valid = (strcmp("Temperature", char_data_buffer) == 0); + } else if (strcmp("STEP_TEMP", _xml_element[5]) == 0) { + _xml_add_temp = atof(char_data_buffer); + } else if (strcmp("STEP_TIME", _xml_element[5]) == 0) { + _xml_add_time = atoi(char_data_buffer); + } else if (strcmp("RAMP_TIME", _xml_element[5]) == 0) { + _xml_add_ramp = atoi(char_data_buffer); + } + } else if ((_xml_depth >= 4) && (strcmp("TUN_TEMP", _xml_element[3]) == 0)) { + // Save this and check later if this is the strike temperature. + _xml_tun_temp = atof(char_data_buffer); + } else if ((_xml_depth >= 4) && (strcmp("SPARGE_TEMP", _xml_element[3]) == 0)) { + recipe.SpargeTemp = atof(char_data_buffer); + } + } + } + } +} + + + +void startElement(void *userData, const char *name, const char **attr) +{ + sprintf(_xml_element[_xml_depth], "%s", name); + _xml_depth++; + process_char_data_buffer(); + reset_char_data_buffer(); +} + + + +void endElement(void *userData, const char *name) +{ + process_char_data_buffer(); + reset_char_data_buffer(); + if ((_xml_depth == 4) && (strcmp("HOP", _xml_element[3]) == 0)) { + if (_xml_add_valid) { + Addition_Add(_xml_add_name, ADDITION_HOP, _xml_add_time); + } + } + if ((_xml_depth == 4) && (strcmp("FERMENTABLE", _xml_element[3]) == 0)) { + if (_xml_add_valid && (_xml_add_type == ADDITION_FERMENTABLE)) { + Addition_Add(_xml_add_name, _xml_add_type, _xml_add_time); + } + } + if ((_xml_depth == 4) && (strcmp("MISC", _xml_element[3]) == 0)) { + if (_xml_add_valid) { + Addition_Add(_xml_add_name, _xml_add_type, _xml_add_time); + } + } + if ((_xml_depth == 5) && (strcmp("MASH_STEP", _xml_element[4]) == 0)) { +// printf("Flush End MASH_STEP %d %s\n", _xml_depth, _xml_add_name); + _xml_mashsteps++; + sprintf(recipe.MashStep[_xml_mashsteps].Name, "%s", _xml_add_name); + recipe.MashStep[_xml_mashsteps].Temperature = _xml_add_temp; + recipe.MashStep[_xml_mashsteps].Resttime = _xml_add_time; + recipe.MashStep[_xml_mashsteps].Steptime = _xml_add_ramp; + } + _xml_depth--; +} + + + +int ParseRecipe(char *fn, char *code) +{ + char buf[512]; + + memset(&recipe, 0, sizeof(recipe)); + XML_Parser parser = XML_ParserCreate(NULL); + + int done; + _xml_depth = 0; + _xml_mashsteps = 0; + _xml_tun_temp = 0.0; + + XML_SetElementHandler(parser, startElement, endElement); + XML_SetCharacterDataHandler(parser, char_data); + + FILE *fp = fopen(fn, "r"); + + do { + int len = (int)fread(buf, 1, sizeof(buf), fp); + done = len < sizeof(buf); + if (XML_Parse(parser, buf, len, done) == XML_STATUS_ERROR) { + printf( "%s at line %5lu\n", XML_ErrorString(XML_GetErrorCode(parser)), XML_GetCurrentLineNumber(parser)); + return 1; + } + vTaskDelay(2 / portTICK_PERIOD_MS); + } while (!done); + + XML_ParserFree(parser); + fclose(fp); + + /* + * Fix missing things. + */ + snprintf(recipe.Code, 31, "%s", code); + // The code is created from the recipe filename. + for (int i = 0; i < strlen(recipe.Code); i++) { + if ((recipe.Code[i] == ' ') || (recipe.Code[i] == '.')) { + recipe.Code[i] = '\0'; + break; + } + } + recipe.CoolTemp = 20.0; + sprintf(recipe.MashStep[0].Name, "Mash-in"); + if (_xml_tun_temp > recipe.MashStep[1].Temperature) { + recipe.MashStep[0].Temperature = _xml_tun_temp; + } else { + recipe.MashStep[0].Temperature = recipe.MashStep[1].Temperature + 1.25; + } + recipe.MashStep[0].Resttime = 1; + + if (! recipe.MashStep[7].Resttime) { + // Move last mash step to position 7. + for (int i = 6; i > 1; i--) { + if (recipe.MashStep[i].Resttime) { + // Got it, move. + sprintf(recipe.MashStep[7].Name, "%s", recipe.MashStep[i].Name); + recipe.MashStep[i].Name[0] = '\0'; + recipe.MashStep[7].Temperature = recipe.MashStep[i].Temperature; + recipe.MashStep[i].Temperature = 0.0; + recipe.MashStep[7].Resttime = recipe.MashStep[i].Resttime; + recipe.MashStep[i].Resttime = 0; + recipe.MashStep[7].Steptime = recipe.MashStep[i].Steptime; + recipe.MashStep[i].Steptime = 0; + break; + } + } + } + + printf("Recipe: %s\n", recipe.Name); + printf("Code : %s\n", recipe.Code); + printf("Boil time %d minutes\n", recipe.BoilTime); + printf("n Stepname temp time ramp\n"); + printf("- ------------------------------ ----- ---- ----\n"); + for (int i = 0; i < 8; i++) { + if (recipe.MashStep[i].Resttime) { + printf("%d %-31s %5.2f %4d %4d\n", i, recipe.MashStep[i].Name, recipe.MashStep[i].Temperature, + recipe.MashStep[i].Resttime, recipe.MashStep[i].Steptime); + } + } + printf("%d additions\n", recipe.Additions); + printf("n Addition name t Tim\n"); + printf("- --------------------------------------------------------------- - ---\n"); + for (int i = 0; i < recipe.Additions; i++) { + printf("%d %-63s %d %3d\n", i, recipe.Addition[i].Name, recipe.Addition[i].Type, recipe.Addition[i].Time); + } + printf("Cooltemp %5.2f\n", recipe.CoolTemp); + if (recipe.Whirlpool9) + printf("Whirlpool9 %d\n", recipe.Whirlpool9); + if (recipe.Whirlpool7) + printf("Whirlpool7 %d\n", recipe.Whirlpool7); + if (recipe.Whirlpool6) + printf("Whirlpool6 %d\n", recipe.Whirlpool6); + if (recipe.Whirlpool2) + printf("Whirlpool2 %d\n", recipe.Whirlpool2); + printf("SpargeTemp %5.2f\n", recipe.SpargeTemp); + + return 0; +} + + + +/* + * Recipes init function, only runs once a new screen is entered. + */ +void Recipes_Init(void) +{ + FILE *f; + DIR *dir; + struct dirent *de; + size_t bytes; + uint8_t *dst; + uint16_t y; + char filename[256], newname[256]; + int rc; + + switch (Main_Screen) { + case MAIN_TOOLS_RECIPE: + TopMessage("Recepten importeren"); + r_Imported = 0; + TFT_setFont(DEFAULT_FONT, NULL); + y = 28; + if ((dir = opendir("/sdcard/recipe"))) { + de = readdir(dir); + while (de) { + if (strstr(de->d_name, ".xml") || strstr(de->d_name, ".XML")) { + _fg = TFT_YELLOW; + TFT_print(de->d_name, 2, y); + snprintf(filename, 255, "/sdcard/recipe/%s", de->d_name); + snprintf(newname, 255, "/sdcard/recipe/%s", de->d_name); + newname[strlen(newname) -2] = 'o'; + newname[strlen(newname) -1] = 'k'; + rc = ParseRecipe(filename, de->d_name); + ESP_LOGI(TAG, "Recipe %s parsed, rc=%d", filename, rc); + if (rc == 0) { + append_recipe(); + rename(filename, newname); + _fg = TFT_GREEN; + TFT_print(" Ok", LASTX, y); + } else { + _fg = TFT_RED; + TFT_print(" Fout", LASTX, y); + } + r_Imported++; // Count them all + y += 16; + } + de = readdir(dir); + } + closedir(dir); + } + + f = fopen("/spiffs/etc/recipe.conf", "r"); + dst = (uint8_t*)&recipe; + r_Records = 0; + while ((bytes = fread(dst, 1, sizeof(recipe), f))) { + r_Records++; + } + fclose(f); + // Load the default record. + r_CurrentRec = config.RecipeRec; + r_UpdateRec = true; + break; + + case MAIN_TOOLS_RECIPE_EDIT: + break; + + default: break; + } +} + + + +/* + * Recipes management loop, non-blocking. + */ +void Recipes_Loop(void) +{ + uint32_t crc1, crc2; + uint8_t *dst; + int mashsteps; + uint16_t y; + char tmp[32]; + float mintemp; + + switch (Main_Screen) { + case MAIN_TOOLS_RECIPE: + if (r_Imported) { + Buttons_Clear(); + Buttons_Add(135, 210, 50, 30, "Ok" , 0); + Buttons_Show(); + + while (true) { + if (Buttons_Scan() == 0) + break; + vTaskDelay(20 / portTICK_PERIOD_MS); + } + Buttons_Clear(); + r_Imported = 0; + } + if (r_UpdateRec) { + _bg = TFT_BLACK; + TFT_fillScreen(_bg); + TFT_resetclipwin(); + TopMessage("Recepten"); + r_UpdateRec = false; + read_recipe(r_CurrentRec); + TFT_setFont(DEFAULT_FONT, NULL); + ShowText(2, 28, "Naam", recipe.Name); + ShowText(2, 44, "Code", recipe.Code); + ShowInteger(162, 44, "Record", NULL, recipe.Record); + ShowInteger(2, 60, "Kooktijd", " min", recipe.BoilTime); + ShowFloat(162, 60, "Koel tot", " C", recipe.CoolTemp, 2); + ShowFloat(2, 76, "Maisch in", " C", recipe.MashStep[0].Temperature, 2); + ShowFloat(162, 76, "Spoelwater", " C", recipe.SpargeTemp, 2); + y = 92; + _fg = TFT_WHITE; + TFT_print("Maisch stap", 2, y); + TFT_print("Temp.", 200, y); + TFT_print("Rust", 260, y); + _fg = TFT_YELLOW; + y += 16; + for (int i = 1; i < 8; i++) { + if (recipe.MashStep[i].Resttime) { + TFT_print(recipe.MashStep[i].Name, 2, y); + sprintf(tmp, "%.2f", recipe.MashStep[i].Temperature); + TFT_print(tmp, 200, y); + sprintf(tmp, "%2d min", recipe.MashStep[i].Resttime); + TFT_print(tmp, 260, y); + y += 16; + } + } + if (recipe.Additions) { + _fg = TFT_YELLOW; + sprintf(tmp, "%d ", recipe.Additions); + TFT_print(tmp, 2, y); + _fg = TFT_WHITE; + TFT_print("toevoegingen om", LASTX, y); + _fg = TFT_YELLOW; + for (int i = 1; i <= recipe.Additions; i++) { + sprintf(tmp, " %d", recipe.Addition[i-1].Time); + TFT_print(tmp, LASTX, y); + } + _fg = TFT_WHITE; + TFT_print(" minuten", LASTX, y); + } else { + _fg = TFT_WHITE; + TFT_print("Geen hop toevoegingen.", 2, y); + } + y += 16; + if (recipe.Whirlpool9 || recipe.Whirlpool7 || recipe.Whirlpool6 || recipe.Whirlpool2) { + _fg = TFT_WHITE; + TFT_print("Whirlpool ", 2, y); + _fg = TFT_YELLOW; + if (recipe.Whirlpool9) + TFT_print("88+ ", LASTX, y); + if (recipe.Whirlpool7) + TFT_print("71+ ", LASTX, y); + if (recipe.Whirlpool6) + TFT_print("60+ ", LASTX, y); + if (recipe.Whirlpool2) + TFT_print("koud", LASTX, y); + } + + Buttons_Clear(); + Buttons_Add( 0, 210, 45, 30, "Ok" , 0); + Buttons_Add( 46, 210, 45, 30, "+" , 1); + if (r_CurrentRec != config.RecipeRec) + Buttons_Add( 92, 210, 45, 30, "-", 2); + else + Buttons_Add( 92, 210, 45, 30, "" , 2); + if (r_CurrentRec > 1) + Buttons_Add(138, 210, 45, 30, "<", 3); + else + Buttons_Add(138, 210, 45, 30, "", 3); + if (r_CurrentRec < r_Records) + Buttons_Add(184, 210, 45, 30, ">", 4); + else + Buttons_Add(184, 210, 45, 30, "", 4); + if (r_CurrentRec != config.RecipeRec) + Buttons_Add(230, 210, 45, 30, "Std", 5); + else + Buttons_Add(230, 210, 45, 30, "", 5); + Buttons_Add(276, 210, 45, 30, "Ed" , 6); + Buttons[0].dark = true; + Buttons_Show(); + } /* if (r_UpdateRec) */ + switch (Buttons_Scan()) { + case 0: Main_Screen = MAIN_TOOLS; + break; + + case 1: memset(&recipe, 0, sizeof(recipe)); + recipe.Version = 1; + recipe.Record = r_Records + 1; + sprintf(recipe.Name, "Recipe %d", r_Records + 1); + sprintf(recipe.Code, "00%d", r_Records + 1); + sprintf(recipe.MashStep[0].Name, "Mash-in"); + recipe.MashStep[0].Temperature = 67.5; + recipe.MashStep[0].Resttime = 1; + sprintf(recipe.MashStep[1].Name, "Mash"); + recipe.MashStep[1].Temperature = 67.0; + recipe.MashStep[1].Resttime = 75; + sprintf(recipe.MashStep[7].Name, "Mash-out"); + recipe.MashStep[7].Temperature = 78.0; + recipe.MashStep[7].Resttime = 5; + recipe.BoilTime = 60; + recipe.Additions = 2; + sprintf(recipe.Addition[0].Name, "Hop"); + recipe.Addition[0].Time = 60; + recipe.Addition[0].Type = ADDITION_HOP; + sprintf(recipe.Addition[1].Name, "Hop"); + recipe.Addition[1].Time = 10; + recipe.Addition[1].Type = ADDITION_HOP; + recipe.CoolTemp = 20.0; + recipe.Whirlpool9 = 0; + recipe.Whirlpool7 = 0; + recipe.Whirlpool6 = 0; + recipe.Whirlpool2 = 0; + recipe.SpargeTemp = 85.0; + append_recipe(); + r_Records++; + r_CurrentRec = r_Records; + r_UpdateRec = true; + ESP_LOGI(TAG, "New recipe record %d", recipe.Record); + break; + + case 2: if ((r_CurrentRec != config.RecipeRec) && (r_Records > 1)) { + delete_recipe(r_CurrentRec); + r_Records--; + if (r_CurrentRec > r_Records) + r_CurrentRec = r_Records; + r_UpdateRec = true; + } + break; + + case 3: if (r_CurrentRec > 1) { + r_CurrentRec--; + r_UpdateRec = true; + } + break; + + case 4: if (r_CurrentRec < r_Records) { + r_CurrentRec++; + r_UpdateRec = true; + } + break; + + case 5: if (r_CurrentRec != config.RecipeRec) { + config.RecipeRec = r_CurrentRec; + write_config(); + r_UpdateRec = true; + ESP_LOGI(TAG, "Recipe %d `%s' set as default", r_CurrentRec, recipe.Name); + } + break; + + case 6: Main_Screen = MAIN_TOOLS_RECIPE_EDIT; + break; + + default: break; + } + break; + + case MAIN_TOOLS_RECIPE_EDIT: + dst = (uint8_t*)&recipe; + crc1 = crc32_le(0, dst, sizeof(recipe)); + + EditText("Naam", recipe.Name, 31); + EditText("Code", recipe.Code, 31); + mashsteps = 0; + for (int i = 1; i < 7; i++) { + if (recipe.MashStep[i].Resttime) + mashsteps++; + } + EditInt("Maisch stappen", &mashsteps, 1, 6); + EditFloat("Inmaisch temperatuur", &recipe.MashStep[0].Temperature, 38, 74, 2); + // Round to 0.25 values + recipe.MashStep[0].Temperature = ((int)(recipe.MashStep[0].Temperature * 4)) / 4.0; + for (int i = 1; i <= mashsteps; i++) { + sprintf(tmp, "Maisch stap %d naam", i); + EditText(tmp, recipe.MashStep[i].Name, 31); + sprintf(tmp, "Maisch stap %d temperatuur", i); + if (i == 1) + mintemp = recipe.MashStep[0].Temperature - 5; + else + mintemp = recipe.MashStep[i - 1].Temperature; + EditFloat("Inmaisch temperatuur", &recipe.MashStep[i].Temperature, mintemp, 74, 2); + recipe.MashStep[i].Temperature = ((int)(recipe.MashStep[i].Temperature * 4)) / 4.0; + sprintf(tmp, "Maisch stap %d rusttijd in minuten", i); + EditUint16(tmp, &recipe.MashStep[i].Resttime, 1, 480); + if (i == 1) { + recipe.MashStep[i].Steptime = 1; + } else { + recipe.MashStep[i].Steptime = (int)(recipe.MashStep[i].Temperature - recipe.MashStep[i - 1].Temperature); + } + } + mintemp = recipe.MashStep[mashsteps].Temperature; + EditFloat("Uitmaischen temperatuur", &recipe.MashStep[7].Temperature, mintemp, 80, 2); + recipe.MashStep[7].Temperature = ((int)(recipe.MashStep[7].Temperature * 4)) / 4.0; + EditUint16("Uitmaischen tijd in minuten", &recipe.MashStep[7].Resttime, 1, 15); + recipe.MashStep[7].Steptime = (int)(recipe.MashStep[7].Temperature - recipe.MashStep[mashsteps].Temperature); + // Zero any higher steps to diable them. + for (int i = (mashsteps + 1); i < 7; i++) + recipe.MashStep[i].Resttime = 0; + + EditUint16("Kook tijd in minuten", &recipe.BoilTime, 3, 480); + EditUint8("Hop/kruiden toevoegingen", &recipe.Additions, 1, 10); + for (uint8_t i = 0; i < recipe.Additions; i++) { + sprintf(tmp, "Toevoeging %d maam", i+1); + if (strlen(recipe.Addition[i].Name) == 00) { + sprintf(recipe.Addition[i].Name, "Hop %d", (int)i+1); + } + EditText(tmp, recipe.Addition[i].Name, 40); + sprintf(tmp, "Toevoeging %d tijd", i+1); + if (i == 0) { + EditUint16(tmp, &recipe.Addition[i].Time, 0, recipe.BoilTime); + } else { + EditUint16(tmp, &recipe.Addition[i].Time, 0, recipe.Addition[i-1].Time - 1); + } + recipe.Addition[i].Type = ADDITION_HOP; + } + + EditFloat("Koel temperatuur", &recipe.CoolTemp, 10, 30, 2); + recipe.CoolTemp = ((int)(recipe.CoolTemp * 4)) / 4.0; + EditFloat("Spoelwater temperatuur", &recipe.SpargeTemp, 75, 98, 2); + recipe.SpargeTemp = ((int)(recipe.SpargeTemp * 4)) / 4.0; + EditUint16("Whirlpool 88..100 graden, 0 = niet", &recipe.Whirlpool9, 0, 120); + EditUint16("Whirlpool 71..77 graden, 0 = niet", &recipe.Whirlpool7, 0, 120); + EditUint16("Whirlpool 60..66 graden, 0 = niet", &recipe.Whirlpool6, 0, 120); + EditUint16("Whirlpool koud, 0 = niet", &recipe.Whirlpool2, 0, 120); + + crc2 = crc32_le(0, dst, sizeof(recipe)); + if ((crc1 != crc2) && Confirm("Gewijzigd, opslaan?", "Ja", "Nee")) { + write_recipe(recipe.Record); + } + Main_Screen = MAIN_TOOLS_RECIPE; + break; + + default: break; + } +} + +