main/task_tft.c

Mon, 19 Feb 2024 15:07:28 +0100

author
Michiel Broek <mbroek@mbse.eu>
date
Mon, 19 Feb 2024 15:07:28 +0100
changeset 128
64886971967b
parent 117
b6d5c4cb61bb
permissions
-rw-r--r--

Fix current year display

/**
 * @file task_tft.c
 * @brief BrewBoard TFT and Touch screen driver for a 320x240 ILI9341 based display.
 *        But because the application is controlled using the touch screen, all the
 *        processing of menus is also found here.
 *        It's the first started task, but it does nothing until the Main_Screen 
 *        variable is set.
 */

#include "config.h"

spi_lobo_device_handle_t	spi;				///< TFT screen SPI handler
spi_lobo_device_handle_t	tsspi = NULL;			///< Touchscreen SPI handler
extern sButton			Buttons[MAXBUTTONS];		///< 40 buttons on a screen.
time_t				now;				///< Current time
time_t				last = 0;			///< Last time
struct tm			timeinfo;			///< Current time structure
char				s_timer[16];			///< Timer string buffer
char				s_top_msg[64];			///< Top message string buffer

extern float			stageTemp;
extern uint16_t			stageTime;
extern uint16_t			TimeWhirlPool;
extern uint32_t			TimeLeft;
extern uint32_t			TimeSpent;
extern uint32_t			SecsCount;
extern uint32_t			pumpTime;
extern uint32_t			TimeBrewing;
extern uint16_t			Steady;
esp_timer_handle_t		timerHandle;			///< Timer handler
extern bool			_NewMinute;
extern bool			_UseHLT;
extern bool			System_TimeOk;
extern const esp_app_desc_t	*app_desc;

static const char		*TAG = "task_tft";

#define SPI_BUS			TFT_HSPI_HOST			///< SPI bus for the TFT, TFT_VSPI_HOST or TFT_HSPI_HOST

extern int			Main_Screen;
extern int			Sub_Screen;
extern int			Old_Screen;
extern int			MLT_pin;
extern int			HLT_pin;
extern int			Pump_pin;
extern DS18B20_State		*ds18b20_state;
extern DRIVER_State		*driver_state;
extern JSON_log			*json_log;
extern SemaphoreHandle_t	xSemaphoreDS18B20;
extern SemaphoreHandle_t	xSemaphoreDriver;
extern SemaphoreHandle_t        xSemaphoreWiFi;
extern WIFI_State               *wifi_state;
extern double			Output;
extern sButton                  Buttons[MAXBUTTONS];

extern int			BoilPower, LastMashStep;
extern char			temp_buf[], logline[], strftime_buf[64];
extern bool			loop, CoolBeep, Resume, pumpRest, updateRuntime;
extern bool			NewMinute, TempReached;
extern uint8_t			MashState;
extern float			temp_MLT, MinMash, MaxMash;
extern uint32_t			power_MLT, power_HLT, counts;


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



/**
 * @brief Seconds timer callback.
 */
void TimerCallback(void *arg);


/***************************************************************************/



