main/automation.c

Mon, 22 Oct 2018 21:43:45 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Mon, 22 Oct 2018 21:43:45 +0200
changeset 6
e84200edc852
parent 1
ad2c8b13eb88
child 19
49e2960d4642
permissions
-rw-r--r--

Updated esp-ide. Removed VNC server corre encoding because no clients would use it. Enabled WiFi error logmessages. Write runtime record is now debug logging. Removed recipe.Record number, not usefull and was wrong too. Removed console print of json log data.

/**
 * @file automation.c
 * @brief Automation functions.
 */

#include "config.h"

int	    			BoilPower = 100;		///< Boil power 0..100%
int				LastMashStep = 0;		///< Last valid mash step
char        			temp_buf[64];			///< Temporary buffer
char				logline[128];			///< Log line buffer
char				strftime_buf[64];		///< Time buffer
bool        			loop;				///< Loop flag
bool				CoolBeep = false;		///< Did beep during cooling
bool				Resume = false;			///< Resume brew flag
bool				pumpRest = false;		///< Pump is resting
bool				updateRuntime = false;		///< Update runtime record
bool        			NewMinute = false;		///< We have a new minute
bool				TempReached = false;		///< Temperature is reached
uint8_t     			MashState = MASH_NONE;		///< Mash states
float       			temp_MLT;			///< MLT temperature
float				MinMash = 38.0;			///< Minimum edit value
float				MaxMash = 80.0;			///< Maximum edit value
uint32_t    			power_MLT = 0;			///< MLT power
uint32_t			power_HLT = 0;			///< HLT power
uint32_t			counts = 0;			///< Counter for power average
float                           stageTemp = 0.0;		///< Current stage temperature
uint16_t                        stageTime = 0;			///< Current stage timer
uint16_t                        TimeWhirlPool = 0;		///< Whirlpool timer
uint32_t                        TimeLeft = 0;			///< Tie left in this stage
uint32_t                        TimeSpent = 0;			///< Tota time spent
uint32_t                        SecsCount = 0;			///< Seconds counter
uint32_t                        pumpTime = 0;			///< Pump running time
uint32_t                        TimeBrewing = 0;		///< Brewing time elapsed
uint16_t                        Steady = 0;			///< Temperature is steady
bool                            _NewMinute = false;		///< New minute slave flag
bool                            _UseHLT = false;		///< Use HLT slave flag
bool				_Prompt = false;		///< Prompt display flag

extern bool			System_TimeOk;			///< System time is valid
extern sButton			Buttons[MAXBUTTONS];		///< Buttons definitions
extern int			Main_Screen;			///< Current screen
extern DS18B20_State            *ds18b20_state;			///< DS18B20 state
extern DRIVER_State             *driver_state;			///< Relays driver state
extern SemaphoreHandle_t        xSemaphoreDS18B20;		///< DS18B20 lock semaphore
extern SemaphoreHandle_t        xSemaphoreDriver;		///< Relays driver lock semaphore
extern double                   Output;				///< Cakculated outpout power
extern time_t			now;				///< Current time
extern struct tm		timeinfo;			///< Current time structure

#ifdef CONFIG_TEMP_SENSORS_SIMULATOR
extern float                    Fake_MLT;
extern float                    Fake_HLT;
#endif

static const char		*TAG = "automation";


/*
 * Automation init function that only runs once when a
 * new screen is entered.
 */
