main/task_user.c

Tue, 05 Nov 2019 11:31:59 +0100

author
Michiel Broek <mbroek@mbse.eu>
date
Tue, 05 Nov 2019 11:31:59 +0100
changeset 21
043ae27633f8
child 22
cceb36fd3a2a
permissions
-rw-r--r--

Moved the user interface into a separate task. Added a real seconds timer to this task.

/**
 * @file task_user.c
 * @brief co2meter project.
 */

#include "config.h"

static const char *TAG = "task_user";


EventGroupHandle_t			xEventGroupUser;		///< Events User task
esp_timer_handle_t			timerHandle;			///< Seconds timer
uint32_t				SecsCount = 0;			///< Seconds counter
uint32_t				UserTimer = 0;                  ///< User inactive timeout
int					Main_Loop2 = -1;		///< Effective menu
int					New_Loop2 = ML2_INIT;		///< New menu
int					SubMenu = 0;			///< Submenu number
u8g2_t					u8g2;				///< A structure which will contain all the data for one display
rotary_encoder_info_t			rinfo = { 0 };			///< Rotary encoder record
rotary_encoder_event_t			event = { 0 };
QueueHandle_t				event_queue;
static int				PushDuration = 0;		///< Duration of the pushed button

extern const esp_app_desc_t		*app_desc;
extern unit_t				units[3];			///< Pressure test units
extern SemaphoreHandle_t		xSemaphoreUnits;		///< Units lock semaphore
extern DS18B20_State            	*ds18b20_state;         	///< DS18B20 state
extern SemaphoreHandle_t        	xSemaphoreDS18B20;      	///< DS18B20 lock semaphore
extern ADC_State                	*adc_state;             	///< ADC state
extern SemaphoreHandle_t        	xSemaphoreADC;          	///< ADC lock semaphore
extern WIFI_State			*wifi_state;			///< WiFi state
extern int				count_pub;			///< Published MQTT messages in transit
static xQueueHandle			gpio_evt_queue = NULL;		///< Rotary pushbutton queue
extern int				Main_Loop1;			///< Main measure loop



const int TASK_USER_COLD = BIT0;					///< System cold start
const int TASK_USER_WAKEUP = BIT1;					///< System wakeup from deepsleep
const int TASK_USER_BUSY = BIT2;					///< User interface is busy doing something.
const int TASK_USER_REFRESH = BIT3;					///< Refresh measurement results



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


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



void TimerCallback(void *arg)
{
    SecsCount++;
    if ((SecsCount % 60) == 0) {
	if (Main_Loop1 == ML1_DONE)
	    Main_Loop1 = ML1_INIT;
    }

    if (UserTimer == 1) {
	ESP_LOGI(TAG, "User inactivity timeout");
	xEventGroupClearBits(xEventGroupUser, TASK_USER_BUSY);
        u8g2_SetPowerSave(&u8g2, 1);
    }
    if (UserTimer) {
	UserTimer--;
    }
}



void user_cold()
{
    xEventGroupSetBits(xEventGroupUser, TASK_USER_COLD);
}



void user_wakeup()
{
    xEventGroupSetBits(xEventGroupUser, TASK_USER_WAKEUP);
}



void user_refresh()
{
    xEventGroupSetBits(xEventGroupUser, TASK_USER_REFRESH);
}



bool user_busy(void)
{
    if (xEventGroupGetBits(xEventGroupUser) & TASK_USER_BUSY)
	return true;
    return false;
}




/**
 * @brief Get a keyboard character from the rotary encoder.
 * @param curkey The referenced value if the key being edited. NOTE, start at 0 for a new char??
 * @param type The edittype, all values, integer or float.
 * @param x The x position on the screen.
 * @param y The y position on the screen.
 * @return 1 if short keypress, meaning enter key. 2 if long press, enter key and editing is ready.
 */