int init_tft_display(void)
{
    esp_err_t ret;
    esp_timer_create_args_t timerSecond = {
	.callback = &TimerCallback,
	.name = "SecondsTimer"
    };

    ESP_LOGD(TAG, "Initialize TFT");

    max_rdclock = 8000000;
    TFT_PinsInit();

    spi_lobo_bus_config_t buscfg = {
	.miso_io_num=PIN_NUM_MISO,		// set SPI MISO pin
	.mosi_io_num=PIN_NUM_MOSI,		// set SPI MOSI pin
	.sclk_io_num=PIN_NUM_CLK,		// set SPI CLK pin
	.quadwp_io_num=-1,
	.quadhd_io_num=-1,
	.max_transfer_sz = 6*1024,
    };
    spi_lobo_device_interface_config_t devcfg={
	.clock_speed_hz=8000000,                // Initial clock out at 8 MHz
	.mode=0,                                // SPI mode 0
	.spics_io_num=-1,                       // we will use external CS pin
	.spics_ext_io_num=PIN_NUM_CS,           // external CS pin
	.flags=LB_SPI_DEVICE_HALFDUPLEX,        // ALWAYS SET  to HALF DUPLEX MODE!! for display spi
    };
    spi_lobo_device_interface_config_t tsdevcfg={
	.clock_speed_hz=2500000,                //Clock out at 2.5 MHz
	.mode=0,                                //SPI mode 0
	.spics_io_num=PIN_NUM_TCS,              //Touch CS pin
	.spics_ext_io_num=-1,                   //Not using the external CS
    };

    ESP_LOGI(TAG, "TFT pins: miso=%d, mosi=%d, sck=%d, cs=%d", PIN_NUM_MISO, PIN_NUM_MOSI, PIN_NUM_CLK, PIN_NUM_CS);

    ret = spi_lobo_bus_add_device(SPI_BUS, &buscfg, &devcfg, &spi);
    assert(ret == ESP_OK);
    disp_spi = spi;

    // ==== Test select/deselect ====
    ret = spi_lobo_device_select(spi, 1);
    assert(ret == ESP_OK);
    ret = spi_lobo_device_deselect(spi);
    assert(ret == ESP_OK);

    ESP_LOGD(TAG, "SPI: attached display, spi bus: %d, speed: %u, bus uses native pins: %s", 
		    SPI_BUS, spi_lobo_get_speed(spi), spi_lobo_uses_native_pins(spi) ? "true" : "false");

    ESP_LOGI(TAG, "TS pins : miso=%d, mosi=%d, sck=%d, cs=%d", PIN_NUM_MISO, PIN_NUM_MOSI, PIN_NUM_CLK, PIN_NUM_TCS);

    ret=spi_lobo_bus_add_device(SPI_BUS, &buscfg, &tsdevcfg, &tsspi);
    assert(ret == ESP_OK);
    ts_spi = tsspi;

    // ==== Test select/deselect ====
    ret = spi_lobo_device_select(tsspi, 1);
    assert(ret == ESP_OK);
    ret = spi_lobo_device_deselect(tsspi);
    assert(ret == ESP_OK);

    ESP_LOGD(TAG, "SPI: attached TS device, spi bus: %d, speed: %u", SPI_BUS, spi_lobo_get_speed(tsspi));

    // ==== Initialize the Display ====
    TFT_display_init();

    // ---- Detect maximum read speed ----
    max_rdclock = find_rd_speed();

    // ==== Set SPI clock used for display operations ====
    spi_lobo_set_speed(spi, DEFAULT_SPI_CLOCK);
    ESP_LOGD(TAG, "SPI: Max rd speed: %u, changed speed to %u", max_rdclock, spi_lobo_get_speed(spi));

    font_rotate = 0;
    text_wrap = 0;
    font_transparent = 0;
    font_forceFixed = 0;
    gray_scale = 0;
    TFT_setGammaCurve(DEFAULT_GAMMA_CURVE);
    TFT_setRotation(LANDSCAPE);
    TFT_setFont(DEFAULT_FONT, NULL);
    TFT_resetclipwin();

    /*
     * Create a one second periodic timer.
     */
    ret = esp_timer_create(&timerSecond, &timerHandle);
    assert(ret == ESP_OK);
    ret = esp_timer_start_periodic(timerHandle, 1000000);
    assert(ret == ESP_OK);

    return ret;
}



void TimerCallback(void *arg) 
{
    TimeSpent++;
    SecsCount++;
    Steady++;
    TimeBrewing++;
    runtime.TimeBrewing++;
    if ((SecsCount % 60) == 0)
	_NewMinute = true;

    if (TimeLeft) {
	TimeLeft--;
	if (TimeLeft == 5) {
	    SoundPlay(SOUND_TimeOut);
	}
	if ((TimeLeft % 60) == 0) {
	    pumpTime++;
	}
    }
}



void TimerSet(uint32_t seconds)
{
    Steady = TimeSpent = SecsCount = 0;
    TimeLeft = seconds;
}



void TimerShow(uint32_t Time, int X, int Y)
{
    uint8_t		Hours   = (uint8_t)(Time / 3600);
    uint8_t		Minutes = (uint8_t)((Time % 3600) / 60);
    uint8_t		Seconds = (uint8_t)(Time % 60);
    char		msg[32];
    static uint32_t	_oldTime = 0;

    if (Time != _oldTime) {
    	_fg = TFT_GREEN;
    	TFT_setFont(FONT_7SEG, NULL);
    	set_7seg_font_atrib(12, 2, 1, TFT_DARKGREY);
    	snprintf(s_timer, 15, "%02d:%02d:%02d", Hours, Minutes, Seconds);
    	TFT_print(s_timer, X, Y);
	_oldTime = Time;
	snprintf(msg, 31, "{\"timer\":\"%s\"}", s_timer);
	ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
    }
}



