diff --git a/board_single/main/button.cpp b/board_single/main/button.cpp index 3d83fbf..d749994 100644 --- a/board_single/main/button.cpp +++ b/board_single/main/button.cpp @@ -72,16 +72,16 @@ void buttonCommands::action (uint8_t count, bool lastPressLong){ break; case 1: - // ## switch to MENU state ## + // ## switch to MENU_SETTINGS state ## if (lastPressLong) { - control->changeMode(controlMode_t::MENU); - ESP_LOGW(TAG, "1x long press -> clear encoder queue and change to menu mode"); + control->changeMode(controlMode_t::MENU_MODE_SELECT); + ESP_LOGW(TAG, "1x long press -> clear encoder queue and change to mode 'menu mode select'"); // clear encoder event queue (prevent menu from exiting immediately due to long press event just happend) rotary_encoder_event_t ev; while (xQueueReceive(encoderQueue, &ev, 0) == pdPASS); - buzzer->beep(20, 20, 10); - vTaskDelay(500 / portTICK_PERIOD_MS); + buzzer->beep(5, 50, 30); + vTaskDelay(200 / portTICK_PERIOD_MS); } // ## toggle joystick freeze ## else if (control->getCurrentMode() == controlMode_t::MASSAGE) @@ -122,6 +122,16 @@ void buttonCommands::action (uint8_t count, bool lastPressLong){ ESP_LOGW(TAG, "cmd %d: switch to HTTP", count); control->changeMode(controlMode_t::HTTP); //switch to HTTP mode break; + + case 5: + // ## switch to MENU_SETTINGS state ## + control->changeMode(controlMode_t::MENU_SETTINGS); + ESP_LOGW(TAG, "5x press -> clear encoder queue and change to mode 'menu settings'"); + // clear encoder event queue (prevent menu from exiting immediately due to long press event just happend) + rotary_encoder_event_t ev; + while (xQueueReceive(encoderQueue, &ev, 0) == pdPASS); + buzzer->beep(20, 20, 10); + vTaskDelay(200 / portTICK_PERIOD_MS); case 6: // ## switch to MASSAGE mode ## @@ -157,7 +167,7 @@ void buttonCommands::action (uint8_t count, bool lastPressLong){ //----------------------------- //------ startHandleLoop ------ //----------------------------- -// when not in MENU mode, repeatedly receives events from encoder button +// when not in MENU_SETTINGS mode, repeatedly receives events from encoder button // and takes the corresponding action // this function has to be started once in a separate task #define INPUT_TIMEOUT 500 // duration of no button events, after which action is run (implicitly also is 'long-press' time) @@ -172,10 +182,11 @@ void buttonCommands::startHandleLoop() { //-- disable functionality when in menu mode -- //(display task uses encoder in that mode) - if (control->getCurrentMode() == controlMode_t::MENU) + if (control->getCurrentMode() == controlMode_t::MENU_SETTINGS + || control->getCurrentMode() == controlMode_t::MENU_MODE_SELECT) { //do nothing every loop cycle - ESP_LOGD(TAG, "in MENU mode -> button commands disabled"); + ESP_LOGD(TAG, "in MENU_SETTINGS or MENU_MODE_SELECT mode -> button commands disabled"); vTaskDelay(1000 / portTICK_PERIOD_MS); continue; } diff --git a/board_single/main/control.cpp b/board_single/main/control.cpp index 684f562..7f467f0 100644 --- a/board_single/main/control.cpp +++ b/board_single/main/control.cpp @@ -21,7 +21,28 @@ extern "C" //tag for logging static const char * TAG = "control"; -const char* controlModeStr[9] = {"IDLE", "JOYSTICK", "MASSAGE", "HTTP", "MQTT", "BLUETOOTH", "AUTO", "ADJUST_CHAIR", "MENU"}; +static const char * ERROR_STR = "ERR"; + +const char* controlModeStr[10] = {"IDLE", "JOYSTICK", "MASSAGE", "HTTP", "MQTT", "BLUETOOTH", "AUTO", "ADJUST_CHAIR", "MENU_SETTINGS", "MENU_MODE_SELECT"}; +const uint8_t controlModeMaxCount = sizeof(controlModeStr) / sizeof(char *); + + +//========================== +//==== controlModeToStr ==== +//========================== +// convert controlMode enum or mode index to string for logging, returns "ERR" when index is out of range of existing modes +const char * controlModeToStr(int modeIndex){ + // return string when in allowed range + if (modeIndex >= 0 && modeIndex < controlModeMaxCount) + return controlModeStr[modeIndex]; + else + // log and return error when not in range + ESP_LOGE(TAG, "controlModeToStr: mode index '%d' is not in valid range - max 0-%d", modeIndex, controlModeMaxCount); + return ERROR_STR; +} +const char * controlModeToStr(controlMode_t mode){ + return controlModeToStr((int)mode); +} //----------------------------- @@ -271,8 +292,9 @@ void controlledArmchair::handle() } break; - //------- handle MENU mode ------- - case controlMode_t::MENU: + //------- handle MENU modes ------- + case controlMode_t::MENU_SETTINGS: + case controlMode_t::MENU_MODE_SELECT: // nothing to do here, display task handles the menu vTaskDelay(1000 / portTICK_PERIOD_MS); break; @@ -507,7 +529,7 @@ void controlledArmchair::changeMode(controlMode_t modeNew) buzzer->beep(3, 100, 50); break; - case controlMode_t::MENU: + case controlMode_t::MENU_SETTINGS: idleBothMotors(); break; diff --git a/board_single/main/control.hpp b/board_single/main/control.hpp index 419e977..fa4d20d 100644 --- a/board_single/main/control.hpp +++ b/board_single/main/control.hpp @@ -20,9 +20,10 @@ extern "C" //---- struct, enum, variable declarations --- //-------------------------------------------- //enum that decides how the motors get controlled -enum class controlMode_t {IDLE, JOYSTICK, MASSAGE, HTTP, MQTT, BLUETOOTH, AUTO, ADJUST_CHAIR, MENU}; +enum class controlMode_t {IDLE, JOYSTICK, MASSAGE, HTTP, MQTT, BLUETOOTH, AUTO, ADJUST_CHAIR, MENU_SETTINGS, MENU_MODE_SELECT}; //string array representing the mode enum (for printing the state as string) -extern const char* controlModeStr[9]; +extern const char* controlModeStr[10]; +extern const uint8_t controlModeMaxCount; //--- control_config_t --- //struct with config parameters @@ -34,6 +35,14 @@ typedef struct control_config_t { } control_config_t; +//========================== +//==== controlModeToStr ==== +//========================== +// convert controlMode enum or index to string for logging +const char * controlModeToStr(controlMode_t mode); +const char * controlModeToStr(int modeIndex); + + //======================================= //============ control task ============= //======================================= @@ -84,8 +93,9 @@ class controlledArmchair { //function that restarts timer which initiates the automatic timeout (switch to IDLE) after certain time of inactivity void resetTimeout(); - //methods to get the current control mode + //methods to get the current or previous control mode controlMode_t getCurrentMode() const {return mode;}; + controlMode_t getPreviousMode() const {return modePrevious;}; const char *getCurrentModeStr() const { return controlModeStr[(int)mode]; }; //--- mode specific --- diff --git a/board_single/main/display.cpp b/board_single/main/display.cpp index 7b00656..b1647c7 100644 --- a/board_single/main/display.cpp +++ b/board_single/main/display.cpp @@ -9,7 +9,7 @@ extern "C"{ //=== content config === -#define STARTUP_MSG_TIMEOUT 2000 +#define STARTUP_MSG_TIMEOUT 2600 #define ADC_BATT_VOLTAGE ADC1_CHANNEL_6 #define BAT_CELL_COUNT 7 // continously vary display contrast from 0 to 250 in OVERVIEW status screen @@ -49,7 +49,7 @@ int readAdc(adc1_channel_t adc, uint32_t samples){ SSD1306_t dev; //tag for logging static const char * TAG = "display"; -//define currently shown status page (continously displayed content when not in MENU mode) +//define currently shown status page (continously displayed content when not in MENU_SETTINGS mode) static displayStatusPage_t selectedStatusPage = STATUS_SCREEN_OVERVIEW; @@ -514,11 +514,78 @@ void display_rotateStatusPage(bool reverseDirection, bool noRotate) } +//========================== +//=== handleStatusScreen === +//========================== +//show currently selected status screen on display +//function is repeatedly called by display task when not in MENU mode +void handleStatusScreen(display_task_parameters_t *objects) +{ + switch (selectedStatusPage) + { + default: + case STATUS_SCREEN_OVERVIEW: + showStatusScreenOverview(objects); + break; + case STATUS_SCREEN_SPEED: + showStatusScreenSpeed(objects); + break; + case STATUS_SCREEN_JOYSTICK: + showStatusScreenJoystick(objects); + break; + case STATUS_SCREEN_MOTORS: + showStatusScreenMotors(objects); + break; + case STATUS_SCREEN_SCREENSAVER: + showStatusScreenScreensaver(objects); + break; + } + + //--- handle timeouts --- + uint32_t inactiveMs = objects->control->getInactivityDurationMs(); + //-- screensaver -- + // handle switch to screensaver when no user input for a long time + if (inactiveMs > displayConfig.timeoutSwitchToScreensaverMs) // timeout - switch to screensaver is due + { + if (selectedStatusPage != STATUS_SCREEN_SCREENSAVER) + { // switch/log only once at change + ESP_LOGW(TAG, "no activity for more than %d min, switching to screensaver", inactiveMs / 1000 / 60); + display_selectStatusPage(STATUS_SCREEN_SCREENSAVER); + } + } + else if (selectedStatusPage == STATUS_SCREEN_SCREENSAVER) // exit screensaver when there was recent activity + { + ESP_LOGW(TAG, "recent activity detected, disabling screensaver"); + display_selectStatusPage(STATUS_SCREEN_OVERVIEW); + } + + //-- reduce brightness -- + // handle brightness reduction when no user input for some time + static bool brightnessIsReduced = false; + if (inactiveMs > displayConfig.timeoutReduceContrastMs) // threshold exceeded - reduction of brightness is due + { + if (!brightnessIsReduced) // change / log only once at change + { + // reduce display brightness (less burn in) + ESP_LOGW(TAG, "no activity for more than %d min, reducing display brightness to %d/255", inactiveMs / 1000 / 60, displayConfig.contrastReduced); + ssd1306_contrast(&dev, displayConfig.contrastReduced); + brightnessIsReduced = true; + } + } + else if (brightnessIsReduced) // threshold not exceeded anymore, but still reduced + { + // increase display brighness again + ESP_LOGW(TAG, "recent activity detected, increasing brightness again"); + ssd1306_contrast(&dev, displayConfig.contrastNormal); + brightnessIsReduced = false; + } +} + + //============================ //======= display task ======= //============================ // TODO: separate task for each loop? - void display_task(void *pvParameters) { ESP_LOGW(TAG, "Initializing display and starting handle loop"); @@ -534,77 +601,31 @@ void display_task(void *pvParameters) vTaskDelay(STARTUP_MSG_TIMEOUT / portTICK_PERIOD_MS); ssd1306_clear_screen(&dev, false); - // repeatedly update display with content + // repeatedly update display with content depending on current mode while (1) { - if (objects->control->getCurrentMode() == controlMode_t::MENU) + switch (objects->control->getCurrentMode()) { - //uses encoder events to control menu and updates display - handleMenu(objects, &dev); - } - else //show selected status screen in any other mode - { - switch (selectedStatusPage) - { - default: - case STATUS_SCREEN_OVERVIEW: - showStatusScreenOverview(objects); - break; - case STATUS_SCREEN_SPEED: - showStatusScreenSpeed(objects); - break; - case STATUS_SCREEN_JOYSTICK: - showStatusScreenJoystick(objects); - break; - case STATUS_SCREEN_MOTORS: - showStatusScreenMotors(objects); - break; - case STATUS_SCREEN_SCREENSAVER: - showStatusScreenScreensaver(objects); - break; - } + case controlMode_t::MENU_SETTINGS: + // uses encoder events to control menu (settings) and updates display + handleMenu_settings(objects, &dev); + break; + case controlMode_t::MENU_MODE_SELECT: + // uses encoder events to control menu (mode select) and updates display + handleMenu_modeSelect(objects, &dev); + break; + default: + // show selected status screen in any other mode + handleStatusScreen(objects); + break; + } // end mode switch-case + // TODO add pages and menus here + } // end while(1) +} // end display-task + + - //--- handle timeouts --- - uint32_t inactiveMs = objects->control->getInactivityDurationMs(); - //-- screensaver -- - // handle switch to screensaver when no user input for a long time - if (inactiveMs > displayConfig.timeoutSwitchToScreensaverMs) // timeout - switch to screensaver is due - { - if (selectedStatusPage != STATUS_SCREEN_SCREENSAVER){ // switch/log only once at change - ESP_LOGW(TAG, "no activity for more than %d min, switching to screensaver", inactiveMs / 1000 / 60); - display_selectStatusPage(STATUS_SCREEN_SCREENSAVER); - } - } - else if (selectedStatusPage == STATUS_SCREEN_SCREENSAVER) // exit screensaver when there was recent activity - { - ESP_LOGW(TAG, "recent activity detected, disabling screensaver"); - display_selectStatusPage(STATUS_SCREEN_OVERVIEW); - } - //-- reduce brightness -- - // handle brightness reduction when no user input for some time - static bool brightnessIsReduced = false; - if (inactiveMs > displayConfig.timeoutReduceContrastMs) // threshold exceeded - reduction of brightness is due - { - if (!brightnessIsReduced) //change / log only once at change - { - // reduce display brightness (less burn in) - ESP_LOGW(TAG, "no activity for more than %d min, reducing display brightness to %d/255", inactiveMs / 1000 / 60, displayConfig.contrastReduced); - ssd1306_contrast(&dev, displayConfig.contrastReduced); - brightnessIsReduced = true; - } - } - else if (brightnessIsReduced) // threshold not exceeded anymore, but still reduced - { - // increase display brighness again - ESP_LOGW(TAG, "recent activity detected, increasing brightness again"); - ssd1306_contrast(&dev, displayConfig.contrastNormal); - brightnessIsReduced = false; - } - } - // TODO add pages and menus - } -} //----------------------------------- //---- text-related example code ---- diff --git a/board_single/main/display.hpp b/board_single/main/display.hpp index f24a16f..791246e 100644 --- a/board_single/main/display.hpp +++ b/board_single/main/display.hpp @@ -53,7 +53,7 @@ typedef struct display_task_parameters_t { } display_task_parameters_t; -// enum for selecting the currently shown status page (display content when not in MENU mode) +// enum for selecting the currently shown status page (display content when not in MENU_SETTINGS mode) typedef enum displayStatusPage_t {STATUS_SCREEN_OVERVIEW=0, STATUS_SCREEN_SPEED, STATUS_SCREEN_JOYSTICK, STATUS_SCREEN_MOTORS, STATUS_SCREEN_SCREENSAVER, __NUMBER_OF_AVAILABLE_SCREENS} displayStatusPage_t; //note: SCREENSAVER has to be last one since it is ignored by rotate and used to determine count // get precise battery voltage (using lookup table) @@ -62,7 +62,7 @@ float getBatteryVoltage(); // get battery charge level in percent (using lookup table as discharge curve) float getBatteryPercent(); -// function to select one of the defined status screens which are shown on display when not in MENU mode +// function to select one of the defined status screens which are shown on display when not in MENU_SETTINGS or MENU_SELECT_MODE mode void display_selectStatusPage(displayStatusPage_t newStatusPage); // select next/previous status screen to be shown, when noRotate is set is stays at first/last screen void display_rotateStatusPage(bool reverseDirection = false, bool noRotate = false); diff --git a/board_single/main/main.cpp b/board_single/main/main.cpp index a3b7ed4..1c3a538 100644 --- a/board_single/main/main.cpp +++ b/board_single/main/main.cpp @@ -265,7 +265,7 @@ extern "C" void app_main(void) { //------------------------------ //--- create task for button --- //------------------------------ - //task that handles button/encoder events in any mode except 'MENU' (e.g. switch modes by pressing certain count) + //task that handles button/encoder events in any mode except 'MENU_SETTINGS' and 'MENU_MODE_SELECT' (e.g. switch modes by pressing certain count) task_button_parameters_t button_param = {control, joystick, encoderQueue, motorLeft, motorRight, buzzer}; xTaskCreate(&task_button, "task_button", 4096, &button_param, 3, NULL); @@ -279,7 +279,7 @@ extern "C" void app_main(void) { //----------------------------------- //----- create task for display ----- //----------------------------------- - //task that handles the display (show stats, handle menu in 'MENU' mode) + //task that handles the display (show stats, handle menu in 'MENU_SETTINGS' and 'MENU_MODE_SELECT' mode) display_task_parameters_t display_param = {display_config, control, joystick, encoderQueue, motorLeft, motorRight, speedLeft, speedRight, buzzer, &nvsHandle}; xTaskCreate(&display_task, "display_task", 3*2048, &display_param, 3, NULL); @@ -303,7 +303,7 @@ extern "C" void app_main(void) { //--- testing force specific mode after startup --- - //control->changeMode(controlMode_t::MENU); + //control->changeMode(controlMode_t::MENU_SETTINGS); diff --git a/board_single/main/menu.cpp b/board_single/main/menu.cpp index eb5e94d..5278d34 100644 --- a/board_single/main/menu.cpp +++ b/board_single/main/menu.cpp @@ -79,7 +79,7 @@ void item_calibrateJoystick_action(display_task_parameters_t *objects, SSD1306_t displayTextLineCentered(display, 1, true, false, "%s", "X-min"); //-- loop until all positions are defined -- - while (running && objects->control->getCurrentMode() == controlMode_t::MENU) + while (running && objects->control->getCurrentMode() == controlMode_t::MENU_SETTINGS) { // repeatedly print adc value depending on currently selected axis switch (mode) @@ -201,7 +201,7 @@ void item_debugJoystick_action(display_task_parameters_t * objects, SSD1306_t * //-- show/update values -- // stop when button pressed or control state changes (timeouts to IDLE) - while (running && objects->control->getCurrentMode() == controlMode_t::MENU) + while (running && objects->control->getCurrentMode() == controlMode_t::MENU_SETTINGS) { // repeatedly print all joystick data joystickData_t data = objects->joystick->getData(); @@ -658,6 +658,98 @@ void showItemList(SSD1306_t *display, int selectedItem) } + +//---------------------------------- +//--- getNextSelectableModeIndex --- +//---------------------------------- +// local function that returns index of the next (or previous) selectable control-mode index +// used for mode select menu +int getNextSelectableModeIndex(int modeIndex, bool reverseDirection = false) +{ + // those modes are selectable via mode-select menu - NOTE: Add other new modes here + static const controlMode_t selectableModes[] = {controlMode_t::IDLE, + controlMode_t::JOYSTICK, + controlMode_t::MASSAGE, + controlMode_t::HTTP, + controlMode_t::ADJUST_CHAIR, + controlMode_t::MENU_SETTINGS}; + static const int selectableModesCount = sizeof(selectableModes) / sizeof(controlMode_t); + + //// verify range + // if (modeIndex < 0 || modeIndex > controlModeMaxCount) { + // ESP_LOGE(TAG, "getSelectableModeItem - index '%d' is out of range! There are max %d modes.", modeIndex, controlModeMaxCount); + // return 0; + // } + + // search next mode that is present in selectableModes + bool rotatedAlready = false; + while (1) + { + // try next/previous item + if (reverseDirection) + modeIndex--; + else + modeIndex++; + + // go back to start/end if last/first possible mode reached + if ((!reverseDirection && modeIndex >= controlModeMaxCount) || (reverseDirection && modeIndex < 0)) + { + // prevent deadlock when no match was found for some reason + if (rotatedAlready) + { + ESP_LOGE(TAG, "search for selectable mode failed - no matching mode found"); + return 0; + } + // go to start/end + if (reverseDirection) + modeIndex = controlModeMaxCount - 1; + else + modeIndex = 0; + rotatedAlready = true; + } + // check if current mode index is present in allowed / selectable modes + for (int j = 0; j < selectableModesCount; j++) + { + if (modeIndex == (int)selectableModes[j]) + // index matches one in the selectable modes -> success + return modeIndex; + } + ESP_LOGD(TAG, "mode index %d is no selectable mode -> trying next", modeIndex); + } +} + + + +//-------------------------- +//------ showModeList ------ +//-------------------------- +//function that renders mode-select menu (one update) +void showModeList(SSD1306_t *display, int selectedMode) +{ + // TODO add blinking of a line to indicate selecting + + // line 1 " - select mode -" + // line 2 " prev mode " + // line 3 "SEL MODE LARGE 1/3" + // line 4 "SEL MODE LARGE 2/3" + // line 5 "SEL MODE LARGE 4/3" + // line 6 " next mode " + // line 7 "click to confirm" + + // print title (0) + displayTextLine(display, 0, false, true, "- select mode -"); // inverted + // print mode before (1) + displayTextLineCentered(display, 1, false, false, "%s", controlModeToStr(getNextSelectableModeIndex(selectedMode, true))); + // print selected mode large (2-4) + displayTextLineCentered(display, 2, true, false, "%s", controlModeToStr(selectedMode)); + // print mode after (5) + displayTextLineCentered(display, 5, false, false, "%s", controlModeToStr(getNextSelectableModeIndex(selectedMode))); + // print message (6) + displayTextLineCentered(display, 6, false, true, "click to confirm"); +} + + + //----------------------------- //--- showValueSelectStatic --- //----------------------------- @@ -732,14 +824,14 @@ void updateValueSelect(SSD1306_t *display, int selectedItem) -//======================== -//====== handleMenu ====== -//======================== +//=========================== +//=== handleMenu_settings === +//=========================== //controls menu with encoder input and displays the text on oled display //function is repeatedly called by display task when in menu state #define QUEUE_TIMEOUT 3000 //timeout no encoder event - to not block the display loop and actually handle menu-timeout #define MENU_TIMEOUT 60000 //inactivity timeout (switch to IDLE mode) note: should be smaller than IDLE timeout in control task -void handleMenu(display_task_parameters_t * objects, SSD1306_t *display) +void handleMenu_settings(display_task_parameters_t * objects, SSD1306_t *display) { static uint32_t lastActivity = 0; static int selectedItem = 0; @@ -749,7 +841,7 @@ void handleMenu(display_task_parameters_t * objects, SSD1306_t *display) switch (menuState) { //------------------------- - //---- State MAIN MENU ---- + //---- State MAIN MENU_SETTINGS ---- //------------------------- case MAIN_MENU: // update display @@ -807,7 +899,7 @@ void handleMenu(display_task_parameters_t * objects, SSD1306_t *display) //--- exit menu mode --- // change to previous mode (e.g. JOYSTICK) objects->buzzer->beep(12, 15, 8); - objects->control->toggleMode(controlMode_t::MENU); //currently already in MENU -> changes to previous mode + objects->control->toggleMode(controlMode_t::MENU_SETTINGS); //currently already in MENU_SETTINGS -> changes to previous mode ssd1306_clear_screen(display, false); break; @@ -892,3 +984,99 @@ void handleMenu(display_task_parameters_t * objects, SSD1306_t *display) return; } } + + + +//============================= +//=== handleMenu_modeSelect === +//============================= +//controls menu for selecting the control mode with encoder input and displays the text on oled display +//function is repeatedly called by display task when in menu state +#define MENU_MODE_SEL_TIMEOUT 10000 // inactivity timeout (switch to IDLE mode) note: should be smaller than IDLE timeout in control task +void handleMenu_modeSelect(display_task_parameters_t *objects, SSD1306_t *display) +{ + static uint32_t lastActivity = 0; + static bool firstRun = true; // track if last mode was already obtained when menu got opened + static int selectedMode = (int)controlMode_t::IDLE; + rotary_encoder_event_t event; // store encoder event data + + // get current mode when run for first time since last select + if (firstRun) + { + firstRun = false; + selectedMode = (int)objects->control->getPreviousMode(); // store previous mode (since current mode is MENU) + ESP_LOGI(TAG, "started mode-select menu, previous active is %s", controlModeStr[(int)selectedMode]); + } + + // renders list of modes with currently selected one on display + showModeList(display, selectedMode); + // wait for encoder event + if (xQueueReceive(objects->encoderQueue, &event, QUEUE_TIMEOUT / portTICK_PERIOD_MS)) + { + // reset menu- and control-timeout on any encoder event + lastActivity = esp_log_timestamp(); + objects->control->resetTimeout(); // user input -> reset switch to IDLE timeout + switch (event.type) + { + case RE_ET_CHANGED: + //--- scroll in list --- + if (event.diff < 0) + { + selectedMode = getNextSelectableModeIndex(selectedMode); + objects->buzzer->beep(1, 20, 0); + ESP_LOGD(TAG, "showing next item: %d '%s'", selectedMode, controlModeToStr(selectedMode)); + } + // note: display will update at start of next run + else + { + selectedMode = getNextSelectableModeIndex(selectedMode, true); + objects->buzzer->beep(1, 20, 0); + objects->buzzer->beep(1, 20, 0); + ESP_LOGD(TAG, "showing previous item: %d '%s'", selectedMode, controlModeToStr(selectedMode)); + // note: display will update at start of next run + } + break; + + case RE_ET_BTN_CLICKED: + //--- confirm mode and exit --- + objects->buzzer->beep(1, 50, 10); + ESP_LOGI(TAG, "Button pressed - confirming selected mode '%s'", controlModeToStr(selectedMode)); + objects->control->changeMode((controlMode_t)selectedMode); + // clear display + ssd1306_clear_screen(display, false); + // reset first run + firstRun = true; + return; // function wont be called again due to mode change + + case RE_ET_BTN_LONG_PRESSED: + //--- exit to previous mode --- + // change to previous mode (e.g. JOYSTICK) + objects->buzzer->beep(12, 15, 8); + objects->control->changeMode(objects->control->getPreviousMode()); + // clear display + ssd1306_clear_screen(display, false); + // reset first run + firstRun = true; + return; // function wont be called again due to mode change + break; + + case RE_ET_BTN_RELEASED: + case RE_ET_BTN_PRESSED: + break; + } + } + + //--- menu timeout --- + // close menu and switch to IDLE mode when no encoder event occured within MENU_TIMEOUT + if (esp_log_timestamp() - lastActivity > MENU_MODE_SEL_TIMEOUT) + { + ESP_LOGW(TAG, "TIMEOUT - no activity for more than %ds -> closing menu, switching to IDLE", MENU_TIMEOUT / 1000); + // clear display + ssd1306_clear_screen(display, false); + // change control mode + objects->control->changeMode(controlMode_t::IDLE); + // reset first run + firstRun = true; + return; // function wont be called again due to mode change + } +} \ No newline at end of file diff --git a/board_single/main/menu.hpp b/board_single/main/menu.hpp index b7682ec..6def104 100644 --- a/board_single/main/menu.hpp +++ b/board_single/main/menu.hpp @@ -30,4 +30,8 @@ typedef struct const char line7[17]; // below value } menuItem_t; -void handleMenu(display_task_parameters_t * objects, SSD1306_t *display); \ No newline at end of file +//controls menu for changing settings with encoder input and displays the text on oled display (has to be repeatedly called by display task) +void handleMenu_settings(display_task_parameters_t * objects, SSD1306_t *display); + +//controls menu for selecting the control mode with encoder input and displays the text on oled display (has to be repeatedly called by display task) +void handleMenu_modeSelect(display_task_parameters_t * objects, SSD1306_t *display); \ No newline at end of file