# HG changeset patch # User Michiel Broek # Date 1591442926 -7200 # Node ID 66c77497d86d402b1f2db99d013814f9a60522be # Parent 3ff381bfa4696e201862a18b994adb3043aa088e 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. diff -r 3ff381bfa469 -r 66c77497d86d image/version.txt --- 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 diff -r 3ff381bfa469 -r 66c77497d86d image/w/js/webui.js --- 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(); diff -r 3ff381bfa469 -r 66c77497d86d main/automation.c --- 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; } diff -r 3ff381bfa469 -r 66c77497d86d main/buttons.c --- 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; } diff -r 3ff381bfa469 -r 66c77497d86d main/config.c --- 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++; diff -r 3ff381bfa469 -r 66c77497d86d main/config.h --- 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 diff -r 3ff381bfa469 -r 66c77497d86d main/recipes.c --- 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); diff -r 3ff381bfa469 -r 66c77497d86d main/task_tft.c --- 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; diff -r 3ff381bfa469 -r 66c77497d86d sdkconfig --- 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