void TopMessage(char *text)
{
    char	msg[96];

    snprintf(s_top_msg, 63, "%s", text);
    _fg = TFT_YELLOW;
    font_transparent = 1;
    TFT_setFont(DEJAVU24_FONT, NULL);
    TFT_fillRect(0, 0, 319, 25, TFT_NAVY);
    TFT_print(s_top_msg, CENTER, 2);
    font_transparent = 0;
    snprintf(msg, 95, "{\"top_msg\":\"%s\"}", s_top_msg);
    ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
}



void MLT_info(int x, int y, bool update)
{
    char	ctemp[16], csp[16], cpower[16], msg[96];
    static char	ltemp[16], lsp[16], lpower[16];
    bool	con, cpwr, cpump = false;
    static bool	lon, lpwr, lpump;

    _bg = (color_t){ 48, 48, 48 };
    _fg = TFT_WHITE;
    color_t _led  = { 31,255, 31};
    color_t _pump = {127,175,255};
    color_t _pwr  = {255, 47, 47};

    if (! update) {
	TFT_fillRect(x, y, 178, 90, _bg);
	TFT_drawRect(x, y, 178, 90, _fg);
	TFT_drawFastHLine(x, y + 21, 178, _fg);
	TFT_setFont(DEJAVU18_FONT, NULL);
	TFT_print((char *)"MLT", x + 67, y + 3);
    }

    if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
	sprintf(ctemp,  "%7.3f",    driver_state->mlt_pv);
	if (driver_state->mlt_mode) {
	    sprintf(csp,    "%6.2f sp", driver_state->mlt_sp);
	} else {
	    csp[0] = '\0';
	}
	if ((driver_state->mlt_mode == MLT_MODE_BANG) || (driver_state->mlt_mode == MLT_MODE_PID) || (driver_state->mlt_mode == MLT_MODE_EXT)) {
	    sprintf(cpower, "%3d%%",    driver_state->mlt_power);
	} else {
	    cpower[0] = '\0';
	}
	xSemaphoreGive(xSemaphoreDriver);
    }

    con = (MLT_pin) ? true : false;
    if ((con != lon) || (! update)) {
	if (con) {
	    TFT_fillCircle(x + 166, y + 11, 8, _led);
	} else {
	    TFT_fillCircle(x + 166, y + 11, 8, _bg);
	}
	lon = con;
	snprintf(msg, 95, "{\"mlt_led\":\"%s\"}", con ? "1":"0");
	ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
    }

    cpump = (Pump_pin) ? true : false;
    if ((cpump != lpump) || (! update)) {
	if (cpump) {
	    TFT_fillCircle(x + 11, y + 11, 8, _pump);
	} else {
	    TFT_fillCircle(x + 11, y + 11, 8, _bg);
	}
	lpump = cpump;
	snprintf(msg, 95, "{\"pump_led\":\"%s\"}", cpump ? "1":"0");
	ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
    }

    if (equipment.SSR2 == SSR2_ON_IDLE) {
    	cpwr = (HLT_pin) ? true : false;
        if ((cpwr != lpwr) || (! update)) {
	    if (cpwr) {
		TFT_fillCircle(x + 126, y + 11, 8, _pwr);
	    } else {
		TFT_fillCircle(x + 126, y + 11, 8, _bg);
	    }
	    lpwr = cpwr;
	    snprintf(msg, 95, "{\"hlt_led\":\"%s\"}", cpwr ? "1":"0");
	    ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
	}
    }

    if (strcmp(ctemp, ltemp) || (! update)) {
    	TFT_setFont(USER_FONT, "/spiffs/fonts/Grotesk24x48.fon");
    	TFT_print(ctemp, x + 5, y + 23);
	strncpy(ltemp, ctemp, 16);
	snprintf(msg, 95, "{\"mlt_pv\":\"%s\"}", ctemp);
	ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
    }

    TFT_setFont(DEJAVU18_FONT, NULL);
    if (strcmp(csp, lsp) || (! update)) {
    	TFT_clearStringRect(x + 5, y + 70, (char *)"123.45 sp");
    	TFT_print(csp, x + 5, y + 70);
	strncpy(lsp, csp, 16);
	snprintf(msg, 95, "{\"mlt_sp\":\"%s\"}", csp);
	ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
    }
    if (strcmp(cpower, lpower) || (! update)) {
    	TFT_clearStringRect(x + 120, y + 70, (char *)"100%");
    	TFT_print(cpower, x + 120, y + 70);
	strncpy(lpower, cpower, 16);
	snprintf(msg, 95, "{\"mlt_power\":\"%s\"}", cpower);
	ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
    }
}