int getkey(int *curkey, int type, int x, int y)
{
    int	key = *curkey;
    int	rc = 0;

    u8g2_DrawHLine(&u8g2, x, y+3, 12);
    u8g2_SendBuffer(&u8g2);

    for (;;) {
    	if (xQueueReceive(event_queue, &event, 100 / portTICK_PERIOD_MS) == pdTRUE) {
	    UserTimer = INACTIVITY;
	    if (event.state.position != 0) {

		u8g2_SetDrawColor(&u8g2, 0);
		u8g2_DrawGlyph(&u8g2, x, y, key);
		u8g2_SetDrawColor(&u8g2, 1);
		u8g2_SendBuffer(&u8g2);

	    	if (event.state.position > 0) {
		    if (key == 126)
		    	key = 171;
		    else if (key < 126)
		    	key++;
	    	} else if (event.state.position < 0) {
		    if (key == 171)
		    	key = 126;
		    else if (key > 32)
		    	key--;
		}

		ESP_ERROR_CHECK(rotary_encoder_reset(&rinfo));
	    	u8g2_DrawGlyph(&u8g2, x, y, key);
	    	u8g2_SendBuffer(&u8g2);
	    }
    	} else {
	    if (PushDuration) {
		if (PushDuration > 500)
		    rc = 2;
		else
		    rc = 1;
	    	PushDuration = 0;
		break;
	    }
    	}
    }
    u8g2_SetDrawColor(&u8g2, 0);
    u8g2_DrawHLine(&u8g2, x, y+3, 12);
    u8g2_SetDrawColor(&u8g2, 1);
    u8g2_SendBuffer(&u8g2);

    *curkey = key;
    return rc;
}



/**
 * @brief Editor using the rotary switch.
 * @param label The label of the edit field.
 * @param txt The string to edit.
 * @param errmsg The error message if needed.
 * @param len The maximum length for the string.
 * @param type The edit type.
 */
void rotary_editer(char *label, char *txt, char *errmsg, int len, int type)
{
    char        buf[65];
    int		key, x, y, rc;

    u8g2_ClearBuffer(&u8g2);
    u8g2_DrawHLine(&u8g2, 0, 14, 128);
    u8g2_DrawHLine(&u8g2, 0, 49, 128);
    u8g2_SetFont(&u8g2, u8g2_font_t0_15_tf);
    sprintf(buf, "Edit %s", label);
    u8g2_DrawStr(&u8g2,0,12,buf);

    if (strlen(errmsg)) {
	u8g2_SetFont(&u8g2, u8g2_font_t0_12b_tf);
	u8g2_DrawStr(&u8g2, 0, 61, errmsg);
    }
    u8g2_SetFont(&u8g2, u8g2_font_t0_12_tf);
    y = 36;
    u8g2_DrawStr(&u8g2, 0, y, txt);
    u8g2_SendBuffer(&u8g2);

    for (;;) {
	x = u8g2_GetUTF8Width(&u8g2, txt);
	key = 'a';
	rc = getkey(&key, type, x, y);
	if (rc == 1) {
	    if (key >= 32 && key <= 126 && strlen(txt) < len) {
		txt[strlen(txt) + 1] = '\0';
                txt[strlen(txt)] = key;
	    } else if (key == 171 && strlen(txt)) {
		// delete key
		txt[strlen(txt) - 1] = '\0';
	    }
printf("strlen %d  x %d  key %d\n", strlen(txt), x, key);
	} else if (rc == 2) {
	    break;
	}
    }
}



/**
 * @brief Write a menu line on the display.
 * @param bright Display the line with a bold or normal font.
 * @param x The horizontal start position of the line.
 * @param y The vertical bottom of the line position.
 * @param format The formatted data to display.
 */
