Browse Source

Touch UI additions, bug fixes (#17379)

vanilla_fb_2.0.x
Marcio T 4 years ago
committed by GitHub
parent
commit
be0e313c07
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp
  2. 12
      Marlin/src/feature/pause.cpp
  3. 7
      Marlin/src/lcd/extui/lib/ftdi_eve_touch_ui/marlin_events.cpp
  4. 268
      Marlin/src/lcd/extui/lib/ftdi_eve_touch_ui/screens/bed_mesh_screen.cpp
  5. 20
      Marlin/src/lcd/extui/lib/ftdi_eve_touch_ui/screens/main_menu.cpp
  6. 6
      Marlin/src/lcd/extui/lib/ftdi_eve_touch_ui/screens/screen_data.h
  7. 3
      Marlin/src/lcd/extui/lib/ftdi_eve_touch_ui/screens/screens.cpp
  8. 30
      Marlin/src/lcd/extui/lib/ftdi_eve_touch_ui/screens/screens.h
  9. 14
      Marlin/src/lcd/extui/ui_api.cpp
  10. 4
      Marlin/src/lcd/extui/ui_api.h
  11. 4
      Marlin/src/lcd/extui_dgus_lcd.cpp
  12. 4
      Marlin/src/lcd/extui_example.cpp
  13. 6
      Marlin/src/lcd/language/language_en.h

4
Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp

@ -776,12 +776,16 @@
: find_closest_mesh_point_of_type(INVALID, near, true);
if (best.pos.x >= 0) { // mesh point found and is reachable by probe
#if ENABLED(EXTENSIBLE_UI)
ExtUI::onMeshUpdate(best.pos, ExtUI::PROBE_START);
#endif
const float measured_z = probe.probe_at_point(
best.meshpos(),
stow_probe ? PROBE_PT_STOW : PROBE_PT_RAISE, g29_verbose_level
);
z_values[best.pos.x][best.pos.y] = measured_z;
#if ENABLED(EXTENSIBLE_UI)
ExtUI::onMeshUpdate(best.pos, ExtUI::PROBE_FINISH);
ExtUI::onMeshUpdate(best.pos, measured_z);
#endif
}

12
Marlin/src/feature/pause.cpp

@ -499,10 +499,10 @@ void wait_for_confirmation(const bool is_reload/*=false*/, const int8_t max_beep
// Wait for filament insert by user and press button
KEEPALIVE_STATE(PAUSED_FOR_USER);
#if ENABLED(HOST_PROMPT_SUPPORT)
host_prompt_do(PROMPT_USER_CONTINUE, PSTR("Nozzle Parked"), CONTINUE_STR);
host_prompt_do(PROMPT_USER_CONTINUE, GET_TEXT(MSG_NOZZLE_PARKED), CONTINUE_STR);
#endif
#if ENABLED(EXTENSIBLE_UI)
ExtUI::onUserConfirmRequired_P(PSTR("Nozzle Parked"));
ExtUI::onUserConfirmRequired_P(GET_TEXT(MSG_NOZZLE_PARKED));
#endif
wait_for_user = true; // LCD click or M108 will clear this
while (wait_for_user) {
@ -523,20 +523,20 @@ void wait_for_confirmation(const bool is_reload/*=false*/, const int8_t max_beep
SERIAL_ECHO_MSG(_PMSG(STR_FILAMENT_CHANGE_HEAT));
#if ENABLED(HOST_PROMPT_SUPPORT)
host_prompt_do(PROMPT_USER_CONTINUE, PSTR("HeaterTimeout"), PSTR("Reheat"));
host_prompt_do(PROMPT_USER_CONTINUE, GET_TEXT(MSG_HEATER_TIMEOUT), GET_TEXT(MSG_REHEAT));
#endif
#if ENABLED(EXTENSIBLE_UI)
ExtUI::onUserConfirmRequired_P(PSTR("HeaterTimeout"));
ExtUI::onUserConfirmRequired_P(GET_TEXT(MSG_HEATER_TIMEOUT));
#endif
wait_for_user_response(0, true); // Wait for LCD click or M108
#if ENABLED(HOST_PROMPT_SUPPORT)
host_prompt_do(PROMPT_INFO, PSTR("Reheating"));
host_prompt_do(PROMPT_INFO, GET_TEXT(MSG_REHEATING));
#endif
#if ENABLED(EXTENSIBLE_UI)
ExtUI::onStatusChanged(PSTR("Reheating..."));
ExtUI::onStatusChanged(GET_TEXT(MSG_REHEATING));
#endif
// Re-enable the heaters if they timed out

7
Marlin/src/lcd/extui/lib/ftdi_eve_touch_ui/marlin_events.cpp

@ -127,7 +127,12 @@ namespace ExtUI {
}
#if HAS_LEVELING && HAS_MESH
void onMeshUpdate(const int8_t, const int8_t, const float) {
void onMeshUpdate(const int8_t x, const int8_t y, const float val) {
BedMeshScreen::onMeshUpdate(x, y, val);
}
void onMeshUpdate(const int8_t x, const int8_t y, const ExtUI::probe_state_t state) {
BedMeshScreen::onMeshUpdate(x, y, state);
}
#endif

268
Marlin/src/lcd/extui/lib/ftdi_eve_touch_ui/screens/bed_mesh_screen.cpp

@ -0,0 +1,268 @@
/***********************
* bed_mesh_screen.cpp *
***********************/
/****************************************************************************
* Written By Marcio Teixeira 2020 *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* To view a copy of the GNU General Public License, go to the following *
* location: <http://www.gnu.org/licenses/>. *
****************************************************************************/
#include "../config.h"
#if ENABLED(TOUCH_UI_FTDI_EVE) && HAS_MESH
#include "screens.h"
#include "screen_data.h"
using namespace FTDI;
using namespace Theme;
using namespace ExtUI;
#ifdef TOUCH_UI_PORTRAIT
#define GRID_COLS 2
#define GRID_ROWS 10
#define MESH_POS BTN_POS(1, 2), BTN_SIZE(2,5)
#define Z_LABEL_POS BTN_POS(1, 8), BTN_SIZE(1,1)
#define Z_VALUE_POS BTN_POS(2, 8), BTN_SIZE(1,1)
#define WAIT_POS BTN_POS(1, 8), BTN_SIZE(2,1)
#define BACK_POS BTN_POS(1,10), BTN_SIZE(2,1)
#else
#define GRID_COLS 5
#define GRID_ROWS 5
#define MESH_POS BTN_POS(2,1), BTN_SIZE(4,5)
#define Z_LABEL_POS BTN_POS(1,3), BTN_SIZE(1,1)
#define Z_VALUE_POS BTN_POS(1,4), BTN_SIZE(2,1)
#define WAIT_POS BTN_POS(1,3), BTN_SIZE(2,2)
#define BACK_POS BTN_POS(1,5), BTN_SIZE(2,1)
#endif
void BedMeshScreen::drawMesh(int16_t x, int16_t y, int16_t w, int16_t h, ExtUI::bed_mesh_t data, uint8_t opts) {
CommandProcessor cmd;
#define TRANSFORM_2(X,Y,Z) (X), (Y) // No transform
#define TRANSFORM_1(X,Y,Z) TRANSFORM_2((X) + (Y) * slant, (Y) - (Z), 0) // Perspective
#define TRANSFORM(X,Y,Z) TRANSFORM_1(float(X)/(cols-1) - 0.5, float(Y)/(rows-1) - 0.5, (Z)) // Normalize
constexpr uint8_t rows = GRID_MAX_POINTS_Y;
constexpr uint8_t cols = GRID_MAX_POINTS_X;
const float slant = 0.5;
const float bounds_min[] = {TRANSFORM(0 ,0 ,0)};
const float bounds_max[] = {TRANSFORM(cols,rows,0)};
const float scale_x = float(w)/(bounds_max[0] - bounds_min[0]);
const float scale_y = float(h)/(bounds_max[1] - bounds_min[1]);
const float center_x = x + w/2;
const float center_y = y + h/2;
float val_mean = 0;
float val_max = -INFINITY;
float val_min = INFINITY;
uint8_t val_cnt = 0;
if (opts & USE_AUTOSCALE) {
// Compute the mean
for (uint8_t y = 0; y < rows; y++) {
for (uint8_t x = 0; x < cols; x++) {
const float val = data[x][y];
if (!isnan(val)) {
val_mean += val;
val_max = max(val_max, val);
val_min = min(val_min, val);
val_cnt++;
}
}
}
if (val_cnt) {
val_mean /= val_cnt;
val_min -= val_mean;
val_max -= val_mean;
} else {
val_mean = 0;
val_min = 0;
val_max = 0;
}
}
const float scale_z = ((val_max == val_min) ? 1 : 1/(val_max - val_min)) * 0.1;
#undef TRANSFORM_2
#define TRANSFORM_2(X,Y,Z) center_x + (X) * scale_x, center_y + (Y) * scale_y // Scale and position
#define VALUE(X,Y) ((data && ISVAL(X,Y)) ? data[X][Y] : 0)
#define ISVAL(X,Y) (data ? !isnan(data[X][Y]) : true)
#define HEIGHT(X,Y) (VALUE(X,Y) * scale_z)
uint16_t basePointSize = min(scale_x,scale_y) / max(cols,rows);
cmd.cmd(SAVE_CONTEXT())
.cmd(VERTEX_FORMAT(0))
.cmd(TAG_MASK(false))
.cmd(SAVE_CONTEXT());
for (uint8_t y = 0; y < rows; y++) {
for (uint8_t x = 0; x < cols; x++) {
if (ISVAL(x,y)) {
const bool hasLeftSegment = x < cols - 1 && ISVAL(x+1,y);
const bool hasRightSegment = y < rows - 1 && ISVAL(x,y+1);
if (hasLeftSegment || hasRightSegment) {
cmd.cmd(BEGIN(LINE_STRIP));
if (hasLeftSegment) cmd.cmd(VERTEX2F(TRANSFORM(x + 1, y , HEIGHT(x + 1, y ))));
cmd.cmd( VERTEX2F(TRANSFORM(x , y , HEIGHT(x , y ))));
if (hasRightSegment) cmd.cmd(VERTEX2F(TRANSFORM(x , y + 1, HEIGHT(x , y + 1))));
}
}
}
if (opts & USE_POINTS) {
cmd.cmd(POINT_SIZE(basePointSize * 2));
cmd.cmd(BEGIN(POINTS));
for (uint8_t x = 0; x < cols; x++) {
if (ISVAL(x,y)) {
if (opts & USE_COLORS) {
const float val_dev = VALUE(x, y) - val_mean;
const uint8_t neg_byte = sq(val_dev) / sq(val_dev < 0 ? val_min : val_max) * 0xFF;
const uint8_t pos_byte = 255 - neg_byte;
cmd.cmd(COLOR_RGB(pos_byte, pos_byte, 0xFF));
}
cmd.cmd(VERTEX2F(TRANSFORM(x, y, HEIGHT(x, y))));
}
}
if (opts & USE_COLORS) {
cmd.cmd(RESTORE_CONTEXT())
.cmd(SAVE_CONTEXT());
}
}
}
cmd.cmd(RESTORE_CONTEXT())
.cmd(TAG_MASK(true));
if (opts & USE_TAGS) {
cmd.cmd(COLOR_MASK(false, false, false, false))
.cmd(POINT_SIZE(basePointSize * 10))
.cmd(BEGIN(POINTS));
for (uint8_t y = 0; y < rows; y++) {
for (uint8_t x = 0; x < cols; x++) {
const uint8_t tag = pointToTag(x, y);
cmd.tag(tag).cmd(VERTEX2F(TRANSFORM(x, y, HEIGHT(x, y))));
}
}
cmd.cmd(COLOR_MASK(true, true, true, true));
}
if (opts & USE_HIGHLIGHT) {
const uint8_t tag = screen_data.BedMeshScreen.highlightedTag;
uint8_t x, y;
tagToPoint(tag, x, y);
cmd.cmd(COLOR_A(128))
.cmd(POINT_SIZE(basePointSize * 6))
.cmd(BEGIN(POINTS))
.tag(tag).cmd(VERTEX2F(TRANSFORM(x, y, HEIGHT(x, y))));
}
cmd.cmd(END());
cmd.cmd(RESTORE_CONTEXT());
}
uint8_t BedMeshScreen::pointToTag(uint8_t x, uint8_t y) {
return y * (GRID_MAX_POINTS_X) + x + 10;
}
void BedMeshScreen::tagToPoint(uint8_t tag, uint8_t &x, uint8_t &y) {
x = (tag - 10) % (GRID_MAX_POINTS_X);
y = (tag - 10) / (GRID_MAX_POINTS_X);
}
void BedMeshScreen::onEntry() {
screen_data.BedMeshScreen.highlightedTag = 0;
screen_data.BedMeshScreen.count = 0;
BaseScreen::onEntry();
}
float BedMeshScreen::getHightlightedValue() {
if (screen_data.BedMeshScreen.highlightedTag) {
xy_uint8_t pt;
tagToPoint(screen_data.BedMeshScreen.highlightedTag, pt.x, pt.y);
return ExtUI::getMeshPoint(pt);
}
return NAN;
}
void BedMeshScreen::drawHighlightedPointValue() {
char str[16];
const float val = getHightlightedValue();
const bool isGood = !isnan(val);
if (isGood)
dtostrf(val, 5, 3, str);
else
strcpy_P(str, PSTR("-"));
CommandProcessor cmd;
cmd.font(Theme::font_medium)
.text(Z_LABEL_POS, GET_TEXT_F(MSG_MESH_EDIT_Z))
.text(Z_VALUE_POS, str)
.colors(action_btn)
.tag(1).button( BACK_POS, GET_TEXT_F(MSG_BACK))
.tag(0);
}
void BedMeshScreen::onRedraw(draw_mode_t what) {
if (what & BACKGROUND) {
CommandProcessor cmd;
cmd.cmd(CLEAR_COLOR_RGB(bg_color))
.cmd(CLEAR(true,true,true));
// Draw the shadow and tags
cmd.cmd(COLOR_RGB(0x444444));
BedMeshScreen::drawMesh(MESH_POS, nullptr, USE_POINTS | USE_TAGS);
cmd.cmd(COLOR_RGB(bg_text_enabled));
}
if (what & FOREGROUND) {
const bool levelingFinished = screen_data.BedMeshScreen.count >= GRID_MAX_POINTS;
if (levelingFinished) drawHighlightedPointValue();
BedMeshScreen::drawMesh(MESH_POS, ExtUI::getMeshArray(),
USE_POINTS | USE_HIGHLIGHT | USE_AUTOSCALE | (levelingFinished ? USE_COLORS : 0));
}
}
bool BedMeshScreen::onTouchStart(uint8_t tag) {
screen_data.BedMeshScreen.highlightedTag = tag;
return true;
}
bool BedMeshScreen::onTouchEnd(uint8_t tag) {
switch(tag) {
case 1:
GOTO_PREVIOUS();
return true;
default:
return false;
}
}
void BedMeshScreen::onMeshUpdate(const int8_t, const int8_t, const float) {
if (AT_SCREEN(BedMeshScreen))
onRefresh();
}
void BedMeshScreen::onMeshUpdate(const int8_t x, const int8_t y, const ExtUI::probe_state_t state) {
if (state == ExtUI::PROBE_FINISH) {
screen_data.BedMeshScreen.highlightedTag = pointToTag(x, y);
screen_data.BedMeshScreen.count++;
}
BedMeshScreen::onMeshUpdate(x, y, 0);
}
#endif // TOUCH_UI_FTDI_EVE

20
Marlin/src/lcd/extui/lib/ftdi_eve_touch_ui/screens/main_menu.cpp

@ -146,14 +146,18 @@ bool MainMenu::onTouchEnd(uint8_t tag) {
#ifdef AXIS_LEVELING_COMMANDS
case 9: SpinnerDialogBox::enqueueAndWait_P(F(AXIS_LEVELING_COMMANDS)); break;
#endif
#ifdef HAS_LEVELING
case 10: SpinnerDialogBox::enqueueAndWait_P(F(
#ifdef BED_LEVELING_COMMANDS
BED_LEVELING_COMMANDS
#else
"G29"
#endif
)); break;
#if HAS_LEVELING
case 10:
#ifndef BED_LEVELING_COMMANDS
#define BED_LEVELING_COMMANDS "G29"
#endif
#if HAS_MESH
GOTO_SCREEN(BedMeshScreen);
injectCommands_P(PSTR(BED_LEVELING_COMMANDS));
#else
SpinnerDialogBox::enqueueAndWait_P(F(BED_LEVELING_COMMANDS));
#endif
break;
#endif
case 11: GOTO_SCREEN(AboutScreen); break;
default:

6
Marlin/src/lcd/extui/lib/ftdi_eve_touch_ui/screens/screen_data.h

@ -60,6 +60,12 @@ union screen_data_t {
struct base_numeric_adjustment_t placeholder;
float e_rel[ExtUI::extruderCount];
} MoveAxisScreen;
#if HAS_MESH
struct {
uint8_t count;
uint8_t highlightedTag;
} BedMeshScreen;
#endif
#if ENABLED(TOUCH_UI_DEVELOPER_MENU)
struct {
uint32_t next_watchdog_trigger;

3
Marlin/src/lcd/extui/lib/ftdi_eve_touch_ui/screens/screens.cpp

@ -55,6 +55,9 @@ SCREEN_TABLE {
#endif
#if ENABLED(BABYSTEPPING)
DECL_SCREEN(NudgeNozzleScreen),
#endif
#if HAS_MESH
DECL_SCREEN(BedMeshScreen),
#endif
DECL_SCREEN(MoveAxisScreen),
DECL_SCREEN(StepsScreen),

30
Marlin/src/lcd/extui/lib/ftdi_eve_touch_ui/screens/screens.h

@ -55,6 +55,9 @@ enum {
MAX_VELOCITY_SCREEN_CACHE,
MAX_ACCELERATION_SCREEN_CACHE,
DEFAULT_ACCELERATION_SCREEN_CACHE,
#if HAS_MESH
BED_MESH_SCREEN_CACHE,
#endif
#if DISABLED(CLASSIC_JERK)
JUNC_DEV_SCREEN_CACHE,
#else
@ -130,6 +133,33 @@ class AboutScreen : public BaseScreen, public UncachedScreen {
static bool onTouchEnd(uint8_t tag);
};
#if HAS_MESH
class BedMeshScreen : public BaseScreen, public CachedScreen<BED_MESH_SCREEN_CACHE> {
private:
enum MeshOpts {
USE_POINTS = 0x01,
USE_COLORS = 0x02,
USE_TAGS = 0x04,
USE_HIGHLIGHT = 0x08,
USE_AUTOSCALE = 0x10
};
static uint8_t pointToTag(uint8_t x, uint8_t y);
static void tagToPoint(uint8_t tag, uint8_t &x, uint8_t &y);
static float getHightlightedValue();
static void drawHighlightedPointValue();
static void drawMesh(int16_t x, int16_t y, int16_t w, int16_t h, ExtUI::bed_mesh_t data, uint8_t opts);
public:
static void onMeshUpdate(const int8_t x, const int8_t y, const float val);
static void onMeshUpdate(const int8_t x, const int8_t y, const ExtUI::probe_state_t);
static void onEntry();
static void onRedraw(draw_mode_t);
static bool onTouchStart(uint8_t tag);
static bool onTouchEnd(uint8_t tag);
};
#endif
#if ENABLED(PRINTCOUNTER)
class StatisticsScreen : public BaseScreen, public UncachedScreen {
public:

14
Marlin/src/lcd/extui/ui_api.cpp

@ -112,6 +112,9 @@ namespace ExtUI {
#if ENABLED(JOYSTICK)
uint8_t jogging : 1;
#endif
#if ENABLED(SDSUPPORT)
uint8_t was_sd_printing : 1;
#endif
} flags;
#ifdef __SAM3X8E__
@ -1032,11 +1035,18 @@ namespace ExtUI {
}
bool isPrintingFromMedia() {
return IFSD(card.isFileOpen(), false);
#if ENABLED(SDSUPPORT)
// Account for when IS_SD_PRINTING() reports the end of the
// print when there is still SD card data in the planner.
flags.was_sd_printing = card.isFileOpen() || (flags.was_sd_printing && commandsInQueue());
return flags.was_sd_printing;
#else
return false;
#endif
}
bool isPrinting() {
return (planner.movesplanned() || isPrintingFromMedia() || IFSD(IS_SD_PRINTING(), false));
return (commandsInQueue() || isPrintingFromMedia() || IFSD(IS_SD_PRINTING(), false));
}
bool isMediaInserted() {

4
Marlin/src/lcd/extui/ui_api.h

@ -141,6 +141,10 @@ namespace ExtUI {
void setMeshPoint(const xy_uint8_t &pos, const float zval);
void onMeshUpdate(const int8_t xpos, const int8_t ypos, const float zval);
inline void onMeshUpdate(const xy_int8_t &pos, const float zval) { onMeshUpdate(pos.x, pos.y, zval); }
typedef enum : unsigned char { PROBE_START, PROBE_FINISH } probe_state_t;
void onMeshUpdate(const int8_t xpos, const int8_t ypos, probe_state_t state);
inline void onMeshUpdate(const xy_int8_t &pos, probe_state_t state) { onMeshUpdate(pos.x, pos.y, state); }
#endif
#endif

4
Marlin/src/lcd/extui_dgus_lcd.cpp

@ -122,6 +122,10 @@ namespace ExtUI {
// Called when any mesh points are updated
}
void onMeshUpdate(const int8_t xpos, const int8_t ypos, const ExtUI::probe_state_t state) {
// Called to indicate a special condition
}
#if ENABLED(POWER_LOSS_RECOVERY)
void onPowerLossResume() {
// Called on resume from power-loss

4
Marlin/src/lcd/extui_example.cpp

@ -93,6 +93,10 @@ namespace ExtUI {
// Called when any mesh points are updated
}
void onMeshUpdate(const int8_t xpos, const int8_t ypos, const ExtUI::probe_state_t state) {
// Called to indicate a special condition
}
#if ENABLED(POWER_LOSS_RECOVERY)
void onPowerLossResume() {
// Called on resume from power-loss

6
Marlin/src/lcd/language/language_en.h

@ -240,6 +240,8 @@ namespace Language_en {
PROGMEM Language_Str MSG_BED_Z = _UxGT("Bed Z");
PROGMEM Language_Str MSG_NOZZLE = _UxGT("Nozzle");
PROGMEM Language_Str MSG_NOZZLE_N = _UxGT("Nozzle ~");
PROGMEM Language_Str MSG_NOZZLE_PARKED = _UxGT("Nozzle Parked");
PROGMEM Language_Str MSG_NOZZLE_STANDBY = _UxGT("Nozzle Standby");
PROGMEM Language_Str MSG_BED = _UxGT("Bed");
PROGMEM Language_Str MSG_CHAMBER = _UxGT("Enclosure");
PROGMEM Language_Str MSG_FAN_SPEED = _UxGT("Fan Speed");
@ -371,7 +373,6 @@ namespace Language_en {
PROGMEM Language_Str MSG_TOOL_CHANGE_ZLIFT = _UxGT("Z Raise");
PROGMEM Language_Str MSG_SINGLENOZZLE_PRIME_SPD = _UxGT("Prime Speed");
PROGMEM Language_Str MSG_SINGLENOZZLE_RETRACT_SPD = _UxGT("Retract Speed");
PROGMEM Language_Str MSG_NOZZLE_STANDBY = _UxGT("Nozzle Standby");
PROGMEM Language_Str MSG_FILAMENTCHANGE = _UxGT("Change Filament");
PROGMEM Language_Str MSG_FILAMENTCHANGE_E = _UxGT("Change Filament *");
PROGMEM Language_Str MSG_FILAMENTLOAD = _UxGT("Load Filament");
@ -605,6 +606,9 @@ namespace Language_en {
PROGMEM Language_Str MSG_LEVEL_X_AXIS = _UxGT("Level X Axis");
PROGMEM Language_Str MSG_AUTO_CALIBRATE = _UxGT("Auto Calibrate");
PROGMEM Language_Str MSG_HEATER_TIMEOUT = _UxGT("Heater Timeout");
PROGMEM Language_Str MSG_REHEAT = _UxGT("Reheat");
PROGMEM Language_Str MSG_REHEATING = _UxGT("Reheating...");
}
#if FAN_COUNT == 1

Loading…
Cancel
Save