void HLT_info(int x, int y, bool update, bool small)
{
    char        ctemp[16], csp[16], cpower[16], msg[96];
    static char	ltemp[16], lsp[16], lpower[16];
    bool        con = false;
    static bool lon;
    uint8_t	H;

    _bg = (color_t){ 63, 63, 64 };
    _fg = TFT_YELLOW;
    color_t _led = {255, 47, 47};
    H = (small) ? 70 : 90;

    if (! update) {
	TFT_fillRect(x, y, 178, H, _bg);
	TFT_drawRect(x, y, 178, H, _fg);
	TFT_drawFastHLine(x, y + 21, 178, _fg);
	TFT_setFont(DEJAVU18_FONT, NULL);
	TFT_print((char *)"HLT", x + 67, y + 3);
    }

    if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
	sprintf(ctemp, "%7.3f", driver_state->hlt_pv);
	if (driver_state->hlt_mode == HLT_MODE_BANG) {
	    sprintf(cpower, "%3d%%", driver_state->hlt_power);
	} else {
	    cpower[0] = '\0';
	}
	if (driver_state->hlt_mode == HLT_MODE_BANG || driver_state->hlt_mode == HLT_MODE_OFF) {
	    sprintf(csp, "%6.2f sp", driver_state->hlt_sp);
	} else {
	    csp[0] = '\0';
	}
	xSemaphoreGive(xSemaphoreDriver);
    }

    con = (HLT_pin) ? true : false;
    if ((con != lon) || (! update)) {
	if (con) {
	    TFT_fillCircle(x + 166, y + 11, 8, _led);
	} else {
	    TFT_fillCircle(x + 166, y + 11, 8, _bg);
	}
	lon = con;
	snprintf(msg, 95, "{\"hlt_led\":\"%s\"}", con ? "1":"0");
	ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
    }

    if (strcmp(ltemp, ctemp) || (! update)) {
	if (small) {
    	    TFT_setFont(USER_FONT, "/spiffs/fonts/DejaVuSans24.fon");
    	    TFT_print(ctemp, x + 40,  y + 25);
	} else {
	    TFT_setFont(USER_FONT, "/spiffs/fonts/Grotesk24x48.fon");
	    TFT_print(ctemp, x + 5,  y + 23);
	}
	strncpy(ltemp, ctemp, 16);
	snprintf(msg, 95, "{\"hlt_pv\":\"%s\"}", ctemp);
	ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
    }

    H = (small) ? 50 : 70;
    TFT_setFont(DEJAVU18_FONT, NULL);
    if (strcmp(csp, lsp) || (! update)) {
	TFT_clearStringRect(x + 5, y + H, (char *)"123.45 sp");
	TFT_print(csp, x + 5, y + H);
	strncpy(lsp, csp, 16);
	snprintf(msg, 95, "{\"hlt_sp\":\"%s\"}", csp);
	ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
    }
    if (strcmp(cpower, lpower) || (! update)) {
	TFT_clearStringRect(x + 120, y + H, (char *)"100%");
	TFT_print(cpower, x + 120, y + H);
	strncpy(lpower, cpower, 16);
	snprintf(msg, 95, "{\"hlt_power\":\"%s\"}", cpower);
	ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
    }
}



void update_json(void)
{
    int Hour   = (TimeBrewing / 3600); 
    int Minute = ((TimeBrewing % 3600) / 60);

    if (counts == 0)
	counts = 1;	// Prevent division by zero.

    if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
	snprintf(json_log->time, 11, "%02d:%02d", Hour, Minute);
	json_log->mlt_sp = driver_state->mlt_sp;
	json_log->mlt_pv = driver_state->mlt_pv;
	json_log->mlt_power = power_MLT / counts;
	json_log->mlt_tempreached = TempReached ? 1:0;
	json_log->pump_run = driver_state->pump_run;
	json_log->hlt_sp = driver_state->hlt_sp;
	json_log->hlt_pv = driver_state->hlt_pv;
	json_log->hlt_power = power_HLT / counts;
	json_log->event[0] = '\0';
	xSemaphoreGive(xSemaphoreDriver);
    }
}



