Fri, 08 Nov 2019 22:40:15 +0100
Increaded stacksize for the user process. Implemented the network update using the proven brewboard code. Reverted the lock release and display sendbuffer lines to the previous code. The networks status screen uses the wifi lock.
/** * @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 xQueueHandle gpio_evt_queue = NULL; ///< Rotary pushbutton 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 SemaphoreHandle_t xSemaphoreWiFi; ///< WiFi lock semaphore extern int count_pub; ///< Published MQTT messages in transit extern int Main_Loop1; ///< Main measure loop extern int update_running; ///< If update is running 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. /** * @brief Seconds timer callback. */ void TimerCallback(void *arg); /***************************************************************************/ void TimerCallback(void *arg) { SecsCount++; if ((SecsCount % 60) == 0) { if (Main_Loop1 == ML1_DONE && update_running == 0) 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); } 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. * @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; int8_t ascent = u8g2_GetAscent(&u8g2); int8_t descent = u8g2_GetDescent(&u8g2); int8_t charheight = u8g2_GetMaxCharHeight(&u8g2); int8_t charwidth = u8g2_GetMaxCharWidth(&u8g2); u8g2_DrawGlyph(&u8g2, x, y, key); u8g2_DrawHLine(&u8g2, x, y+3, 12); u8g2_UpdateDisplay(&u8g2); ESP_LOGI(TAG, "getkey(%c, %d, %d, %d) a %d d %d h %d w %d", key, type, x, y, ascent, descent, charheight, charwidth); for (;;) { if (xQueueReceive(event_queue, &event, 100 / portTICK_PERIOD_MS) == pdTRUE) { UserTimer = INACTIVITY; if (event.state.position != 0) { u8g2_SetDrawColor(&u8g2, 0); u8g2_DrawBox(&u8g2, x, y - 12, charwidth, charheight); u8g2_SetDrawColor(&u8g2, 1); u8g2_DrawHLine(&u8g2, x, y+3, charwidth); if (event.state.position > 0) { /* * If turned fast, the encoder registers multiple steps. * So follow these steps. */ for (int i = 0; i < event.state.position; i++) { if (type == EDIT_TYPE_CAPS) { if (key < 127) key++; if (key > 96 && key < 123) key = 123; } else if (type == EDIT_TYPE_INT) { if (key < 127) key++; if (key < 45) key = 45; if (key == 46 || key == 47) key = 48; if (key > 57) key = 127; } else if (type == EDIT_TYPE_FLOAT) { } else { // EDIT_TYPE_TEXT if (key < 127) key++; } } } else if (event.state.position < 0) { for (int i = 0; i > event.state.position; i--) { if (type == EDIT_TYPE_CAPS) { if (key > 32) key--; if (key > 96 && key < 123) key = 96; } else if (type == EDIT_TYPE_INT) { if (key > 45) key--; if (key > 57) key = 57; if (key == 46 || key == 47) key = 45; } else if (type == EDIT_TYPE_FLOAT) { } else { // EDIT_TYPE_TEXT if (key > 32) key--; } } } ESP_ERROR_CHECK(rotary_encoder_reset(&rinfo)); u8g2_DrawGlyph(&u8g2, x, y, key); u8g2_SendBuffer(&u8g2); u8g2_UpdateDisplay(&u8g2); } } else { if (PushDuration) { UserTimer = INACTIVITY; if (PushDuration > 500) rc = 2; else rc = 1; PushDuration = 0; break; } } } u8g2_SetDrawColor(&u8g2, 0); if (key == 127) u8g2_DrawBox(&u8g2, x, y - 12, charwidth, charheight); // Erase DEL character u8g2_DrawHLine(&u8g2, x, y+3, charwidth); u8g2_SetDrawColor(&u8g2, 1); u8g2_UpdateDisplay(&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_unifont_t_symbols); y = 36; u8g2_DrawStr(&u8g2, 0, y, txt); u8g2_SendBuffer(&u8g2); ESP_LOGI(TAG, "rotary_editer(%s, %s, %s, %d, %d)", label, txt, errmsg, len, type); /* * Choose initial edit key */ if (type == EDIT_TYPE_CAPS) key = 'A'; else if (type == EDIT_TYPE_INT || type == EDIT_TYPE_FLOAT) key = '0'; else key = 'a'; for (;;) { x = u8g2_GetUTF8Width(&u8g2, txt); 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 == 127 && strlen(txt)) { // delete key txt[strlen(txt) - 1] = '\0'; if (strlen(txt)) key = txt[strlen(txt) - 1]; } ESP_LOGI(TAG, " strlen %d x %d key %d `%s`", strlen(txt), x, key, txt); } else if (rc == 2) { break; } } ESP_LOGI(TAG, "rotary_editer() `%s`", txt); } /** * @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); } else { ESP_LOGE(TAG, "screen_main() lock error"); } 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, 35) == 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); u8g2_SendBuffer(&u8g2); xSemaphoreGive(xSemaphoreUnits); } else { ESP_LOGE(TAG, "screen_unit(%d) lock error", no); } } /** * @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 zeroset", 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"); u8g2_SendBuffer(&u8g2); } /** * @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 %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); } void screen_wifi() { char buf[65]; if (xSemaphoreTake(xSemaphoreWiFi, 25) == pdTRUE) { screen_top("WiFi Status"); snprintf(buf, 65, "SSID %s", wifi_state->STA_ssid); u8g2_DrawStr(&u8g2, 1, 28, buf); snprintf(buf, 65, "RSSI %d", wifi_state->STA_rssi); u8g2_DrawStr(&u8g2, 1, 43, buf); snprintf(buf, 65, "Online %s", wifi_state->STA_online ? "Yes":"No"); u8g2_DrawStr(&u8g2, 1, 59, buf); u8g2_SendBuffer(&u8g2); xSemaphoreGive(xSemaphoreWiFi); } else { ESP_LOGE(TAG, "screen_wifi() lock error"); } } 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); } void screen_network() { if (xSemaphoreTake(xSemaphoreWiFi, 25) == pdTRUE) { 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); xSemaphoreGive(xSemaphoreWiFi); } else { ESP_LOGE(TAG, "screen_network() lock error"); } } void screen_network_setup(int sub) { screen_top("Network setup"); menu_line(sub == 0, 2, 25, "Name %s", config.hostname); menu_line(sub == 1, 2, 37, "NTP %s", config.ntp_server); menu_line(sub == 2, 2, 49, "Return"); u8g2_SendBuffer(&u8g2); } 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); } void screen_mqtt_setup(int sub) { screen_top("MQTT Setup"); menu_line(sub == 0, 2, 25, "serv %s", config.mqtt_server); menu_line(sub == 1, 2, 37, "port %d", config.mqtt_port); menu_line(sub == 2, 2, 49, "user %s", config.mqtt_user); menu_line(sub == 3, 2, 61, "Return"); u8g2_SendBuffer(&u8g2); } void screen_update() { screen_top("Update firmware"); menu_line(0, 1, 43, "Push to update"); u8g2_SendBuffer(&u8g2); } void screen_updating(char *m1, char *m2) { screen_top("Updating ..."); u8g2_SetFont(&u8g2, u8g2_font_unifont_t_symbols); if (m1) { u8g2_DrawUTF8(&u8g2,2,30, m1); } if (m2) { u8g2_DrawUTF8(&u8g2,2,55, m2); } u8g2_SendBuffer(&u8g2); } /** * @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) { 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_SETUP_NETWORK: ESP_LOGI(TAG, "Loop user: Network setup"); screen_network_setup(SubMenu); break; case ML2_MQTT: ESP_LOGI(TAG, "Loop user: MQTT"); screen_mqtt(); break; case ML2_SETUP_MQTT: ESP_LOGI(TAG, "Loop user: MQTT setup"); screen_mqtt_setup(SubMenu); break; case ML2_UPDATE: ESP_LOGI(TAG, "Loop user: Update"); screen_update(); break; case ML2_DO_UPDATE: ESP_LOGI(TAG, "Loop user: Do 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; case ML2_SETUP_MQTT: if (rotate_to_sub(event.state.position, 0, 3, &SubMenu)) screen_mqtt_setup(SubMenu); break; case ML2_SETUP_NETWORK: if (rotate_to_sub(event.state.position, 0, 2, &SubMenu)) screen_network_setup(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"); } } /* * Refresh screens that are in focus. Called by the main measurement loop. */ void user_refresh(void) { 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; case ML2_WIFI: screen_wifi(); break; } } void menu_loop(void) { int idx = 0; char txt[32]; 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); } else { ESP_LOGE(TAG, "menu_loop() ML2_SETUP_UNIT%d units lock", idx + 1); } 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); ESP_LOGI(TAG, "Zero set unit %d, %d mV set %d", idx, adc_state->Pressure[idx].voltage, units[idx].pressure_zero); 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_NETWORK: New_Loop2 = ML2_SETUP_NETWORK; break; case ML2_SETUP_NETWORK: if (SubMenu == 0) { rotary_editer("Hostname", config.hostname, "", 16, EDIT_TYPE_TEXT); screen_network_setup(SubMenu); } if (SubMenu == 1) { rotary_editer("NTP server", config.ntp_server, "", 16, EDIT_TYPE_TEXT); screen_network_setup(SubMenu); } if (SubMenu == 2) { ESP_LOGI(TAG, "Hostname `%s`", config.hostname); ESP_LOGI(TAG, "NTP server `%s`", config.ntp_server); write_config(); New_Loop2 = ML2_NETWORK; } break; case ML2_MQTT: New_Loop2 = ML2_SETUP_MQTT; break; case ML2_SETUP_MQTT: if (SubMenu == 0) { rotary_editer("MQTT server", config.mqtt_server, "", 16, EDIT_TYPE_TEXT); screen_mqtt_setup(SubMenu); } if (SubMenu == 1) { sprintf(txt, "%d", config.mqtt_port); rotary_editer("MQTT server poort", txt, "", 6, EDIT_TYPE_INT); config.mqtt_port = atoi(txt); screen_mqtt_setup(SubMenu); } if (SubMenu == 2) { rotary_editer("MQTT user", config.mqtt_user, "", 16, EDIT_TYPE_TEXT); rotary_editer("MQTT password", config.mqtt_pwd, "", 16, EDIT_TYPE_TEXT); screen_mqtt_setup(SubMenu); } if (SubMenu == 3) { ESP_LOGI(TAG, "mqtt_server %s:%d", config.mqtt_server, config.mqtt_port); ESP_LOGI(TAG, "mqtt_user/pass `%s/%s`", config.mqtt_user, config.mqtt_pwd); write_config(); New_Loop2 = ML2_MQTT; } break; case ML2_UPDATE: New_Loop2 = ML2_DO_UPDATE; break; case ML2_DO_UPDATE: bin_update(); New_Loop2 = ML2_UPDATE; 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; /* * 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); screen_main(); UserTimer = INACTIVITY; New_Loop2 = ML2_INIT; Main_Loop2 = -1; SubMenu = 0; while (UserTimer) { menu_change(); if (xQueueReceive(event_queue, &event, 100 / portTICK_PERIOD_MS) == pdTRUE) { UserTimer = INACTIVITY; menu_rotary(); } if (PushDuration) { PushDuration = 0; menu_loop(); } } xEventGroupClearBits(xEventGroupUser, TASK_USER_WAKEUP); } } // 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 }