diff --git a/Firmware/mmu2.cpp b/Firmware/mmu2.cpp index 4ee696c75..ca22c2f8b 100644 --- a/Firmware/mmu2.cpp +++ b/Firmware/mmu2.cpp @@ -615,56 +615,16 @@ void MMU2::ReportError(ErrorCode ec) { // The longest error description in errors_list.h is 144 bytes. // and the longest error title is 20 bytes. msg buffer needs // to have enough space to fit both. - //char msg[192]; - //int len = snprintf(msg, sizeof(msg), "MMU2:E=%hu ", (uint16_t)ec); + char msg[192]; + int len = snprintf(msg, sizeof(msg), "MMU2:E=%hu ", (uint16_t)ec); // Append a human readable form of the error code(s) - //TranslateErr((uint16_t)ec, &msg[len], 192 - len); - - const uint16_t ei = MMUErrorCodeIndex((uint16_t)ec); - // Testing - uint8_t choice_selected = 0; - back_to_choices: - // 504 = ERR_SYSTEM_VERSION_MISMATCH - lcd_clear(); - lcd_update_enable(false); - lcd_printf_P(PSTR("%S\nprusa3d.com/ERR04%hu"), - static_cast(pgm_read_ptr(&errorTitles[ei])), - reinterpret_cast(const_cast(pgm_read_ptr(&errorCodes[ei]))) - ); - choice_selected = lcd_show_multiscreen_message_two_choices_and_wait_P( - NULL, // NULL, since title screen is not in PROGMEM - false, - false, - btnRetry, - btnContinue, - btnMore, - 7, - 13 - ); - - if (choice_selected == 2) { - // 'More' show error description - lcd_show_fullscreen_message_and_wait_P( - static_cast(pgm_read_ptr(&errorDescs[ei])) - ); - - // Return back to the choice menu - goto back_to_choices; - } else if(choice_selected == 1) { - // 'Done' return to status screen - lcd_update_enable(true); - lcd_return_to_status(); - } else { - // 'Retry' TODO: not yet implemented - lcd_update_enable(true); - lcd_return_to_status(); - } + TranslateErr((uint16_t)ec, &msg[len], 192 - len); // beware - the prefix in the message ("MMU2") will get stripped by the logging subsystem // and a correct MMU2 component will be assigned accordingly - see appmain.cpp // Therefore I'm not calling MMU2_ERROR_MSG or MMU2_ECHO_MSG here - //SERIAL_ECHO_START; - //SERIAL_ECHOLN(msg); + SERIAL_ECHO_START; + SERIAL_ECHOLN(msg); } static_assert(mmu2Magic[0] == 'M' diff --git a/Firmware/mmu2/errors_list.h b/Firmware/mmu2/errors_list.h index d87e786eb..244597ef8 100644 --- a/Firmware/mmu2/errors_list.h +++ b/Firmware/mmu2/errors_list.h @@ -243,17 +243,20 @@ static const char * const errorDescs[] PROGMEM = { descRUNTIME_ERROR, }; +#define BUTTON_OP_HIGH_NIBBLE_MSK 0xF0 +#define BUTTON_OP_LOW_NIBBLE_MSK 0x0F + /// Will be mapped onto dialog button responses in the FW /// Those responses have their unique+translated texts as well enum class ButtonOperations : uint8_t { - NoOperation, - Retry, - SlowLoad, - Continue, - RestartMMU, - Unload, - StopPrint, - DisableMMU, + NoOperation = 0, + Retry = 1, + SlowLoad = 2, + Continue = 3, + RestartMMU = 4, + Unload = 5, + StopPrint = 6, + DisableMMU = 7, }; // we have max 3 buttons/operations to select from @@ -271,6 +274,17 @@ static const char btnStop[] PROGMEM_I1 = ISTR("Stop"); static const char btnDisableMMU[] PROGMEM_I1 = ISTR("Disable"); static const char btnMore[] PROGMEM_I1 = ISTR("More\x01"); +// Used to parse the buttons from Btns(). +static const char * const btnOperation[] PROGMEM = { + btnRetry, + btnSlowLoad, + btnContinue, + btnRestartMMU, + btnUnload, + btnStop, + btnDisableMMU +}; + // We have 8 different operations/buttons at this time, so we need at least 4 bits to encode each. // Since one of the buttons is always "More", we can skip that one. // Therefore we need just 1 byte to describe the necessary buttons for each screen. diff --git a/Firmware/mmu2_reporting.cpp b/Firmware/mmu2_reporting.cpp index 1bc638c25..6d87ba8a5 100644 --- a/Firmware/mmu2_reporting.cpp +++ b/Firmware/mmu2_reporting.cpp @@ -1,4 +1,7 @@ #include "mmu2_reporting.h" +#include "mmu2_error_converter.h" +#include "mmu2/error_codes.h" +#include "mmu2/errors_list.h" #include "ultralcd.h" namespace MMU2 { @@ -16,8 +19,106 @@ void EndReport(CommandInProgress cip, uint16_t ec) { } void ReportErrorHook(CommandInProgress cip, uint16_t ec) { - // @@TODO - display an error screen - we still don't know how that will look like - // The only thing we know is the fact, that the screen must not block the MMU automaton + //! Show an error screen + //! When an MMU error occurs, the LCD content will look like this: + //! |01234567890123456789| + //! |MMU FW update needed| <- title/header of the error: max 20 characters + //! |prusa3d.com/ERR04504| <- URL 20 characters + //! | | <- empty line + //! |>Retry >Done >MoreW| <- buttons + const uint16_t ei = MMUErrorCodeIndex((uint16_t)ec); + uint8_t choice_selected = 0; + bool two_choices = false; + + // Read and determine what operations should be shown on the menu + // Note: uint16_t is used here to avoid compiler warning. uint8_t is only half the size of void* + uint8_t button_operation = reinterpret_cast(const_cast(pgm_read_ptr(&errorButtons[ei]))); + uint8_t button_high_nibble = (button_operation & BUTTON_OP_HIGH_NIBBLE_MSK) >> 4; + uint8_t button_low_nibble = button_operation & BUTTON_OP_LOW_NIBBLE_MSK; + + // Check if the menu should have three or two choices + if (button_low_nibble == (uint8_t)ButtonOperations::NoOperation) + { + // Two operations not specified, the error menu should only show two choices + two_choices = true; + } + +back_to_choices: + lcd_clear(); + lcd_update_enable(false); + + // Print title and header + lcd_printf_P(PSTR("%S\nprusa3d.com/ERR04%hu"), + static_cast(pgm_read_ptr(&errorTitles[ei])), + reinterpret_cast(const_cast(pgm_read_ptr(&errorCodes[ei]))) + ); + + // Render the choices and store selection in 'choice_selected' + choice_selected = lcd_show_multiscreen_message_with_choices_and_wait_P( + NULL, // NULL, since title screen is not in PROGMEM + false, + false, + two_choices ? + static_cast(pgm_read_ptr(&btnOperation[button_high_nibble - 1])) + : static_cast(pgm_read_ptr(&btnOperation[button_low_nibble - 1])), + two_choices ? + btnMore + : static_cast(pgm_read_ptr(&btnOperation[button_high_nibble - 1])), + two_choices ? nullptr : btnMore, + two_choices ? + 10 // If two choices, allow the first choice to have more characters + : 7, + 13 + ); + + if ((two_choices && choice_selected == 1) // Two choices and middle button selected + || (!two_choices && choice_selected == 2)) // Three choices and right most button selected + { + // 'More' show error description + lcd_show_fullscreen_message_and_wait_P( + static_cast(pgm_read_ptr(&errorDescs[ei])) + ); + + // Return back to the choice menu + goto back_to_choices; + } else if(choice_selected == 1) { + // TODO: User selected middle choice, not sure what to do. + // At the moment just return to the status screen + switch (button_high_nibble) + { + case (uint8_t)ButtonOperations::Retry: + case (uint8_t)ButtonOperations::SlowLoad: + case (uint8_t)ButtonOperations::Continue: + case (uint8_t)ButtonOperations::RestartMMU: + case (uint8_t)ButtonOperations::Unload: + case (uint8_t)ButtonOperations::StopPrint: + case (uint8_t)ButtonOperations::DisableMMU: + default: + lcd_update_enable(true); + lcd_return_to_status(); + break; + } + } else { + // TODO: User selected the left most choice, not sure what to do. + // At the moment just return to the status screen + switch ( two_choices ? + button_high_nibble + : button_low_nibble + ) + { + case (uint8_t)ButtonOperations::Retry: + case (uint8_t)ButtonOperations::SlowLoad: + case (uint8_t)ButtonOperations::Continue: + case (uint8_t)ButtonOperations::RestartMMU: + case (uint8_t)ButtonOperations::Unload: + case (uint8_t)ButtonOperations::StopPrint: + case (uint8_t)ButtonOperations::DisableMMU: + default: + lcd_update_enable(true); + lcd_return_to_status(); + break; + } + } } void ReportProgressHook(CommandInProgress cip, uint16_t ec) { diff --git a/Firmware/ultralcd.cpp b/Firmware/ultralcd.cpp index 7e7bfbebb..f2daf00f3 100755 --- a/Firmware/ultralcd.cpp +++ b/Firmware/ultralcd.cpp @@ -3193,7 +3193,7 @@ lcd_wait_for_click_delay(0); //! @retval -1 screen timed out int8_t lcd_show_multiscreen_message_yes_no_and_wait_P(const char *msg, bool allow_timeouting, bool default_yes) //currently just max. n*4 + 3 lines supported (set in language header files) { - return lcd_show_multiscreen_message_two_choices_and_wait_P(msg, allow_timeouting, default_yes, _T(MSG_YES), _T(MSG_NO), nullptr, 10); + return lcd_show_multiscreen_message_with_choices_and_wait_P(msg, allow_timeouting, default_yes, _T(MSG_YES), _T(MSG_NO), nullptr, 10); } //! @brief Show a two-choice prompt on the last line of the LCD //! @param first_selected Show first choice as selected if true, the second otherwise @@ -3202,10 +3202,10 @@ int8_t lcd_show_multiscreen_message_yes_no_and_wait_P(const char *msg, bool allo void lcd_show_two_choices_prompt_P(uint8_t first_selected, const char *first_choice, const char *second_choice, uint8_t second_col) { lcd_set_cursor(0, 3); - lcd_print(first_selected? '>': ' '); + lcd_print(first_selected == 0 ? '>': ' '); lcd_puts_P(first_choice); lcd_set_cursor(second_col, 3); - lcd_print(!first_selected? '>': ' '); + lcd_print(first_selected == 1 ? '>': ' '); lcd_puts_P(second_choice); } @@ -3224,14 +3224,18 @@ void lcd_show_three_choices_prompt_P(uint8_t selected, const char *first_choice, //! @brief Show single or multiple screen message with two possible choices and wait with possible timeout //! @param msg Message to show. If NULL, do not clear the screen and handle choice selection only. -//! @param allow_timeouting if true, allows time outing of the screen -//! @param default_first if true, fist choice is selected by default, otherwise second choice is preselected -//! @param first_choice text caption of first possible choice -//! @param second_choice text caption of second possible choice +//! @param allow_timeouting bool, if true, allows time outing of the screen +//! @param default_first uint8_t, Control which choice is selected first. 0: left most, 1: middle, 2: right most choice. The first choice is selected by default +//! @param first_choice text caption of first possible choice. Must be in PROGMEM +//! @param second_choice text caption of second possible choice. Must be in PROGMEM +//! @param third_choice text caption of second possible choice. Must be in PROGMEM. When not set to nullptr first_choice and second_choice may not be more than 5 characters long. +//! @param second_col column on LCD where second_choice starts +//! @param third_col column on LCD where second_choice starts +//! @retval 0 first choice selected by user //! @retval 1 first choice selected by user -//! @retval 0 second choice selected by user -//! @retval -1 screen timed out -int8_t lcd_show_multiscreen_message_two_choices_and_wait_P(const char *msg, bool allow_timeouting, bool default_first, +//! @retval 2 third choice selected by user +//! @retval -1 screen timed out (only possible if allow_timeouting is true) +int8_t lcd_show_multiscreen_message_with_choices_and_wait_P(const char *msg, bool allow_timeouting, bool default_first, const char *first_choice, const char *second_choice, const char *third_choice, uint8_t second_col, uint8_t third_col) { const char *msg_next = msg ? lcd_display_message_fullscreen_P(msg) : NULL; @@ -3242,7 +3246,7 @@ int8_t lcd_show_multiscreen_message_two_choices_and_wait_P(const char *msg, bool uint8_t yes = default_first ? 1 : 0; if (!msg_next) { if (third_choice) - { // third_choice is not nullptr, safe to derefence + { // third_choice is not nullptr, safe to dereference lcd_show_three_choices_prompt_P(yes, first_choice, second_choice, third_choice, second_col, third_col); } else { lcd_show_two_choices_prompt_P(yes, first_choice, second_choice, second_col); @@ -3265,21 +3269,24 @@ int8_t lcd_show_multiscreen_message_two_choices_and_wait_P(const char *msg, bool if (abs(enc_dif - lcd_encoder_diff) >= ENCODER_PULSES_PER_STEP) { if (msg_next == NULL) { - if (enc_dif > lcd_encoder_diff) { - // Rotating knob counter clockwise - if (yes != 0) yes = yes - 1; - } - - else if (enc_dif < lcd_encoder_diff) { - // Rotating knob clockwise - if (yes != 2) yes = yes + 1; - } - - // Render the options: if (third_choice) - { // third_choice is not nullptr, safe to derefence + { // third_choice is not nullptr, safe to dereference + if (enc_dif > lcd_encoder_diff && yes != 0) { + // Rotating knob counter clockwise + yes = yes - 1; + } else if (enc_dif < lcd_encoder_diff && yes != 2) { + // Rotating knob clockwise + yes = yes + 1; + } lcd_show_three_choices_prompt_P(yes, first_choice, second_choice, third_choice, second_col, third_col); } else { + if (enc_dif > lcd_encoder_diff && yes != 0) { + // Rotating knob counter clockwise + yes = 0; + } else if (enc_dif < lcd_encoder_diff && yes != 1) { + // Rotating knob clockwise + yes = 1; + } lcd_show_two_choices_prompt_P(yes, first_choice, second_choice, second_col); } enc_dif = lcd_encoder_diff; @@ -3309,7 +3316,7 @@ int8_t lcd_show_multiscreen_message_two_choices_and_wait_P(const char *msg, bool } if (msg_next == NULL) { if (third_choice) - { // third_choice is not nullptr, safe to derefence + { // third_choice is not nullptr, safe to dereference lcd_show_three_choices_prompt_P(yes, first_choice, second_choice, third_choice, second_col, third_col); } else { lcd_show_two_choices_prompt_P(yes, first_choice, second_choice, second_col); diff --git a/Firmware/ultralcd.h b/Firmware/ultralcd.h index fb60ea61a..e4406eac3 100755 --- a/Firmware/ultralcd.h +++ b/Firmware/ultralcd.h @@ -75,7 +75,7 @@ extern void lcd_show_fullscreen_message_and_wait_P(const char *msg); extern int8_t lcd_show_yes_no_and_wait(bool allow_timeouting = true, bool default_yes = false); // 0: no, 1: yes, -1: timeouted extern int8_t lcd_show_fullscreen_message_yes_no_and_wait_P(const char *msg, bool allow_timeouting = true, bool default_yes = false); -extern int8_t lcd_show_multiscreen_message_two_choices_and_wait_P(const char *msg, bool allow_timeouting, bool default_yes, +extern int8_t lcd_show_multiscreen_message_with_choices_and_wait_P(const char *msg, bool allow_timeouting, bool default_yes, const char *first_choice, const char *second_choice, const char *third_choice = nullptr, uint8_t second_col = 7, uint8_t third_col = 13); extern int8_t lcd_show_multiscreen_message_yes_no_and_wait_P(const char *msg, bool allow_timeouting = true, bool default_yes = false); // Ask the user to move the Z axis up to the end stoppers and let