void TFTstartWS(int client)
{
    char	msg[1024];
    char	mlt_sp[16], mlt_power[16], hlt_sp[16], hlt_power[16];

    if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
	if (driver_state->mlt_sp) {
	    snprintf(mlt_sp, 15, "%6.2f sp", driver_state->mlt_sp);
	    snprintf(mlt_power, 15, "%3d%%", driver_state->mlt_power);
	} else {
	    mlt_sp[0] = '\0';
	    mlt_power[0] = '\0';
	}
	if (driver_state->hlt_sp) {
	    snprintf(hlt_sp, 15, "%6.2f sp", driver_state->hlt_sp);
	    snprintf(hlt_power, 15, "%3d%%", driver_state->hlt_power);
	} else {
	    hlt_sp[0] = '\0';
	    hlt_power[0] = '\0';
	}
    	snprintf(msg, 1023, "{\"main\":\"%d\",\"sub\":\"%d\",\"mlt_led\":\"%d\",\"mlt_pv\":\"%7.3f\",\"mlt_sp\":\"%s\",\"mlt_power\":\"%s\"" \
		    ",\"pump_led\":\"%d\",\"hlt_led\":\"%d\",\"hlt_pv\":\"%7.3f\",\"hlt_sp\":\"%s\",\"hlt_power\":\"%s\"" \
		    ",\"timer\":\"%s\",\"top_msg\":\"%s\"}",
		    Main_Screen, Sub_Screen, (MLT_pin) ? 1:0, driver_state->mlt_pv, mlt_sp, mlt_power,
		    (Pump_pin) ? 1:0, (HLT_pin) ? 1:0, driver_state->hlt_pv, hlt_sp, hlt_power,
		    s_timer, s_top_msg);

    	xSemaphoreGive(xSemaphoreDriver);
	ws_server_send_text_client(client, msg, strlen(msg));
    }

}



