Changed the recipe database so that it is expandable, version 2. More mash fields and allow 16 steps. Allow 20 Additions. Removed separate mash steps from the state machine, the steps are moved to the runtime data. There is no fixed step number for mashout anymore. There is no fixed step for mash-in anymore, just use the first step and heat to the infusion temperature. After malt add, switch to the normal step temperature. Implemented decoction steps.

Sat, 06 Jun 2020 13:28:46 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Sat, 06 Jun 2020 13:28:46 +0200
changeset 77
66c77497d86d
parent 76
3ff381bfa469
child 78
b58e0c9897e1

Changed the recipe database so that it is expandable, version 2. More mash fields and allow 16 steps. Allow 20 Additions. Removed separate mash steps from the state machine, the steps are moved to the runtime data. There is no fixed step number for mashout anymore. There is no fixed step for mash-in anymore, just use the first step and heat to the infusion temperature. After malt add, switch to the normal step temperature. Implemented decoction steps.

image/version.txt file | annotate | diff | comparison | revisions
image/w/js/webui.js file | annotate | diff | comparison | revisions
main/automation.c file | annotate | diff | comparison | revisions
main/buttons.c file | annotate | diff | comparison | revisions
main/config.c file | annotate | diff | comparison | revisions
main/config.h file | annotate | diff | comparison | revisions
main/recipes.c file | annotate | diff | comparison | revisions
main/task_tft.c file | annotate | diff | comparison | revisions
sdkconfig file | annotate | diff | comparison | revisions
--- a/image/version.txt	Mon Jun 01 20:27:00 2020 +0200
+++ b/image/version.txt	Sat Jun 06 13:28:46 2020 +0200
@@ -1,1 +1,1 @@
-0.3.7a
+0.3.7b
--- a/image/w/js/webui.js	Mon Jun 01 20:27:00 2020 +0200
+++ b/image/w/js/webui.js	Sat Jun 06 13:28:46 2020 +0200
@@ -78,15 +78,8 @@
 			$("#hltline").show();
                         $("#hlt_table").show();
 			break;
-	    case 100:	// All handled at Subscreen
-            case 101:
-            case 102:
-            case 103:
-            case 104:
-            case 105:
-            case 106:
-            case 107:	break;
-	    case 108:	this.clearScreen();
+	    case 100:	break;	// All handled at Subscreen
+	    case 120:	this.clearScreen();
 			$("#mltline").show();	// to boil
 			$("#mlt_table").show();
 			$("#mlt00").show();
@@ -94,7 +87,7 @@
                         $("#but00").text('+sp');
                         $("#but20").text('-sp');
 			break;
-            case 109:	this.clearScreen();
+            case 121:	this.clearScreen();
 			$("#mltline").show();	// boil
 			$("#mlt_table").show();
 			$("#mlt00").show();
@@ -113,19 +106,19 @@
                         $("#but04").text('+1m');
                         $("#but24").text('-1m');
 			break;
-	    case 110:	// whirlpool 9
-	    case 111:	// cooling H
-	    case 112:	// whirlpool 7
-	    case 113:	// cooling M
-	    case 114:	// whirlpool 6
-	    case 115:	// cooling C
-	    case 116:	// whirlpool 2
+	    case 122:	// whirlpool 9
+	    case 123:	// cooling H
+	    case 124:	// whirlpool 7
+	    case 125:	// cooling M
+	    case 126:	// whirlpool 6
+	    case 127:	// cooling C
+	    case 128:	// whirlpool 2
 			break;
-	    case 117:	this.clearScreen();
+	    case 129:	this.clearScreen();
                         $("#note").show();
 			$("#note_msg").text('Brouwen is gereed.');
 			break;
-	    case 118:	this.clearScreen();
+	    case 130:	this.clearScreen();
                         $("#note").show();
                         $("#note_msg").text('Brouwen is afgebroken.');
 			break;
@@ -166,7 +159,7 @@
 	    } else if (Subscreen == 1) {
 		$("#brew").show();
 	    }
-	} else if ((Screen >= 100) && (Screen < 108)) {
+	} else if ((Screen >= 100) && (Screen < 120)) {
 	    this.clearScreen();
             $("#mltline").show();
             $("#mlt_table").show();
@@ -192,6 +185,7 @@
 		case 4:	// MASH_IODINE
 		case 5:	// MASH_REMOVE
 		case 6:	// MASH_INFUSE
+		case 7: // MASH_DECOCT
 			this.setTimer(0);
 			$("#mid00").show();
 			$("#mid00").css("background-color", "#575757");
@@ -201,7 +195,7 @@
                         $("#but22").text('Ok');
 			break;
 	    };
-	} else if ((Screen == 111) || (Screen == 113) || (Screen == 115)) { // cooling H,M,C
+	} else if ((Screen == 123) || (Screen == 125) || (Screen == 127)) { // cooling H,M,C
 	    this.clearScreen();
 	    if (Subscreen == 0) {
 		$("#prompt").show();
@@ -222,7 +216,7 @@
 		$("#hlt01").css("background-color", "#575757");
                 $("#hlt01").css("color", "white");
 	    }
-	} else if ((Screen == 110) || (Screen == 112) || (Screen == 114) || (Screen == 116)) { // whirlpools
+	} else if ((Screen == 122) || (Screen == 124) || (Screen == 126) || (Screen == 128)) { // whirlpools
 	    this.clearScreen();
             if (Subscreen == 0) {
         	$("#prompt").show();
--- a/main/automation.c	Mon Jun 01 20:27:00 2020 +0200
+++ b/main/automation.c	Sat Jun 06 13:28:46 2020 +0200
@@ -104,7 +104,7 @@
 	case MAIN_AUTO_INIT1:
 		log_msg(TAG, "Automation startup");
 #ifdef CONFIG_TEMP_SENSORS_SIMULATOR
-		Fake_MLT = recipe.MashStep[0].Temperature - 10;
+		Fake_MLT = recipe.MashStep[0].Step_temp - 10;
 		Fake_HLT = recipe.SpargeTemp - 15;
                 if (xSemaphoreTake(xSemaphoreDS18B20, 10) == pdTRUE) {
                     ds18b20_state->mlt_temperature = ((int)(Fake_MLT * 16)) / 16.0;
@@ -112,10 +112,10 @@
                     xSemaphoreGive(xSemaphoreDS18B20);
                 }
 #endif
-                for (int i = 0; i < 7; i++) {
-                    if (recipe.MashStep[i].Resttime)
-                        LastMashStep = i;
-                }
+		if (recipe.Mashsteps > 1)
+		    LastMashStep = recipe.Mashsteps - 2;
+		else
+		    LastMashStep = 0;
                 log_msg(TAG, "Last mash step %d", LastMashStep);
 
                 // Check for a crashed session.
@@ -178,6 +178,8 @@
 		runtime.HopAddition = 0;
 		runtime.Logfile[0] = '\0';
 		runtime.PumpCooling = false;
+		runtime.MashStep = 0;
+		runtime.MaltAdded = false;
 		write_runtime();
                 power_MLT = power_HLT = counts = 0;
 		log_clean();
@@ -200,17 +202,10 @@
 		}
 		break;
 
-	case MAIN_AUTO_MASH_IN:
-        case MAIN_AUTO_MASH_1:
-        case MAIN_AUTO_MASH_2:
-        case MAIN_AUTO_MASH_3:
-        case MAIN_AUTO_MASH_4:
-        case MAIN_AUTO_MASH_5:
-        case MAIN_AUTO_MASH_6:
-        case MAIN_AUTO_MASH_OUT:
-                if (Main_Screen == MAIN_AUTO_MASH_IN) {
+	case MAIN_AUTO_MASH:
+                if (runtime.MashStep == 0) {
                     MinMash = 38.0;
-                    MaxMash = recipe.MashStep[1].Temperature + 10.0;
+                    MaxMash = recipe.MashStep[0].Step_temp+ 10.0;
                     TimeBrewing = 0;
 		    runtime.TimeBrewing = 0;
                     if (System_TimeOk) {
@@ -223,20 +218,19 @@
 			runtime.BrewStart = (time_t)0;
                     }
 		    updateRuntime = true;
-                } else if (Main_Screen == MAIN_AUTO_MASH_OUT) {
+                } else if (runtime.MashStep == (recipe.Mashsteps - 1)) {
                     MinMash = 75.0;
                     MaxMash = 80.0;
                 } else {
-                    MinMash = recipe.MashStep[Main_Screen - MAIN_AUTO_MASH_IN - 1].Temperature;
-                    if (recipe.MashStep[Main_Screen - MAIN_AUTO_MASH_IN + 1].Resttime) {
-                        MaxMash = recipe.MashStep[Main_Screen - MAIN_AUTO_MASH_IN + 1].Temperature;
-                    } else {
-                        MaxMash = 75.0;
-                    }
+                    MinMash = recipe.MashStep[runtime.MashStep - 1].Step_temp;
+                    MaxMash = recipe.MashStep[runtime.MashStep + 1].Step_temp;
+		    if (MaxMash >= 75.0)
+			MaxMash = 74.75;
                 }
                 MashState = Sub_Screen = MASH_NONE;
-		snprintf(msg, 63, "{\"main\":\"%d\",\"sub\":\"%d\",\"timer\":\"\"}", Main_Screen, Sub_Screen);
+		snprintf(msg, 63, "{\"main\":\"%d\",\"sub\":\"%d\",\"step\":\"%d\",\"timer\":\"\"}", Main_Screen, Sub_Screen, runtime.MashStep);
 		ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
+		ESP_LOGI(TAG, "%s", msg);
                 pumpTime = 0;
                 pumpRest = false;
 		runtime.StageResume = Main_Screen;
@@ -446,7 +440,7 @@
 		y += 16;
 		ShowText(2,y,(char *)"Recept", recipe.Name);
 		y += 16;
-		ShowFloat(2, y, (char *)"Maisch in", (char *)" C", recipe.MashStep[0].Temperature, 2);
+		ShowFloat(2, y, (char *)"Maisch in", (char *)" C", recipe.MashStep[0].Infuse_temp, 3);
 		ShowFloat(162, y, (char *)"Spoelwater", (char *)" C", recipe.SpargeTemp, 2);
 		y += 16;
 		_fg = TFT_WHITE;
@@ -456,18 +450,19 @@
 		TFT_print((char *)"Rust", 280, 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);
-			strcpy(tmp, mashTypes[recipe.MashStep[i].Type]);
-			tmp[1] = '\0';
-			TFT_print(tmp, 200, y);
-			sprintf(tmp, "%.2f", recipe.MashStep[i].Temperature);
-			TFT_print(tmp, 220, y);
-			sprintf(tmp, "%2d m", recipe.MashStep[i].Resttime);
-			TFT_print(tmp, 280, y);
-			y += 16;
-		    }
+		for (int i = 0; i < recipe.Mashsteps; i++) {
+		    TFT_print(recipe.MashStep[i].Name, 2, y);
+		    if (recipe.MashStep[i].Type < 3)
+		    	strcpy(tmp, mashTypes[recipe.MashStep[i].Type]);
+		    else
+			tmp[0] = '?';
+		    tmp[1] = '\0';
+		    TFT_print(tmp, 200, y);
+		    sprintf(tmp, "%.2f", recipe.MashStep[i].Step_temp);
+		    TFT_print(tmp, 220, y);
+		    sprintf(tmp, "%2d m", recipe.MashStep[i].Step_time);
+		    TFT_print(tmp, 280, y);
+		    y += 16;
 		}
 		ShowInteger(2, y, (char *)"Kooktijd", (char *)" min", recipe.BoilTime);
 		ShowFloat(162, y, (char *)"Koel tot", (char *)" C", recipe.CoolTemp, 2);
@@ -589,7 +584,7 @@
                      */
                     int AvailableTime = 0;
                     for (int i = 1; i < 6; i++) // Only normal Mash steps
-                        AvailableTime += recipe.MashStep[i].Resttime;
+                        AvailableTime += recipe.MashStep[i].Step_time;
                     if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
                         driver_state->hlt_sp = recipe.SpargeTemp - ((AvailableTime / 2) + 2);
                         log_msg(TAG, "HLT preheat set to %4.2f", driver_state->hlt_sp);
@@ -606,7 +601,7 @@
 
 	case MAIN_AUTO_HEATUP:
                 if (! runtime.UseHLT) {         // Skip if HLT is off
-                    Main_Screen = MAIN_AUTO_MASH_IN;
+                    Main_Screen = MAIN_AUTO_MASH;
                     break;
                 }
 
@@ -614,7 +609,7 @@
                 HLT_info(71,150, true, false);
                 if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
                     if (driver_state->hlt_pv >= driver_state->hlt_sp) {
-                        Main_Screen = MAIN_AUTO_MASH_IN;
+                        Main_Screen = MAIN_AUTO_MASH;
                         driver_state->hlt_sp = recipe.SpargeTemp;       // Set final setpoint
 			log_msg(TAG, "HLT preheat done");
                     }
@@ -622,24 +617,17 @@
                 }
                 break;
 