void menu_line(int bright, int x, int y, const char *format, ...)
{
    char        buf[65];
    va_list     va_ptr;

    if (bright)
        u8g2_SetFont(&u8g2, u8g2_font_t0_12b_tr);
    else
        u8g2_SetFont(&u8g2, u8g2_font_t0_12_tr);

    va_start(va_ptr, format);
    vsnprintf(buf, 65, format, va_ptr);
    va_end(va_ptr);

    u8g2_DrawStr(&u8g2, x, y, buf);
}



/**
 * @brief Clear the display and prepare the top of the display.
 * @format The formatted data to display at the top.
 */
void screen_top(const char *format, ...)
{
    char        buf[65];
    va_list     va_ptr;

    va_start(va_ptr, format);
    vsnprintf(buf, 65, format, va_ptr);
    va_end(va_ptr);

    u8g2_ClearBuffer(&u8g2);
    u8g2_DrawHLine(&u8g2, 0, 14, 128);

    u8g2_SetFont(&u8g2, u8g2_font_t0_15_tr);
    u8g2_uint_t w = u8g2_GetStrWidth(&u8g2, buf);
    u8g2_DrawStr(&u8g2, (128 - w) / 2,12, buf);
}



/**
 * @brief The splash screen shown during cold boot or user wakeup.
 */
void screen_splash()
{
    screen_top("CO2 meter %s", app_desc->version);

    u8g2_SetFont(&u8g2, u8g2_font_t0_22b_tf);
    u8g2_uint_t w = u8g2_GetUTF8Width(&u8g2, "START");
    u8g2_DrawUTF8(&u8g2, (128 - w) / 2,50, "START");

    u8g2_SendBuffer(&u8g2);
    u8g2_SetPowerSave(&u8g2, 0); // wake up display
}



/**
 * @brief The main overview screen.
 */
void screen_main()
{
    char	buf[65];
    int		i;

    screen_top("CO2 meter %s", app_desc->version);

    if (xSemaphoreTake(xSemaphoreUnits, 25) == pdTRUE) {

    	u8g2_SetFont(&u8g2, u8g2_font_t0_22b_tf);
    	sprintf(buf, "%.1f °C", units[0].temperature / 1000.0);
    	u8g2_uint_t w = u8g2_GetUTF8Width(&u8g2, buf);
    	u8g2_DrawUTF8(&u8g2, (128 - w) / 2,40, buf);
    	u8g2_SetFont(&u8g2, u8g2_font_t0_18b_tf);

	for (i = 0; i < 3; i++) {
    	    sprintf(buf, "%.1f", units[i].pressure / 1000.0);
    	    w = u8g2_GetUTF8Width(&u8g2, buf);
    	    u8g2_DrawUTF8(&u8g2, ((42 - w) / 2) + i * 43,63, buf);
	}
	xSemaphoreGive(xSemaphoreUnits);
    }
    u8g2_SendBuffer(&u8g2);
    u8g2_SetPowerSave(&u8g2, 0); // wake up display
}



/**
 * @brief The unit display screen.
 * @param no The unit index number.
 */
void screen_unit(int no)
{
    char        buf[65];

    if (xSemaphoreTake(xSemaphoreUnits, 25) == pdTRUE) {

	screen_top("Unit %d %s", no + 1, units[no].mode ? "On":"Off");

    	u8g2_SetFont(&u8g2, u8g2_font_t0_22b_tf);
    	sprintf(buf, "%.1f °C", units[no].temperature / 1000.0);
    	u8g2_uint_t w = u8g2_GetUTF8Width(&u8g2, buf);
    	u8g2_DrawUTF8(&u8g2, (128 - w) / 2,40, buf);

    	sprintf(buf, "%.2f bar", units[no].pressure / 1000.0);
    	w = u8g2_GetUTF8Width(&u8g2, buf);
    	u8g2_DrawUTF8(&u8g2, (128 - w) / 2,63, buf);

	xSemaphoreGive(xSemaphoreUnits);
    }
    u8g2_SendBuffer(&u8g2);
    u8g2_SetPowerSave(&u8g2, 0); // wake up display
}