bool Automation_Init(void)
{
    switch (Main_Screen) {
	case MAIN_AUTO_INIT:
#ifdef CONFIG_TEMP_SENSORS_SIMULATOR
		Fake_MLT = recipe.MashStep[0].Temperature - 10;
		Fake_HLT = recipe.SpargeTemp - 15;
                if (xSemaphoreTake(xSemaphoreDS18B20, 10) == pdTRUE) {
                    ds18b20_state->mlt_temperature = ((int)(Fake_MLT * 16)) / 16.0;
                    ds18b20_state->hlt_temperature = ((int)(Fake_HLT * 16)) / 16.0;
                    xSemaphoreGive(xSemaphoreDS18B20);
                }
#endif
                for (int i = 0; i < 7; i++) {
                    if (recipe.MashStep[i].Resttime)
                        LastMashStep = i;
                }
                ESP_LOGI(TAG, "Last mash step %d", LastMashStep);

                // Check for a crashed session.
                if (runtime.AutoModeStarted) {
                    TopMessage("Brouwen hervatten?");
                    Buttons_Add( 40, 100, 80, 40, "Ja", 0);
                    Buttons_Add(200, 100, 80, 40, "Nee",  1);
                    Buttons_Show();
                    SoundPlay(SOUND_Prompt);
                    loop = true;
                    while (loop) {
                        switch (Buttons_Scan()) {
                            case 0:     loop = false;
                                        Resume = true;
                                        Main_Screen = runtime.StageResume;
                                        TimeLeft = runtime.StageTimeLeft;
					TimeBrewing = runtime.TimeBrewing;
					_UseHLT = runtime.UseHLT;
                                        MashState = MASH_NONE;
                                        pumpTime = 0;
                                        pumpRest = false;
					if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
					    driver_state->enable = true;
					    if (_UseHLT) {
					    	driver_state->hlt_sp = recipe.SpargeTemp;
						driver_state->hlt_mode = HLT_MODE_BANG;
					    }
					    xSemaphoreGive(xSemaphoreDriver);
					}
					ESP_LOGI(TAG, "Resume brew screen %d, time left %d", Main_Screen, TimeLeft);
					log_begin((time_t)0);
					update_json();
					log_annotation(ANNOTATION_SYSTEM, "Resume");
					return true;
                                        break;

                            case 1:     loop = false;
                                        Resume = false;
                                        break;

                            default:
                                        break;
                        }
                        vTaskDelay(50 / portTICK_PERIOD_MS);
                    }
                    Buttons_Clear();
                    TFT_fillScreen(_bg);
                }

                if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
                    driver_state->enable = true;
                    xSemaphoreGive(xSemaphoreDriver);
                }
                runtime.AutoModeStarted = true;
                runtime.UseHLT = _UseHLT = false;
		runtime.TimeBrewing = 0;
                TimeBrewing = 0;
		runtime.StageResume = MAIN_AUTO_INIT;
		runtime.StageTimeLeft = 0;
		runtime.HopAddition = 0;
		runtime.Logfile[0] = '\0';
		runtime.PumpCooling = false;
		write_runtime();
                power_MLT = power_HLT = counts = 0;
		log_clean();
		vTaskDelay(250 / portTICK_PERIOD_MS);	// Allow some time
                break;

	case MAIN_AUTO_DELAYSTART:
		break;

	case MAIN_AUTO_HEATUP:
		if (runtime.UseHLT) {
		    if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
			driver_state->hlt_mode = HLT_MODE_BANG;
			xSemaphoreGive(xSemaphoreDriver);
		    }
		    TopMessage("Spoelwater opwarmen");
		    MLT_info(71, 26, false);
		    HLT_info(71,150, false, false);
		}
		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) {
                    MinMash = 38.0;
                    MaxMash = recipe.MashStep[1].Temperature + 10.0;
                    TimeBrewing = 0;
		    runtime.TimeBrewing = 0;
                    if (System_TimeOk) {
                        time(&now);
                        localtime_r(&now, &timeinfo);
                        log_begin(now);
			runtime.BrewStart = now;
                    } else {
                        log_begin((time_t)0);
			runtime.BrewStart = (time_t)0;
                    }
		    updateRuntime = true;
                } else if (Main_Screen == MAIN_AUTO_MASH_OUT) {
                    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;
                    }
                }
                MashState = MASH_NONE;
                pumpTime = 0;
                pumpRest = false;
		runtime.StageResume = Main_Screen;
		updateRuntime = true;
                TopMessage("Maischen");
                MLT_info(71, 26, false);
                if (_UseHLT) {
                    HLT_info(71,170, false, true);
                }
                break;

	case MAIN_AUTO_TOBOIL:
                if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
                    driver_state->mlt_sp = stageTemp = config.BoilTemperature;
                    driver_state->mlt_mode = MLT_MODE_EXT;
                    driver_state->hlt_sp = 0.0;
                    driver_state->hlt_mode = HLT_MODE_NONE;
                    xSemaphoreGive(xSemaphoreDriver);
                }

                runtime.StageResume = Main_Screen;
                updateRuntime = true;
		TempReached = false;

                TopMessage("Naar koken");
                MLT_info(71, 26, false);
                Buttons_Add(  5,  30, 60, 40, "+sp",  0);
                Buttons_Add(255,  30, 60, 40, "-sp",  1);
                Buttons_Show();
                ESP_LOGI(TAG, "Mash done, going to boil.");
                break;

	case MAIN_AUTO_BOILING:
                if (Resume) {
                    TimeLeft = runtime.StageTimeLeft * 60;
                } else {
                    // +1 minute for flameout and 2 seconds for a smooth transition.
                    runtime.StageTimeLeft = TimeLeft = (recipe.BoilTime * 60) + 60;
                    runtime.StageResume = Main_Screen;
                    runtime.HopAddition = 0;
                }
                SecsCount = 0;

                if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
                    driver_state->mlt_sp = stageTemp = config.BoilTemperature;
                    driver_state->mlt_mode = MLT_MODE_EXT;
                    xSemaphoreGive(xSemaphoreDriver);
                }
                SoundPlay(SOUND_TempReached);
                BoilPower = equipment.BoilPower;
                updateRuntime = true;
                TopMessage("Koken");
                MLT_info(71, 26, false);
                Buttons_Add(  3,  30, 60, 40, "+sp",  0);
                Buttons_Add(257,  30, 60, 40, "-sp",  1);
                Buttons_Add(  3, 190, 60, 40, "+1m",  2);
                Buttons_Add(257, 190, 60, 40, "-1m",  3);
                Buttons_Show();
                ESP_LOGI(TAG, "Boil temperature reached, boil %d minutes", (TimeLeft / 60) -1);
		log_annotation(ANNOTATION_STAGE, "Koken");
                break;

	case MAIN_AUTO_COOLING_H:
	case MAIN_AUTO_COOLING_M:
	case MAIN_AUTO_COOLING_C:
		TempReached = false;
                runtime.StageResume = Main_Screen;
                runtime.StageTimeLeft = 0;
                updateRuntime = true;
                if ((Main_Screen == MAIN_AUTO_COOLING_H) && (! recipe.Whirlpool7)) {
                    // Skip cooling before whirlpool 74 degrees
                    Main_Screen = MAIN_AUTO_COOLING_M;
                    return true; //goto startover;
                }
                if ((Main_Screen == MAIN_AUTO_COOLING_M) && (! recipe.Whirlpool6)) {
                    // Skip cooling before whirlpool 63 degrees.
                    Main_Screen = MAIN_AUTO_COOLING_C;
                    return true; //goto startover;
                }
                TopMessage("Start koelen?");
                Buttons_Add( 40, 100, 80, 40, "Start", 0);
                Buttons_Add(200, 100, 80, 40, "Stop",  1);
		Buttons[1].dark = true;
                Buttons_Show();
                SoundPlay(SOUND_Prompt);
		_Prompt = true;
                break;

	case MAIN_AUTO_WHIRLPOOL7:
	case MAIN_AUTO_WHIRLPOOL6:
	case MAIN_AUTO_WHIRLPOOL2:
		TempReached = true;
                runtime.StageResume = Main_Screen;
                updateRuntime = true;
                if ((Main_Screen == MAIN_AUTO_WHIRLPOOL9) && (! recipe.Whirlpool9)) {
                    // Skip whirlpool 93 degrees.
                    Main_Screen = MAIN_AUTO_COOLING_H;
                    return true; //goto startover;
                }
                if ((Main_Screen == MAIN_AUTO_WHIRLPOOL7) && (! recipe.Whirlpool7)) {
                    // Skip whirlpool 74 degrees.
                    Main_Screen = MAIN_AUTO_COOLING_M;
                    return true; //goto startover;
                }
                if ((Main_Screen == MAIN_AUTO_WHIRLPOOL6) && (! recipe.Whirlpool6)) {
                    // Skip whirlpool 63 degrees.
                    Main_Screen = MAIN_AUTO_COOLING_C;
                    return true; //goto startover;
                }
                if ((Main_Screen == MAIN_AUTO_WHIRLPOOL2) && (! recipe.Whirlpool2)) {
                    // Skip final whirlpool.
                    Main_Screen = MAIN_AUTO_DONE;
                    return true; //goto startover;
                }

                TopMessage("Start Whirlpool?");
                Buttons_Add( 40, 100, 80, 40, "Start", 0);
                Buttons_Add(200, 100, 80, 40, "Stop",  1);
		Buttons[1].dark = true;
                Buttons_Show();
                SoundPlay(SOUND_Prompt);
		_Prompt = true;
                break;

	case MAIN_AUTO_DONE:
	case MAIN_AUTO_ABORT:
                if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
                    driver_state->enable = false;
                    driver_state->mlt_mode = MLT_MODE_NONE;
                    driver_state->mlt_sp = 0.0;
                    driver_state->hlt_mode = HLT_MODE_NONE;
                    driver_state->hlt_sp = 0.0;
                    driver_state->pump_run = 0;
                    xSemaphoreGive(xSemaphoreDriver);
                }
                _fg = TFT_YELLOW;
                TFT_setFont(DEJAVU24_FONT, NULL);
                if (Main_Screen == MAIN_AUTO_DONE) {
                    TFT_print("Brouwen is gereed.", CENTER, CENTER);
                    ESP_LOGI(TAG, "Brew is done");
                    SoundPlay(SOUND_End);
                } else {
                    TFT_print("Brouwen is afgebroken.", CENTER, CENTER);
                    ESP_LOGI(TAG, "Brew is aborted");
                    SoundPlay(SOUND_Warn);
                }
                log_close();
		runtime.Logfile[0] = '\0';
		runtime.BrewStart = (time_t)0;
                runtime.AutoModeStarted = false;
                runtime.StageResume = MAIN_MODE_FREE;
		runtime.PumpCooling = false;
		runtime.HopAddition = 0;
		runtime.TimeBrewing = 0;
		runtime.StageTimeLeft = 0;
                updateRuntime = true;
                Buttons_Add(130, 200, 60, 40, "Ok", 0);
		Buttons[0].dark = true;
                Buttons_Show();
                break;

	default:
		break;
    }

    return false;
}