-	case MAIN_AUTO_MASH_IN:
-	case MAIN_AUTO_MASH_1:
-	case MAIN_AUTO_MASH_2:
-	case MAIN_AUTO_MASH_3:
-	case MAIN_AUTO_MASH_4:
-	case MAIN_AUTO_MASH_5:
-	case MAIN_AUTO_MASH_6:
-	case MAIN_AUTO_MASH_OUT:
+	case MAIN_AUTO_MASH:
                 temp_MLT = 0.0;
                 if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
                     temp_MLT = driver_state->mlt_pv;
 
-		    if (MashState == MASH_ADD || MashState == MASH_REMOVE) {
+		    if (MashState == MASH_ADD || MashState == MASH_REMOVE || MashState == MASH_INFUSE) {
 			driver_state->pump_run = 0;
 		    } else if (MashState != MASH_NONE) {
-                        if (Main_Screen == MAIN_AUTO_MASH_IN) {
+                        if (runtime.MashStep == 0) {
                             driver_state->pump_run = (equipment.PumpPreMash && ! pumpRest) ? 1 : 0;
-                        } else if (Main_Screen == MAIN_AUTO_MASH_OUT) {
+                        } else if (runtime.MashStep == (recipe.Mashsteps -1)) {
                             driver_state->pump_run = (equipment.PumpMashOut && ! pumpRest) ? 1 : 0;
                         } else {
                             driver_state->pump_run = (equipment.PumpOnMash && ! pumpRest) ? 1 : 0;
@@ -648,44 +636,47 @@
                     xSemaphoreGive(xSemaphoreDriver);
                 }
                 if (MashState == MASH_NONE) {
-                    stageTemp = recipe.MashStep[Main_Screen - MAIN_AUTO_MASH_IN].Temperature;
-                    stageTime = recipe.MashStep[Main_Screen - MAIN_AUTO_MASH_IN].Resttime;
+		    if (runtime.MashStep == 0 && ! runtime.MaltAdded && recipe.MashStep[0].Infuse_temp) {
+			stageTemp = recipe.MashStep[0].Infuse_temp;
+		    } else {
+                    	stageTemp = recipe.MashStep[runtime.MashStep].Step_temp;
+		    }
+                    stageTime = recipe.MashStep[runtime.MashStep].Step_time;
                     TempReached = false;
-                    if (stageTime == 0) {
-                        ESP_LOGI(TAG, "Mash step %d skipped", Main_Screen - MAIN_AUTO_MASH_IN);
-                        Main_Screen++;
-                        break;
-                    }
                     if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
                         driver_state->mlt_sp = stageTemp;
                         driver_state->mlt_mode = MLT_MODE_PID;
                         xSemaphoreGive(xSemaphoreDriver);
                     }
                     MashState = Sub_Screen = MASH_WAITTEMP;
-		    snprintf(msg, 255, "{\"main\":\"%d\",\"sub\":\"%d\",\"timer\":\"\"}", Main_Screen, Sub_Screen);
+		    ESP_LOGI(TAG, "** Step %d Stage %d Malt %s", runtime.MashStep, MashState, runtime.MaltAdded ? (char *)"yes":(char *)"no");
+		    snprintf(msg, 255, "{\"main\":\"%d\",\"sub\":\"%d\",\"step\":\"%d\",\"timer\":\"\"}", Main_Screen, Sub_Screen, runtime.MashStep);
                     ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
-                    log_msg(TAG, "Mash step %d type: %s time: %d sp: %4.2f  sv: %4.3f",
-				    Main_Screen - MAIN_AUTO_MASH_IN, mashTypes[recipe.MashStep[Main_Screen - MAIN_AUTO_MASH_IN].Type],
+                    log_msg(TAG, "Mash step %d type: %s time: %d sp: %6.4f  sv: %6.4f",
+				    runtime.MashStep, mashTypes[recipe.MashStep[runtime.MashStep].Type],
                                     stageTime, stageTemp, temp_MLT);
 
-		    if (Main_Screen > MAIN_AUTO_MASH_IN) {
+		    if (runtime.MashStep) {
 			// Do not annotate before the log is open.
-			if (Main_Screen == MAIN_AUTO_MASH_OUT) {
+			if (runtime.MashStep == (recipe.Mashsteps -1)) {
 			    log_annotation(ANNOTATION_STAGE, (char *)"Uitmaischen");
 			} else {
-                    	    sprintf(logline, "Maisch: %d", Main_Screen - MAIN_AUTO_MASH_IN);
+                    	    sprintf(logline, "Maisch: %d", runtime.MashStep);
 		    	    log_annotation(ANNOTATION_STAGE, logline);
 			}
 		    }
 
-                    if (strlen(recipe.MashStep[Main_Screen - MAIN_AUTO_MASH_IN].Name)) {
-                        TopMessage(recipe.MashStep[Main_Screen - MAIN_AUTO_MASH_IN].Name);
+		    if (! runtime.MaltAdded) {
+			TopMessage((char *)"Opwarmen MLT");
+                    } else if (strlen(recipe.MashStep[runtime.MashStep].Name)) {
+                        TopMessage(recipe.MashStep[runtime.MashStep].Name);
                     } else {
-                        sprintf(temp_buf, "Maisch stap #%d", Main_Screen - MAIN_AUTO_MASH_IN);
+                        sprintf(temp_buf, "Maisch stap #%d", runtime.MashStep);
                         TopMessage(temp_buf);
                     }
-		    if ((Main_Screen > MAIN_AUTO_MASH_IN) && (Main_Screen < MAIN_AUTO_MASH_OUT) && 
-			 (recipe.MashStep[Main_Screen - MAIN_AUTO_MASH_IN].Type == MASHTYPE_INFUSION)) {
+		    if (runtime.MashStep && (runtime.MashStep < recipe.Mashsteps) && (recipe.MashStep[runtime.MashStep].Type != MASHTYPE_TEMPERATURE)) {
+			TFT_fillRect(0, 120, 320, 50, TFT_BLACK);
+                        Buttons_Clear();
 			Buttons_Add(  5,120, 60, 40, (char *)"Halt", 0);
 			Buttons[0].dark = true;
 			Buttons_Add(255,120, 60, 40, (char *)"Ok",   1);
@@ -693,20 +684,29 @@
 			_fg = TFT_WHITE;
 			_bg = TFT_BLACK;
 			TFT_setFont(DEJAVU18_FONT, NULL);
-			sprintf(temp_buf, "Infuse %.1f L/%.1f C", recipe.MashStep[Main_Screen - MAIN_AUTO_MASH_IN].Infusion_amount,
-					recipe.MashStep[Main_Screen - MAIN_AUTO_MASH_IN].Infusion_temp);
+			if (recipe.MashStep[runtime.MashStep].Type == MASHTYPE_INFUSION) {
+			    sprintf(temp_buf, "Infusie %.1f L/%.1f C", recipe.MashStep[runtime.MashStep].Infuse_amount,
+					recipe.MashStep[runtime.MashStep].Infuse_temp);
+			    MashState = Sub_Screen = MASH_INFUSE;
+			    log_msg(TAG, "Mash infusion prompt");
+			} else if (recipe.MashStep[runtime.MashStep].Type == MASHTYPE_DECOCTION) {
+			    sprintf(temp_buf, "Decoctie %.1f Liter", recipe.MashStep[runtime.MashStep].Infuse_amount);
+			    MashState = Sub_Screen = MASH_DECOCT;
+			    log_msg(TAG, "Mash decoction prompt");
+			}
 			TFT_print(temp_buf, CENTER, 135);
 			SoundPlay(SOUND_Prompt);
-			MashState = Sub_Screen = MASH_INFUSE;
-			snprintf(msg, 255, "{\"main\":\"%d\",\"sub\":\"%d\",\"timer\":\"%s\"}", Main_Screen, Sub_Screen, temp_buf);
+			snprintf(msg, 255, "{\"main\":\"%d\",\"sub\":\"%d\",\"step\":\"%d\",\"timer\":\"%s\"}",
+					Main_Screen, Sub_Screen, runtime.MashStep, temp_buf);
                 	ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
-			if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
-			    // No heating during the infusion.
-			    driver_state->mlt_sp = stageTemp;
-			    driver_state->mlt_mode = MLT_MODE_OFF;
-			    xSemaphoreGive(xSemaphoreDriver);
+			if (recipe.MashStep[runtime.MashStep].Type == MASHTYPE_INFUSION) {
+			    if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
+			    	// No heating during the infusion.
+			    	driver_state->mlt_sp = stageTemp;
+			    	driver_state->mlt_mode = MLT_MODE_OFF;
+			    	xSemaphoreGive(xSemaphoreDriver);
+			    }
 			}
-			log_msg(TAG, "Mash infusion prompt");
 		    } else {
 			Buttons_Add(  5,  30, 60, 40, (char *)"+sp",   0);
 			Buttons_Add(255,  30, 60, 40, (char *)"-sp",   1);
@@ -722,9 +722,10 @@
                         SoundPlay(SOUND_TempReached);
                         TempReached = true;
                         MashState = Sub_Screen = MASH_REST;
-			snprintf(msg, 63, "{\"main\":\"%d\",\"sub\":\"%d\",\"timer\":\"\"}", Main_Screen, Sub_Screen);
+			ESP_LOGI(TAG, "** Step %d Stage %d Malt %s", runtime.MashStep, MashState, runtime.MaltAdded ? (char *)"yes":(char *)"no");
+			snprintf(msg, 63, "{\"main\":\"%d\",\"sub\":\"%d\",\"step\":\"%d\",\"timer\":\"\"}", Main_Screen, Sub_Screen, runtime.MashStep);
                 	ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
-                        if (Main_Screen == MAIN_AUTO_MASH_IN) {
+                        if (! runtime.MaltAdded && runtime.MashStep == 0) {
                             TimerSet(0);
                         } else {
                             if (Resume && (runtime.StageTimeLeft < stageTime))
@@ -735,7 +736,7 @@
 			Resume = false;
 			runtime.StageTimeLeft = TimeLeft / 60;
 			updateRuntime = true;
-                        log_msg(TAG, "Mash step %d temperature reached, rest %d minutes", Main_Screen - MAIN_AUTO_MASH_IN, TimeLeft / 60);
+                        log_msg(TAG, "Mash step %d temperature reached, rest %d minutes", runtime.MashStep, TimeLeft / 60);
                         Buttons_Clear();
                         Buttons_Add(  0, 120, 60, 40, (char *)"+1m",   0);
                         Buttons_Add(260, 120, 60, 40, (char *)"-1m",   1);
@@ -760,8 +761,8 @@
                     /*
                      * Mash step rest time and pump control
                      */
-                    if (((Main_Screen == MAIN_AUTO_MASH_OUT) && equipment.PumpMashOut) ||
-                        ((Main_Screen != MAIN_AUTO_MASH_OUT) && equipment.PumpOnMash)) {
+                    if (((runtime.MashStep == (recipe.Mashsteps -1)) && equipment.PumpMashOut) ||
+                        ((runtime.MashStep < recipe.Mashsteps) && equipment.PumpOnMash)) {
                         float DeltaTemp = equipment.PumpRest * stageTemp / 120; // Maximum temperature drop before heating again.
                         if (pumpTime >= (equipment.PumpCycle + equipment.PumpRest) || ((stageTemp - temp_MLT) > DeltaTemp)) {
                             pumpTime = 0;
@@ -795,13 +796,20 @@
                     }
 
                     if (TimeLeft == 0) {
+			ESP_LOGI(TAG, "** Step %d Stage %d Malt %s", runtime.MashStep, MashState, runtime.MaltAdded ? (char *)"yes":(char *)"no");
 			runtime.StageTimeLeft = 0;
 			updateRuntime = true;
-                        if ((Main_Screen == MAIN_AUTO_MASH_IN) && config.AskAdd) {
+                        if (runtime.MashStep == 0 && ! runtime.MaltAdded && config.AskAdd) {
                             /*
                              * Add Mash prompt.
                              */
+			    stageTemp = recipe.MashStep[runtime.MashStep].Step_temp; // Set normal step temperature
+                    	    if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
+                        	driver_state->mlt_sp = stageTemp;
+                        	xSemaphoreGive(xSemaphoreDriver);
+                    	    }
 			    log_annotation(ANNOTATION_EVENT, (char *)"Mout storten");
+			    TFT_fillRect(0, 120, 320, 50, TFT_BLACK);
                             Buttons_Clear();
                             Buttons_Add(  5,120, 60, 40, (char *)"Halt", 0);
 			    Buttons[0].dark = true;
@@ -813,12 +821,13 @@
                             TFT_print((char *)"Mout storten?", CENTER, 135);
                             SoundPlay(SOUND_Prompt);
 			    MashState = Sub_Screen = MASH_ADD;
-			    snprintf(msg, 63, "{\"main\":\"%d\",\"sub\":\"%d\",\"timer\":\"Mout storten?\"}", Main_Screen, Sub_Screen);
+			    snprintf(msg, 63, "{\"main\":\"%d\",\"sub\":\"%d\",\"step\":\"%d\",\"timer\":\"Mout storten?\"}",
+						Main_Screen, Sub_Screen, runtime.MashStep);
                 	    ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
 			    log_msg(TAG, "Mash add prompt");
 			    break;
                         }
-                        if (((Main_Screen - MAIN_AUTO_MASH_IN) == LastMashStep) && config.AskIodine) {
+                        if ((runtime.MashStep == LastMashStep) && config.AskIodine) {
                             /*
                              * Iodone test prompt.
                              */
@@ -837,12 +846,14 @@
                             beeped = false;
                             TimerSet(config.IodineTime * 60);
 			    MashState = Sub_Screen = MASH_IODINE;
-			    snprintf(msg, 63, "{\"main\":\"%d\",\"sub\":\"%d\",\"timer\":\"Jodium test?\"}", Main_Screen, Sub_Screen);
+		ESP_LOGI(TAG, "** Step %d Stage %d Malt %s", runtime.MashStep, MashState, runtime.MaltAdded ? (char *)"yes":(char *)"no");
+			    snprintf(msg, 63, "{\"main\":\"%d\",\"sub\":\"%d\",\"step\":\"%d\",\"timer\":\"Jodium test?\"}",
+						Main_Screen, Sub_Screen, runtime.MashStep);
                 	    ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
 			    log_msg(TAG, "Mash iodine test prompt");
 			    break;
                         }
-                        if ((Main_Screen == MAIN_AUTO_MASH_OUT) && config.AskRemove) {
+                        if ((runtime.MashStep == (recipe.Mashsteps - 1)) && config.AskRemove) {
                             /*
                              * Mash remove prompt.
                              */
@@ -859,13 +870,20 @@
                             TFT_print((char *)"Mout verwijderen?", CENTER, 135);
                             SoundPlay(SOUND_Prompt);
 			    MashState = Sub_Screen = MASH_REMOVE;
-			    snprintf(msg, 63, "{\"main\":\"%d\",\"sub\":\"%d\",\"timer\":\"Mout verwijderen?\"}", Main_Screen, Sub_Screen);
+		ESP_LOGI(TAG, "** Step %d Stage %d Malt %s", runtime.MashStep, MashState, runtime.MaltAdded ? (char *)"yes":(char *)"no");
+			    snprintf(msg, 127, "{\"main\":\"%d\",\"sub\":\"%d\",\"step\":\"%d\",\"timer\":\"Mout verwijderen?\"}",
+						Main_Screen, Sub_Screen, runtime.MashStep);
                 	    ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
 			    log_msg(TAG, "Mash remove prompt");
 			    break;
                         }
-                        if (Main_Screen != MAIN_AUTO_ABORT)
-                            Main_Screen++;
+                        if (Main_Screen != MAIN_AUTO_ABORT) {
+                            runtime.MashStep++;
+			    MashState = Sub_Screen = MASH_NONE;
+			    if (runtime.MashStep == recipe.Mashsteps)
+				Main_Screen = MAIN_AUTO_TOBOIL;
+			    updateRuntime = true;
+			}
                         TempReached = false;
                     } else {
                         TimerShow(TimeLeft, 65, 122);
@@ -888,32 +906,72 @@
 		    switch (Buttons_Scan()) {
 			case 0:     Main_Screen = MAIN_AUTO_ABORT;
 				    break;
-			case 1:     Main_Screen++;
+			case 1:     //Main_Screen++;
+				    if (MashState == MASH_ADD) {
+					MashState = Sub_Screen = MASH_NONE;
+					runtime.MaltAdded = true;
+					updateRuntime = true;
+				    } else if (MashState == MASH_IODINE) {
+					MashState = Sub_Screen = MASH_NONE;
+					runtime.MashStep++;
+					if (runtime.MashStep == recipe.Mashsteps)
+					    Main_Screen = MAIN_AUTO_TOBOIL;
+					updateRuntime = true;
+				    } else if (MashState == MASH_REMOVE) {
+					Main_Screen = MAIN_AUTO_TOBOIL;
+				    }
+				    TFT_fillRect(0, 120, 320, 50, TFT_BLACK);
+				    Buttons_Clear();
+		ESP_LOGI(TAG, "** Step %d Stage %d Malt %s", runtime.MashStep, MashState, runtime.MaltAdded ? (char *)"yes":(char *)"no");	
 				    break;
 			default:    break;
 		    }
 		    if (MashState == MASH_IODINE && TimeLeft == 0) {
-			Main_Screen++;
+			MashState = Sub_Screen = MASH_NONE;
+                        runtime.MashStep++;
+                        if (runtime.MashStep == recipe.Mashsteps)
+                            Main_Screen = MAIN_AUTO_TOBOIL;
+                        updateRuntime = true;
+			TFT_fillRect(0, 120, 320, 50, TFT_BLACK);
+			Buttons_Clear();
 		    }
-		} else if (MashState == MASH_INFUSE) {
+		} else if (MashState == MASH_INFUSE || MashState == MASH_DECOCT) {
 		    switch (Buttons_Scan()) {
 			case 0:     Main_Screen = MAIN_AUTO_ABORT;
 				    break;
-			case 1:	    MashState = Sub_Screen = MASH_WAITTEMP;
-				    snprintf(msg, 63, "{\"main\":\"%d\",\"sub\":\"%d\",\"timer\":\"\"}", Main_Screen, Sub_Screen);
-                		    ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
+			case 1:
+#ifdef CONFIG_TEMP_SENSORS_SIMULATOR
+				    if (MashState == MASH_DECOCT) {
+					Fake_MLT = recipe.MashStep[runtime.MashStep].Step_temp - 0.72; // Fake the decoction amt.
+					if (xSemaphoreTake(xSemaphoreDS18B20, 10) == pdTRUE) {
+					    ds18b20_state->mlt_temperature = ((int)(Fake_MLT * 16)) / 16.0;
+					    xSemaphoreGive(xSemaphoreDS18B20);
+					}
+				    }
+#endif
 				    if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
 					// Start PID again.
 					driver_state->mlt_sp = stageTemp;
 					driver_state->mlt_mode = MLT_MODE_PID;
 					xSemaphoreGive(xSemaphoreDriver);
 				    }
+				    TFT_fillRect(0, 120, 320, 50, TFT_BLACK);
 				    Buttons_Clear();
 			    	    Buttons_Add(  5,  30, 60, 40, (char *)"+sp",   0);
 				    Buttons_Add(255,  30, 60, 40, (char *)"-sp",   1);
 				    Buttons_Show();
 				    TFT_fillRect(65, 120, 190, 40, TFT_BLACK);
-				    log_annotation(ANNOTATION_EVENT, (char *)"Eind infusie");
+				    if (MashState == MASH_INFUSE) {
+					log_msg(TAG, "Eind infusie");
+				    	log_annotation(ANNOTATION_EVENT, (char *)"Eind infusie");
+				    } else {
+					log_msg(TAG, "Eind decoctie");
+					log_annotation(ANNOTATION_EVENT, (char *)"Eind decoctie");
+				    }
+				    MashState = Sub_Screen = MASH_WAITTEMP;
+				    snprintf(msg, 63, "{\"main\":\"%d\",\"sub\":\"%d\",\"step\":\"%d\",\"timer\":\"\"}",
+							Main_Screen, Sub_Screen, runtime.MashStep);
+                                    ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
 				    break;
 			default:    break;
 		    }
--- a/main/buttons.c	Mon Jun 01 20:27:00 2020 +0200
+++ b/main/buttons.c	Sat Jun 06 13:28:46 2020 +0200
@@ -850,7 +850,7 @@
 	Editer(label, valstr, errmsg, 25, EDIT_TYPE_INT);
 	newval = atof(valstr);
 	if (newval < min || newval > max) {
-	    sprintf(errmsg, "De waarde moet tussen %.2f en %.2f zijn.", min, max);
+	    sprintf(errmsg, "De waarde moet tussen %.*f en %.*f zijn.", decimals, min, decimals, max);
 	} else {
 	    break;
 	}
@@ -875,7 +875,7 @@
 	Editer(label, valstr, errmsg, 25, EDIT_TYPE_INT);
 	newval = atof(valstr);
 	if (newval < min || newval > max) {
-	    sprintf(errmsg, "De waarde moet tussen %.2f en %.2f zijn.", min, max);
+	    sprintf(errmsg, "De waarde moet tussen %.*f en %.*f zijn.", decimals, min, decimals, max);
 	} else {
 	    break;
 	}
--- a/main/config.c	Mon Jun 01 20:27:00 2020 +0200
+++ b/main/config.c	Sat Jun 06 13:28:46 2020 +0200
@@ -366,6 +366,7 @@
 	runtime.Logfile[0] = '\0';
 	runtime.PumpCooling = false;
 	runtime.TimeBrewing = 0;
+	runtime.MashStep = 0;
 	write_runtime();
     } else {
 	dst = (uint8_t*)&runtime;
@@ -373,6 +374,8 @@
 	fclose(f);
 	if (bytes != sizeof(runtime)) {
 	    ESP_LOGE(TAG, "/spiffs/etc/runtime.conf read %d/%d bytes", bytes, sizeof(runtime));
+	    runtime.MashStep = 0;
+	    runtime.MaltAdded = false;
 	}
 #if 0
 	printf("Auto started     %s\n", runtime.AutoModeStarted ? "yes":"no");
@@ -389,7 +392,8 @@
 
 
 
-void append_recipe() {
+void append_recipe()
+{
     uint8_t *dst = (uint8_t *)&recipe;
     FILE *f = fopen("/spiffs/etc/recipe.conf", "a");
 
@@ -398,7 +402,7 @@
 	return;
     }
 
-    size_t bytes = fwrite(dst, 1, sizeof(recipe), f);
+    size_t bytes = fwrite(dst, 1, recipe_hdr.recsize, f);
     fclose(f);
     ESP_LOGI(TAG, "/spiffs/etc/recipe.conf appended %d bytes", bytes);
 }
@@ -415,11 +419,11 @@
 	return;
     }
 
-    fseek(f, (RecNo - 1) * sizeof(recipe), SEEK_SET);
-    size_t bytes = fwrite(dst, 1, sizeof(recipe), f);
+    fseek(f, (RecNo - 1) * recipe_hdr.recsize + recipe_hdr.hdrsize, SEEK_SET);
+    size_t bytes = fwrite(dst, 1, recipe_hdr.recsize, f);
     fclose(f);
-    if (bytes != sizeof(recipe)) {
-	ESP_LOGE(TAG, "/spiffs/etc/recipe.conf write record %d, %d/%d bytes", RecNo, bytes, sizeof(recipe));
+    if (bytes != recipe_hdr.recsize) {
+	ESP_LOGE(TAG, "/spiffs/etc/recipe.conf write record %d, %d/%d bytes", RecNo, bytes, recipe_hdr.recsize);
     }
 }
 
@@ -428,31 +432,44 @@
 void read_recipe(int RecNo)
 {
     uint8_t     *dst;
+    size_t	bytes;
     FILE        *f = fopen("/spiffs/etc/recipe.conf", "r");
 
     if (f == NULL) {
 	// No recipe yet, create it.
+	dst = (uint8_t*)&recipe_hdr;
+	memset(dst, 0, sizeof(recipe_hdr));
+	recipe_hdr.version = RECIPE_VERSION;
+	recipe_hdr.hdrsize = sizeof(recipe_hdr);
+	recipe_hdr.recsize = sizeof(recipe);
+	recipe_hdr.mashmax = MASH_MAX;
+	recipe_hdr.additionmax = ADDITION_MAX;
+	f = fopen("/spiffs/etc/recipe.conf", "w");
+	bytes = fwrite(dst, 1, sizeof(recipe_hdr), f);
+	if (bytes != sizeof(recipe_hdr)) {
+	    ESP_LOGE(TAG, "/spiffs/etc/recipe.conf write header, %d/%d bytes", bytes, sizeof(recipe_hdr));
+	}
 	dst = (uint8_t*)&recipe;
 	memset(dst, 0, sizeof(recipe));
-	recipe.Version = 1;
 	sprintf(recipe.Name, "Recipe 1");
 	sprintf(recipe.Code, "001");
 	sprintf(recipe.MashStep[0].Name, "Mash-in");
 	recipe.MashStep[0].Type = MASHTYPE_INFUSION;
-	recipe.MashStep[0].Temperature = recipe.MashStep[0].Infusion_temp = 67.5;
-	recipe.MashStep[0].Infusion_amount = 15.0;
-	recipe.MashStep[0].Resttime = 1;
-	recipe.MashStep[0].Ramptime = 1;
-	for (int i = 1; i < 8; i++)
+	recipe.MashStep[0].Step_temp = recipe.MashStep[0].End_temp = recipe.MashStep[0].Infuse_temp = 67.5;
+	recipe.MashStep[0].Infuse_amount = 15.0;
+	recipe.MashStep[0].Step_time = 1;
+	recipe.MashStep[0].Ramp_time = 1;
+	for (int i = 1; i < MASH_MAX; i++)
 	    recipe.MashStep[i].Type = MASHTYPE_TEMPERATURE;
 	sprintf(recipe.MashStep[1].Name, "Mash");
-	recipe.MashStep[1].Temperature = 67.0;
-	recipe.MashStep[1].Resttime = 75;
-	recipe.MashStep[1].Ramptime = 1;
-	sprintf(recipe.MashStep[7].Name, "Mash-out");
-	recipe.MashStep[7].Temperature = 78.0; 
-	recipe.MashStep[7].Resttime = 5;
-	recipe.MashStep[7].Ramptime = 11;
+	recipe.MashStep[1].Step_temp = recipe.MashStep[1].End_temp = 67.0;
+	recipe.MashStep[1].Step_time = 75;
+	recipe.MashStep[1].Ramp_time = 1;
+	sprintf(recipe.MashStep[2].Name, "Mash-out");
+	recipe.MashStep[2].Step_temp = recipe.MashStep[2].End_temp = 78.0;
+	recipe.MashStep[2].Step_time = 5;
+	recipe.MashStep[2].Ramp_time = 11;
+	recipe.Mashsteps = 3;
 	recipe.BoilTime = 60;
 	recipe.Additions = 2;
 	sprintf(recipe.Addition[0].Name, "Hop");
@@ -467,11 +484,48 @@
 	recipe.Whirlpool6 = 0;
 	recipe.Whirlpool2 = 0;
 	recipe.SpargeTemp = 85.0;
-        append_recipe();
+	bytes = fwrite(dst, 1, sizeof(recipe), f);
+	fclose(f);
     } else {
+	/*
+	 * Try to read the new file header
+	 */
+	dst = (uint8_t*)&recipe_hdr;
+	fseek(f, 0, SEEK_SET);
+	bytes = fread(dst, 1, sizeof(recipe_hdr), f);
+	if (bytes != sizeof(recipe_hdr)) {
+            ESP_LOGE(TAG, "/spiffs/etc/recipe.conf read header, %d/%d bytes", bytes, sizeof(recipe_hdr));
+	    fclose(f);
+	    return;
+        }
+/*
+	if (recipe_hdr.version < RECIPE_VERSION) {
+	    FILE        *nf = fopen("/spiffs/etc/recipe.new", "w");
+
+	    ESP_LOGI(TAG, "/spiffs/etc/recipe.conf version %d, new %d", recipe_hdr.version, RECIPE_VERSION);
+
+	    fseek(f, recipe_hdr.hdrsize, SEEK_SET);
+	    dst = (uint8_t*)&recipe;
+	    while ((bytes = fread(dst, 1, recipe_hdr.recsize, f))) {
+
+		// Upgrade data here
+		bytes = fwrite(dst, 1, sizeof(recipe), nf);
+                if (bytes != sizeof(recipe)) {
+		    ESP_LOGE(TAG, "/spiffs/etc/recipe.new write data, %d/%d bytes", bytes, sizeof(recipe));
+            	}
+	    }
+	    // Update the header with new sizes
+	    fclose(nf);
+	    fclose(f);
+	    rename("/spiffs/etc/recipe.conf", "/spiffs/etc/recipe.old");
+	    rename("/spiffs/etc/recipe.new", "/spiffs/etc/recipe.conf");
+	    unlink("/spiffs/etc/recipe.old");
+	    f = fopen("/spiffs/etc/recipe.conf", "r");
+	}
+*/
 	dst = (uint8_t*)&recipe;
-	fseek(f, (RecNo - 1) * sizeof(recipe), SEEK_SET);
-	size_t bytes = fread(dst, 1, sizeof(recipe), f);
+	fseek(f, (RecNo - 1) * recipe_hdr.recsize + recipe_hdr.hdrsize, SEEK_SET);
+	bytes = fread(dst, 1, sizeof(recipe), f);
 	fclose(f);
 	if (bytes != sizeof(recipe)) {
 	    ESP_LOGE(TAG, "/spiffs/etc/recipe.conf read record %d, %d/%d bytes", RecNo, bytes, sizeof(recipe));
@@ -500,9 +554,13 @@
         return;
     }
 
+    dst = (uint8_t*)&recipe_hdr;
+    fread(dst, 1, recipe_hdr.hdrsize, o);
+    fwrite(dst, 1, recipe_hdr.hdrsize, n);
+
     dst = (uint8_t*)&recipe;
     while (true) {
-        bytes = fread(dst, 1, sizeof(recipe), o);
+        bytes = fread(dst, 1, recipe_hdr.recsize, o);
         if (bytes == 0)
             break;
 
@@ -513,7 +571,7 @@
                 config.RecipeRec = RecWrite;
                 write_config();
             }
-            fwrite(dst, 1, sizeof(recipe), n);
+            fwrite(dst, 1, recipe_hdr.recsize, n);
             RecWrite++;
         }
 	RecRead++;
--- a/main/config.h	Mon Jun 01 20:27:00 2020 +0200
+++ b/main/config.h	Sat Jun 06 13:28:46 2020 +0200
@@ -110,16 +110,9 @@
     MAIN_AUTO_INIT1 = 96,		///< Automatic start part 1
     MAIN_AUTO_INIT2,			///< Automatic start part 2
     MAIN_AUTO_DELAYSTART,		///< Delayed start
-    MAIN_AUTO_HEATUP,			///< Heatup the system
-    MAIN_AUTO_MASH_IN = 100,		///< Mash-in
-    MAIN_AUTO_MASH_1,			///< Mash step 1
-    MAIN_AUTO_MASH_2,			///< Mash step 2
-    MAIN_AUTO_MASH_3,			///< Mash step 3
-    MAIN_AUTO_MASH_4,			///< Mash step 4
-    MAIN_AUTO_MASH_5,			///< Mash step 5
-    MAIN_AUTO_MASH_6,			///< Mash step 6
-    MAIN_AUTO_MASH_OUT,			///< Mash-out
-    MAIN_AUTO_TOBOIL,			///< Going to boil
+    MAIN_AUTO_HEATUP,			///< Heatup the HLT
+    MAIN_AUTO_MASH = 100,		///< Mash steps
+    MAIN_AUTO_TOBOIL = 120,		///< Going to boil
     MAIN_AUTO_BOILING,			///< Boiling
     MAIN_AUTO_WHIRLPOOL9,		///< Whirlpool
     MAIN_AUTO_COOLING_H,		///< Cooling hot type
@@ -141,10 +134,11 @@
     MASH_NONE = 0,			///< Initial Mash state
     MASH_WAITTEMP,			///< Wait to reach temperature.
     MASH_REST,				///< Mash rest
-    MASH_ADD,				///< Add mash wait
+    MASH_ADD,				///< Wait add mash
     MASH_IODINE,			///< Wait iodine test
     MASH_REMOVE,			///< Wait mash remove
     MASH_INFUSE,			///< Wait infusion prompt
+    MASH_DECOCT,			///< Wait decoction prompt
 } MASH_TYPE;
 
 /**
@@ -307,6 +301,8 @@
     bool		UseHLT;				///< Use HLT during brew.
     bool		PumpCooling;			///< Pump during cooling.
     uint32_t		TimeBrewing;			///< Time we are brewing.
+    uint8_t		MashStep;			///< Current mash step.
+    bool		MaltAdded;			///< If malt was added.
 } runtime;						///< Runtime record.
 
 
@@ -323,8 +319,12 @@
 
 
 
+#define	RECIPE_VERSION		2		///< Recipe file version
+#define MASH_MAX		16		///< Maximum 16 mash steps
+#define ADDITION_MAX		20		///< Maximum 20 additions
+
 /**
- * @brief Mash step type
+ * @brief Mash step types
  */
 typedef enum {
     MASHTYPE_INFUSION = 0,
@@ -350,13 +350,14 @@
  * @brief Mash steps
  */
 typedef struct strMashStep {
-    char		Name[32];			///< Step name.
-    uint8_t		Type;				///< Step Type.
-    float		Temperature;			///< Step temperature.
-    uint16_t		Resttime;			///< Step resttime.
-    uint16_t		Ramptime;			///< Step time to reach temp.
-    float		Infusion_amount;		///< Infusion amount in liters.
-    float		Infusion_temp;			///< Infusion temperature.
+    char                Name[32];                       ///< Step name.
+    uint8_t             Type;                           ///< Step Type.
+    float               Step_temp;                      ///< Step start temperature.
+    uint16_t            Step_time;                      ///< Step rest time.
+    uint16_t            Ramp_time;                      ///< Step time to reach temp.
+    float               End_temp;                       ///< Step end temperature.
+    float               Infuse_amount;                  ///< Infuse/decoct amount in liters.
+    float               Infuse_temp;                    ///< Infuse temperature.
 } mashstep_t;
 
 /**
@@ -369,23 +370,31 @@
 } addition_t;
 
 /**
- * @brief Current recipe
+ * @brief Recipes database. Starts with a header block for future upgrades,
+ *        followed by the actual recpie records.
  */
+struct hdrRecipe {
+    uint32_t		version;			///< Structure version.
+    uint32_t		hdrsize;			///< Size of this header.
+    uint32_t		recsize;			///< Size of a record.
+    uint8_t		mashmax;			///< Maximum mash steps.
+    uint8_t		additionmax;			///< Maximum additions.
+} recipe_hdr;
+
 struct strRecipe {
-    uint8_t		Version;			///< Record version number.
-    int			o_Record;			///< Record number, obsolete.
     char		Name[128];			///< Recipe name.
     char		Code[32];			///< Recipe code.
-    mashstep_t		MashStep[8];			///< Mash steps.
     uint16_t		BoilTime;			///< Boil time.
+    uint8_t		Mashsteps;			///< Number of mashsteps.
     uint8_t		Additions;			///< Number of additions.
-    addition_t		Addition[10];			///< Additions
     float		CoolTemp;			///< Cool temperature.
     uint16_t		Whirlpool9;			///< Zero or the Hot Whirlpool time 88..100 °
     uint16_t		Whirlpool7;			///< Zero or the Sub Isomerization Whirlpool time 71..77 °
     uint16_t		Whirlpool6;			///< Zero or the "Tepid" Whirlpool time 60..66 °
     uint16_t		Whirlpool2;			///< Zero or the Cold Whirlpool time < 30 °
     float		SpargeTemp;			///< Sparge water temperature.
+    mashstep_t		MashStep[MASH_MAX];		///< Mash steps.
+    addition_t		Addition[ADDITION_MAX];		///< Additions.
 } recipe;						///< Recipe record.
 
 /**
@@ -411,4 +420,5 @@
  */
 void delete_recipe(int RecNo);
 
+
 #endif
--- a/main/recipes.c	Mon Jun 01 20:27:00 2020 +0200
+++ b/main/recipes.c	Sat Jun 06 13:28:46 2020 +0200
@@ -20,12 +20,12 @@
 int				r_Imported = 0;			///< Total imported
 char                            _xml_element[10][32];		///< XML element array
 int                             _xml_depth;			///< Current depths
-int				_xml_mashsteps;			///< Mash steps
 char                            _xml_add_name[64];		///< Mash name
 int                             _xml_add_type;			///< Mash type 
 int                             _xml_add_time;			///< Mash rest time
 int				_xml_add_ramp;			///< Mash ramp time
-float				_xml_add_temp;			///< Mash temperature
+float				_xml_add_temp;			///< Mash start temperature
+float				_xml_add_end;			///< Mash end temperature
 float				_xml_add_amount;		///< Mash infusion amount
 float				_xml_add_infusion;		///< Mash infusion temperature
 float				_xml_tun_temp;			///< TUN temperature
@@ -52,8 +52,6 @@
     if (! recipe.Additions) {
 	// No entries yet, add the first one.
     	snprintf(recipe.Addition[recipe.Additions].Name, 63, "%s", Name);
-//        recipe.Addition[recipe.Additions].Name = "";
-//	strncat(recipe.Addition[recipe.Additions].Name, Name, 63);
     	recipe.Addition[recipe.Additions].Type = Type;
     	recipe.Addition[recipe.Additions].Time = Time;
     	recipe.Additions++;
@@ -64,7 +62,6 @@
     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);
 	    strncat(recipe.Addition[i].Name, ", ", 63 - strlen(recipe.Addition[i].Name));
 	    strncat(recipe.Addition[i].Name, Name, 63 - strlen(recipe.Addition[i].Name));
 	    return;
@@ -223,8 +220,8 @@
 			_xml_add_name[0] = '\0';
 			strncat(_xml_add_name, char_data_buffer, 63);
 		    } else if (strcmp("TYPE", _xml_element[5]) == 0) {
-			// Temperature Infusion Decoction
-			_xml_add_valid = (strcmp("Temperature", char_data_buffer) == 0);
+			// Step_temp Infusion Decoction
+			_xml_add_valid = (strcmp("Step_temp", char_data_buffer) == 0);
 			if (strcmp("Infusion", char_data_buffer) == 0)
 			    _xml_add_type = MASHTYPE_INFUSION;
 			else if (strcmp("Temperature", char_data_buffer) == 0)
@@ -241,6 +238,10 @@
 			_xml_add_amount = atof(char_data_buffer);
 		    } else if (strcmp("INFUSE_TEMP", _xml_element[5]) == 0) {
 			_xml_add_infusion = atof(char_data_buffer);
+		    } else if (strcmp("DECOCTION_AMT", _xml_element[5]) == 0) {
+			_xml_add_amount = atof(char_data_buffer);
+		    } else if (strcmp("END_TEMP", _xml_element[5]) == 0) {
+			_xml_add_end = atof(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.
@@ -296,20 +297,24 @@
 	}
     }
     if ((_xml_depth == 5) && (strcmp("MASH_STEP", _xml_element[4]) == 0)) {
-	_xml_mashsteps++;
-	recipe.MashStep[_xml_mashsteps].Name[0] = '\0';
-	strncat(recipe.MashStep[_xml_mashsteps].Name, _xml_add_name, 31);
-	recipe.MashStep[_xml_mashsteps].Type = _xml_add_type;
-	recipe.MashStep[_xml_mashsteps].Temperature = _xml_add_temp;
-	recipe.MashStep[_xml_mashsteps].Resttime = _xml_add_time;
-	recipe.MashStep[_xml_mashsteps].Ramptime = _xml_add_ramp;
+	recipe.MashStep[recipe.Mashsteps].Name[0] = '\0';
+	strncat(recipe.MashStep[recipe.Mashsteps].Name, _xml_add_name, 31);
+	recipe.MashStep[recipe.Mashsteps].Type = _xml_add_type;
+	recipe.MashStep[recipe.Mashsteps].Step_temp = _xml_add_temp;
+	recipe.MashStep[recipe.Mashsteps].Step_time = _xml_add_time;
+	recipe.MashStep[recipe.Mashsteps].Ramp_time = _xml_add_ramp;
+	recipe.MashStep[recipe.Mashsteps].End_temp = _xml_add_end;
 	if (_xml_add_type == MASHTYPE_INFUSION) {
-	    recipe.MashStep[_xml_mashsteps].Infusion_temp = _xml_add_infusion;
-	    recipe.MashStep[_xml_mashsteps].Infusion_amount = _xml_add_amount;
+	    recipe.MashStep[recipe.Mashsteps].Infuse_temp = _xml_add_infusion;
+	    recipe.MashStep[recipe.Mashsteps].Infuse_amount = _xml_add_amount;
+	} else if (_xml_add_type == MASHTYPE_DECOCTION) {
+	    recipe.MashStep[recipe.Mashsteps].Infuse_temp = 0.0;
+	    recipe.MashStep[recipe.Mashsteps].Infuse_amount = _xml_add_amount;
 	} else {
-	    recipe.MashStep[_xml_mashsteps].Infusion_temp = 0.0;
-	    recipe.MashStep[_xml_mashsteps].Infusion_amount = 0.0;
+	    recipe.MashStep[recipe.Mashsteps].Infuse_temp = 0.0;
+	    recipe.MashStep[recipe.Mashsteps].Infuse_amount = 0.0;
 	}
+	recipe.Mashsteps++;
     }
     _xml_depth--;
 }
@@ -320,12 +325,13 @@
 {
     char buf[512];
 
-    memset(&recipe, 0, sizeof(recipe));
+    memset(&recipe, 0, recipe_hdr.recsize);
+    for (int i = 0; i < recipe_hdr.mashmax; i++)
+	recipe.MashStep[i].Type = 255;
     XML_Parser parser = XML_ParserCreate(NULL);
 
     int done;
     _xml_depth = 0;
-    _xml_mashsteps = 0;
     _xml_tun_temp = 0.0;
 
     XML_SetElementHandler(parser, startElement, endElement);
@@ -337,7 +343,7 @@
         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));
+            ESP_LOGE(TAG, "%s at line %5lu", XML_ErrorString(XML_GetErrorCode(parser)), XML_GetCurrentLineNumber(parser));
             return 1;
         }
         vTaskDelay(2 / portTICK_PERIOD_MS);
@@ -358,49 +364,6 @@
 	}
     }
     recipe.CoolTemp = 20.0;
-    sprintf(recipe.MashStep[0].Name, "Mash-in");
-    if ((recipe.MashStep[1].Type == MASHTYPE_INFUSION) && (recipe.MashStep[1].Infusion_temp > 0.0)) {
-	/* If next (first original) step is infusion, take that temperature. */
-	recipe.MashStep[0].Temperature = recipe.MashStep[1].Infusion_temp;
-    } else 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 = recipe.MashStep[0].Ramptime = 1;
-    recipe.MashStep[0].Type = MASHTYPE_INFUSION;
-    /*
-     * Because we inserted the first infusion step and the next (original first)
-     * step is infusion, change that one into temperature.
-     */
-    if (recipe.MashStep[1].Type == MASHTYPE_INFUSION) {
-	recipe.MashStep[1].Type = MASHTYPE_TEMPERATURE;
-	recipe.MashStep[1].Infusion_temp = recipe.MashStep[1].Infusion_amount = 0.0;
-    }
-
-    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].Type = recipe.MashStep[i].Type;
-		recipe.MashStep[i].Type = 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].Ramptime = recipe.MashStep[i].Ramptime;
-		recipe.MashStep[i].Ramptime = 0;
-		recipe.MashStep[7].Infusion_temp = recipe.MashStep[i].Infusion_temp;
-		recipe.MashStep[i].Infusion_temp = 0.0;
-		recipe.MashStep[7].Infusion_amount = recipe.MashStep[i].Infusion_amount;
-		recipe.MashStep[i].Infusion_amount = 0.0;
-		break;
-	    }
-	}
-    }
 
 #if 0
     printf("Recipe: %s\n", recipe.Name);
@@ -409,10 +372,10 @@
     printf("n Stepname                       T  temp time ramp  inft  infa\n");
     printf("- ------------------------------ - ----- ---- ---- ----- -----\n");
     for (int i = 0; i < 8; i++) {
-        if (recipe.MashStep[i].Resttime) {
-            printf("%d %-30s %d %5.2f %4d %4d %5.2f %5.2f\n", i, recipe.MashStep[i].Name, recipe.MashStep[i].Type, recipe.MashStep[i].Temperature,
-                            recipe.MashStep[i].Resttime, recipe.MashStep[i].Ramptime,
-			    recipe.MashStep[i].Infusion_temp, recipe.MashStep[i].Infusion_amount);
+        if (recipe.MashStep[i].Step_time) {
+            printf("%d %-30s %d %5.2f %4d %4d %5.2f %5.2f\n", i, recipe.MashStep[i].Name, recipe.MashStep[i].Type, recipe.MashStep[i].Step_temp,
+                            recipe.MashStep[i].Step_time, recipe.MashStep[i].Ramp_time,
+			    recipe.MashStep[i].Infuse_temp, recipe.MashStep[i].Infuse_amount);
         }
     }
     printf("%d additions\n", recipe.Additions);
@@ -488,9 +451,10 @@
 			}
 
 			f = fopen("/spiffs/etc/recipe.conf", "r");
+			fseek(f, recipe_hdr.hdrsize, SEEK_SET);
 			dst = (uint8_t*)&recipe;
 			r_Records = 0;
-			while ((bytes = fread(dst, 1, sizeof(recipe), f))) {
+			while ((bytes = fread(dst, 1, recipe_hdr.recsize, f))) {
 			    r_Records++;
 			}
 			fclose(f);
@@ -515,10 +479,10 @@
 {
     uint32_t	crc1, crc2;
     uint8_t	*dst;
-    int		mashsteps;
     uint16_t	y;
     char	tmp[64];
-    float	mintemp;
+    float	mintemp, maxtemp;
+    int		i;
 
     switch (Main_Screen) {
 	case MAIN_TOOLS_RECIPE:
@@ -548,25 +512,28 @@
 			    ShowInteger(162, 44, (char *)"Record", NULL, r_CurrentRec);
 			    ShowInteger(2, 60, (char *)"Kooktijd", (char *)" min", recipe.BoilTime);
 			    ShowFloat(162, 60, (char *)"Koel tot", (char *)" C", recipe.CoolTemp, 2);
-			    ShowFloat(2, 76, (char *)"Maisch in", (char *)" C", recipe.MashStep[0].Temperature, 2);
+			    ShowFloat(2, 76, (char *)"Maisch in", (char *)" C", recipe.MashStep[0].Infuse_temp, 3);
 			    ShowFloat(162, 76, (char *)"Spoelwater", (char *)" C", recipe.SpargeTemp, 2);
 			    y =  92;
 			    _fg = TFT_WHITE;
 			    TFT_print((char *)"Maisch stap", 2, y);
-			    TFT_print((char *)"T", 200, y);
-			    TFT_print((char *)"Temp.", 220, y);
+			    TFT_print((char *)"T", 180, y);
+			    TFT_print((char *)"Temp.", 200, y);
 			    TFT_print((char *)"Rust", 280, y);
 			    _fg = TFT_YELLOW;
 			    y += 16;
-			    for (int i = 1; i < 8; i++) {
-				if (recipe.MashStep[i].Resttime) {
+			    for (i = 0; i < recipe_hdr.mashmax; i++) {
+				ESP_LOGI(TAG, "%2d %-31s %3d %5.2f %5.2f %2d %2d %7.4f %6.3f", i, recipe.MashStep[i].Name, recipe.MashStep[i].Type,
+					recipe.MashStep[i].Step_temp, recipe.MashStep[i].End_temp, recipe.MashStep[i].Step_time, recipe.MashStep[i].Ramp_time,
+					recipe.MashStep[i].Infuse_temp, recipe.MashStep[i].Infuse_amount);
+				if (recipe.MashStep[i].Type != 255) {
 				    TFT_print(recipe.MashStep[i].Name, 2, y);
 				    strcpy(tmp, mashTypes[recipe.MashStep[i].Type]);
 				    tmp[1] = '\0';
+				    TFT_print(tmp, 180, y);
+				    sprintf(tmp, "%.1f %.1f", recipe.MashStep[i].Step_temp, recipe.MashStep[i].End_temp);
 				    TFT_print(tmp, 200, y);
-				    sprintf(tmp, "%.2f", recipe.MashStep[i].Temperature);
-				    TFT_print(tmp, 220, y);
-				    sprintf(tmp, "%2d m", recipe.MashStep[i].Resttime);
+				    sprintf(tmp, "%2d m", recipe.MashStep[i].Step_time);
 				    TFT_print(tmp, 280, y);
 				    y += 16;
 				}
@@ -632,26 +599,24 @@
 			    case 0:	Main_Screen = MAIN_TOOLS;
 					break;
 
-			    case 1:	memset(&recipe, 0, sizeof(recipe));
-					recipe.Version = 1;
-					for (int i = 1; i < 8; i++) 
-					    recipe.MashStep[i].Type = MASHTYPE_TEMPERATURE;
-					sprintf(recipe.Name, "Recipe %d", r_Records + 1);
+			    case 1:	memset(&recipe, 0, sizeof(recipe)); // new recipe
+					for (i = 1; i < recipe_hdr.mashmax; i++)
+					    recipe.MashStep[i].Type = 255;
+					sprintf(recipe.Name, "Recept %d", r_Records + 1);
 					sprintf(recipe.Code, "00%d", r_Records + 1);
-					sprintf(recipe.MashStep[0].Name, "Mash-in");
+					sprintf(recipe.MashStep[0].Name, "Maischen");
 					recipe.MashStep[0].Type = MASHTYPE_INFUSION;
-					recipe.MashStep[0].Temperature = recipe.MashStep[0].Infusion_temp = 67.5;
-					recipe.MashStep[0].Infusion_amount = 15.0;
-					recipe.MashStep[0].Resttime = 1;
-					recipe.MashStep[0].Ramptime = 1;
-					sprintf(recipe.MashStep[1].Name, "Mash");
-					recipe.MashStep[1].Temperature = 67.0;
-					recipe.MashStep[1].Resttime = 75;
-					recipe.MashStep[1].Ramptime = 1;
-					sprintf(recipe.MashStep[7].Name, "Mash-out");
-					recipe.MashStep[7].Temperature = 78.0;
-					recipe.MashStep[7].Resttime = 5;
-					recipe.MashStep[7].Ramptime = 11;
+					recipe.MashStep[0].Step_temp = recipe.MashStep[0].End_temp = 67.0;
+					recipe.MashStep[0].Infuse_temp = 67.5;
+					recipe.MashStep[0].Infuse_amount = 15.0;
+					recipe.MashStep[0].Step_time = 75;
+					recipe.MashStep[0].Ramp_time = 1;
+					sprintf(recipe.MashStep[1].Name, "Mash-out");
+					recipe.MashStep[1].Type = MASHTYPE_TEMPERATURE;
+					recipe.MashStep[1].Step_temp = recipe.MashStep[1].End_temp = 78.0;
+					recipe.MashStep[1].Step_time = 10;
+					recipe.MashStep[1].Ramp_time = 13;
+					recipe.Mashsteps = 2;
 					recipe.BoilTime = 60;
 					recipe.Additions = 2;
 					sprintf(recipe.Addition[0].Name, "Bitterhop");
@@ -674,9 +639,13 @@
 					break;
 
 			    case 2:	if ((r_CurrentRec != config.RecipeRec) && (r_Records > 1)) {
+					    _bg = TFT_BLACK;
+                            		     TFT_fillScreen(_bg);
+                            		    TFT_resetclipwin();
+					    TopMessage((char *)"Recept verwijderen");
 					    delete_recipe(r_CurrentRec);
 					    r_Records--;
-					    if (r_CurrentRec > r_Records)
+					    if (r_CurrentRec >= r_Records)
 						r_CurrentRec = r_Records;
 					    r_UpdateRec = true;
 					}
@@ -715,52 +684,71 @@
 
 			EditText((char *)"Naam", recipe.Name, 31);
 			EditText((char *)"Code", recipe.Code, 31);
-			mashsteps = 0;
-			for (int i = 1; i < 7; i++) {
-			    if (recipe.MashStep[i].Resttime)
-				mashsteps++;
-			}
-			EditInt((char *)"Maisch stappen", &mashsteps, 1, 6);
-			EditFloat((char *)"Inmaisch temperatuur", &recipe.MashStep[0].Temperature, 38, 74, 2);
-			// Round to 0.0625 values
-			recipe.MashStep[0].Temperature = ((int)(recipe.MashStep[0].Temperature * 16)) / 16.0;
-			for (int i = 1; i <= mashsteps; i++) {
-			    sprintf(tmp, "Maisch stap %d naam", i);
+			EditUint8((char *)"Maisch stappen", &recipe.Mashsteps, 1, 6);
+			for (i = 0; i < recipe.Mashsteps; i++) {
+			    int step = i + 1;
+			    if (i == (recipe.Mashsteps - 1)) {
+				mintemp = 75.0;
+				maxtemp = 80.0;
+			    } else {
+				mintemp = 35.0;
+				maxtemp = 74.0;
+			    }
+			    if (recipe.MashStep[i].Type == 255) {
+				// A new step, set default values.
+				recipe.MashStep[i].Type = MASHTYPE_TEMPERATURE;
+				recipe.MashStep[i].Step_time = 20;
+				if (i == 0) {
+				    recipe.MashStep[i].Type = MASHTYPE_INFUSION;
+				    recipe.MashStep[i].Step_temp = recipe.MashStep[i].End_temp = 62.0;
+				    recipe.MashStep[i].Infuse_temp = 62.5;
+				    recipe.MashStep[i].Infuse_amount = 15;
+				    recipe.MashStep[i].Ramp_time = 1;
+				} else if (i == (recipe.Mashsteps - 1)) {
+				    recipe.MashStep[i].Step_temp = recipe.MashStep[i].End_temp = 78.0;
+				    recipe.MashStep[i].Step_time = 10;
+				    recipe.MashStep[i].Ramp_time = (int)(recipe.MashStep[i].Step_temp - recipe.MashStep[i - 1].Step_temp + 2);
+				} else {
+				    recipe.MashStep[i].Step_temp = recipe.MashStep[i].End_temp = recipe.MashStep[i - 1].Step_temp + 2.0;
+				    recipe.MashStep[i].Ramp_time = (int)(recipe.MashStep[i].Step_temp - recipe.MashStep[i - 1].Step_temp + 2);
+				}
+			    }
+			    sprintf(tmp, "Maisch stap %d naam", step);
 			    EditText(tmp, recipe.MashStep[i].Name, 31);
 			    EditMashType(&recipe.MashStep[i].Type);
-			    sprintf(tmp, "Maisch stap %d temperatuur", i);
-			    if (i == 1)
-				mintemp = recipe.MashStep[0].Temperature - 5;
-			    else
-				mintemp = recipe.MashStep[i - 1].Temperature;
-			    EditFloat(tmp, &recipe.MashStep[i].Temperature, mintemp, 74, 2);
+			    sprintf(tmp, "Maisch stap %d start temperatuur", step);
+			    EditFloat(tmp, &recipe.MashStep[i].Step_temp, mintemp, maxtemp, 2);
 			    // Round to 0.25 values
-			    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].Ramptime = 1;
+			    recipe.MashStep[i].Step_temp = ((int)(recipe.MashStep[i].Step_temp * 4)) / 4.0;
+			    sprintf(tmp, "Maisch stap %d eind temperatuur", step);
+			    EditFloat(tmp, &recipe.MashStep[i].End_temp, mintemp, maxtemp, 2);
+			    recipe.MashStep[i].End_temp = ((int)(recipe.MashStep[i].End_temp * 4)) / 4.0;
+			    sprintf(tmp, "Maisch stap %d rusttijd in minuten", step);
+			    EditUint16(tmp, &recipe.MashStep[i].Step_time, 1, 480);
+			    if (recipe.MashStep[i].Type == MASHTYPE_INFUSION) {
+				sprintf(tmp, "Stap %d infusie temperatuur", step);
+				EditFloat(tmp, &recipe.MashStep[i].Infuse_temp, 10, 99, 3);
+				recipe.MashStep[i].Infuse_temp = ((int)(recipe.MashStep[i].Infuse_temp * 16)) / 16.0;
+				sprintf(tmp, "Stap %d infusie volume", step);
+				EditFloat(tmp, &recipe.MashStep[i].Infuse_amount, 0.5, 1000.0, 3);
+				recipe.MashStep[i].Ramp_time = 2;
+			    } else if (recipe.MashStep[i].Type == MASHTYPE_DECOCTION) {
+				recipe.MashStep[i].Infuse_temp = 0.0;
+				recipe.MashStep[i].Ramp_time = 2;
+				sprintf(tmp, "Stap %d decoctie volume", step);
+                                EditFloat(tmp, &recipe.MashStep[i].Infuse_amount, 0.5, 1000.0, 3);
 			    } else {
-				recipe.MashStep[i].Ramptime = (int)(recipe.MashStep[i].Temperature - recipe.MashStep[i - 1].Temperature);
-			    }
-			    if (recipe.MashStep[i].Type == MASHTYPE_INFUSION) {
-				sprintf(tmp, "Stap %d infusie temperatuur", i);
-				mintemp = recipe.MashStep[i].Temperature;
-				EditFloat(tmp, &recipe.MashStep[i].Infusion_temp, mintemp, 100, 2);
-				sprintf(tmp, "Stap %d infusie volume", i);
-				EditFloat(tmp, &recipe.MashStep[i].Infusion_amount, 0.5, 1000.0, 2);
-			    } else {
-				recipe.MashStep[i].Infusion_temp = recipe.MashStep[i].Infusion_amount = 0.0;
+				recipe.MashStep[i].Infuse_temp = recipe.MashStep[i].Infuse_amount = 0.0;
+				sprintf(tmp, "Stap %d opwarm tijd", step);
+				EditUint16(tmp, &recipe.MashStep[i].Ramp_time, 1, 480);
 			    }
 			}
-			mintemp = recipe.MashStep[mashsteps].Temperature;
-			EditFloat((char *)"Uitmaischen temperatuur", &recipe.MashStep[7].Temperature, mintemp, 80, 2);
-			recipe.MashStep[7].Temperature = ((int)(recipe.MashStep[7].Temperature * 4)) / 4.0;
-			EditUint16((char *)"Uitmaischen tijd in minuten", &recipe.MashStep[7].Resttime, 1, 15);
-			recipe.MashStep[7].Ramptime = (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;
+			for (i = recipe.Mashsteps; i < recipe_hdr.mashmax; i++) {
+			    recipe.MashStep[i].Type = 255;
+			    recipe.MashStep[i].Step_temp = recipe.MashStep[i].End_temp = recipe.MashStep[i].Infuse_temp = recipe.MashStep[i].Infuse_amount = 0.0;
+			    recipe.MashStep[i].Step_time = recipe.MashStep[i].Ramp_time = 0;
+			    recipe.MashStep[i].Name[0] = '\0';
+			}
 
 			EditUint16((char *)"Kook tijd in minuten", &recipe.BoilTime, 0, 480);
 			if (recipe.BoilTime) {
@@ -779,6 +767,8 @@
 			    	}
 			    	recipe.Addition[i].Type = ADDITION_HOP;
 			    }
+			} else {
+			    recipe.Additions = 0;
 			}
 
 			EditFloat((char *)"Koel temperatuur", &recipe.CoolTemp, 10, 45, 2);
--- a/main/task_tft.c	Mon Jun 01 20:27:00 2020 +0200
+++ b/main/task_tft.c	Sat Jun 06 13:28:46 2020 +0200
@@ -621,14 +621,7 @@
 		case MAIN_AUTO_INIT2:
 		case MAIN_AUTO_DELAYSTART:
 		case MAIN_AUTO_HEATUP:
-		case MAIN_AUTO_MASH_IN:
-		case MAIN_AUTO_MASH_1:
-		case MAIN_AUTO_MASH_2:
-		case MAIN_AUTO_MASH_3:
-		case MAIN_AUTO_MASH_4:
-		case MAIN_AUTO_MASH_5:
-		case MAIN_AUTO_MASH_6:
-		case MAIN_AUTO_MASH_OUT:
+		case MAIN_AUTO_MASH:
 		case MAIN_AUTO_TOBOIL:
 		case MAIN_AUTO_BOILING:
 		case MAIN_AUTO_COOLING_H:
@@ -744,14 +737,7 @@
 	    case MAIN_AUTO_INIT2:
 	    case MAIN_AUTO_DELAYSTART:
 	    case MAIN_AUTO_HEATUP:
-	    case MAIN_AUTO_MASH_IN:
-	    case MAIN_AUTO_MASH_1:
-	    case MAIN_AUTO_MASH_2:
-	    case MAIN_AUTO_MASH_3:
-	    case MAIN_AUTO_MASH_4:
-	    case MAIN_AUTO_MASH_5:
-	    case MAIN_AUTO_MASH_6:
-	    case MAIN_AUTO_MASH_OUT:
+	    case MAIN_AUTO_MASH:
 	    case MAIN_AUTO_TOBOIL:
 	    case MAIN_AUTO_BOILING:
 	    case MAIN_AUTO_COOLING_H:
@@ -784,7 +770,7 @@
 	/*
 	 * Count power average during brewing.
 	 */
-	if ((Main_Screen >= MAIN_AUTO_MASH_IN) && (Main_Screen < MAIN_AUTO_DONE)) {
+	if ((Main_Screen >= MAIN_AUTO_MASH) && (Main_Screen < MAIN_AUTO_DONE)) {
 	    if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
 		power_MLT += driver_state->mlt_power;
 		power_HLT += driver_state->hlt_power;
@@ -797,7 +783,7 @@
 	    /*
 	     * Brew logging.
 	     */
-	    if ((Main_Screen >= MAIN_AUTO_MASH_IN) && (Main_Screen < MAIN_AUTO_DONE)) {
+	    if ((Main_Screen >= MAIN_AUTO_MASH) && (Main_Screen < MAIN_AUTO_DONE)) {
 		update_json();
 		log_json();
 		power_MLT = power_HLT = counts = 0;
--- a/sdkconfig	Mon Jun 01 20:27:00 2020 +0200
+++ b/sdkconfig	Sat Jun 06 13:28:46 2020 +0200
@@ -77,10 +77,8 @@
 CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
 CONFIG_PARTITION_TABLE_OFFSET=0x8000
 CONFIG_PARTITION_TABLE_MD5=y
-CONFIG_TEMP_SENSORS_ONEWIRE=y
-# CONFIG_TEMP_SENSORS_SIMULATOR is not set
-CONFIG_ONE_WIRE_MLT=27
-CONFIG_ONE_WIRE_HLT=26
+# CONFIG_TEMP_SENSORS_ONEWIRE is not set
+CONFIG_TEMP_SENSORS_SIMULATOR=y
 CONFIG_SSR_MLT_GPIO=32
 CONFIG_SSR_HLT_GPIO=33
 CONFIG_SSR_PUMP_GPIO=12

mercurial