/**
 * @brief The unit zero setup screen.
 * @param no The unit index number.
 * @param sub The submenu index number.
 */
void screen_unit_zero(int no, int sub)
{
    screen_top("Unit %d zero mV", no + 1);
    menu_line(       0, 2, 25, "Current   %d", units[no].pressure_zero);
    menu_line(sub == 0, 2, 37, "New value %d", units[no].pressure_voltage / (adc_state->Batt_voltage / 1000));
    menu_line(sub == 1, 2, 49, "Return");
printf("current %d  p_voltage %d  batt %d\n", units[no].pressure_zero, units[no].pressure_voltage, adc_state->Batt_voltage);
    u8g2_SendBuffer(&u8g2);
    u8g2_SetPowerSave(&u8g2, 0);
}



/**
 * @brief The unit setup screen.
 * @param no The unit index number.
 * @param sub The submenu index number.
 */
void screen_unit_setup(int no, int sub)
{
    screen_top("Unit %d setup", no + 1);
    menu_line(sub == 0, 2, 25, "Mode    %s", units[no].mode ? "ON":"OFF");
    menu_line(sub == 1, 2, 37, "Zero mV %d", units[no].pressure_zero);
    menu_line(sub == 2, 2, 49, "DS18B20 %s", units[no].temperature_rom_code);
    menu_line(sub == 3, 2, 61, "Return");
    u8g2_SendBuffer(&u8g2);
    u8g2_SetPowerSave(&u8g2, 0);
}



void screen_wifi()
{
    char	buf[65];

    screen_top("WiFi Status");
    snprintf(buf, 65, "SSID %s", wifi_state->STA_ssid);
    u8g2_DrawStr(&u8g2, 1, 28, buf);
    snprintf(buf, 65, "Online %s", wifi_state->STA_online ? "Yes":"No");
    u8g2_DrawStr(&u8g2, 1, 43, buf);
    snprintf(buf, 65, "RSSI %d", wifi_state->STA_rssi);
    u8g2_DrawStr(&u8g2, 1, 59, buf);
    u8g2_SendBuffer(&u8g2);
    u8g2_SetPowerSave(&u8g2, 0);
}



void screen_wifi_setup(int sub)
{
    screen_top("WiFi Setup");
    menu_line(sub == 0, 2, 25, "Connect");
    menu_line(sub == 1, 2, 37, "New");
    menu_line(sub == 2, 2, 49, "Delete");
    menu_line(sub == 3, 2, 61, "Return");
    u8g2_SendBuffer(&u8g2);
    u8g2_SetPowerSave(&u8g2, 0);
}



void screen_network()
{
    screen_top("Network Status");
    menu_line(0, 1, 25, "IP   %s", wifi_state->STA_ip);
    menu_line(0, 1, 37, "Mask %s", wifi_state->STA_nm);
    menu_line(0, 1, 49, "GW   %s", wifi_state->STA_gw);
    menu_line(0, 1, 61, "Name %s", config.hostname);
    u8g2_SendBuffer(&u8g2);
    u8g2_SetPowerSave(&u8g2, 0);
}



void screen_mqtt()
{
    screen_top("MQTT Status");
    menu_line(0, 1, 25, "serv %s", config.mqtt_server);
    menu_line(0, 1, 37, "port %d", config.mqtt_port);
    menu_line(0, 1, 49, "user %s", config.mqtt_user);
    u8g2_SendBuffer(&u8g2);
    u8g2_SetPowerSave(&u8g2, 0);
}



void screen_update()
{
    screen_top("Update firmware");
    menu_line(0, 1, 43, "Push to update");
    u8g2_SendBuffer(&u8g2);
    u8g2_SetPowerSave(&u8g2, 0);
}



/**
 * @brief Fatal messages on the screen.
 * @param e1 The first line.
 * @param e2 The second line.
 */