/*
 * Automation loop screens. Mostly non-blocking.
 */
bool Automation_Loop(void)
{
    static bool beeped = false;
    char	tmp[32];
    uint16_t	y;

    switch (Main_Screen) {

	case MAIN_AUTO_INIT:
		/*
		 * Present selected equipment and recipe.
		 */
		read_recipe(config.RecipeRec);
		y = 28;
		TopMessage("Automaat");
		TFT_setFont(DEFAULT_FONT, NULL);
		ShowText(2,y,"Installatie", equipment.Name);
		y += 16;
		ShowText(2,y,"Recept", recipe.Name);
		y += 16;
		ShowFloat(2, y, "Maisch in", " C", recipe.MashStep[0].Temperature, 2);
		ShowFloat(162, y, "Spoelwater", " C", recipe.SpargeTemp, 2);
		y += 16;
		_fg = TFT_WHITE;
		TFT_print("Maisch stap", 2, y);
		TFT_print("Temp.", 200, y);
		TFT_print("Rust", 260, y);
		_fg = TFT_YELLOW;
		y += 16;
		for (int i = 1; i < 8; i++) {
		    if (recipe.MashStep[i].Resttime) {
			TFT_print(recipe.MashStep[i].Name, 2, y);
			sprintf(tmp, "%.2f", recipe.MashStep[i].Temperature);
			TFT_print(tmp, 200, y);
			sprintf(tmp, "%2d min", recipe.MashStep[i].Resttime);
			TFT_print(tmp, 260, y);
			y += 16;
		    }
		}
		ShowInteger(2, y, "Kooktijd", " miniuten", recipe.BoilTime);
		y += 16;
		if (recipe.Additions) {
		    _fg = TFT_YELLOW;
		    sprintf(tmp, "%d ", recipe.Additions);
		    TFT_print(tmp, 2, y);
		    _fg = TFT_WHITE;
		    TFT_print("toevoegingen om", LASTX, y);
		    _fg = TFT_YELLOW;
		    for (int i = 1; i <= recipe.Additions; i++) {
			sprintf(tmp, " %d", recipe.Addition[i-1].Time);
			TFT_print(tmp, LASTX, y);
		    }
		    _fg = TFT_WHITE;
		    TFT_print(" minuten", LASTX, y);
		} else {
		    _fg = TFT_WHITE;
		    TFT_print("Geen hop toevoegingen.", 2, y);
		}
		y += 16;
		ShowFloat(2, y, "Koelen tot", " C", recipe.CoolTemp, 2);
		if (recipe.Whirlpool9) {
		    ShowInteger(2, y, "Whirlpool 88..100 graden", " minuten", recipe.Whirlpool9);
		    y += 16;
		}
		if (recipe.Whirlpool7) {
		    ShowInteger(2, y, "Whirlpool 71..77 graden", " minuten", recipe.Whirlpool7);
		    y += 16;
		}
		if (recipe.Whirlpool6) {
		    ShowInteger(2, y, "Whirlpool 60..66 graden", " minuten", recipe.Whirlpool6);
		    y += 16;
		}
		if (recipe.Whirlpool2) {
		    ShowInteger(2, y, "Whirlpool koud", " minuten", recipe.Whirlpool2);
		    y += 16;
		}
		Buttons_Add(  0, 210, 70, 30, "Stop"   , 0);
		Buttons_Add(250, 210, 70, 30, "Start"  , 1);
		Buttons[0].dark = true;
		Buttons_Show();
		loop = true;
		while (loop) {
		    switch (Buttons_Scan()) {
			case 0:         loop = false;
					Main_Screen = MAIN_AUTO_ABORT;
					break;

			case 1:         loop = false;
					break;

			default:        break;
		    }
		    vTaskDelay(20 / portTICK_PERIOD_MS);
		}
		if (Main_Screen == MAIN_AUTO_ABORT)
		    break;

                _UseHLT = false;
		_bg = TFT_BLACK;
		TFT_fillScreen(_bg);
                TopMessage("Maisch water aanwezig?");
		Buttons_Clear();
                Buttons_Add( 40, 100, 80, 40, "Ja", 0);
                Buttons_Add(200, 100, 80, 40, "Nee",  1);
                Buttons_Show();
                SoundPlay(SOUND_Prompt);
                loop = true;
                while (loop) {
                    switch (Buttons_Scan()) {
                        case 0:         loop = false;
                                        break;

                        case 1:         loop = false;
                                        Main_Screen = MAIN_AUTO_ABORT;
                                        break;

                        default:        break;
                    }
                    vTaskDelay(20 / portTICK_PERIOD_MS);
                }
                if (Main_Screen == MAIN_AUTO_ABORT)
                    break;

		if ((equipment.SSR2 == SSR2_HLT_SHARE) || (equipment.SSR2 == SSR2_HLT_IND)) {
                    TopMessage("Spoelwater aanwezig?");
                    SoundPlay(SOUND_Prompt);
                    loop = true;
                    while (loop) {
                    	switch (Buttons_Scan()) {
                            case 0:         loop = false;
                                        _UseHLT = true;
                                        break;

                            case 1:         loop = false;
                                        break;

                            default:        break;
                    	}
                   	vTaskDelay(20 / portTICK_PERIOD_MS);
                    }
                    runtime.UseHLT = _UseHLT;
		} else {
		    runtime.UseHLT = _UseHLT = false;
		}
                updateRuntime = true;
                if (_UseHLT) {
                    /*
                     * Calculate HLT setpoint for pre-heat. Substract the
                     * available Mash rest times, asume 0.5 degrees/minute
                     * heat capacity during mash.
                     */
                    int AvailableTime = 0;
                    for (int i = 1; i < 6; i++) // Only normal Mash steps
                        AvailableTime += recipe.MashStep[i].Resttime;
                    if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
                        driver_state->hlt_sp = recipe.SpargeTemp - ((AvailableTime / 2) + 2);
                        ESP_LOGI(TAG, "HLT preheat set to %4.1f", driver_state->hlt_sp);
                        xSemaphoreGive(xSemaphoreDriver);
                    }
                }
                Buttons_Clear();
                Main_Screen = MAIN_AUTO_DELAYSTART;
                break;

	case MAIN_AUTO_DELAYSTART:
                Main_Screen = MAIN_AUTO_HEATUP;
                break;

	case MAIN_AUTO_HEATUP:
                if (! runtime.UseHLT) {         // Skip if HLT is off
                    Main_Screen = MAIN_AUTO_MASH_IN;
                    break;
                }

                MLT_info(71, 26, true);
                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;
                        driver_state->hlt_sp = recipe.SpargeTemp;       // Set final setpoint
                    }
                    xSemaphoreGive(xSemaphoreDriver);
                }
                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:
                temp_MLT = 0.0;
                if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
                    temp_MLT = driver_state->mlt_pv;

		    if (MashState == MASH_ADD || MashState == MASH_REMOVE) {
			driver_state->pump_run = 0;
		    } else if (MashState != MASH_NONE) {
                        if (Main_Screen == MAIN_AUTO_MASH_IN) {
                            driver_state->pump_run = (equipment.PumpPreMash && ! pumpRest) ? 1 : 0;
                        } else if (Main_Screen == MAIN_AUTO_MASH_OUT) {
                            driver_state->pump_run = (equipment.PumpMashOut && ! pumpRest) ? 1 : 0;
                        } else {
                            driver_state->pump_run = (equipment.PumpOnMash && ! pumpRest) ? 1 : 0;
                        }
                    }
                    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;
                    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 = MASH_WAITTEMP;
                    ESP_LOGI(TAG, "Mash step %d time: %d temp: %4.1f  min: %4.1f max: %4.1f",
                                    Main_Screen - MAIN_AUTO_MASH_IN, stageTime, stageTemp, MinMash, MaxMash);

		    if (Main_Screen > MAIN_AUTO_MASH_IN) {
			// Do not annotate before the log is open.
			if (Main_Screen == MAIN_AUTO_MASH_OUT) {
			    log_annotation(ANNOTATION_STAGE, "Uitmaischen");
			} else {
                    	    sprintf(logline, "Maisch: %d", Main_Screen - MAIN_AUTO_MASH_IN);
		    	    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);
                    } else {
                        sprintf(temp_buf, "Maisch stap #%d", Main_Screen - MAIN_AUTO_MASH_IN);
                        TopMessage(temp_buf);
                    }
                    Buttons_Add(  5,  30, 60, 40, "+sp",   0);
                    Buttons_Add(255,  30, 60, 40, "-sp",   1);
                    Buttons_Show();

                } else if (MashState == MASH_WAITTEMP) {
                    pumpRest = false;
                    if (temp_MLT < stageTemp) {
                        Steady = 0;
                    }
                    if ((temp_MLT >= stageTemp) && (Steady > 10)) {
                        SoundPlay(SOUND_TempReached);
                        TempReached = true;
                        MashState = MASH_REST;
                        if (Main_Screen == MAIN_AUTO_MASH_IN) {
                            TimerSet(0);
                        } else {
                            if (Resume && (runtime.StageTimeLeft < stageTime))
                                TimerSet(runtime.StageTimeLeft * 60);
                            else
                                TimerSet(stageTime * 60);
                        }
			Resume = false;
			runtime.StageTimeLeft = TimeLeft / 60;
			updateRuntime = true;
                        ESP_LOGI(TAG, "Mash step %d temperature reached, rest time %d", Main_Screen - MAIN_AUTO_MASH_IN, TimeLeft / 60);
                        Buttons_Clear();
                        Buttons_Add(  0, 120, 60, 40, "+1m",   0);
                        Buttons_Add(260, 120, 60, 40, "-1m",   1);
                        Buttons_Show();
                    }
                    switch (Buttons_Scan()) {
                        case 0:         if (stageTemp < MaxMash) {
                                            stageTemp += 0.25;
                                            if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
                                                driver_state->mlt_sp = stageTemp;
                                                xSemaphoreGive(xSemaphoreDriver);
                                            }
                                        }
                                        break;

                        case 1:         if (stageTemp > MinMash) {
                                            stageTemp -= 0.25;
                                            if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
                                                driver_state->mlt_sp = stageTemp;
                                                xSemaphoreGive(xSemaphoreDriver);
                                            }
                                        }
                                        break;

                        default:
                                        break;
                    }
		    if (NewMinute)
			updateRuntime = true;

                } else if (MashState == MASH_REST) {
                    /*
                     * Mash step rest time and pump control
                     */
                    if (((Main_Screen == MAIN_AUTO_MASH_OUT) && equipment.PumpMashOut) ||
                        ((Main_Screen != MAIN_AUTO_MASH_OUT) && 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;
                        }
                        if (pumpTime >= equipment.PumpCycle) {
                            if (! pumpRest) {
                                pumpRest = true;
                                ESP_LOGI(TAG, "Pump rest");
                            }
                        } else {
                            if (pumpRest) {
                                pumpRest = false;
                                ESP_LOGI(TAG, "Pump start");
                            }
                        }
                    }
                    if (TimeLeft) {
                        switch (Buttons_Scan()) {
                            case 0:     TimeLeft += 60;
					runtime.StageTimeLeft = TimeLeft / 60;
					updateRuntime = true;
                                        break;

                            case 1:     if (TimeLeft < 60)
                                            TimeLeft = 0;
                                        else
                                            TimeLeft -= 60;
					runtime.StageTimeLeft = TimeLeft / 60;
					updateRuntime = true;
                                        break;

                            default:    break;
                        }
                    }

                    if (TimeLeft == 0) {
			runtime.StageTimeLeft = TimeLeft / 60;
			updateRuntime = true;
                        if ((Main_Screen == MAIN_AUTO_MASH_IN) && config.AskAdd) {
                            /*
                             * Add Mash prompt.
                             */
			    log_annotation(ANNOTATION_EVENT, "Mout storten");
                            Buttons_Clear();
                            Buttons_Add(  5,120, 60, 40, "Halt", 0);
			    Buttons[0].dark = true;
                            Buttons_Add(255,120, 60, 40, "Ok",   1);
                            Buttons_Show();
                            _fg = TFT_WHITE;
                            _bg = TFT_BLACK;
                            TFT_setFont(DEJAVU24_FONT, NULL);
                            TFT_print("Mout storten?", CENTER, 135);
                            SoundPlay(SOUND_Prompt);
			    MashState = MASH_ADD;
			    ESP_LOGI(TAG, "Mash add prompt");
			    break;
                        }
                        if (((Main_Screen - MAIN_AUTO_MASH_IN) == LastMashStep) && config.AskIodine) {
                            /*
                             * Iodone test prompt.
                             */
			    log_annotation(ANNOTATION_EVENT, "Jodium test");
                            TFT_fillRect(0, 120, 320, 50, TFT_BLACK);
                            Buttons_Clear();
                            Buttons_Add(  5,120, 60, 40, "Halt", 0);
			    Buttons[0].dark = true;
                            Buttons_Add(255,120, 60, 40, "Ok",   1);
                            Buttons_Show();
                            _fg = TFT_WHITE;
                            _bg = TFT_BLACK;
                            TFT_setFont(DEJAVU24_FONT, NULL);
                            TFT_print("Jodium test?", CENTER, 127);
                            SoundPlay(SOUND_Prompt);
                            beeped = false;
                            TimerSet(config.IodineTime * 60);
			    MashState = MASH_IODINE;
			    ESP_LOGI(TAG, "Mash iodine test prompt");
			    break;
                        }
                        if ((Main_Screen == MAIN_AUTO_MASH_OUT) && config.AskRemove) {
                            /*
                             * Mash remove prompt.
                             */
			    log_annotation(ANNOTATION_EVENT, "Mout verwijderen");
                            TFT_fillRect(0, 120, 320, 50, TFT_BLACK);
                            Buttons_Clear();
                            Buttons_Add(  5,120, 60, 40, "Halt", 0);
			    Buttons[0].dark = true;
                            Buttons_Add(255,120, 60, 40, "Ok",   1);
                            Buttons_Show();
                            _fg = TFT_WHITE;
                            _bg = TFT_BLACK;
                            TFT_setFont(DEJAVU18_FONT, NULL);
                            TFT_print("Mout verwijderen?", CENTER, 135);
                            SoundPlay(SOUND_Prompt);
			    MashState = MASH_REMOVE;
			    ESP_LOGI(TAG, "Mash remove prompt");
			    break;
                        }
                        if (Main_Screen != MAIN_AUTO_ABORT)
                            Main_Screen++;
                        TempReached = false;
                    } else {
                        TimerShow(TimeLeft, 65, 122);
			if (NewMinute) {
			    runtime.StageTimeLeft = TimeLeft / 60;
			    updateRuntime = true;
			}
                    }
		} else if (MashState == MASH_ADD) {
		    switch (Buttons_Scan()) {
			case 0:     Main_Screen = MAIN_AUTO_ABORT;
				    break;
			case 1:     Main_Screen++;
				    break;
			default:    break;
		    }
		} else if (MashState == MASH_IODINE) {
		    if (TimeSpent % 45 == 0) {
			if (! beeped) {
			    SoundPlay(SOUND_Warn);
			    beeped = true;
			}
		    } else {
			beeped = false;
		    }
		    switch (Buttons_Scan()) {
			case 0:     Main_Screen = MAIN_AUTO_ABORT;
				    break;
			case 1:     Main_Screen++;
				    break;
			default:    break;
		    }
		    if (TimeLeft == 0) {
			Main_Screen++;
		    }
		} else if (MashState == MASH_REMOVE) {
		    switch (Buttons_Scan()) {
			case 0:     Main_Screen = MAIN_AUTO_ABORT;
				    break;
			case 1:     Main_Screen++;
				    break;
			default:    break;
		    }
                } /* MashState */
                MLT_info(71, 26, true);
                if (_UseHLT) {
                    HLT_info(71, 170, true, true);
                }
                break;

	case MAIN_AUTO_TOBOIL:
                Output = 255;
                /*
                 * Go to the boil temperature and wait until it is steady.
                 */
                if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
                    if (driver_state->mlt_pv < stageTemp) {
                        Steady = 0;
                    } else {
                        if (Steady > 10) {
                            Main_Screen = MAIN_AUTO_BOILING;
			    TempReached = true;
                        }
                    }
                    driver_state->pump_run = (equipment.PumpOnBoil && (driver_state->mlt_pv < equipment.PumpMaxTemp)) ? 1 : 0;
                    xSemaphoreGive(xSemaphoreDriver);
                }

                MLT_info(71, 26, true);
                switch (Buttons_Scan()) {
                    case 0:     if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
                                    driver_state->mlt_sp += 0.25;
                                    xSemaphoreGive(xSemaphoreDriver);
                                }
                                break;

                    case 1:     if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
                                    driver_state->mlt_sp -= 0.25;
                                    xSemaphoreGive(xSemaphoreDriver);
                                }
                                break;

                    default:    break;
                }
		if (Resume)
		    Resume = false;
                break;

	case MAIN_AUTO_BOILING:
		if (Resume)
		    Resume = false;
                if (NewMinute) {
                    if ((runtime.HopAddition < recipe.Additions) && (TimeLeft <= ((recipe.Addition[runtime.HopAddition].Time * 60) + 60))) {
                        ESP_LOGI(TAG, "Hop addition %d at %d minutes", runtime.HopAddition + 1, recipe.Addition[runtime.HopAddition].Time);
                        TopMessage(recipe.Addition[runtime.HopAddition].Name);
                        sprintf(logline, "Hopgift %d %s", runtime.HopAddition + 1, recipe.Addition[runtime.HopAddition].Name);
			log_annotation(ANNOTATION_EVENT, logline);
                        SoundPlay(SOUND_AddHop);
                        runtime.HopAddition++;
                    } else {
                        TopMessage("Koken");
                    }
                    runtime.StageTimeLeft = TimeLeft / 60;
                    updateRuntime = true;
                }
                if (TimeLeft < 60) {
		    if (Output) {
			log_annotation(ANNOTATION_STAGE, "Vlamuit");
		    }
                    // Flameout
                    Output = 0;
                } else if (driver_state->mlt_pv >= stageTemp) {
                    Output = (int)((BoilPower * 255.0) / 100.0);
                    if (Buttons[4].x == -1) {
                        Buttons_Add(  3,110, 60, 40, "+%", 4);
                        Buttons_Add(257,110, 60, 40, "-%", 5);
                        Buttons_Show();
                    }
                } else {
                    Output = 255;
                    if (Buttons[4].x != -1) {
                        Buttons[4].x = Buttons[5].x = -1;
                        Buttons_Show();
                        TFT_fillRect(  3,110, 60, 40, TFT_BLACK);
                        TFT_fillRect(257,110, 60, 40, TFT_BLACK);
                    }
                }

                MLT_info(71, 26, true);
                TimerShow(TimeLeft, 65, 190);

                switch (Buttons_Scan()) {
                    case 0:     if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
                                    driver_state->mlt_sp += 0.25;
                                    xSemaphoreGive(xSemaphoreDriver);
                                }
                                break;

                    case 1:     if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
                                    driver_state->mlt_sp -= 0.25;
                                    xSemaphoreGive(xSemaphoreDriver);
                                }
                                break;

                    case 2:     TimeLeft += 60;
                                break;

                    case 3:     if (TimeLeft > 60)
                                    TimeLeft -= 60;
                                else
                                    TimeLeft = 0;
                                break;

                    case 4:     if (BoilPower < 100)
                                    BoilPower++;
                                break;

                    case 5:     if (BoilPower > 0)
                                    BoilPower--;
                                break;

                    default:    break;
                }

                if (TimeLeft == 0) {
                    Main_Screen = MAIN_AUTO_WHIRLPOOL9;
                    ESP_LOGI(TAG, "Boil is ready");
                }
                break;

	case MAIN_AUTO_COOLING_H:
	case MAIN_AUTO_COOLING_M:
	case MAIN_AUTO_COOLING_C:
		if (_Prompt) {
		    /*
		     * Prompt mode
		     */
		    switch (Buttons_Scan()) {
			case 0: _Prompt = false;
				Buttons_Clear();
				break;
			case 1: Main_Screen = MAIN_AUTO_DONE;
				Buttons_Clear();
				return true; //goto startover;
				break;
			default:
				break;
		    }

		    if (! _Prompt) {
			/*
			 * Starting cooling, setup the screen.
			 */
			Buttons_Clear();
			TFT_fillScreen(_bg);
			if (Main_Screen == MAIN_AUTO_COOLING_H) {
			    stageTemp = 77.0;
			} else if (Main_Screen == MAIN_AUTO_COOLING_M) {
			    stageTemp = 66.0;
			} else {
			    stageTemp = recipe.CoolTemp;
			}
			CoolBeep = false;
			ESP_LOGI(TAG, "Start cooling from %6.2f to %4.1f", ds18b20_state->mlt_temperature, stageTemp);
			if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
			    driver_state->mlt_mode = MLT_MODE_OFF;
			    driver_state->mlt_sp = stageTemp;
			    xSemaphoreGive(xSemaphoreDriver);
			}
			log_annotation(ANNOTATION_STAGE, "Koelen");
			TopMessage("Koelen");
			MLT_info(71, 26, false);
			Buttons_Add(  5, 200, 60, 40, "Stop", 0);
			Buttons[0].dark = true;
			Buttons_Add(  5,  26, 60, 40, "+1",   1);
			Buttons_Add(255,  26, 60, 40, "-1",   2);
			/*
			 * The next key is not a mistake, but we need a key entry which
			 * will later become the pump key. The keyscan routine will find
			 * the original key if pressed.
			 */
			Buttons_Add(255,  26, 60, 40, "-1",   3);
			Buttons_Show();
		    }
		} else {
		    /*
		     * Not in prompt mode.
		     */
#ifdef CONFIG_TEMP_SENSORS_SIMULATOR
		    if (Fake_MLT > 12.0) {
			if (driver_state->pump_run)
			    Fake_MLT -= 0.00025 * (Fake_MLT - 12.0);
			else
			    Fake_MLT -= 0.00015 * (Fake_MLT - 12.0);
		    }
		    if (xSemaphoreTake(xSemaphoreDS18B20, 10) == pdTRUE) {
			ds18b20_state->mlt_temperature = ((int)(Fake_MLT * 16)) / 16.0;
			xSemaphoreGive(xSemaphoreDS18B20);
		    }
#endif
                    MLT_info(71, 26, true);
                    if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
                    	/*
                    	 * If the pump safe temperature is reached, add the control button.
		    	 * Redefine key number 3 if it is at the position of key 2.
                    	 */
                    	if ((driver_state->mlt_pv < equipment.PumpMaxTemp) && (Buttons[3].x == Buttons[2].x) &&(Buttons[3].y == Buttons[2].y)) {
                            Buttons_Add(255, 200, 60, 40, "Pomp", 3);
                            Buttons_Show();
                    	}
                    	xSemaphoreGive(xSemaphoreDriver);
                    }
                    switch (Buttons_Scan()) {
                    	case 1:	if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
                                    if (Main_Screen == MAIN_AUTO_COOLING_H) {
                                        if (driver_state->mlt_sp < 77.0)
                                            driver_state->mlt_sp += 1.0;
                                    } else if (Main_Screen == MAIN_AUTO_COOLING_M) {
                                        if (driver_state->mlt_sp < 66.0)
                                            driver_state->mlt_sp += 1.0;
                                    } else if (Main_Screen == MAIN_AUTO_COOLING_C) {
                                        if (driver_state->mlt_sp < 30.0)
                                            driver_state->mlt_sp += 1.0;
                                    }
                                    xSemaphoreGive(xSemaphoreDriver);
                                }
                                break;

                    	case 0: Buttons_Add( 60, 150, 90, 40, "Stoppen", 4);
				Buttons[4].dark = true;
                                Buttons_Add(170, 150, 90, 40, "Sorry",   5);
                                Buttons_Show();
                                break;

                    	case 2: if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
                                    if (Main_Screen == MAIN_AUTO_COOLING_H) {
                                        if (driver_state->mlt_sp > 71.0)
                                            driver_state->mlt_sp -= 1.0;
                                    } else if (Main_Screen == MAIN_AUTO_COOLING_M) {
                                        if (driver_state->mlt_sp > 60.0)
                                            driver_state->mlt_sp -= 1.0;
                                    } else if (Main_Screen == MAIN_AUTO_COOLING_C) {
                                        if (driver_state->mlt_sp > 10.0)
                                            driver_state->mlt_sp -= 1.0;
                                    }
                                    xSemaphoreGive(xSemaphoreDriver);
                                }
                                break;

                    	case 3: if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
                                    if (driver_state->mlt_pv < equipment.PumpMaxTemp) {
                                        if (driver_state->pump_run)
                                            driver_state->pump_run = 0;
                                        else
                                            driver_state->pump_run = 1;
                                    } else {
                                        driver_state->pump_run = 0;
                                    }
				    runtime.PumpCooling = driver_state->pump_run;
				    updateRuntime = true;
                                    xSemaphoreGive(xSemaphoreDriver);
                                }
                                break;

		    	case 4:	Main_Screen++;
				break;

		    	case 5:	Buttons[4].x = Buttons[5].x = -1;
				TFT_fillRect(60, 150, 200, 40, TFT_BLACK);
				break;

		    	default:
				break;
                    }

                    if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
                    	if (! CoolBeep && (driver_state->mlt_pv < (driver_state->mlt_sp + 2.0))) {
                            SoundPlay(SOUND_Warn);
                            CoolBeep = true;
                    	}
                    	if (driver_state->mlt_pv <= driver_state->mlt_sp) {
                            SoundPlay(SOUND_TempReached);
                            Main_Screen++;
                    	}
                    	xSemaphoreGive(xSemaphoreDriver);
                    }
		}
                break;

	case MAIN_AUTO_WHIRLPOOL9:
	case MAIN_AUTO_WHIRLPOOL7:
	case MAIN_AUTO_WHIRLPOOL6:
	case MAIN_AUTO_WHIRLPOOL2:
		if (_Prompt) {

		    switch (Buttons_Scan()) {
			case 0: _Prompt = false;
				break;
			case 1: if (Main_Screen == MAIN_AUTO_WHIRLPOOL2) {
				    Main_Screen = MAIN_AUTO_DONE;
				} else {
				    Main_Screen++;
				}
				Buttons_Clear();
				return true; //goto startover;
				break;
			default:
				break;
		    }

		    if (! _Prompt) {
			/*
			 * Prepare the screen for the actual whirpool.
			 */
			Buttons_Clear();
			TFT_fillScreen(_bg);
			if (Main_Screen == MAIN_AUTO_WHIRLPOOL9) {
			    TimeWhirlPool = recipe.Whirlpool9;
			    if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
				driver_state->mlt_sp = 93.0;
				driver_state->mlt_mode = MLT_MODE_PID;
				xSemaphoreGive(xSemaphoreDriver);
			    }
			    TopMessage("Whirlpool 88..100");
			} else if (Main_Screen == MAIN_AUTO_WHIRLPOOL7) {
			    TimeWhirlPool = recipe.Whirlpool7;
			    if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
				driver_state->mlt_sp = 74.0;
				driver_state->mlt_mode = MLT_MODE_PID;
				xSemaphoreGive(xSemaphoreDriver);
			    }
			    TopMessage("Whirlpool 71..77");
			} else if (Main_Screen == MAIN_AUTO_WHIRLPOOL6) {
			    TimeWhirlPool = recipe.Whirlpool6;
			    if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
				driver_state->mlt_sp = 63.0;
				driver_state->mlt_mode = MLT_MODE_PID;
				xSemaphoreGive(xSemaphoreDriver);
			    }
			    TopMessage("Whirlpool 60..66");
			} else {
			    TimeWhirlPool = recipe.Whirlpool2;
			    TopMessage("Koude whirlpool");
			}
			if (Resume) {
			    TimeWhirlPool = runtime.StageTimeLeft;
			}

			/*
			 * If the pump is allowed at the current temperature, turn it on.
			 */
			if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
			    driver_state->pump_run = (driver_state->mlt_pv < equipment.PumpMaxTemp) ? 1 : 0;
			    xSemaphoreGive(xSemaphoreDriver);
			}
			log_annotation(ANNOTATION_STAGE, "Whirlpool");

			TimerSet(TimeWhirlPool * 60);
			runtime.StageTimeLeft = TimeWhirlPool;
			updateRuntime = true;
			MLT_info(71, 26, false);
			ESP_LOGI(TAG, "Whirlpool %d minutes, sp %4.1f", TimeWhirlPool, driver_state->mlt_sp);
			Buttons_Add(255, 120, 60, 40, "+1m", 0);
			Buttons_Add(  5, 120, 60, 40, "-1m", 1);
			Buttons_Add(130, 200, 60, 40, "Pomp",  2);
			Buttons_Show();
		    }
		} else {
		    /*
		     * Not running in prompt mode, do the whirlpool.
		     */
                    if (TimeLeft == 120) {
                    	/*
                    	 * Drop the temperature when whirlpool is almost ready.
                    	 * If we are lucky the heater element will cool down so
                    	 * the next cooling stage will not wast too much energy.
                    	 */
                    	if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
                            if (Main_Screen == MAIN_AUTO_WHIRLPOOL9)
                            	driver_state->mlt_sp = 88.0;
                            if (Main_Screen == MAIN_AUTO_WHIRLPOOL7)
                            	driver_state->mlt_sp = 71.0;
                            if (Main_Screen == MAIN_AUTO_WHIRLPOOL6)
                            	driver_state->mlt_sp = 60.0;
                            xSemaphoreGive(xSemaphoreDriver);
                    	}
                    }

                    MLT_info(71, 26, true);
                    TimerShow(TimeLeft, 65, 122);
                    switch (Buttons_Scan()) {
                    	case 0:     TimeLeft += 60;
                                break;
                    	case 1:     if (TimeLeft > 60)
                                    TimeLeft -= 60;
                                else
                                    TimeLeft = 0;
                                break;
                    	case 2:     if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
                                    if (driver_state->mlt_pv < equipment.PumpMaxTemp) {
                                        if (driver_state->pump_run)
                                            driver_state->pump_run = 0;
                                        else
                                            driver_state->pump_run = 1;
                                    } else {
                                        driver_state->pump_run = 0;
                                    }
                                    xSemaphoreGive(xSemaphoreDriver);
                                }
                    }

                    if (NewMinute) {
                    	runtime.StageTimeLeft = TimeLeft / 60;
                    	updateRuntime = true;
                    }

                    if ((TimeLeft == 0)) {
                    	if (Main_Screen == MAIN_AUTO_WHIRLPOOL9) {
                            Main_Screen = MAIN_AUTO_COOLING_H;
			    if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
			    	driver_state->pump_run = runtime.PumpCooling;
			    	xSemaphoreGive(xSemaphoreDriver);
			    }
                    	} else if (Main_Screen == MAIN_AUTO_WHIRLPOOL7) {
                            Main_Screen = MAIN_AUTO_COOLING_M;
			    if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
			    	driver_state->pump_run = runtime.PumpCooling;
			    	xSemaphoreGive(xSemaphoreDriver);
			    }
                    	} else if (Main_Screen == MAIN_AUTO_WHIRLPOOL6) {
                            Main_Screen = MAIN_AUTO_COOLING_C;
			    if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
			    	driver_state->pump_run = runtime.PumpCooling;
			    	xSemaphoreGive(xSemaphoreDriver);
			    }
                        } else if (Main_Screen == MAIN_AUTO_WHIRLPOOL2) {
                            Main_Screen = MAIN_AUTO_DONE;
                    	}
                    }
		}
                break;

	case MAIN_AUTO_DONE:
	case MAIN_AUTO_ABORT:
		if (Buttons_Scan() == 0)
                    Main_Screen = MAIN_MODE_FREE;
                break;

	default:
		break;
    }

    return false;
}

mercurial