void task_tft(void *pvParameter)
{
    char	msg[96];

    ESP_LOGI(TAG, "Start TFT/Touch");

    /*
     * Task loop. Read touchscreen events.
     */
    while (1) {
	/*
	 * Build new screen.
	 */
	updateRuntime = false;
	if (_NewMinute) {
	    _NewMinute = false;
	    NewMinute = true;
	}
startover:

	/*
	 * Timekeeping.
	 * In the WiFi task sntp is started and sets System_TimeOk if the
	 * clock is synced once.
	 */
	time(&now);
	localtime_r(&now, &timeinfo);

	if (Old_Screen != Main_Screen) {

	    if ((Main_Screen == MAIN_MODE_FREE) && ((config.ts_xleft == 0) || (config.ts_ybottom == 0))) {
		Main_Screen = MAIN_MODE_CALIBRATION;
	    }

	    /*
	     * With each screenchange, remove the timer too.
	     */
	    Sub_Screen = 0;
	    snprintf(msg, 95, "{\"main\":\"%d\",\"sub\":\"%d\",\"timer\":\"\"}", Main_Screen, Sub_Screen);
	    ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));

	    ESP_LOGD(TAG, "Change screen %d to %d", Old_Screen, Main_Screen);
	    _bg = TFT_BLACK;
	    TFT_fillScreen(_bg);
	    TFT_resetclipwin();
	    Buttons_Clear();
	    Old_Screen = Main_Screen;

	    switch (Main_Screen) {
		case MAIN_MODE_FREE:
			TopMessage((char *)"Hoofdmenu");
			MLT_info(71, 26, false);
			if ((equipment.SSR2 == SSR2_HLT_SHARE) || (equipment.SSR2 == SSR2_HLT_IND)) {
			    HLT_info(71,150, false, false);
			}
			Buttons_Add(  5,  26, 60, 40, (char *)"Hand",  0);
			Buttons_Add(255,  26, 60, 40, (char *)"Auto",  1);
			Buttons_Add(  5, 200, 60, 40, (char *)"Info",  2);
			Buttons_Add(255, 200, 60, 40, (char *)"Tools", 3);
			Buttons_Show();
			break;

		case MAIN_MODE_CALIBRATION:
			Calibration_Init();
			break;

		case MAIN_INFO:
			sprintf(temp_buf, "BrewBoard %s", app_desc->version);
			TopMessage(temp_buf);
			_fg = TFT_YELLOW;
			TFT_setFont(UBUNTU16_FONT, NULL);
			TFT_print((char *)"Written by Michiel Broek (C) 2018-2024\r\n\n", 0, 50);
			//         -------------------------------------
			_fg = TFT_ORANGE;
			TFT_print((char *)"Parts are written by Chris Morgan,\r\n", 0, LASTY);
			TFT_print((char *)"Brett Beauregard, Blake Felt, LoBo,\r\n", 0, LASTY);
			TFT_print((char *)"and David Antliff.\r\n", 0, LASTY);
			ShowInteger(1,140, (char *)"Free memory", (char *)" bytes", esp_get_free_heap_size());
			ShowText(1,158, (char *)"IDF version", (char *)esp_get_idf_version());
			Buttons_Add(130, 200, 60, 40, (char *)"Ok", 0);
			Buttons[0].dark = true;
			Buttons_Show();
			break;

		case MAIN_TOOLS:
			TopMessage((char *)"Tools menu");
			Buttons_Add( 20, 40,120, 40, (char *)"Setup", 0);
			Buttons_Add( 20,120,120, 40, (char *)"Bestanden", 1);
			Buttons_Add(180, 40,120, 40, (char *)"Recepten", 2);
			Buttons_Add(180,120,120, 40, (char *)"Updates", 3);
			Buttons_Add(130, 200, 60, 40, (char *)"Ok", 4);
			Buttons[4].dark = true;
			Buttons_Show();
			break;

		case MAIN_TOOLS_SETUP:
		case MAIN_TOOLS_SETUP_CONFIG:
		case MAIN_TOOLS_SETUP_CO_EDIT:
		case MAIN_TOOLS_SETUP_EQUIPMENT:
		case MAIN_TOOLS_SETUP_EQ_EDIT:
		case MAIN_TOOLS_SETUP_CALIBRATION:
			Setup_Init();
			break;

		case MAIN_TOOLS_SETUP_WIFI:
		case MAIN_TOOLS_SETUP_WIFI_CUR:
		case MAIN_TOOLS_SETUP_WIFI_CON:
		case MAIN_TOOLS_SETUP_WIFI_NEW:
			if (WiFi_Init())
			    goto startover;
			break;

		case MAIN_TOOLS_RECIPE:
		case MAIN_TOOLS_RECIPE_EDIT:
			Recipes_Init();
			break;

		case MAIN_TOOLS_FILES:
		case MAIN_TOOLS_FILES_DIR:
		case MAIN_TOOLS_FILES_RESTORE:
		case MAIN_TOOLS_FILES_BACKUP:
			Files_Init();
			break;

		case MAIN_TOOLS_UPDATES:
			Updates_Init();
			break;

		case MAIN_AUTO_INIT1:
		case MAIN_AUTO_INIT2:
		case MAIN_AUTO_HEATUP:
		case MAIN_AUTO_MASH:
		case MAIN_AUTO_TOBOIL:
		case MAIN_AUTO_BOILING:
		case MAIN_AUTO_COOLING_H:
		case MAIN_AUTO_COOLING_M:
		case MAIN_AUTO_COOLING_C:
		case MAIN_AUTO_WHIRLPOOL9:
		case MAIN_AUTO_WHIRLPOOL7:
		case MAIN_AUTO_WHIRLPOOL6:
		case MAIN_AUTO_WHIRLPOOL2:
		case MAIN_AUTO_DONE:
		case MAIN_AUTO_ABORT:
			if (Automation_Init())
			    goto startover;
			break;

		case MAIN_MANUAL_INIT:
		case MAIN_MANUAL_MAIN:
			if (Manual_Init())
			    goto startover;
			break;

		default:
			break;
	    }
	}

	/*
	 * Update screen
	 */
	switch (Main_Screen) {
	    case MAIN_MODE_FREE:
		MLT_info(71, 26, true);
		if ((equipment.SSR2 == SSR2_HLT_SHARE) || (equipment.SSR2 == SSR2_HLT_IND)) {
		    HLT_info(71, 150, true, false);
		}
		switch (Buttons_Scan()) {
		    case 0:	Main_Screen = MAIN_MANUAL_INIT;	break;
		    case 1:	Main_Screen = MAIN_AUTO_INIT1;	break;
		    case 2:	Main_Screen = MAIN_INFO;	break;
		    case 3:	Main_Screen = MAIN_TOOLS;	break;
		    default:	break;
		}
		if (System_TimeOk && (now != last)) {
		    last = now;
		    //strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
		    strftime(strftime_buf, sizeof(strftime_buf), "%a %e %b %Y %T", &timeinfo);
		    _bg = TFT_BLACK;
		    _fg = TFT_ORANGE;
		    TFT_setFont(DEJAVU18_FONT, NULL);
		    snprintf(msg, 95, " %s ", strftime_buf);
		    TFT_print(msg, CENTER, 125);
		    snprintf(msg, 95, "{\"timer\":\"%s\"}", strftime_buf);	// Fix string termination and only send once/second.
		    ws_server_send_text_clients((char *)"/ws", msg, strlen(msg));
		}
		break;

	    case MAIN_MODE_CALIBRATION:
		Calibration_Loop();
		Main_Screen = MAIN_MODE_FREE;
		break;

	    case MAIN_TOOLS:
		switch (Buttons_Scan()) {
		    case 0:	Main_Screen = MAIN_TOOLS_SETUP;		break;
		    case 1:	Main_Screen = MAIN_TOOLS_FILES;		break;
		    case 2:	Main_Screen = MAIN_TOOLS_RECIPE;	break;
		    case 3:	Main_Screen = MAIN_TOOLS_UPDATES;	break;
		    case 4:	Main_Screen = MAIN_MODE_FREE;		break;
		    default:	break;
		}
		break;

	    case MAIN_TOOLS_SETUP:
	    case MAIN_TOOLS_SETUP_CONFIG:
	    case MAIN_TOOLS_SETUP_CO_EDIT:
	    case MAIN_TOOLS_SETUP_EQUIPMENT:
	    case MAIN_TOOLS_SETUP_EQ_EDIT:
	    case MAIN_TOOLS_SETUP_CALIBRATION:
		Setup_Loop();
		break;

	    case MAIN_TOOLS_SETUP_WIFI:
	    case MAIN_TOOLS_SETUP_WIFI_CUR:
	    case MAIN_TOOLS_SETUP_WIFI_CON:
	    case MAIN_TOOLS_SETUP_WIFI_NEW:
		if (WiFi_Loop())
		    goto startover;
		break;

	    case MAIN_TOOLS_RECIPE:
	    case MAIN_TOOLS_RECIPE_EDIT:
		Recipes_Loop();
		break;

	    case MAIN_TOOLS_FILES:
	    case MAIN_TOOLS_FILES_DIR:
	    case MAIN_TOOLS_FILES_RESTORE:
	    case MAIN_TOOLS_FILES_BACKUP:
		Files_Loop();
		break;

	    case MAIN_TOOLS_UPDATES:
		Updates_Loop();
		break;

	    case MAIN_INFO:
		if (Buttons_Scan() == 0) {
		    Main_Screen = MAIN_MODE_FREE;
		}
		break;

	    case MAIN_AUTO_INIT1:
	    case MAIN_AUTO_INIT2:
	    case MAIN_AUTO_HEATUP:
	    case MAIN_AUTO_MASH:
	    case MAIN_AUTO_TOBOIL:
	    case MAIN_AUTO_BOILING:
	    case MAIN_AUTO_COOLING_H:
	    case MAIN_AUTO_COOLING_M:
	    case MAIN_AUTO_COOLING_C:
	    case MAIN_AUTO_WHIRLPOOL9:
	    case MAIN_AUTO_WHIRLPOOL7:
	    case MAIN_AUTO_WHIRLPOOL6:
	    case MAIN_AUTO_WHIRLPOOL2:
	    case MAIN_AUTO_DONE:
	    case MAIN_AUTO_ABORT:
		if (Automation_Loop())
		    goto startover;
		break;

	    case MAIN_MANUAL_INIT:
	    case MAIN_MANUAL_MAIN:
		if (Manual_Loop())
		    goto startover;
		break;

	    default:
		break;
	}

	if (updateRuntime) {
	    write_runtime();
	}

	/*
	 * Count power average during brewing.
	 */
	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;
		counts++;
		xSemaphoreGive(xSemaphoreDriver);
	    }
	}
	
    	if (NewMinute) {
	    /*
	     * Brew logging.
	     */
	    if ((Main_Screen >= MAIN_AUTO_MASH) && (Main_Screen < MAIN_AUTO_DONE)) {
		update_json();
		log_json();
		power_MLT = power_HLT = counts = 0;
	    }
	}

	NewMinute = false;
	vTaskDelay(50 / portTICK_PERIOD_MS);
    }
}

mercurial