void screen_fatal(char *e1, char *e2)
{
    u8g2_SetFont(&u8g2, u8g2_font_t0_15_tr);
    u8g2_DrawStr(&u8g2,2,12,e1);
    u8g2_DrawStr(&u8g2,2,24,e2);
    u8g2_SendBuffer(&u8g2);
    u8g2_SetPowerSave(&u8g2, 0);
}



/**
 * @brief Interrupt service routine for the rotary pushbutton.
 */
static void IRAM_ATTR gpio_isr_handler(void* arg)
{
    uint32_t gpio_num = (uint32_t) arg;
    xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
}



/**
 * @brief GPIO queue task. See if there is a rotary pushbutton event on the queue.
 */
static void gpio_task(void* arg)
{
    uint32_t		io_num;
    static int64_t	pushed = 0;

    for(;;) {
        if (xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {
	    if (io_num == ROT_ENC_SW_GPIO) {
		if (gpio_get_level(io_num) == 0) {
		    pushed = esp_timer_get_time();
		    PushDuration = 0;
		} else if (gpio_get_level(io_num) == 1) {
		    PushDuration = (esp_timer_get_time() - pushed) / 1000;
		    ESP_LOGI(TAG, "GPIO rotary button intr, val: %d time: %d", gpio_get_level(io_num), PushDuration);
		}
	    } else {
            	ESP_LOGE(TAG, "GPIO[%d] unknown intr, val: %d", io_num, gpio_get_level(io_num));
	    }
	    UserTimer = INACTIVITY;
        }
    }
}



/**
 * @brief Select new menu number on a postitive or negative rotary position.
 * @param pos The new position, positive, negative or zero.
 * @param next_menu The selected menu if rotated clockwise.
 * @param prev_menu The selected menu if rotated counter-clockwise.
 */
static void rotate_to_menu(rotary_encoder_position_t pos, int next_menu, int prev_menu)
{
    if (pos > 0)
	New_Loop2 = next_menu;
    else if (pos < 0)
	New_Loop2 = prev_menu;
    ESP_ERROR_CHECK(rotary_encoder_reset(&rinfo));
}



/**
 * @brief Rotate subscreens numbers.
 * @param pos The new position, positive, negative or zero.
 * @param min The lowest number. If already at the lowest, select the highest.
 * @param max The highest number. If already at the highest, select the lowest.
 * @param cursub The subscreen number by reference. This is updated with the new number.
 * @return Returns true if a new number is selected, false if nothing changed.
 */
bool rotate_to_sub(rotary_encoder_position_t pos, int min, int max, int *cursub)
{
   int	sub = *cursub;
   bool	rc = false;

   if (pos > 0) {
	if (sub < max)
	    sub++;
	else
	    sub = min;
	ESP_ERROR_CHECK(rotary_encoder_reset(&rinfo));
	rc = true;
    } else if (pos < 0) {
	if (sub > min)
	    sub--;
	else
	    sub = max;
	ESP_ERROR_CHECK(rotary_encoder_reset(&rinfo));
	rc = true;
    }

    *cursub = sub;
    return rc;
}



void menu_change(void)
{
    char	txt[65];

    if (New_Loop2 != Main_Loop2) {

	Main_Loop2 = New_Loop2;

        switch (Main_Loop2) {
              case ML2_INIT:
                      ESP_LOGI(TAG, "Loop user: Init");
                      New_Loop2 = ML2_USER;
                      break;

              case ML2_USER:
                      ESP_LOGI(TAG, "Loop user: User mainmenu");
                      screen_main();
                      break;

              case ML2_UNIT1:
              case ML2_UNIT2:
              case ML2_UNIT3:
                      ESP_LOGI(TAG, "Loop user: Unit %d", Main_Loop2 - ML2_UNIT1);
                      screen_unit(Main_Loop2 - ML2_UNIT1);
                      break;

              case ML2_WIFI:
                      ESP_LOGI(TAG, "Loop user: WiFi");
                      screen_wifi();
                      break;

              case ML2_NETWORK:
                      ESP_LOGI(TAG, "Loop user: Network");
                      screen_network();
                      break;

              case ML2_MQTT:
                      ESP_LOGI(TAG, "Loop user: MQTT");
                      screen_mqtt();
                      break;

              case ML2_SETUP_MQTT:
                      ESP_LOGI(TAG, "Loop user: MQTT setup");
                      sprintf(txt, "EDtXt");
                      rotary_editer("MQTT demo", txt, "", 16, EDIT_TYPE_TEXT);
                      New_Loop2 = ML2_MQTT;
                      break;

              case ML2_UPDATE:
                      ESP_LOGI(TAG, "Loop user: Update");
                      screen_update();
                      break;

              case ML2_SETUP_UNIT1:
              case ML2_SETUP_UNIT2:
              case ML2_SETUP_UNIT3:
                      ESP_LOGI(TAG, "Loop user: Setup Unit %d", Main_Loop2 - ML2_SETUP_UNIT1);
                      SubMenu = 0;
                      screen_unit_setup(Main_Loop2 - ML2_SETUP_UNIT1, SubMenu);
                      break;

              case ML2_ZERO_UNIT1:
              case ML2_ZERO_UNIT2:
              case ML2_ZERO_UNIT3:
                      ESP_LOGI(TAG, "Loop user: Zero Unit %d", Main_Loop2 - ML2_ZERO_UNIT1);
                        SubMenu = 0;
                        screen_unit_zero(Main_Loop2 - ML2_ZERO_UNIT1, SubMenu);
                        break;

              case ML2_INACTIVE:
                      ESP_LOGI(TAG, "Loop user: Inactive");
                      u8g2_SetPowerSave(&u8g2, 1); // powersave display
                      New_Loop2 = ML2_DONE;
                      break;

              default:
                      break;
	}
    }
}



void menu_rotary(void)
{
    switch (Main_Loop2) {
	case ML2_USER:          rotate_to_menu(event.state.position, ML2_UNIT1, ML2_USER); break;
	case ML2_UNIT1:         rotate_to_menu(event.state.position, ML2_UNIT2, ML2_USER); break;
	case ML2_UNIT2:         rotate_to_menu(event.state.position, ML2_UNIT3, ML2_UNIT1); break;
	case ML2_UNIT3:         rotate_to_menu(event.state.position, ML2_WIFI, ML2_UNIT2); break;
	case ML2_WIFI:          rotate_to_menu(event.state.position, ML2_NETWORK, ML2_UNIT3); break;
	case ML2_NETWORK:       rotate_to_menu(event.state.position, ML2_MQTT, ML2_WIFI); break;
	case ML2_MQTT:          rotate_to_menu(event.state.position, ML2_UPDATE, ML2_NETWORK); break;
	case ML2_UPDATE:        rotate_to_menu(event.state.position, ML2_UPDATE, ML2_MQTT); break;
	case ML2_SETUP_UNIT1:
	case ML2_SETUP_UNIT2:
	case ML2_SETUP_UNIT3:   if (rotate_to_sub(event.state.position, 0, 3, &SubMenu))
				    screen_unit_setup(Main_Loop2 - ML2_SETUP_UNIT1, SubMenu);
				break;
	case ML2_ZERO_UNIT1:
	case ML2_ZERO_UNIT2:
	case ML2_ZERO_UNIT3:    if (rotate_to_sub(event.state.position, 0, 1, &SubMenu))
				    screen_unit_zero(Main_Loop2 - ML2_ZERO_UNIT1, SubMenu);
				break;
	default:
				ESP_LOGI(TAG, "Event: position %d, direction %s", event.state.position,
				    event.state.direction ? (event.state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ? "CW":"CCW"):"NOT_SET");
    }
}



void menu_loop(void)
{
    int idx = 0;

    switch (Main_Loop2) {
	case ML2_UNIT1:
	case ML2_UNIT2:
	case ML2_UNIT3:
		New_Loop2 = ML2_SETUP_UNIT1 + (Main_Loop2 - ML2_UNIT1);
		break;

	case ML2_SETUP_UNIT1:
	case ML2_SETUP_UNIT2:
	case ML2_SETUP_UNIT3:
		idx = Main_Loop2 - ML2_SETUP_UNIT1;
		if (SubMenu == 0) {
		    if (xSemaphoreTake(xSemaphoreUnits, 25) == pdTRUE) {
			if (units[idx].mode)
			    units[idx].mode = 0;
			else
			    units[idx].mode = 1;
			write_units();
			xSemaphoreGive(xSemaphoreUnits);
		    }
		    screen_unit_setup(idx, SubMenu);
		    if (Main_Loop1 == ML1_DONE)
			Main_Loop1 = ML1_INIT;
		}
		if (SubMenu == 1)
		    New_Loop2 = ML2_ZERO_UNIT1 + idx;
		if (SubMenu == 3)
		    New_Loop2 = ML2_UNIT1 + idx;
		printf("unit setup sub %d  new %d  idx %d\n", SubMenu, New_Loop2, idx);
		break;

	case ML2_ZERO_UNIT1:
	case ML2_ZERO_UNIT2:
	case ML2_ZERO_UNIT3:
		idx = Main_Loop2 - ML2_ZERO_UNIT1;
		if (SubMenu == 0) {
		    if (xSemaphoreTake(xSemaphoreUnits, 25) == pdTRUE &&
			xSemaphoreTake(xSemaphoreADC, 10) == pdTRUE &&
			adc_state->Pressure[idx].voltage > 165 &&
			adc_state->Pressure[idx].voltage < 660) {
			units[idx].pressure_zero = adc_state->Pressure[idx].voltage / (adc_state->Batt_voltage / 1000);
			write_units();
			xSemaphoreGive(xSemaphoreADC);
			xSemaphoreGive(xSemaphoreUnits);
			screen_unit_zero(idx, SubMenu);
			if (Main_Loop1 == ML1_DONE)
			    Main_Loop1 = ML1_INIT;
		    }
		}
		if (SubMenu == 1) {
		    New_Loop2 = ML2_SETUP_UNIT1 + idx;
		    SubMenu = 1;
		}
		printf("unit zero sub %d  new %d  idx %d\n", SubMenu, New_Loop2, idx);
		break;

	case ML2_MQTT:
		New_Loop2 = ML2_SETUP_MQTT;
		break;

	default:
		break;
    }
}



void task_user(void *pvParameter)
{
    esp_err_t           ret;

    ESP_LOGI(TAG, "Starting User task");
    Main_Loop2 = -1;

    /*
     * Setup the OLED display.
     * See: https://github.com/nkolban/esp32-snippets/blob/master/hardware/displays/U8G2/
     */
    u8g2_esp32_hal_t u8g2_esp32_hal = U8G2_ESP32_HAL_DEFAULT;
    u8g2_esp32_hal.sda = PIN_SDA;
    u8g2_esp32_hal.scl = PIN_SCL;
    u8g2_esp32_hal_init(u8g2_esp32_hal);

    u8g2_Setup_sh1106_i2c_128x64_noname_f(&u8g2, U8G2_R0, u8g2_esp32_i2c_byte_cb, u8g2_esp32_gpio_and_delay_cb);  // init u8g2 structure
    u8x8_SetI2CAddress(&u8g2.u8x8, 0x78);
    u8g2_InitDisplay(&u8g2); // send init sequence to the display, display is in sleep mode after this,

    /*
     * Setup the Rotary Encoder.
     * esp32-rotary-encoder requires that the GPIO ISR service is 
     * installed before calling rotary_encoder_register()
     */
    ESP_ERROR_CHECK(gpio_install_isr_service(0));
    ESP_ERROR_CHECK(rotary_encoder_init(&rinfo, ROT_ENC_A_GPIO, ROT_ENC_B_GPIO));
    ESP_ERROR_CHECK(rotary_encoder_enable_half_steps(&rinfo, false));

    gpio_config_t io_conf;
    io_conf.intr_type = GPIO_PIN_INTR_ANYEDGE;
    io_conf.pin_bit_mask = (1ULL << ROT_ENC_SW_GPIO);
    io_conf.mode = GPIO_MODE_INPUT;
    gpio_config(&io_conf);

    gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
    xTaskCreate(gpio_task, "gpio_task", 2048, NULL, 10, NULL);

    gpio_isr_handler_add(ROT_ENC_SW_GPIO, gpio_isr_handler, (void*) ROT_ENC_SW_GPIO);

    // Create a queue for events from the rotary encoder driver.
    // Tasks can read from this queue to receive up to date position information.
    event_queue = rotary_encoder_create_queue();
    ESP_ERROR_CHECK(rotary_encoder_set_queue(&rinfo, event_queue));

    esp_timer_create_args_t timerSecond = {
        .callback = &TimerCallback,
        .name = "SecondsTimer"
    };

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

    EventBits_t uxBits;
    ESP_LOGI(TAG, "User task loop enter");

    /*
     * Task loop forever.
     */
    while (1) {

	uxBits = xEventGroupWaitBits(xEventGroupUser, TASK_USER_COLD | TASK_USER_WAKEUP, pdFALSE, pdFALSE, portMAX_DELAY );

	if (uxBits & TASK_USER_COLD) {
	    ESP_LOGI(TAG, "User task cold start");
	    screen_splash();
	    xEventGroupClearBits(xEventGroupUser, TASK_USER_COLD);
	    UserTimer = 10;
	}

	if (uxBits & TASK_USER_WAKEUP) {
	    ESP_LOGI(TAG, "User task wakeup");
	    xEventGroupSetBits(xEventGroupUser, TASK_USER_BUSY);
	    xEventGroupClearBits(xEventGroupUser, TASK_USER_REFRESH);
	    screen_main();
	    UserTimer = INACTIVITY;
	    New_Loop2 = ML2_INIT;
	    Main_Loop2 = -1;
	    SubMenu = 0;

	    while (UserTimer) {

		menu_change();
		if (xQueueReceive(event_queue, &event, 250 / portTICK_PERIOD_MS) == pdTRUE) {
		    UserTimer = INACTIVITY;
		    menu_rotary();
		}

		if (PushDuration) {
		    PushDuration = 0;
		    menu_loop();
		}

		if (xEventGroupGetBits(xEventGroupUser) & TASK_USER_REFRESH) {
		    ESP_LOGI(TAG, "User task refresh");
		    switch (Main_Loop2) {
			case ML2_USER:  screen_main(); break;
			case ML2_UNIT1: screen_unit(0); break;
			case ML2_UNIT2: screen_unit(1); break;
			case ML2_UNIT3: screen_unit(2); break;
		    }
		    xEventGroupClearBits(xEventGroupUser, TASK_USER_REFRESH);
		}

		vTaskDelay(10 / portTICK_PERIOD_MS);
	    }

	    xEventGroupClearBits(xEventGroupUser, TASK_USER_WAKEUP);
	}
	vTaskDelay(10 / portTICK_PERIOD_MS);
    }

// If not configured, start configure
// If configured select first unit
// Handle screen (first is show measured values)
// Display per unit. Temp + Pressure + state. Press is setup this sensor.
// Setup menu: 	Sensors
//		WiFi
//		MQTT
//		System (timers etc)
//		Update OTA
//		Return

// Sensors menu:	Assignments, turn to choose one.
// Sensors setup menu:	DS18B20 addr	Press is assign
//			DS18B20 addr
}

mercurial