diff --git a/README.md b/README.md index f6fbc4cdd9..d29d01b69c 100644 --- a/README.md +++ b/README.md @@ -85,9 +85,10 @@ Only the TFTs listed below are currently supported. Trying to install the firmwa **MKS TFT** MKS_TFT28_V3.0 and V4.0 + MKS_TFT28_NEW_GENIUS MKS_TFT32_V1.3 and V1.4 - MKS_TFT32L_V3_0 - MKS_TFT35_V1_0 + MKS_TFT32L_V3.0 + MKS_TFT35_V1.0 **MKS TFT with GigaDevice MCUs** @@ -457,23 +458,39 @@ Please, see [Customization Guides](https://github.com/bigtreetech/BIGTREETECH-To
  • After opening the project, edit platformio.ini and change the default_envs to one that matches your TFT model and version: -
    ;BIGTREE_TFT35_V1_0
    +
    ;BIGTREE_TFT24_V1_1
    +;BIGTREE_TFT28_V1_0
    +;BIGTREE_TFT28_V3_0
    +;BIGTREE_TFT35_V1_0
     ;BIGTREE_TFT35_V1_1
     ;BIGTREE_TFT35_V1_2
     ;BIGTREE_TFT35_V2_0
     ;BIGTREE_TFT35_V3_0
     ;BIGTREE_TFT35_E3_V3_0
    -;BIGTREE_TFT28_V1_0
    -;BIGTREE_TFT28_V3_0
    -;BIGTREE_TFT24_V1_1
    -;MKS_TFT32_V1_3
    -;MKS_TFT32_V1_4
    -;MKS_TFT32_V1_4_NOBL
    +;BIGTREE_TFT35_B1_V3_0
    +;BIGTREE_TFT43_V3_0
    +;BIGTREE_TFT50_V3_0
    +;BIGTREE_TFT70_V3_0
    +
    +;BIGTREE_GD_TFT24_V1_1
    +;BIGTREE_GD_TFT35_V2_0
    +;BIGTREE_GD_TFT35_V3_0
    +;BIGTREE_GD_TFT35_E3_V3_0
    +;BIGTREE_GD_TFT35_B1_V3_0
    +;BIGTREE_GD_TFT43_V3_0
    +;BIGTREE_GD_TFT50_V3_0
    +;BIGTREE_GD_TFT70_V3_0
    +
     ;MKS_TFT28_V3_0
     ;MKS_TFT28_V4_0
     ;MKS_TFT28_NEW_GENIUS
    +;MKS_TFT32_V1_3
    +;MKS_TFT32_V1_4
    +;MKS_TFT32_V1_4_NOBL
    +;MKS_TFT32L_V3_0
     ;MKS_TFT35_V1_0
     ;MKS_GD_TFT28_V1_2-4
    +
     [platformio]
     src_dir      = TFT
     boards_dir   = buildroot/boards
    diff --git a/TFT/src/User/API/AddonHardware.c b/TFT/src/User/API/AddonHardware.c
    index f393d5c8ac..027003550e 100644
    --- a/TFT/src/User/API/AddonHardware.c
    +++ b/TFT/src/User/API/AddonHardware.c
    @@ -3,25 +3,22 @@
     #include "GPIO_Init.h"
     
     //
    -// Power Supply
    +// power supply
     //
     
     #ifdef PS_ON_PIN
     
    -// Power Supply Control pins Initialization
     void PS_ON_Init(void)
     {
       GPIO_InitSet(PS_ON_PIN, MGPIO_MODE_OUT_PP, 0);
       GPIO_SetLevel(PS_ON_PIN, infoSettings.ps_active_high);
     }
     
    -// Power Supply Control turn on, M80
     void PS_ON_On(void)
     {
       GPIO_SetLevel(PS_ON_PIN, infoSettings.ps_active_high);
     }
     
    -// Power Supply Control turn off, M81
     void PS_ON_Off(void)
     {
       GPIO_SetLevel(PS_ON_PIN, !infoSettings.ps_active_high);
    @@ -30,20 +27,23 @@ void PS_ON_Off(void)
     #endif  // PS_ON_PIN
     
     //
    -// Filament runout detection
    +// filament runout detection
     //
     
     #ifdef FIL_RUNOUT_PIN
     
    +#define FIL_POS_E_REFRESH_TIME  2000
    +#define FIL_ALARM_REMINDER_TIME 10000
    +
     enum
     {
       FILAMENT_SENSOR_NORMAL,
       FILAMENT_SENSOR_SMART,
     };
     
    -static uint32_t nextUpdateTime = FIL_ALARM_REMINDER_TIME;  // Give TFT time to connect to mainboard first before polling for runout
    -static bool posE_updateWaiting = false;
    -static bool sfs_alive = false;  // Use an encoder disc to toggles the runout. Suitable for BigTreeTech Smart Filament Sensor
    +static uint32_t posE_nextUpdateTime = FIL_ALARM_REMINDER_TIME;  // give TFT time to connect to mainboard first before polling for runout
    +static bool posE_sendingWaiting = false;
    +static bool sfs_alive = false;  // use an encoder disc to toggles the runout. Suitable for BigTreeTech Smart Filament Sensor
     
     void FIL_Runout_Init(void)
     {
    @@ -73,15 +73,9 @@ void FIL_Runout_Init(void)
       #endif
     }
     
    -static inline void FIL_SetNextUpdateTime(uint32_t timeInterval)
    -{
    -  nextUpdateTime = OS_GetTimeMs() + timeInterval;
    -}
    -
    -// Set whether we need to query the current position
    -void FIL_PosE_SetUpdateWaiting(bool waiting)
    +void FIL_PosE_ClearSendingWaiting(void)
     {
    -  posE_updateWaiting = waiting;
    +  posE_sendingWaiting = false;
     }
     
     void FIL_SFS_SetAlive(bool alive)
    @@ -93,14 +87,9 @@ bool FIL_NormalRunoutDetect(void)
     {
       static bool runout = false;
       static int32_t trigBalance = 0;
    +  static uint32_t nextUpdateTime = 0;
     
    -  if (OS_GetTimeMs() > nextUpdateTime)
    -  {
    -    runout = (trigBalance > 0);
    -    trigBalance = 0;
    -    FIL_SetNextUpdateTime(infoSettings.runout_noise);
    -  }
    -  else
    +  if (OS_GetTimeMs() < nextUpdateTime)
       {
         bool pinState = false;
         uint8_t toolNum = heatGetToolIndex();
    @@ -116,34 +105,47 @@ bool FIL_NormalRunoutDetect(void)
               pinState = GPIO_GetLevel(FIL_RUNOUT_PIN_1);
               break;
           #endif
    +
           #ifdef FIL_RUNOUT_PIN_2
             case 2:
               pinState = GPIO_GetLevel(FIL_RUNOUT_PIN_2);
               break;
           #endif
    +
           #ifdef FIL_RUNOUT_PIN_3
             case 3:
               pinState = GPIO_GetLevel(FIL_RUNOUT_PIN_3);
               break;
           #endif
    +
           #ifdef FIL_RUNOUT_PIN_4
             case 4:
               pinState = GPIO_GetLevel(FIL_RUNOUT_PIN_4);
               break;
           #endif
    +
           #ifdef FIL_RUNOUT_PIN_5
             case 5:
               pinState = GPIO_GetLevel(FIL_RUNOUT_PIN_5);
               break;
           #endif
    +
           default:
             pinState = GPIO_GetLevel(FIL_RUNOUT_PIN);
             break;
         }
     
         trigBalance += (pinState == GET_BIT(infoSettings.runout, RUNOUT_INVERTED)) ? 1: -1;  // if triggered add 1 else substract 1
    +
    +    return runout;
       }
     
    +  // if OS_GetTimeMs() >= nextUpdateTime
    +
    +  runout = (trigBalance > 0);
    +  trigBalance = 0;
    +  nextUpdateTime = OS_GetTimeMs() + infoSettings.runout_noise;
    +
       return runout;
     }
     
    @@ -156,34 +158,23 @@ static inline bool FIL_SmartRunoutDetect(void)
       bool runout = FIL_NormalRunoutDetect();
     
       do
    -  {
    -    // Send M114 E to query extrude position continuously
    -    if (posE_updateWaiting == true)
    -    {
    -      FIL_SetNextUpdateTime(FIL_POS_E_UPDATE_TIME);
    -      break;
    -    }
    +  { // send M114 E to query extrude position continuously
     
    -    if (OS_GetTimeMs() < nextUpdateTime)
    +    if (OS_GetTimeMs() < posE_nextUpdateTime)  // if next check time not yet elapsed, do nothing
           break;
     
    -    if (requestCommandInfoIsRunning())  // To avoid collision in gcode response processing
    -      break;
    +    posE_nextUpdateTime = OS_GetTimeMs() + FIL_POS_E_REFRESH_TIME;  // extend next check time
     
    -    if (!storeCmd("M114 E\n"))
    +    // if M114 previously enqueued and not yet sent or pending command
    +    // (to avoid collision in gcode response processing), do nothing
    +    if (posE_sendingWaiting || requestCommandInfoIsRunning())
           break;
     
    -    FIL_SetNextUpdateTime(FIL_POS_E_UPDATE_TIME);
    -    posE_updateWaiting = true;
    +    posE_sendingWaiting = storeCmd("M114 E\n");
       } while (0);
     
    -  if (sfs_alive == false)
    -  {
    -    if (lastRunout != runout)
    -    {
    -      sfs_alive = true;
    -    }
    -  }
    +  if (!sfs_alive && lastRunout != runout)
    +    sfs_alive = true;
     
       if (ABS(posE - lastPosE) >= infoSettings.runout_distance)
       {
    @@ -220,10 +211,10 @@ static inline bool FIL_IsRunout(void)
     
     void FIL_BE_CheckRunout(void)
     {
    -  if (!GET_BIT(infoSettings.runout, RUNOUT_ENABLED))  // Filament runout turned off
    +  if (!GET_BIT(infoSettings.runout, RUNOUT_ENABLED))  // filament runout turned off
         return;
     
    -  setPrintRunout(FIL_IsRunout());  // Need constant scanning to filter interference
    +  setPrintRunout(FIL_IsRunout());  // need constant scanning to filter interference
     }
     
     void FIL_FE_CheckRunout(void)
    @@ -233,13 +224,13 @@ void FIL_FE_CheckRunout(void)
       if (!getPrintRunout() && !getRunoutAlarm())
         return;
     
    -  if (pausePrint(true, PAUSE_NORMAL) && !getRunoutAlarm())  // If not printing, pausePrint() function will always fail
    +  if (pausePrint(true, PAUSE_NORMAL) && !getRunoutAlarm())  // if not printing, pausePrint() function will always fail
       {                                                         // so no useless error message is displayed
         setRunoutAlarmTrue();
         popupDialog(DIALOG_TYPE_ALERT, LABEL_WARNING, LABEL_FILAMENT_RUNOUT, LABEL_CONFIRM, LABEL_NULL, setRunoutAlarmFalse, NULL, NULL);
       }
     
    -  if ((OS_GetTimeMs() > nextReminderTime) && (getRunoutAlarm() == true))
    +  if (OS_GetTimeMs() >= nextReminderTime && getRunoutAlarm())
       {
         BUZZER_PLAY(SOUND_ERROR);
         nextReminderTime = OS_GetTimeMs() + FIL_ALARM_REMINDER_TIME;
    diff --git a/TFT/src/User/API/AddonHardware.h b/TFT/src/User/API/AddonHardware.h
    index 1023ea6f58..063d7a767b 100644
    --- a/TFT/src/User/API/AddonHardware.h
    +++ b/TFT/src/User/API/AddonHardware.h
    @@ -8,23 +8,20 @@ extern "C" {
     #include 
     #include "variants.h"  // for PS_ON_PIN, FIL_RUNOUT_PIN etc.
     
    -// Power Supply
    +// power supply
     #ifdef PS_ON_PIN
    -  void PS_ON_Init(void);
    -  void PS_ON_On(void);
    -  void PS_ON_Off(void);
    +  void PS_ON_Init(void);  // power supply control pins initialization
    +  void PS_ON_On(void);    // power supply control turn on, M80
    +  void PS_ON_Off(void);   // power supply control turn off, M81
     #endif
     
    -// Filament runout detection
    +// filament runout detection
     #ifdef FIL_RUNOUT_PIN
    -  #define FIL_POS_E_UPDATE_TIME    2000
    -  #define FIL_ALARM_REMINDER_TIME 10000
    -
       void FIL_Runout_Init(void);
    -  void FIL_PosE_SetUpdateWaiting(bool waiting);
    +  void FIL_PosE_ClearSendingWaiting(void);  // clear sending waiting for position query
       void FIL_SFS_SetAlive(bool alive);
    -  void FIL_BE_CheckRunout(void);
    -  void FIL_FE_CheckRunout(void);
    +  void FIL_BE_CheckRunout(void);            // called in loopBackEnd()
    +  void FIL_FE_CheckRunout(void);            // called in loopFrontEnd()
     #endif
     
     #ifdef __cplusplus
    diff --git a/TFT/src/User/API/FanControl.c b/TFT/src/User/API/FanControl.c
    index 776d9b7c71..107de2447e 100644
    --- a/TFT/src/User/API/FanControl.c
    +++ b/TFT/src/User/API/FanControl.c
    @@ -1,17 +1,16 @@
     #include "FanControl.h"
     #include "includes.h"
     
    -#define NEXT_FAN_WAIT 500  // 1 second is 1000
    +#define FAN_REFRESH_TIME 500  // 1 second is 1000
     
    -const char* fanID[MAX_FAN_COUNT] = FAN_DISPLAY_ID;
    -const char* fanCmd[MAX_FAN_COUNT] = FAN_CMD;
    +const char * fanID[MAX_FAN_COUNT] = FAN_DISPLAY_ID;
    +const char * fanCmd[MAX_FAN_COUNT] = FAN_CMD;
     
     static uint8_t setFanSpeed[MAX_FAN_COUNT] = {0};
     static uint8_t curFanSpeed[MAX_FAN_COUNT] = {0};
     static uint8_t needSetFanSpeed = 0;
     
    -static bool ctrlFanQueryWait = false;
    -static uint32_t nextCtrlFanTime = 0;
    +static bool ctrlFanSendingWaiting = false;
     
     void fanResetSpeed(void)
     {
    @@ -20,7 +19,6 @@ void fanResetSpeed(void)
       memset(curFanSpeed, 0, sizeof(curFanSpeed));
     }
     
    -// Check whether the index is a valid fan index.
     bool fanIsValid(const uint8_t index)
     {
       if (index >= infoSettings.fan_count && index < MAX_COOLING_FAN_COUNT)  // invalid cooling fan index
    @@ -74,32 +72,32 @@ uint8_t fanGetCurPercent(const uint8_t i)
       return (curFanSpeed[i] * 100.0f) / infoSettings.fan_max[i] + 0.5f;
     }
     
    -void loopFan(void)
    +void loopCheckFan(void)
     {
    +  static uint32_t nextUpdateTime = 0;
    +
    +  if (OS_GetTimeMs() < nextUpdateTime)  // avoid rapid fire, clogging the queue
    +    return;
    +
    +  nextUpdateTime = OS_GetTimeMs() + FAN_REFRESH_TIME;  // extend next check time
    +
       for (uint8_t i = 0; i < MAX_FAN_COUNT; i++)
       {
    -    if (GET_BIT(needSetFanSpeed, i) && (OS_GetTimeMs() > nextCtrlFanTime))
    +    if (GET_BIT(needSetFanSpeed, i))
         {
           if (storeCmd(fanCmd[i], setFanSpeed[i]))
    -      {
             SET_BIT_OFF(needSetFanSpeed, i);
    -      }
    -
    -      nextCtrlFanTime = OS_GetTimeMs() + NEXT_FAN_WAIT;  // avoid rapid fire, clogging the queue
         }
       }
     }
     
    -void ctrlFanQuerySetWait(const bool wait)
    +void ctrlFanQueryClearSendingWaiting(void)
     {
    -  ctrlFanQueryWait = wait;
    +  ctrlFanSendingWaiting = false;
     }
     
    -// query for controller fan only
     void ctrlFanQuery(void)
     { // following conditions ordered by importance
    -  if (!ctrlFanQueryWait && infoHost.tx_slots != 0 && infoHost.connected && infoSettings.ctrl_fan_en)
    -  {
    -    ctrlFanQueryWait = storeCmd("M710\n");
    -  }
    +  if (infoSettings.ctrl_fan_en && !ctrlFanSendingWaiting && infoHost.tx_slots != 0 && infoHost.connected)
    +    ctrlFanSendingWaiting = storeCmd("M710\n");
     }
    diff --git a/TFT/src/User/API/FanControl.h b/TFT/src/User/API/FanControl.h
    index 9eac89130f..a3ca822e38 100644
    --- a/TFT/src/User/API/FanControl.h
    +++ b/TFT/src/User/API/FanControl.h
    @@ -9,27 +9,28 @@ extern "C" {
     #include 
     #include "Settings.h"
     
    -#define FAN_TYPE_F       0  // Default cooling fan
    -#define FAN_TYPE_CTRL_S  1  // Controller FAN on Stepper/Bed On  (Check - Marlin M710)
    -#define FAN_TYPE_CTRL_I  2  // Controller FAN on Idle            (Check - Marlin M710)
    -#define FAN_TYPE_UNKNOWN 8  // Unknown / Not defined!
    +#define FAN_TYPE_F       0  // default cooling fan
    +#define FAN_TYPE_CTRL_S  1  // controller FAN on stepper/bed on  (check - Marlin M710)
    +#define FAN_TYPE_CTRL_I  2  // controller FAN on idle            (check - Marlin M710)
    +#define FAN_TYPE_UNKNOWN 8  // unknown / not defined!
     
    -extern const char* fanID[MAX_FAN_COUNT];
    -extern const char* fanCmd[MAX_FAN_COUNT];
    +extern const char * fanID[MAX_FAN_COUNT];
    +extern const char * fanCmd[MAX_FAN_COUNT];
     
     void fanResetSpeed(void);
    -bool fanIsValid(const uint8_t index);
    +bool fanIsValid(const uint8_t index);                           // check whether the index is a valid fan index
     void fanSetSpeed(const uint8_t i, const uint8_t speed);
     uint8_t fanGetSetSpeed(const uint8_t i);
     void fanSetPercent(const uint8_t i, const uint8_t percent);
     uint8_t fanGetSetPercent(const uint8_t i);
     void fanSetCurSpeed(const uint8_t i, const uint8_t speed);
     uint8_t fanGetCurSpeed(const uint8_t i);
    -void fanSetCurPercent(const uint8_t i, const uint8_t percent);
    -uint8_t fanGetCurPercent(const uint8_t i);
    -void loopFan(void);
    -void ctrlFanQuerySetWait(const bool wait);
    -void ctrlFanQuery(void);
    +void fanSetCurPercent(const uint8_t i, const uint8_t percent);  // set current fan percent
    +uint8_t fanGetCurPercent(const uint8_t i);                      // get current fan percent
    +
    +void loopCheckFan(void);                     // called in loopBackEnd(). Loop for check on fan
    +void ctrlFanQueryClearSendingWaiting(void);  // called in sendQueueCmd(). Clear sending waiting for controller fan query
    +void ctrlFanQuery(void);                     // query for controller fan only
     
     #ifdef __cplusplus
     }
    diff --git a/TFT/src/User/API/FlashStore.c b/TFT/src/User/API/FlashStore.c
    index 83346883b6..59e222b84b 100644
    --- a/TFT/src/User/API/FlashStore.c
    +++ b/TFT/src/User/API/FlashStore.c
    @@ -9,7 +9,7 @@
     #endif
     
     #define TSC_SIGN  0x20200512  // (YYYYMMDD) DO NOT MODIFY (Touch Screem Calibration sign)
    -#define PARA_SIGN 0x20220321  // (YYYYMMDD) if a new setting parameter is added, modify here and
    +#define PARA_SIGN 0x20240203  // (YYYYMMDD) if a new setting parameter is added, modify here and
                                   // initialize the initial value in the "initSettings()" function
     enum
     {
    diff --git a/TFT/src/User/API/Gcode/gcode.c b/TFT/src/User/API/Gcode/gcode.c
    index 9dbf5e824e..1b8b9cb062 100644
    --- a/TFT/src/User/API/Gcode/gcode.c
    +++ b/TFT/src/User/API/Gcode/gcode.c
    @@ -1,9 +1,10 @@
     #include "gcode.h"
     #include "includes.h"
    +#include "RRFStatusControl.h"
     
     REQUEST_COMMAND_INFO requestCommandInfo = {0};
     
    -void waitForResponse(void)
    +static void waitForResponse(void)
     {
       TASK_LOOP_WHILE(!requestCommandInfo.done);
     }
    @@ -22,6 +23,14 @@ void clearRequestCommandInfo(void)
       }
     }
     
    +void abortRequestCommandInfo(void)
    +{
    +  requestCommandInfo.inWaitResponse = false;
    +  requestCommandInfo.inResponse = false;
    +  requestCommandInfo.done = true;
    +  requestCommandInfo.inError = true;
    +}
    +
     static void resetRequestCommandInfo(
       const char * string_start,   // The magic to identify the start
       const char * string_stop,    // The magic to identify the stop
    @@ -34,8 +43,9 @@ static void resetRequestCommandInfo(
     
       requestCommandInfo.cmd_rev_buf = malloc(CMD_MAX_REV);
     
    -  while (!requestCommandInfo.cmd_rev_buf)
    -    ;  // malloc failed
    +  while (!requestCommandInfo.cmd_rev_buf)  // if malloc failed, block the TFT
    +  {
    +  }
     
       memset(requestCommandInfo.cmd_rev_buf, 0, CMD_MAX_REV);
       requestCommandInfo.startMagic = string_start;
    @@ -187,7 +197,7 @@ long request_M23_M36(const char * filename)
       const char * sizeTag;
       char * strPtr;
     
    -  if (infoMachineSettings.firmwareType != FW_REPRAPFW)  // all other firmwares except reprap firmware
    +  if (infoMachineSettings.firmwareType != FW_REPRAPFW)  // all other firmwares except RepRap firmware
       {
         resetRequestCommandInfo("File opened",    // The magic to identify the start
                                 "File selected",  // The magic to identify the stop
    @@ -200,7 +210,7 @@ long request_M23_M36(const char * filename)
     
         sizeTag = "Size:";
       }
    -  else  // reprap firmware
    +  else  // RepRap firmware
       {
         resetRequestCommandInfo("{\"err\"",  // The magic to identify the start
                                 "}",         // The magic to identify the stop
    @@ -210,7 +220,7 @@ long request_M23_M36(const char * filename)
     
         mustStoreCmd("M36 /%s\n", filename);
     
    -    sizeTag = "size\":";  // reprap firmware reports size JSON
    +    sizeTag = "size\":";  // RepRap firmware reports size JSON
       }
     
       waitForResponse();  // wait for response
    @@ -223,7 +233,7 @@ long request_M23_M36(const char * filename)
       }
     
       if (infoMachineSettings.firmwareType == FW_REPRAPFW)
    -    mustStoreCmd("M23 /%s\n", filename);  // send M23 for reprap firmware
    +    mustStoreCmd("M23 /%s\n", filename);  // send M23 for RepRap firmware
     
       // Find file size and report it
       strPtr = strstr(requestCommandInfo.cmd_rev_buf, sizeTag);
    @@ -278,7 +288,7 @@ void request_M125(void)
     }
     
     /**
    - * Stop or Unconditional stop in reprap firmware
    + * Stop or Unconditional stop in RepRap firmware
      */
     void request_M0(void)
     {
    diff --git a/TFT/src/User/API/Gcode/gcode.h b/TFT/src/User/API/Gcode/gcode.h
    index 10d5955e9a..5c2c9186cb 100644
    --- a/TFT/src/User/API/Gcode/gcode.h
    +++ b/TFT/src/User/API/Gcode/gcode.h
    @@ -34,6 +34,7 @@ extern REQUEST_COMMAND_INFO requestCommandInfo;
     
     bool requestCommandInfoIsRunning(void);
     void clearRequestCommandInfo(void);
    +void abortRequestCommandInfo(void);
     
     void detectAdvancedOk(void);
     bool request_M21(void);
    diff --git a/TFT/src/User/API/HW_Init.c b/TFT/src/User/API/HW_Init.c
    index 29ecba7fb4..f7c649af74 100644
    --- a/TFT/src/User/API/HW_Init.c
    +++ b/TFT/src/User/API/HW_Init.c
    @@ -7,11 +7,11 @@
     
     void HW_GetClocksFreq(CLOCKS *clk)
     {
    -#if defined GD32F2XX || defined GD32F3XX
    -  RCU_GetClocksFreq(&clk->rccClocks);
    -#else
    -  RCC_GetClocksFreq(&clk->rccClocks);
    -#endif
    +  #if defined GD32F2XX || defined GD32F3XX
    +    RCU_GetClocksFreq(&clk->rccClocks);
    +  #else
    +    RCC_GetClocksFreq(&clk->rccClocks);
    +  #endif
     
       if (clk->rccClocks.PCLK1_Frequency < clk->rccClocks.HCLK_Frequency)  // if (APBx presc = 1) x1 else x2
         clk->PCLK1_Timer_Frequency = clk->rccClocks.PCLK1_Frequency * 2;
    @@ -27,11 +27,13 @@ void HW_GetClocksFreq(CLOCKS *clk)
     void HW_Init(void)
     {
       HW_GetClocksFreq(&mcuClocks);
    -#if defined GD32F2XX || defined GD32F3XX
    -  nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
    -#else
    -  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    -#endif
    +
    +  #if defined GD32F2XX || defined GD32F3XX
    +    nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
    +  #else
    +    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    +  #endif
    +
       Delay_init();
     
       #ifdef DISABLE_JTAG
    @@ -42,7 +44,7 @@ void HW_Init(void)
         DISABLE_DEBUG();  // disable JTAG & SWD
       #endif
     
    -  #if defined(MKS_TFT) && !defined (MKS_TFT35_V1_0) // not used by MKS_TFT35_V1_0
    +  #if defined(MKS_TFT) && !defined(MKS_TFT35_V1_0)  // not used by MKS_TFT35_V1_0
         #if defined (GD32F3XX)
           rcu_periph_clock_enable(RCU_AF);
           gpio_pin_remap_config(GPIO_USART1_REMAP, ENABLE);
    diff --git a/TFT/src/User/API/LCD_Dimming.c b/TFT/src/User/API/LCD_Dimming.c
    index 3716b9f89e..ca89d2a77f 100644
    --- a/TFT/src/User/API/LCD_Dimming.c
    +++ b/TFT/src/User/API/LCD_Dimming.c
    @@ -43,7 +43,7 @@ const LABEL lcd_idle_time_names[LCD_IDLE_TIME_COUNT] = {
     
     typedef struct
     {
    -  uint32_t idle_ms;
    +  uint32_t next_idle_time;
       bool dimmed;
       bool locked;
     } LCD_AUTO_DIM;
    @@ -62,10 +62,9 @@ bool LCD_IsBlocked(void)
     
     void LCD_Wake(void)
     {
    -  if (infoSettings.lcd_idle_time != IDLE_TIME_OFF)
    +  if (infoSettings.lcd_idle_time != IDLE_TIME_OFF)  // if LCD dim function is activated
       {
    -    // the LCD dim function is activated. First check if it's dimmed
    -    if (lcd_dim.dimmed)
    +    if (lcd_dim.dimmed)  // if LCD is dimmed
         {
           lcd_dim.locked = false;
           lcd_dim.dimmed = false;
    @@ -79,8 +78,8 @@ void LCD_Wake(void)
           #endif
         }
     
    -    // set a new idle_ms time
    -    lcd_dim.idle_ms = OS_GetTimeMs();
    +    // always set next idle time
    +    lcd_dim.next_idle_time = OS_GetTimeMs() + SEC_TO_MS(lcd_idle_times[infoSettings.lcd_idle_time]);
       }
     }
     
    @@ -89,49 +88,51 @@ void LCD_CheckDimming(void)
       if (infoSettings.lcd_idle_time == IDLE_TIME_OFF)
         return;
     
    -  if (TS_IsPressed()
    +  if (!TS_IsPressed()
         #if LCD_ENCODER_SUPPORT
    -      || LCD_Enc_CheckState()
    +      && !LCD_Enc_CheckState()
         #endif
    -    )
    +    )  // if touch screen not pressed and rotary encoder neither pressed nor rotated
       {
    -    if (lcd_dim.dimmed)
    -    {
    -      if (infoSettings.lcd_lock_on_idle && TS_IsPressed())  // if touch is blocked on idle and pressing on the LCD (not on the encoder),
    -        lcd_dim.locked = true;                              // the first touch will be skipped preventing to trigger any undesired action
    +    if (lcd_dim.dimmed || OS_GetTimeMs() < lcd_dim.next_idle_time)
    +      return;
     
    -      lcd_dim.dimmed = false;
    -      LCD_SET_BRIGHTNESS(lcd_brightness[infoSettings.lcd_brightness]);
    +    lcd_dim.locked = false;
    +    lcd_dim.dimmed = true;
    +    LCD_SET_BRIGHTNESS(lcd_brightness[infoSettings.lcd_idle_brightness]);
     
    -      #ifdef KNOB_LED_COLOR_PIN
    -        if (infoSettings.knob_led_idle)
    -        {
    -          Knob_LED_SetColor(knob_led_colors[infoSettings.knob_led_color], infoSettings.neopixel_pixels);
    -        }
    -      #endif
    -    }
    +    #ifdef KNOB_LED_COLOR_PIN
    +      if (infoSettings.knob_led_idle)
    +      {
    +        Knob_LED_SetColor(knob_led_colors[KNOB_LED_OFF], infoSettings.neopixel_pixels);
    +      }
    +    #endif
     
    -    lcd_dim.idle_ms = OS_GetTimeMs();
    +    return;
       }
    -  else
    -  {
    -    if (OS_GetTimeMs() - lcd_dim.idle_ms < SEC_TO_MS(lcd_idle_times[infoSettings.lcd_idle_time]))
    -      return;
     
    -    if (!lcd_dim.dimmed)
    -    {
    -      lcd_dim.locked = false;
    -      lcd_dim.dimmed = true;
    -      LCD_SET_BRIGHTNESS(lcd_brightness[infoSettings.lcd_idle_brightness]);
    +  // touch screen pressed and/or rotary encoder pressed or rotated
     
    -      #ifdef KNOB_LED_COLOR_PIN
    -        if (infoSettings.knob_led_idle)
    -        {
    -          Knob_LED_SetColor(knob_led_colors[KNOB_LED_OFF], infoSettings.neopixel_pixels);
    -        }
    -      #endif
    +  // always set next idle time
    +  lcd_dim.next_idle_time = OS_GetTimeMs() + SEC_TO_MS(lcd_idle_times[infoSettings.lcd_idle_time]);
    +
    +  if (!lcd_dim.dimmed)
    +    return;
    +
    +  // if touch is blocked on idle and pressing on the LCD (not on the rotary encoder),
    +  // the first touch will be skipped preventing to trigger any undesired action
    +  if (infoSettings.lcd_lock_on_idle && TS_IsPressed())
    +    lcd_dim.locked = true;
    +
    +  lcd_dim.dimmed = false;
    +  LCD_SET_BRIGHTNESS(lcd_brightness[infoSettings.lcd_brightness]);
    +
    +  #ifdef KNOB_LED_COLOR_PIN
    +    if (infoSettings.knob_led_idle)
    +    {
    +      Knob_LED_SetColor(knob_led_colors[infoSettings.knob_led_color], infoSettings.neopixel_pixels);
         }
    -  }
    +  #endif
     }
     
     #ifdef KNOB_LED_COLOR_PIN
    diff --git a/TFT/src/User/API/LED_Event.c b/TFT/src/User/API/LED_Event.c
    index 779db0cc27..b98b78c0f4 100644
    --- a/TFT/src/User/API/LED_Event.c
    +++ b/TFT/src/User/API/LED_Event.c
    @@ -1,19 +1,14 @@
     #include "LED_Event.h"
     #include "includes.h"
     
    -#define UPDATE_TIME 2000  // 1 seconds is 1000
    -
    -uint32_t lastUpdateTime = 0;
    -uint8_t prevLedValue = 0;
    -bool heatingDone = false;
    -bool printingDone = false;
    +#define LED_REFRESH_TIME 2000  // 1 seconds is 1000
     
     //#define ROOM_TEMPERATURE
     #ifdef ROOM_TEMPERATURE
       #define MAX_COLD_TEMP     50
       #define DEFAULT_COLD_TEMP 25
     
    -  uint16_t coldTemperature = 0;
    +  static uint16_t coldTemperature = 0;
     
       #define COLD_TEMPERATURE coldTemperature
     #else
    @@ -22,16 +17,14 @@ bool printingDone = false;
     
     inline static bool nextUpdate(void)
     {
    -  uint32_t curTime = OS_GetTimeMs();
    +  static uint32_t lastUpdateTime = 0;
     
    -  if (curTime > (lastUpdateTime + UPDATE_TIME))
    -  {
    -    lastUpdateTime = curTime;
    +  if (OS_GetTimeMs() - lastUpdateTime < LED_REFRESH_TIME)
    +    return false;
     
    -    return true;
    -  }
    +  lastUpdateTime = OS_GetTimeMs();
     
    -  return false;
    +  return true;
     }
     
     #ifdef ROOM_TEMPERATURE
    @@ -67,6 +60,10 @@ void getColdTemperature(void)
     
     void LED_CheckEvent(void)
     {
    +  static uint8_t prevLedValue = 0;
    +  static bool heatingDone = false;
    +  static bool printingDone = false;
    +
       #ifdef ROOM_TEMPERATURE
         getColdTemperature();
       #endif
    diff --git a/TFT/src/User/API/Language/Language.inc b/TFT/src/User/API/Language/Language.inc
    index 020575869d..6ff4efc2ea 100644
    --- a/TFT/src/User/API/Language/Language.inc
    +++ b/TFT/src/User/API/Language/Language.inc
    @@ -11,6 +11,7 @@
     // config.ini Parameter Settings - Screen Settings and Feature Settings
     X_WORD (LANGUAGE)
     X_WORD (ADVANCED_OK)
    +X_WORD (COMMAND_CHECKSUM)
     X_WORD (EMULATED_M600)
     X_WORD (EMULATED_M109_M190)
     X_WORD (EVENT_LED)
    diff --git a/TFT/src/User/API/Language/language_am.h b/TFT/src/User/API/Language/language_am.h
    index f9b91998ff..cbc48d8c6a 100644
    --- a/TFT/src/User/API/Language/language_am.h
    +++ b/TFT/src/User/API/Language/language_am.h
    @@ -4,6 +4,7 @@
         // config.ini Parameter Settings - Screen Settings and Feature Settings
         #define STRING_LANGUAGE               "Հայերեն"
         #define STRING_ADVANCED_OK            "Advanced OK"
    +    #define STRING_COMMAND_CHECKSUM       "Command checksum"
         #define STRING_EMULATED_M600          "Emulated M600"
         #define STRING_EMULATED_M109_M190     "Emulated M109 / M190"
         #define STRING_EVENT_LED              "Event LED"
    diff --git a/TFT/src/User/API/Language/language_br.h b/TFT/src/User/API/Language/language_br.h
    index 160602c349..c9f3dd679a 100644
    --- a/TFT/src/User/API/Language/language_br.h
    +++ b/TFT/src/User/API/Language/language_br.h
    @@ -4,6 +4,7 @@
         // config.ini Parameter Settings - Screen Settings and Feature Settings
         #define STRING_LANGUAGE               "Português BRASIL"
         #define STRING_ADVANCED_OK            "Advanced OK"
    +    #define STRING_COMMAND_CHECKSUM       "Command checksum"
         #define STRING_EMULATED_M600          "M600 emulado"
         #define STRING_EMULATED_M109_M190     "M109 / M190 emulado"
         #define STRING_EVENT_LED              "Event LED"
    diff --git a/TFT/src/User/API/Language/language_ca.h b/TFT/src/User/API/Language/language_ca.h
    index a8c293c163..510433c065 100644
    --- a/TFT/src/User/API/Language/language_ca.h
    +++ b/TFT/src/User/API/Language/language_ca.h
    @@ -4,6 +4,7 @@
         // config.ini Parameter Settings - Screen Settings and Feature Settings
         #define STRING_LANGUAGE               "Català"
         #define STRING_ADVANCED_OK            "Advanced OK"
    +    #define STRING_COMMAND_CHECKSUM       "Command checksum"
         #define STRING_EMULATED_M600          "Emulated M600"
         #define STRING_EMULATED_M109_M190     "Emulated M109 / M190"
         #define STRING_EVENT_LED              "Event LED"
    diff --git a/TFT/src/User/API/Language/language_cn.h b/TFT/src/User/API/Language/language_cn.h
    index d48f8b7257..dba6a6d7db 100644
    --- a/TFT/src/User/API/Language/language_cn.h
    +++ b/TFT/src/User/API/Language/language_cn.h
    @@ -4,6 +4,7 @@
         // config.ini Parameter Settings - Screen Settings and Feature Settings
         #define STRING_LANGUAGE               "简体中文"
         #define STRING_ADVANCED_OK            "Advanced OK"
    +    #define STRING_COMMAND_CHECKSUM       "Command checksum"
         #define STRING_EMULATED_M600          "模拟M600"
         #define STRING_EMULATED_M109_M190     "Emulated M109 / M190"
         #define STRING_EVENT_LED              "Event LED"
    diff --git a/TFT/src/User/API/Language/language_cz.h b/TFT/src/User/API/Language/language_cz.h
    index 633d6c22a4..b3a30f9ee1 100644
    --- a/TFT/src/User/API/Language/language_cz.h
    +++ b/TFT/src/User/API/Language/language_cz.h
    @@ -4,6 +4,7 @@
         // config.ini Parameter Settings - Screen Settings and Feature Settings
         #define STRING_LANGUAGE               "Čeština"
         #define STRING_ADVANCED_OK            "Advanced OK"
    +    #define STRING_COMMAND_CHECKSUM       "Command checksum"
         #define STRING_EMULATED_M600          "Emulace M600"
         #define STRING_EMULATED_M109_M190     "Emulace M109 / M190"
         #define STRING_EVENT_LED              "LED události"
    diff --git a/TFT/src/User/API/Language/language_de.h b/TFT/src/User/API/Language/language_de.h
    index 92aa2db4db..d041bee0c2 100644
    --- a/TFT/src/User/API/Language/language_de.h
    +++ b/TFT/src/User/API/Language/language_de.h
    @@ -4,6 +4,7 @@
         // config.ini Parameter Settings - Screen Settings and Feature Settings
         #define STRING_LANGUAGE               "Deutsch"
         #define STRING_ADVANCED_OK            "Advanced OK"
    +    #define STRING_COMMAND_CHECKSUM       "Command checksum"
         #define STRING_EMULATED_M600          "Emuliere M600"
         #define STRING_EMULATED_M109_M190     "Emuliere M109 / M190"
         #define STRING_EVENT_LED              "Event LED"
    diff --git a/TFT/src/User/API/Language/language_en.h b/TFT/src/User/API/Language/language_en.h
    index 91f2714c67..94c0cb34fd 100644
    --- a/TFT/src/User/API/Language/language_en.h
    +++ b/TFT/src/User/API/Language/language_en.h
    @@ -4,6 +4,7 @@
         // config.ini Parameter Settings - Screen Settings and Feature Settings
         #define STRING_LANGUAGE               "English"
         #define STRING_ADVANCED_OK            "Advanced OK"
    +    #define STRING_COMMAND_CHECKSUM       "Command checksum"
         #define STRING_EMULATED_M600          "Emulated M600"
         #define STRING_EMULATED_M109_M190     "Emulated M109 / M190"
         #define STRING_EVENT_LED              "Event LED"
    diff --git a/TFT/src/User/API/Language/language_es.h b/TFT/src/User/API/Language/language_es.h
    index 967dd70d76..6828c34f92 100644
    --- a/TFT/src/User/API/Language/language_es.h
    +++ b/TFT/src/User/API/Language/language_es.h
    @@ -4,6 +4,7 @@
         // config.ini Parameter Settings - Screen Settings and Feature Settings
         #define STRING_LANGUAGE               "Español"
         #define STRING_ADVANCED_OK            "Advanced OK"
    +    #define STRING_COMMAND_CHECKSUM       "Command checksum"
         #define STRING_EMULATED_M600          "Emulated M600"
         #define STRING_EMULATED_M109_M190     "Emulated M109 / M190"
         #define STRING_EVENT_LED              "Event LED"
    diff --git a/TFT/src/User/API/Language/language_fr.h b/TFT/src/User/API/Language/language_fr.h
    index 56dacfa83f..da7a776b87 100644
    --- a/TFT/src/User/API/Language/language_fr.h
    +++ b/TFT/src/User/API/Language/language_fr.h
    @@ -4,6 +4,7 @@
         // config.ini Parameter Settings - Screen Settings and Feature Settings
         #define STRING_LANGUAGE               "Français"
         #define STRING_ADVANCED_OK            "Advanced OK"
    +    #define STRING_COMMAND_CHECKSUM       "Command checksum"
         #define STRING_EMULATED_M600          "Emuler M600"
         #define STRING_EMULATED_M109_M190     "Emuler M109 / M190"
         #define STRING_EVENT_LED              "LED Neopixel"
    diff --git a/TFT/src/User/API/Language/language_gr.h b/TFT/src/User/API/Language/language_gr.h
    index c3f2415460..17786dde70 100644
    --- a/TFT/src/User/API/Language/language_gr.h
    +++ b/TFT/src/User/API/Language/language_gr.h
    @@ -4,6 +4,7 @@
         // config.ini Parameter Settings - Screen Settings and Feature Settings
         #define STRING_LANGUAGE               "Ελληνικά"
         #define STRING_ADVANCED_OK            "Advanced OK"
    +    #define STRING_COMMAND_CHECKSUM       "Command checksum"
         #define STRING_EMULATED_M600          "Emulated M600"
         #define STRING_EMULATED_M109_M190     "Emulated M109 / M190"
         #define STRING_EVENT_LED              "Event LED"
    diff --git a/TFT/src/User/API/Language/language_hr.h b/TFT/src/User/API/Language/language_hr.h
    index b49500d542..b4795a7c70 100644
    --- a/TFT/src/User/API/Language/language_hr.h
    +++ b/TFT/src/User/API/Language/language_hr.h
    @@ -4,6 +4,7 @@
         // config.ini Parameter Settings - Screen Settings and Feature Settings
         #define STRING_LANGUAGE               "Hrvatski"
         #define STRING_ADVANCED_OK            "Advanced OK"
    +    #define STRING_COMMAND_CHECKSUM       "Command checksum"
         #define STRING_EMULATED_M600          "Emuliraj M600"
         #define STRING_EMULATED_M109_M190     "Emuliraj M109 / M190"
         #define STRING_EVENT_LED              "LED event"
    diff --git a/TFT/src/User/API/Language/language_hu.h b/TFT/src/User/API/Language/language_hu.h
    index 742bfb1d11..342ae898da 100644
    --- a/TFT/src/User/API/Language/language_hu.h
    +++ b/TFT/src/User/API/Language/language_hu.h
    @@ -4,6 +4,7 @@
         // config.ini Parameter Settings - Screen Settings and Feature Settings
         #define STRING_LANGUAGE               "Magyar"
         #define STRING_ADVANCED_OK            "Advanced OK"
    +    #define STRING_COMMAND_CHECKSUM       "Command checksum"
         #define STRING_EMULATED_M600          "Emulált M600"
         #define STRING_EMULATED_M109_M190     "Emulált M109 / M190"
         #define STRING_EVENT_LED              "Esemény LED"
    diff --git a/TFT/src/User/API/Language/language_it.h b/TFT/src/User/API/Language/language_it.h
    index 796a89969f..ac2ecc161e 100644
    --- a/TFT/src/User/API/Language/language_it.h
    +++ b/TFT/src/User/API/Language/language_it.h
    @@ -4,6 +4,7 @@
         // config.ini Parameter Settings - Screen Settings and Feature Settings
         #define STRING_LANGUAGE               "Italiano"
         #define STRING_ADVANCED_OK            "OK avanzato"
    +    #define STRING_COMMAND_CHECKSUM       "Checksum comando"
         #define STRING_EMULATED_M600          "M600 emulato"
         #define STRING_EMULATED_M109_M190     "M109 / M190 emulati"
         #define STRING_EVENT_LED              "LED evento"
    diff --git a/TFT/src/User/API/Language/language_jp.h b/TFT/src/User/API/Language/language_jp.h
    index 795495b790..1ec81a993a 100644
    --- a/TFT/src/User/API/Language/language_jp.h
    +++ b/TFT/src/User/API/Language/language_jp.h
    @@ -4,6 +4,7 @@
         // config.ini Parameter Settings - Screen Settings and Feature Settings
         #define STRING_LANGUAGE               "日本語"
         #define STRING_ADVANCED_OK            "Advanced OK"
    +    #define STRING_COMMAND_CHECKSUM       "Command checksum"
         #define STRING_EMULATED_M600          "Emulated M600"
         #define STRING_EMULATED_M109_M190     "Emulated M109 / M190"
         #define STRING_EVENT_LED              "Event LED"
    diff --git a/TFT/src/User/API/Language/language_keywords.h b/TFT/src/User/API/Language/language_keywords.h
    index 4cfe967642..bd585b5619 100644
    --- a/TFT/src/User/API/Language/language_keywords.h
    +++ b/TFT/src/User/API/Language/language_keywords.h
    @@ -11,6 +11,7 @@ extern "C" {
     // config.ini Parameter Settings - Screen Settings and Feature Settings
     #define LANG_KEY_LANGUAGE                     "label_language:"
     #define LANG_KEY_ADVANCED_OK                  "label_advanced_ok:"
    +#define LANG_KEY_COMMAND_CHECKSUM             "label_command_checksum:"
     #define LANG_KEY_EMULATED_M600                "label_emulated_m600:"
     #define LANG_KEY_EMULATED_M109_M190           "label_emulated_m109_m190:"
     #define LANG_KEY_EVENT_LED                    "label_event_led:"
    diff --git a/TFT/src/User/API/Language/language_nl.h b/TFT/src/User/API/Language/language_nl.h
    index 4a55305626..93a8ecee1b 100644
    --- a/TFT/src/User/API/Language/language_nl.h
    +++ b/TFT/src/User/API/Language/language_nl.h
    @@ -4,6 +4,7 @@
         // config.ini Parameter Settings - Screen Settings and Feature Settings
         #define STRING_LANGUAGE               "Dutch"
         #define STRING_ADVANCED_OK            "Advanced OK"
    +    #define STRING_COMMAND_CHECKSUM       "Command checksum"
         #define STRING_EMULATED_M600          "Emulated M600"
         #define STRING_EMULATED_M109_M190     "Emulated M109 / M190"
         #define STRING_EVENT_LED              "Event LED"
    diff --git a/TFT/src/User/API/Language/language_pl.h b/TFT/src/User/API/Language/language_pl.h
    index 55dba591c9..a949debcef 100644
    --- a/TFT/src/User/API/Language/language_pl.h
    +++ b/TFT/src/User/API/Language/language_pl.h
    @@ -4,6 +4,7 @@
         // config.ini Parameter Settings - Screen Settings and Feature Settings
         #define STRING_LANGUAGE               "Polski"
         #define STRING_ADVANCED_OK            "Advanced OK"
    +    #define STRING_COMMAND_CHECKSUM       "Command checksum"
         #define STRING_EMULATED_M600          "Emulowane M600"
         #define STRING_EMULATED_M109_M190     "Emulowane M109 / M190"
         #define STRING_EVENT_LED              "Sygn. zdarzenia diodą LED"
    diff --git a/TFT/src/User/API/Language/language_pt.h b/TFT/src/User/API/Language/language_pt.h
    index 3a5635e385..b3a4e339fa 100644
    --- a/TFT/src/User/API/Language/language_pt.h
    +++ b/TFT/src/User/API/Language/language_pt.h
    @@ -4,6 +4,7 @@
         // config.ini Parameter Settings - Screen Settings and Feature Settings
         #define STRING_LANGUAGE               "Portugues"
         #define STRING_ADVANCED_OK            "Advanced OK"
    +    #define STRING_COMMAND_CHECKSUM       "Command checksum"
         #define STRING_EMULATED_M600          "Emulated M600"
         #define STRING_EMULATED_M109_M190     "Emulated M109 / M190"
         #define STRING_EVENT_LED              "Event LED"
    diff --git a/TFT/src/User/API/Language/language_ru.h b/TFT/src/User/API/Language/language_ru.h
    index 17d7f9b976..d843efa38b 100644
    --- a/TFT/src/User/API/Language/language_ru.h
    +++ b/TFT/src/User/API/Language/language_ru.h
    @@ -4,6 +4,7 @@
         // config.ini Parameter Settings - Screen Settings and Feature Settings
         #define STRING_LANGUAGE               "Русский"
         #define STRING_ADVANCED_OK            "Advanced OK"
    +    #define STRING_COMMAND_CHECKSUM       "Command checksum"
         #define STRING_EMULATED_M600          "Эмуляция M600"
         #define STRING_EMULATED_M109_M190     "Emulated M109 / M190"
         #define STRING_EVENT_LED              "Event LED"
    diff --git a/TFT/src/User/API/Language/language_sk.h b/TFT/src/User/API/Language/language_sk.h
    index 45d6295b17..41a47dd221 100644
    --- a/TFT/src/User/API/Language/language_sk.h
    +++ b/TFT/src/User/API/Language/language_sk.h
    @@ -4,6 +4,7 @@
         // config.ini Parameter Settings - Screen Settings and Feature Settings
         #define STRING_LANGUAGE               "Slovensky"
         #define STRING_ADVANCED_OK            "Advanced OK"
    +    #define STRING_COMMAND_CHECKSUM       "Command checksum"
         #define STRING_EMULATED_M600          "Emulated M600"
         #define STRING_EMULATED_M109_M190     "Emulated M109 / M190"
         #define STRING_EVENT_LED              "Event LED"
    diff --git a/TFT/src/User/API/Language/language_sl.h b/TFT/src/User/API/Language/language_sl.h
    index eda325177f..8e0628641d 100644
    --- a/TFT/src/User/API/Language/language_sl.h
    +++ b/TFT/src/User/API/Language/language_sl.h
    @@ -4,6 +4,7 @@
         // config.ini Parameter Settings - Screen Settings and Feature Settings
         #define STRING_LANGUAGE               "Slovenski"
         #define STRING_ADVANCED_OK            "Advanced OK"
    +    #define STRING_COMMAND_CHECKSUM       "Command checksum"
         #define STRING_EMULATED_M600          "Emulated M600"
         #define STRING_EMULATED_M109_M190     "Emulated M109 / M190"
         #define STRING_EVENT_LED              "Event LED"
    diff --git a/TFT/src/User/API/Language/language_tc.h b/TFT/src/User/API/Language/language_tc.h
    index 18074e7d1d..fcbe2609c9 100644
    --- a/TFT/src/User/API/Language/language_tc.h
    +++ b/TFT/src/User/API/Language/language_tc.h
    @@ -4,6 +4,7 @@
         // config.ini Parameter Settings - Screen Settings and Feature Settings
         #define STRING_LANGUAGE               "正體中文"
         #define STRING_ADVANCED_OK            "Advanced OK"
    +    #define STRING_COMMAND_CHECKSUM       "Command checksum"
         #define STRING_EMULATED_M600          "Emulated M600"
         #define STRING_EMULATED_M109_M190     "Emulated M109 / M190"
         #define STRING_EVENT_LED              "Event LED"
    diff --git a/TFT/src/User/API/Language/language_tr.h b/TFT/src/User/API/Language/language_tr.h
    index 3bd1dbec58..8b4b352e71 100644
    --- a/TFT/src/User/API/Language/language_tr.h
    +++ b/TFT/src/User/API/Language/language_tr.h
    @@ -4,6 +4,7 @@
         // config.ini Parameter Settings - Screen Settings and Feature Settings
         #define STRING_LANGUAGE               "Türkçe"
         #define STRING_ADVANCED_OK            "Advanced OK"
    +    #define STRING_COMMAND_CHECKSUM       "Command checksum"
         #define STRING_EMULATED_M600          "Emulated M600"
         #define STRING_EMULATED_M109_M190     "Emulated M109 / M190"
         #define STRING_EVENT_LED              "Event LED"
    diff --git a/TFT/src/User/API/Language/language_uk.h b/TFT/src/User/API/Language/language_uk.h
    index 65a4657d7e..98fc67df35 100644
    --- a/TFT/src/User/API/Language/language_uk.h
    +++ b/TFT/src/User/API/Language/language_uk.h
    @@ -4,6 +4,7 @@
         // config.ini Parameter Settings - Screen Settings and Feature Settings
         #define STRING_LANGUAGE               "Українська"
         #define STRING_ADVANCED_OK            "Advanced OK"
    +    #define STRING_COMMAND_CHECKSUM       "Command checksum"
         #define STRING_EMULATED_M600          "Emulated M600"
         #define STRING_EMULATED_M109_M190     "Emulated M109 / M190"
         #define STRING_EVENT_LED              "Event LED"
    diff --git a/TFT/src/User/API/parseACK.c b/TFT/src/User/API/Mainboard_AckHandler.c
    similarity index 94%
    rename from TFT/src/User/API/parseACK.c
    rename to TFT/src/User/API/Mainboard_AckHandler.c
    index 233153ecf2..1ff03b1089 100644
    --- a/TFT/src/User/API/parseACK.c
    +++ b/TFT/src/User/API/Mainboard_AckHandler.c
    @@ -1,6 +1,6 @@
    -#include "parseACK.h"
    +#include "Mainboard_AckHandler.h"
     #include "includes.h"
    -#include "RRFParseACK.hpp"
    +#include "RRFAckHandler.hpp"
     
     typedef enum  // popup message types available to display an echo message
     {
    @@ -21,9 +21,6 @@ const ECHO knownEcho[] = {
       {ECHO_NOTIFY_NONE, "busy: processing"},
       {ECHO_NOTIFY_NONE, "Now fresh file:"},
       {ECHO_NOTIFY_NONE, "Now doing file:"},
    -  //{ECHO_NOTIFY_NONE, "Probe Offset"},
    -  //{ECHO_NOTIFY_NONE, "enqueueing \"M117\""},
    -  {ECHO_NOTIFY_NONE, "Flow:"},
       {ECHO_NOTIFY_NONE, "echo:;"},                   // M503
       {ECHO_NOTIFY_NONE, "echo:  G"},                 // M503
       {ECHO_NOTIFY_NONE, "echo:  M"},                 // M503
    @@ -357,9 +354,9 @@ static inline void hostActionCommands(void)
       }
     }
     
    -void parseACK(void)
    +void parseAck(void)
     {
    -  while (Serial_NewDataAvailable(SERIAL_PORT) && (ack_len = Serial_Get(SERIAL_PORT, ack_cache, ACK_CACHE_SIZE)) != 0)  // if some data have been retrieved
    +  while (Serial_DataAvailableRX(SERIAL_PORT) && (ack_len = Serial_Get(SERIAL_PORT, ack_cache, ACK_CACHE_SIZE)) != 0)  // if some data have been retrieved
       {
         UPD_RX_KPIS(ack_len);  // debug monitoring KPI
     
    @@ -369,6 +366,8 @@ void parseACK(void)
           Serial_Put(SERIAL_DEBUG_PORT, ack_cache);
         #endif
     
    +    InfoHost_UpdateAckTimestamp();  // update last received ACK message timestamp
    +
         bool avoid_terminal = false;
     
         //----------------------------------------
    @@ -401,13 +400,12 @@ void parseACK(void)
           if (ack_seen(heaterID[CHAMBER]))
             infoSettings.chamber_en = ENABLED;
     
    -      updateNextHeatCheckTime();
    +      heatSetNextUpdateTime();  // set next temperature query time or timeout
     
           if (!ack_seen("@"))  // it's RepRapFirmware
           {
             storeCmd("M92\n");
             storeCmd("M115\n");  // as last command to identify the FW type!
    -        coordinateQuerySetWait(true);
           }
           else if (infoMachineSettings.firmwareType == FW_NOT_DETECTED)  // if never connected to the printer since boot
           {
    @@ -420,7 +418,7 @@ void parseACK(void)
     
           // 1) store on command queue the above gcodes to detect printer info
           // 2) re-initialize infoHost when connected to avoid this code branch is executed again
    -      // 3) set requestCommandInfo.inJson to "false" and detect the presence of Marlin ADVANCED_OK
    +      // 3) set requestCommandInfo.inJson to "false" and detect the presence of Marlin "ADVANCED_OK"
           //    feature (if any) and its command queue size
           // 4) finally, set listening mode flag according to its last state stored in flash
           //
    @@ -515,7 +513,7 @@ void parseACK(void)
             // proceed with generic OK response handling to update infoHost.tx_slots and infoHost.tx_count
             //
             if (infoMachineSettings.firmwareType == FW_REPRAPFW || ack_starts_with("ok"))
    -          InfoHost_HandleOkAck(HOST_SLOTS_GENERIC_OK);
    +          InfoHost_HandleAckOk(HOST_SLOTS_GENERIC_OK);
           }
     
           goto parse_end;
    @@ -526,7 +524,7 @@ void parseACK(void)
         //----------------------------------------
     
         // check for a possible json response and eventually parse and process it
    -    else if (!requestCommandInfo.inWaitResponse && infoMachineSettings.firmwareType == FW_REPRAPFW)
    +    else if (infoMachineSettings.firmwareType == FW_REPRAPFW && !requestCommandInfo.inWaitResponse)
         {
           if (strchr(ack_cache, '{') != NULL)
             requestCommandInfo.inJson = true;
    @@ -537,10 +535,10 @@ void parseACK(void)
           if (ack_seen(magic_warning))
             ackPopupInfo(magic_warning);
           else
    -        rrfParseACK(ack_cache);
    +        rrfParseAck(ack_cache);
     
           // proceed with generic OK response handling to update infoHost.tx_slots and infoHost.tx_count
    -      InfoHost_HandleOkAck(HOST_SLOTS_GENERIC_OK);
    +      InfoHost_HandleAckOk(HOST_SLOTS_GENERIC_OK);
     
           goto parse_end;
         }
    @@ -555,7 +553,7 @@ void parseACK(void)
           // if regular OK response ("ok\n")
           if (ack_cache[ack_index] == '\n')
           {
    -        InfoHost_HandleOkAck(infoSettings.tx_slots);
    +        InfoHost_HandleAckOk(infoSettings.tx_slots);
     
             goto parse_end;  // nothing else to do
           }
    @@ -563,15 +561,15 @@ void parseACK(void)
           // if ADVANCED_OK response (e.g. "ok N10 P15 B3\n"). Required "ADVANCED_OK" in Marlin
           if (ack_continue_seen("P") && ack_continue_seen("B"))
           {
    -        InfoHost_HandleOkAck(ack_value());
    +        InfoHost_HandleAckOk(ack_value());
     
             goto parse_end;  // nothing else to do
           }
     
    -      // if here, it is a temperature response (e.g. "ok T:16.13 /0.00 B:16.64 /0.00 @:0 B@:0\n").
    +      // if here, it is an M105 temperature response (e.g. "ok T:16.13 /0.00 B:16.64 /0.00 @:0 B@:0\n").
           // Proceed with generic OK response handling to update infoHost.tx_slots and infoHost.tx_count
           // and then continue applying the next matching patterns to handle the temperature response
    -      InfoHost_HandleOkAck(HOST_SLOTS_GENERIC_OK);
    +      InfoHost_HandleAckOk(HOST_SLOTS_GENERIC_OK);
         }
     
         //----------------------------------------
    @@ -588,7 +586,9 @@ void parseACK(void)
         // Pushed / polled / on printing parsed responses
         //----------------------------------------
     
    -    // parse and store temperatures (e.g. "ok T:16.13 /0.00 B:16.64 /0.00 @:0 B@:0\n")
    +    // parse and store M105 or auto reported (M155) temperature response,
    +    // e.g. "ok T:16.13 /0.00 B:16.64 /0.00 @:0 B@:0\n" for M105,
    +    // e.g. "T:16.13 /0.00 B:16.64 /0.00 @:0 B@:0\n" for auto reported (M155)
         else if ((ack_seen("@") && ack_seen("T:")) || ack_seen("T0:"))
         {
           uint8_t heaterIndex = NOZZLE0;
    @@ -612,7 +612,12 @@ void parseACK(void)
           }
     
           avoid_terminal = !infoSettings.terminal_ack;
    -      updateNextHeatCheckTime();
    +      heatSetNextUpdateTime();  // set next temperature query time or timeout
    +    }
    +    // parse and store M114 E, extruder position. Required "M114_DETAIL" in Marlin
    +    else if (ack_seen("Count E:"))
    +    {
    +      coordinateSetExtruderActualSteps(ack_value());
         }
         // parse and store M114, current position
         else if (ack_starts_with("X:") || ack_seen("C: X:"))  // Smoothieware axis position starts with "C: X:"
    @@ -631,37 +636,16 @@ void parseACK(void)
                 coordinateSetAxisActual(E_AXIS, ack_value());
             }
           }
    -
    -      coordinateQuerySetWait(false);
    -    }
    -    // parse and store M114 E, extruder position. Required "M114_DETAIL" in Marlin
    -    else if (ack_seen("Count E:"))
    -    {
    -      coordinateSetExtruderActualSteps(ack_value());
         }
         // parse and store feed rate percentage
    -    else if (ack_seen("FR:"))
    +    else if (ack_seen("FR:") || (infoMachineSettings.firmwareType == FW_SMOOTHIEWARE && ack_seen("Speed factor at ")))
         {
           speedSetCurPercent(0, ack_value());
    -      speedQuerySetWait(false);
         }
         // parse and store flow rate percentage
    -    else if (ack_seen("Flow:"))
    -    {
    -      speedSetCurPercent(1, ack_value());
    -      speedQuerySetWait(false);
    -    }
    -    // parse and store feed rate percentage in case of Smoothieware
    -    else if ((infoMachineSettings.firmwareType == FW_SMOOTHIEWARE) && ack_seen("Speed factor at "))
    -    {
    -      speedSetCurPercent(0, ack_value());
    -      speedQuerySetWait(false);
    -    }
    -    // parse and store flow rate percentage in case of Smoothieware
    -    else if ((infoMachineSettings.firmwareType == FW_SMOOTHIEWARE) && ack_seen("Flow rate at "))
    +    else if (ack_seen("Flow:") || (infoMachineSettings.firmwareType == FW_SMOOTHIEWARE && ack_seen("Flow rate at ")))
         {
           speedSetCurPercent(1, ack_value());
    -      speedQuerySetWait(false);
         }
         // parse and store M106, fan speed
         else if (ack_starts_with("M106"))
    @@ -676,8 +660,6 @@ void parseACK(void)
     
           if (ack_seen("I"))
             fanSetCurSpeed(MAX_COOLING_FAN_COUNT + 1, ack_value());
    -
    -      ctrlFanQuerySetWait(false);
         }
         // parse pause message
         else if (!infoMachineSettings.promptSupport && ack_seen("paused for user"))
    @@ -759,6 +741,8 @@ void parseACK(void)
                 setPrintAbort();
               }
             }
    +
    +        printSetNextUpdateTime();  // set next printing query time or timeout
           }
           // parse and store M73
           else
    @@ -782,6 +766,11 @@ void parseACK(void)
             }
           }
         }
    +    // "Resend:" response handling. Required COMMAND_CHECKSUM feature enabled in TFT or managed by remote host
    +    else if (ack_starts_with("Resend:"))
    +    {
    +      handleCmdLineNumberMismatch((uint32_t)ack_value());
    +    }
     
         //----------------------------------------
         // Tuning parsed responses
    @@ -1356,7 +1345,21 @@ void parseACK(void)
         // parse error messages
         else if (ack_seen(magic_error))
         {
    -      ackPopupInfo(magic_error);
    +      // command line number mismatch or checksum mismatch.
    +      // Required COMMAND_CHECKSUM feature enabled in TFT or managed by remote host
    +      if (ack_continue_seen("Last Line:"))
    +      {
    +        if (getCmdLineNumberOk() != (uint32_t)ack_value())  // if error message not already displayed for the same line number
    +        {
    +          setCmdLineNumberOk((uint32_t)ack_value());
    +          ack_seen(magic_error);      // just to reset ack_index to the beginning of the full error message to display
    +          ackPopupInfo(magic_error);  // display error message
    +        }
    +      }
    +      else
    +      {
    +        ackPopupInfo(magic_error);
    +      }
         }
         // parse echo messages
         else if (ack_starts_with(magic_echo))
    @@ -1410,16 +1413,14 @@ void parseACK(void)
         {
           if (ack_seen("External") || ack_seen("Software") || ack_seen("Watchdog") || ack_seen("Brown out"))
           {
    -        // proceed to reset the command queue, host status, fan speeds and load default machine settings.
    +        // proceed to reset the host status (it includes also command queue, pending queries and info queries)
    +        // and load default machine settings.
             // These functions will also trigger the query of temperatures which together with the resets
             // done will also trigger the query of the motherboard capabilities and settings. It is necessary
             // to do so because after the motherboard reset things might have changed (ex. FW update by M997)
     
    -        clearCmdQueue();
             InfoHost_Init(false);
             initMachineSettings();
    -        fanResetSpeed();
    -        coordinateSetKnown(false);
           }
         }
     
    @@ -1442,7 +1443,7 @@ void parseACK(void)
             Serial_Forward(ack_port_index, ack_cache);
     
             // if no pending gcode (all "ok" have been received), reset ACK port index to avoid wrong relaying (in case no
    -        // more commands will be sent by interfaceCmd) of any successive spontaneous ACK message
    +        // more commands will be sent by Mainboard_CmdHandler.c) of any successive spontaneous ACK message
             if (infoHost.tx_count == 0)
               ack_port_index = PORT_1;
           }
    diff --git a/TFT/src/User/API/parseACK.h b/TFT/src/User/API/Mainboard_AckHandler.h
    similarity index 73%
    rename from TFT/src/User/API/parseACK.h
    rename to TFT/src/User/API/Mainboard_AckHandler.h
    index d8c44ac6cb..588d6075fd 100644
    --- a/TFT/src/User/API/parseACK.h
    +++ b/TFT/src/User/API/Mainboard_AckHandler.h
    @@ -1,5 +1,5 @@
    -#ifndef _PARSE_ACK_H_
    -#define _PARSE_ACK_H_
    +#ifndef _MAINBOARD_ACK_HANDLER_H_
    +#define _MAINBOARD_ACK_HANDLER_H_
     
     #ifdef __cplusplus
     extern "C" {
    @@ -11,7 +11,7 @@ extern "C" {
     void setHostDialog(bool isHostDialog);
     bool getHostDialog(void);
     void setCurrentAckSrc(SERIAL_PORT_INDEX portIndex);
    -void parseACK(void);
    +void parseAck(void);
     
     #ifdef __cplusplus
     }
    diff --git a/TFT/src/User/API/Mainboard_CmdControl.c b/TFT/src/User/API/Mainboard_CmdControl.c
    new file mode 100644
    index 0000000000..b85b48eb57
    --- /dev/null
    +++ b/TFT/src/User/API/Mainboard_CmdControl.c
    @@ -0,0 +1,167 @@
    +#include 
    +#include 
    +
    +// line number of last command properly processed by mainboard,
    +// base line number and line number of last command sent by TFT respectively.
    +// Required COMMAND_CHECKSUM feature enabled in TFT
    +static uint32_t cmd_line_number_ok = NO_LINE_NUMBER;
    +static uint32_t cmd_line_number_base = 0;
    +static uint32_t cmd_line_number = 0;
    +
    +uint32_t getCmdLineNumberOk(void)
    +{
    +  return cmd_line_number_ok;
    +}
    +
    +void setCmdLineNumberOk(const uint32_t lineNumber)
    +{
    +  cmd_line_number_ok = lineNumber;
    +}
    +
    +uint32_t getCmdLineNumberBase(void)
    +{
    +  return cmd_line_number_base;
    +}
    +
    +void setCmdLineNumberBase(const uint32_t lineNumber)
    +{
    +  cmd_line_number_base = cmd_line_number = lineNumber;
    +}
    +
    +uint32_t getCmdLineNumber(void)
    +{
    +  return cmd_line_number;
    +}
    +
    +uint32_t addCmdLineNumberAndChecksum(CMD cmd, uint8_t cmdIndex, uint8_t * cmdLen)
    +{
    +  CMD plainCmd;  // plain command (with no line number and checksum, if any)
    +
    +  strcpy(plainCmd, &cmd[cmdIndex]);  // e.g. "N1 G28*18\n" -> "G28*18\n"
    +  stripCmdChecksum(plainCmd);        // e.g. "G28*18\n" -> "G28"
    +
    +  if (strlen(plainCmd) + 16 > CMD_MAX_SIZE)  // we consider extra bytes for line number, checksum, "\n" and "\0"
    +  {
    +    addNotification(DIALOG_TYPE_ERROR, "Cmd too long", cmd, true);
    +
    +    return NO_LINE_NUMBER;
    +  }
    +
    +  cmd_line_number++;  // next command line number to be used
    +
    +  sprintf(cmd, "N%lu %s", cmd_line_number, plainCmd);        // e.g. "G28" -> "N2 G28"
    +  sprintf(strchr(cmd, '\0'), "*%u\n", getCmdChecksum(cmd));  // e.g. "N2 G28" -> "N2 G28*17\n"
    +
    +  *cmdLen = strlen(cmd);
    +
    +  return cmd_line_number;
    +}
    +
    +const char * stripCmdHead(const CMD cmd)
    +{
    +  // example: ":    /test/cap2.gcode\n" -> "test/cap2.gcode\n"
    +
    +  while (*cmd == ' ' || *cmd == '/' || *cmd == ':')
    +  {
    +    cmd++;
    +  }
    +
    +  return cmd;
    +}
    +
    +void stripCmdChecksum(CMD cmd)
    +{
    +  // examples:
    +  //
    +  // "/test/cap2.gcode  *18\n\0" -> "/test/cap2.gcode"
    +  // "/test/cap2.gcode  \n\0" -> "/test/cap2.gcode"
    +
    +  char * cmdPtr = strrchr(cmd, '*');  // e.g. "/test/cap2.gcode  *18\n\0" -> "*18\n\0"
    +
    +  if (cmdPtr == NULL)
    +    cmdPtr = cmd + strlen(cmd);       // e.g. "/test/cap2.gcode  \n\0" -> "\0"
    +
    +  while (cmdPtr != cmd)
    +  {
    +    // e.g. "*18\n\0" -> " *18\n\0"
    +    // e.g. "\0" -> "\n\0"
    +    //
    +    --cmdPtr;
    +
    +    if (*cmdPtr != ' ' && *cmdPtr != '\t' && *cmdPtr != '\n' && *cmdPtr != '\r')
    +    {
    +      cmdPtr++;  // next char has to be set to "\0"
    +      break;
    +    }
    +  }
    +
    +  // e.g. "  *18\n\0" -> "\0 *18\n\0"
    +  // e.g. "  \n\0" -> "\0 \n\0"
    +  //
    +  *cmdPtr = '\0';
    +}
    +
    +uint8_t getCmdChecksum(const CMD cmd)
    +{
    +  uint8_t checksum = 0;
    +
    +  while (*cmd != '\0')
    +  {
    +    checksum ^= *(cmd++);
    +  }
    +
    +  return checksum;
    +}
    +
    +bool validateCmdChecksum(const CMD cmd)
    +{
    +  char * cmdPtr = strrchr(cmd, '*');  // e.g. "N1 G28*18\n\0" -> "*18\n\0"
    +
    +  if (cmdPtr == NULL)
    +    return false;
    +
    +  uint8_t checksum = 0;
    +  uint8_t value = strtol(&cmdPtr[1], NULL, 10);
    +
    +  while (cmdPtr != cmd)
    +  {
    +    checksum ^= *(--cmdPtr);
    +  }
    +
    +  return (checksum == value ? true : false);
    +}
    +
    +const char * parseM118(CMD cmd, bool * hasE, bool * hasA)
    +{
    +  stripCmdChecksum((char *) stripCmdHead(cmd));
    +
    +  *hasE = false;
    +  *hasA = false;
    +
    +  for (uint8_t i = 3; i--;)
    +  {
    +    // A1, E1 and Pn are always parsed out
    +    if (!(((cmd[0] == 'A' || cmd[0] == 'E') && cmd[1] == '1') || (cmd[0] == 'P' && NUMERIC(cmd[1]))))
    +      break;
    +
    +    switch (cmd[0])
    +    {
    +      case 'A':
    +        *hasA = true;
    +        break;
    +
    +      case 'E':
    +        *hasE = true;
    +        break;
    +    }
    +
    +    cmd += 2;
    +
    +    while (*cmd == ' ')
    +    {
    +      ++cmd;
    +    }
    +  }
    +
    +  return cmd;
    +}
    diff --git a/TFT/src/User/API/Mainboard_CmdControl.h b/TFT/src/User/API/Mainboard_CmdControl.h
    new file mode 100644
    index 0000000000..9d7ab3e685
    --- /dev/null
    +++ b/TFT/src/User/API/Mainboard_CmdControl.h
    @@ -0,0 +1,35 @@
    +#ifndef _MAINBOARD_CMD_CONTROL_
    +#define _MAINBOARD_CMD_CONTROL_
    +
    +#ifdef __cplusplus
    +extern "C" {
    +#endif
    +
    +#include 
    +#include 
    +#include   // for CMD
    +
    +#define NO_LINE_NUMBER 0xFFFFFFFF;                     // no processed line number
    +
    +uint32_t getCmdLineNumberOk(void);                     // get line number of last command properly processed by mainboard
    +void setCmdLineNumberOk(const uint32_t lineNumber);    // set line number of last command properly processed by mainboard
    +uint32_t getCmdLineNumberBase(void);                   // get base line number
    +void setCmdLineNumberBase(const uint32_t lineNumber);  // set base line number
    +uint32_t getCmdLineNumber(void);                       // get line number of last command sent by TFT
    +
    +// add line number and checksum to the provided command (cmd and cmdLen are properly set)
    +// and return the used line number
    +uint32_t addCmdLineNumberAndChecksum(CMD cmd, uint8_t cmdIndex, uint8_t * cmdLen);
    +
    +const char * stripCmdHead(const CMD cmd);  // strip out any leading " ", "/" or ":" character that might be in the command
    +void stripCmdChecksum(CMD cmd);            // strip out any trailing checksum that might be in the command
    +uint8_t getCmdChecksum(const CMD cmd);     // return checksum for the provided command
    +bool validateCmdChecksum(const CMD cmd);   // validate checksum for the provided command
    +
    +const char * parseM118(CMD cmd, bool * hasE, bool * hasA);  // parse M118 gcode
    +
    +#ifdef __cplusplus
    +}
    +#endif
    +
    +#endif
    diff --git a/TFT/src/User/API/interfaceCmd.c b/TFT/src/User/API/Mainboard_CmdHandler.c
    similarity index 86%
    rename from TFT/src/User/API/interfaceCmd.c
    rename to TFT/src/User/API/Mainboard_CmdHandler.c
    index 1846afa17c..f9eeaf2c85 100644
    --- a/TFT/src/User/API/interfaceCmd.c
    +++ b/TFT/src/User/API/Mainboard_CmdHandler.c
    @@ -1,8 +1,9 @@
    -#include "interfaceCmd.h"
    +#include "Mainboard_CmdHandler.h"
     #include "includes.h"
    -#include "RRFSendCmd.h"
    +#include "RRFStatusControl.h"
     
    -#define CMD_QUEUE_SIZE 20
    +#define CMD_QUEUE_SIZE  20
    +#define CMD_RETRY_COUNT 3
     
     typedef struct
     {
    @@ -18,6 +19,14 @@ typedef struct
       uint8_t count;    // count of commands in the queue
     } GCODE_QUEUE;
     
    +typedef struct
    +{
    +  uint32_t line_number;    // line number used as matching key with value reported by mainboard on "Resend: " ACK message
    +  bool retry;              // flag set to "true" to trigger a command resend. Initially set to "false" (no need to resend)
    +  int8_t retry_attempts;   // remaining retry attempts. Initially set to default max value CMD_RETRY_COUNT
    +  GCODE_INFO gcode_info;   // command to resend
    +} GCODE_RETRY_INFO;
    +
     typedef enum
     {
       NO_WRITING = 0,
    @@ -25,13 +34,15 @@ typedef enum
       ONBOARD_WRITING
     } WRITING_MODE;
     
    -GCODE_QUEUE cmdQueue;              // command queue where commands to be sent are stored
    +GCODE_QUEUE cmdQueue;                    // command queue where commands to be sent are stored
    +GCODE_RETRY_INFO cmdRetryInfo = {0};     // command retry info. Required COMMAND_CHECKSUM feature enabled in TFT
    +
     char * cmd_ptr;
     uint8_t cmd_len;
    -SERIAL_PORT_INDEX cmd_port_index;  // index of serial port originating the gcode
    -uint8_t cmd_base_index;            // base index in case the gcode has checksum ("Nxxx " is present at the beginning of gcode)
    +SERIAL_PORT_INDEX cmd_port_index;        // index of serial port originating the gcode
    +uint8_t cmd_base_index;                  // base index in case the gcode has checksum ("Nxx " is present at the beginning of gcode)
     uint8_t cmd_index;
    -WRITING_MODE writing_mode = NO_WRITING;
    +WRITING_MODE writing_mode = NO_WRITING;  // writing mode. Used by M28 and M29
     FIL file;
     
     uint8_t getQueueCount(void)
    @@ -182,25 +193,20 @@ void clearCmdQueue(void)
     
     // Strip out any leading space from the passed command.
     // Furthermore, skip any N[-0-9] (line number) and return a pointer to the beginning of the command.
    -char * stripCmd(char ** cmdPtr)
    +char * stripCmd(char * cmdPtr)
     {
    -  char * strPtr = *cmdPtr;
    -
       // skip leading spaces
    -  while (*strPtr == ' ') strPtr++;           // e.g. "  N1   G28*46\n" -> "N1   G28*46\n"
    -
    -  // save pointer to skipped spaces
    -  *cmdPtr = strPtr;                          // e.g. "  N1   G28*46\n" -> "N1   G28*46\n"
    +  while (*cmdPtr == ' ') cmdPtr++;           // e.g. "  N1   G28*18\n" -> "N1   G28*18\n"
     
       // skip N[-0-9] (line number) if included in the command line
    -  if (*strPtr == 'N' && NUMERIC(strPtr[1]))  // e.g. "N1   G28*46\n" -> "G28*46\n"
    +  if (*cmdPtr == 'N' && NUMERIC(cmdPtr[1]))  // e.g. "N1   G28*18\n" -> "G28*18\n"
       {
    -    strPtr += 2;                             // skip N[-0-9]
    -    while (NUMERIC(*strPtr)) strPtr++;       // skip [0-9]*
    -    while (*strPtr == ' ') strPtr++;         // skip [ ]*
    +    cmdPtr += 2;                             // skip N[-0-9]
    +    while (NUMERIC(*cmdPtr)) cmdPtr++;       // skip [0-9]*
    +    while (*cmdPtr == ' ') cmdPtr++;         // skip [ ]*
       }
     
    -  return strPtr;
    +  return cmdPtr;
     }
     
     // Get the data of the next to be sent command in cmdQueue
    @@ -215,16 +221,33 @@ static inline bool getCmd(void)
       //
       // set cmd_base_index with index of gcode command
       //
    -  cmd_base_index = stripCmd(&cmd_ptr) - cmd_ptr;                 // e.g. "N1   G28*46\n" -> "G28*46\n"
    +  cmd_base_index = stripCmd(cmd_ptr) - cmd_ptr;                 // e.g. "N1   G28*18\n" -> "G28*18\n"
     
       // set cmd_index with index of gcode value
    -  cmd_index = cmd_base_index + 1;                                // e.g. "G28*46\n" -> "28*46\n"
    +  cmd_index = cmd_base_index + 1;                                // e.g. "G28*18\n" -> "28*18\n"
     
       cmd_len = strlen(cmd_ptr);                                     // length of gcode
     
       return (cmd_port_index == PORT_1);  // if gcode is originated by TFT (SERIAL_PORT), return "true"
     }
     
    +static inline void getCmdFromCmdRetryInfo(void)
    +{
    +  cmd_ptr = cmdRetryInfo.gcode_info.gcode;
    +  cmd_port_index = cmdRetryInfo.gcode_info.port_index;
    +  cmd_len = strlen(cmd_ptr);
    +}
    +
    +void setCmdRetryInfo(uint32_t lineNumber)
    +{
    +  cmdRetryInfo.line_number = lineNumber;          // set line number to the provided value
    +  cmdRetryInfo.retry = false;                     // set retry flag to "false" (no need to resend)
    +  cmdRetryInfo.retry_attempts = CMD_RETRY_COUNT;  // set retry attempts to default max value CMD_RETRY_COUNT
    +
    +  strncpy_no_pad(cmdRetryInfo.gcode_info.gcode, cmd_ptr, CMD_MAX_SIZE);  // copy command
    +  cmdRetryInfo.gcode_info.port_index = cmd_port_index;                   // copy port index
    +}
    +
     // Purge gcode cmd or send it to the printer and then remove it from cmdQueue queue.
     bool sendCmd(bool purge, bool avoidTerminal)
     {
    @@ -238,24 +261,40 @@ bool sendCmd(bool purge, bool avoidTerminal)
         // dump serial data sent to debug port
         Serial_Put(SERIAL_DEBUG_PORT, serialPort[cmd_port_index].id);  // serial port ID (e.g. "2" for SERIAL_PORT_2)
         Serial_Put(SERIAL_DEBUG_PORT, ">>");
    -
    -    if (purge)
    -      Serial_Put(SERIAL_DEBUG_PORT, purgeStr);
    -
    -    Serial_Put(SERIAL_DEBUG_PORT, cmd_ptr);
       #endif
     
       if (!purge)  // if command is not purged, send it to printer
       {
    +    if (!cmdRetryInfo.retry)  // if there is no pending command to resend
    +    {
    +      if (GET_BIT(infoSettings.general_settings, INDEX_COMMAND_CHECKSUM) == 1)
    +        setCmdRetryInfo(addCmdLineNumberAndChecksum(cmd_ptr, cmd_base_index, &cmd_len));  // cmd_ptr and cmd_len are updated
    +
    +      cmdQueue.count--;
    +      cmdQueue.index_r = (cmdQueue.index_r + 1) % CMD_QUEUE_SIZE;
    +    }
    +    else  // if there is a pending command to resend
    +    {
    +      cmdRetryInfo.retry = false;     // disable command resend flag
    +      cmdRetryInfo.retry_attempts--;  // update remaining retry attempts
    +    }
    +
    +    // send or resend command
    +
         UPD_TX_KPIS(cmd_len);  // debug monitoring KPI
     
    -    if (infoMachineSettings.firmwareType != FW_REPRAPFW)
    -      Serial_Put(SERIAL_PORT, cmd_ptr);
    -    else
    -      rrfSendCmd(cmd_ptr);
    +    Serial_Put(SERIAL_PORT, cmd_ptr);
     
         setCurrentAckSrc(cmd_port_index);
       }
    +  #if defined(SERIAL_DEBUG_PORT) && defined(DEBUG_SERIAL_COMM)
    +    else  // if command is purged
    +    {
    +      Serial_Put(SERIAL_DEBUG_PORT, purgeStr);
    +    }
    +
    +    Serial_Put(SERIAL_DEBUG_PORT, cmd_ptr);
    +  #endif
     
       if (!avoidTerminal && MENU_IS(menuTerminal))
       {
    @@ -265,9 +304,6 @@ bool sendCmd(bool purge, bool avoidTerminal)
         terminalCache(cmd_ptr, cmd_len, cmd_port_index, SRC_TERMINAL_GCODE);
       }
     
    -  cmdQueue.count--;
    -  cmdQueue.index_r = (cmdQueue.index_r + 1) % CMD_QUEUE_SIZE;
    -
       return !purge;  // return "true" if command was sent. Otherwise, return "false"
     }
     
    @@ -334,12 +370,12 @@ bool initRemoteTFT()
     {
       // examples:
       //
    -  // "cmd_ptr" = "N1 M23 SD:/test/cap2.gcode*36\n"
    -  // "cmd_ptr" = "N1 M23 S /test/cap2.gcode*36\n"
    +  // "cmd_ptr" = "N1 M23 SD:/test/cap2.gcode*12\n"
    +  // "cmd_ptr" = "N1 M23 S /test/cap2.gcode*82\n"
       //
       // "infoFile.path" = "SD:/test/cap2.gcode"
     
    -  // e.g. "N1 M23 SD:/test/cap2.gcode*36\n" -> "SD:/test/cap2.gcode*36\n"
    +  // e.g. "N1 M23 SD:/test/cap2.gcode*12\n" -> "SD:/test/cap2.gcode*12\n"
       //
       if (cmd_seen_from(cmd_base_index, "SD:") || cmd_seen_from(cmd_base_index, "S "))
         infoFile.source = FS_TFT_SD;   // set source first
    @@ -354,11 +390,11 @@ bool initRemoteTFT()
       CMD path;  // temporary working buffer (cmd_ptr buffer must always remain unchanged)
     
       // cmd_index was set by cmd_seen_from() function
    -  strcpy(path, &cmd_ptr[cmd_index]);  // e.g. "N1 M23 SD:/test/cap2.gcode*36\n" -> "/test/cap2.gcode*36\n"
    -  stripChecksum(path);                // e.g. "/test/cap2.gcode*36\n" -> /test/cap2.gcode"
    +  strcpy(path, &cmd_ptr[cmd_index]);  // e.g. "N1 M23 SD:/test/cap2.gcode*12\n" -> "/test/cap2.gcode*12\n"
    +  stripCmdChecksum(path);             // e.g. "/test/cap2.gcode*12\n" -> /test/cap2.gcode"
     
    -  resetInfoFile();               // then reset infoFile (source is restored)
    -  enterFolder(stripHead(path));  // set path as last
    +  resetInfoFile();                  // then reset infoFile (source is restored)
    +  enterFolder(stripCmdHead(path));  // set path as last
     
       return true;
     }
    @@ -419,12 +455,12 @@ static inline void writeRemoteTFT()
     {
       // examples:
       //
    -  // "cmd_ptr" = "N1 G28*46\n"
    -  // "cmd_ptr" = "N2 G29*56\n"
    -  // "cmd_ptr" = "N3 M29*66\n"
    +  // "cmd_ptr" = "N1 G28*18\n"
    +  // "cmd_ptr" = "N2 G29*16\n"
    +  // "cmd_ptr" = "N3 M29*27\n"
     
       // if M29, stop writing mode. cmd_index (used by cmd_value() function) was set by sendQueueCmd() function
    -  if (cmd_ptr[cmd_base_index] == 'M' && cmd_value() == 29)  // e.g. "N3 M29*66\n" -> "M29*66\n"
    +  if (cmd_ptr[cmd_base_index] == 'M' && cmd_value() == 29)  // e.g. "N3 M29*27\n" -> "M29*27\n"
       {
         f_close(&file);
     
    @@ -437,12 +473,13 @@ static inline void writeRemoteTFT()
         UINT br;
         CMD cmd;  // temporary working buffer (cmd_ptr buffer must always remain unchanged)
     
    -    strcpy(cmd, &cmd_ptr[cmd_base_index]);  // e.g. "N1 G28*46\n" -> "G28*46\n"
    -    stripChecksum(cmd);                     // e.g. "G28*46\n" -> "G28"
    +    strcpy(cmd, &cmd_ptr[cmd_base_index]);  // e.g. "N1 G28*18\n" -> "G28*18\n"
    +    stripCmdChecksum(cmd);                  // e.g. "G28*18\n" -> "G28"
     
         f_write(&file, cmd, strlen(cmd), &br);
     
    -    // "\n" is always removed by stripChecksum() function even if there is no checksum, so we need to write it on file separately
    +    // "\n" is always removed by stripCmdChecksum() function even if there is no checksum,
    +    // so we need to write it on file separately
         f_write(&file, "\n", 1, &br);
         f_sync(&file);
       }
    @@ -476,6 +513,37 @@ void syncTargetTemp(uint8_t index)
       }
     }
     
    +void handleCmdLineNumberMismatch(const uint32_t lineNumber)
    +{
    +  // if no buffered command with the requested line number is found or it is found but the maximum number of retry
    +  // attempts is reached, reset the line number with M110 just to try to avoid further retransmission requests for
    +  // the same line number or for any out of synch command already sent to the mainboard (e.g. in case ADVANCED_OK
    +  // feature is enabled in TFT)
    +  if (cmdRetryInfo.line_number != lineNumber || cmdRetryInfo.retry_attempts == 0)
    +  {
    +    if (getCmdLineNumberBase() != lineNumber)  // if notification not already displayed for the same line number
    +    {
    +      char msgText[MAX_MSG_LENGTH];
    +
    +      snprintf(msgText, MAX_MSG_LENGTH, "line: cur=%lu, exp=%lu", cmdRetryInfo.line_number, lineNumber);
    +
    +      addNotification(DIALOG_TYPE_ERROR, "Cmd not found", msgText, false);
    +    }
    +
    +    setCmdLineNumberBase(lineNumber);  // set base line number of next command sent by the TFT to the requested line number
    +
    +    CMD cmd;
    +
    +    sprintf(cmd, "M110 N%lu", lineNumber);
    +
    +    sendEmergencyCmd(cmd);  // immediately send M110 command to set new base line number on mainboard
    +  }
    +  else  // if a command with the requested line number is present on the buffer and
    +  {     // not already resent for the maximum retry attempts, mark it as to be sent
    +    cmdRetryInfo.retry = true;
    +  }
    +}
    +
     // Check if the received gcode is an emergency command or not
     // (M108, M112, M410, M524, M876) and parse it accordingly.
     // Otherwise, store the gcode on command queue.
    @@ -484,13 +552,13 @@ void handleCmd(CMD cmd, const SERIAL_PORT_INDEX portIndex)
       // strip out any leading space from the passed command.
       // Furthermore, skip any N[-0-9] (line number) and return a pointer to the beginning of the command
       //
    -  char * strPtr = stripCmd(&cmd);  // e.g. "  N1   G28*46\n" -> "G28*46\n"
    +  char * cmdPtr = stripCmd(cmd);  // e.g. "  N1   G28*18\n" -> "G28*18\n"
     
       // check if the received gcode is an emergency command and parse it accordingly
     
    -  if (strPtr[0] == 'M')
    +  if (cmdPtr[0] == 'M')
       {
    -    switch (strtol(&strPtr[1], NULL, 10))
    +    switch (strtol(&cmdPtr[1], NULL, 10))
         {
           case 108:  // M108
           case 112:  // M112
    @@ -529,23 +597,32 @@ void sendEmergencyCmd(const CMD emergencyCmd, const SERIAL_PORT_INDEX portIndex)
         Serial_Put(SERIAL_DEBUG_PORT, emergencyCmd);
       #endif
     
    -  if (infoMachineSettings.firmwareType != FW_REPRAPFW)
    -    Serial_Put(SERIAL_PORT, emergencyCmd);
    -  else
    -    rrfSendCmd(emergencyCmd);
    +  uint8_t cmdLen = strlen(emergencyCmd);
    +
    +  UPD_TX_KPIS(cmdLen);  // debug monitoring KPI
    +
    +  Serial_Put(SERIAL_PORT, emergencyCmd);
     
       setCurrentAckSrc(portIndex);
     
       if (MENU_IS(menuTerminal))
    -    terminalCache(emergencyCmd, strlen(emergencyCmd), portIndex, SRC_TERMINAL_GCODE);
    +    terminalCache(emergencyCmd, cmdLen, portIndex, SRC_TERMINAL_GCODE);
     }
     
     // Parse and send gcode cmd in cmdQueue queue.
     void sendQueueCmd(void)
     {
    -  if (infoHost.tx_slots == 0 || cmdQueue.count == 0) return;
    +  if (infoHost.tx_slots == 0 || (cmdQueue.count == 0 && !cmdRetryInfo.retry)) return;
     
       bool avoid_terminal = false;
    +
    +  if (cmdRetryInfo.retry)  // if there is a pending command to resend
    +  {
    +    getCmdFromCmdRetryInfo();  // retrieve gcode from cmdRetryInfo
    +
    +    goto send_cmd;  // send the command
    +  }
    +
       bool fromTFT = getCmd();  // retrieve leading gcode in the queue and check if it is originated by TFT or other hosts
     
       if (writing_mode != NO_WRITING)  // if writing mode (previously triggered by M28)
    @@ -653,8 +730,8 @@ void sendQueueCmd(void)
                 if (!fromTFT)
                 {
                   // NOTE: If the file was selected (with M23) from onboard media, infoFile.source will be set to
    -              //       FS_ONBOARD_MEDIA_REMOTE by the startPrintingFromRemoteHost() function called in parseAck.c
    -              //       during M23 ACK parsing
    +              //       FS_ONBOARD_MEDIA_REMOTE by the startPrintingFromRemoteHost() function called in
    +              //       Mainboard_AckHandler.c during M23 ACK parsing
     
                   if (infoFile.source < FS_ONBOARD_MEDIA)  // if a file was selected from TFT media with M23
                   {
    @@ -725,7 +802,7 @@ void sendQueueCmd(void)
                 }
                 else
                 {
    -              setPrintUpdateWaiting(false);
    +              printClearSendingWaiting();
                 }
                 break;
     
    @@ -838,7 +915,7 @@ void sendQueueCmd(void)
     
             #else  // not SERIAL_PORT_2
               case 27:  // M27
    -            setPrintUpdateWaiting(false);
    +            printClearSendingWaiting();
                 break;
             #endif  // SERIAL_PORT_2
     
    @@ -865,17 +942,15 @@ void sendQueueCmd(void)
               }
               break;
     
    -        case 80:  // M80
    -          #ifdef PS_ON_PIN
    +        #ifdef PS_ON_PIN
    +          case 80:  // M80
                 PS_ON_On();
    -          #endif
    -          break;
    +            break;
     
    -        case 81:  // M81
    -          #ifdef PS_ON_PIN
    +          case 81:  // M81
                 PS_ON_Off();
    -          #endif
    -          break;
    +            break;
    +        #endif
     
             case 82:  // M82
               eSetRelative(false);
    @@ -895,7 +970,7 @@ void sendQueueCmd(void)
     
               if (fromTFT)
               {
    -            heatSetUpdateWaiting(false);
    +            heatClearSendingWaiting();
     
                 if (cmd_value() == 105)  // if M105
                 {
    @@ -934,11 +1009,20 @@ void sendQueueCmd(void)
               }
               break;
     
    +        case 110:  // M110
    +          setCmdLineNumberBase(cmd_seen('N') ? (uint32_t)cmd_value() : 0);
    +          break;
    +
             case 114:  // M114
    -          #ifdef FIL_RUNOUT_PIN
    -            if (fromTFT)
    -              FIL_PosE_SetUpdateWaiting(false);
    -          #endif
    +          if (fromTFT)
    +          {
    +            if (!cmd_seen('E'))
    +              coordinateQueryClearSendingWaiting();
    +            #ifdef FIL_RUNOUT_PIN
    +              else
    +                FIL_PosE_ClearSendingWaiting();
    +            #endif
    +          }
               break;
     
             case 117:  // M117
    @@ -968,8 +1052,8 @@ void sendQueueCmd(void)
                 strncpy_no_pad(rawMsg, &cmd_ptr[cmd_base_index + 4], CMD_MAX_SIZE);
     
                 // retrieve message text
    -            stripChecksum(rawMsg);
    -            msgText = stripHead(rawMsg);
    +            stripCmdChecksum(rawMsg);
    +            msgText = stripCmdHead(rawMsg);
     
                 statusSetMsg((uint8_t *)"M117", (uint8_t *)msgText);
     
    @@ -1104,11 +1188,17 @@ void sendQueueCmd(void)
             case 220:  // M220
               if (cmd_seen('S'))
                 speedSetCurPercent(0, cmd_value());
    +
    +          if (fromTFT)
    +            speedQueryClearSendingWaiting();
               break;
     
             case 221:  // M221
               if (cmd_seen('S'))
                 speedSetCurPercent(1, cmd_value());
    +
    +          if (fromTFT)
    +            speedQueryClearSendingWaiting();
               break;
     
             #ifdef BUZZER_PIN
    @@ -1157,7 +1247,7 @@ void sendQueueCmd(void)
                 caseLightSetPercent(cmd_value());
               break;
     
    -        case 376:  // M376 (Reprap FW)
    +        case 376:  // M376 (RepRap firmware)
               if (infoMachineSettings.firmwareType == FW_REPRAPFW && cmd_seen('H'))
                 setParameter(P_ABL_STATE, 1, cmd_float());
               break;
    @@ -1287,6 +1377,9 @@ void sendQueueCmd(void)
     
               if (cmd_seen('I'))
                 fanSetCurSpeed(MAX_COOLING_FAN_COUNT + 1, cmd_value());
    +
    +          if (fromTFT)
    +            ctrlFanQueryClearSendingWaiting();
               break;
     
             case 900:  // M900 linear advance factor
    diff --git a/TFT/src/User/API/interfaceCmd.h b/TFT/src/User/API/Mainboard_CmdHandler.h
    similarity index 90%
    rename from TFT/src/User/API/interfaceCmd.h
    rename to TFT/src/User/API/Mainboard_CmdHandler.h
    index 79995c0937..193d89da73 100644
    --- a/TFT/src/User/API/interfaceCmd.h
    +++ b/TFT/src/User/API/Mainboard_CmdHandler.h
    @@ -1,5 +1,5 @@
    -#ifndef _INTERFACE_CMD_H_
    -#define _INTERFACE_CMD_H_
    +#ifndef _MAINBOARD_CMD_HANDLER_H_
    +#define _MAINBOARD_CMD_HANDLER_H_
     
     #ifdef __cplusplus
     extern "C" {
    @@ -33,6 +33,7 @@ void mustStoreCmd(const char * format, ...);
     void mustStoreScript(const char * format, ...);
     bool storeCmdFromUART(const CMD cmd, const SERIAL_PORT_INDEX portIndex);
     void clearCmdQueue(void);
    +void handleCmdLineNumberMismatch(const uint32_t lineNumber);
     void handleCmd(CMD cmd, const SERIAL_PORT_INDEX portIndex);
     void sendEmergencyCmd(const CMD emergencyCmd, const SERIAL_PORT_INDEX portIndex);
     void sendQueueCmd(void);
    diff --git a/TFT/src/User/API/Mainboard_FlowControl.c b/TFT/src/User/API/Mainboard_FlowControl.c
    new file mode 100644
    index 0000000000..c48a50fb48
    --- /dev/null
    +++ b/TFT/src/User/API/Mainboard_FlowControl.c
    @@ -0,0 +1,287 @@
    +#include "Mainboard_FlowControl.h"
    +#include "includes.h"
    +#include "RRFStatusControl.h"
    +
    +CLOCKS mcuClocks;
    +PRIORITY_COUNTER priorityCounter;
    +HOST infoHost;
    +MENU infoMenu;
    +
    +void resetInfoQueries(void)
    +{
    +  fanResetSpeed();
    +  coordinateSetKnown(false);
    +  setRunoutAlarmFalse();
    +}
    +
    +void resetPendingQueries(void)
    +{
    +  abortRequestCommandInfo();             // abort pending command query
    +
    +  ctrlFanQueryClearSendingWaiting();     // clear sending waiting for controller fan query
    +  speedQueryClearSendingWaiting();       // clear sending waiting for speed query
    +  coordinateQueryClearSendingWaiting();  // clear sending waiting for coordinate query
    +
    +  heatClearSendingWaiting();             // clear sending waiting for temperature query
    +  printClearSendingWaiting();            // clear sending waiting for printing query
    +
    +  #ifdef FIL_RUNOUT_PIN
    +    FIL_PosE_ClearSendingWaiting();      // clear sending waiting for position query
    +  #endif
    +}
    +
    +// non-UI background loop tasks
    +void loopBackEnd(void)
    +{
    +  UPD_SCAN_RATE();  // debug monitoring KPI
    +
    +  // handle a print from TFT media, if any
    +  loopPrintFromTFT();
    +
    +  // parse and send gcode commands in the queue
    +  sendQueueCmd();
    +
    +  // parse the received slave response information
    +  parseAck();
    +
    +  // retrieve and store (in command queue) the gcodes received from other UART, such as ESP3D etc...
    +  #ifdef SERIAL_PORT_2
    +    Serial_GetFromUART();
    +  #endif
    +
    +  // handle USB communication
    +  #ifdef USB_FLASH_DRIVE_SUPPORT
    +    USB_LoopProcess();
    +  #endif
    +
    +  if ((priorityCounter.be++ % BE_PRIORITY_DIVIDER) != 0)  // a divider value of 16 -> run 6% of the time only
    +    return;
    +
    +  // handle ACK message timeout
    +  if (InfoHost_HandleAckTimeout())  // if ACK message timeout, unlock any pending query waiting for an update
    +    resetPendingQueries();
    +
    +  // fan speed monitor
    +  loopCheckFan();
    +
    +  // speed & flow monitor
    +  loopCheckSpeed();
    +
    +  if (infoMachineSettings.firmwareType != FW_REPRAPFW)
    +    loopCheckHeater();  // temperature monitor
    +  else
    +    rrfStatusQuery();  // query RRF status
    +
    +  // handle a print from (remote) onboard media, if any
    +  if (infoMachineSettings.onboardSD == ENABLED)
    +    loopPrintFromOnboard();
    +
    +  // Buzzer handling
    +  #ifdef BUZZER_PIN
    +    loopBuzzer();
    +  #endif
    +
    +  // check filament runout status
    +  #ifdef FIL_RUNOUT_PIN
    +    FIL_BE_CheckRunout();
    +  #endif
    +
    +  // check changes in encoder steps
    +  #if LCD_ENCODER_SUPPORT
    +    #ifdef HAS_EMULATOR
    +      if (MENU_IS_NOT(menuMarlinMode))
    +    #endif
    +    {
    +      LCD_Enc_CheckSteps();
    +    }
    +  #endif
    +
    +  // check mode switching
    +  #ifdef HAS_EMULATOR
    +    Mode_CheckSwitching();
    +  #endif
    +
    +  // handle screenshot capture
    +  #ifdef SCREEN_SHOT_TO_SD
    +    loopScreenShot();
    +  #endif
    +
    +  // check if Back is pressed and held
    +  #ifdef SMART_HOME
    +    loopCheckBackPress();
    +  #endif
    +
    +  // check LCD screen dimming
    +  #ifdef LCD_LED_PWM_CHANNEL
    +    LCD_CheckDimming();
    +  #endif
    +
    +  // check LED Event
    +  if (GET_BIT(infoSettings.general_settings, INDEX_EVENT_LED) == 1)
    +    LED_CheckEvent();
    +}
    +
    +// UI-related background loop tasks
    +void loopFrontEnd(void)
    +{
    +  // check if volume source (SD/USB) insert
    +  loopVolumeSource();
    +
    +  // loop to check and run toast messages
    +  loopToast();
    +
    +  // if there is a message in the status bar, timed clear
    +  loopReminderManage();
    +
    +  // busy Indicator clear
    +  loopBusySignClear();
    +
    +  // check update temperature status
    +  loopTemperatureStatus();
    +
    +  // loop for filament runout detection
    +  #ifdef FIL_RUNOUT_PIN
    +    FIL_FE_CheckRunout();
    +  #endif
    +
    +  // loop for popup menu
    +  loopPopup();
    +}
    +
    +void loopProcess(void)
    +{
    +  loopBackEnd();
    +
    +  if ((priorityCounter.fe++ % FE_PRIORITY_DIVIDER) != 0)  // a divider value of 16 -> run 6% of the time only
    +    return;
    +
    +  loopFrontEnd();
    +}
    +
    +void menuDummy(void)
    +{
    +  CLOSE_MENU();
    +}
    +
    +void loopProcessAndGUI(void)
    +{
    +  uint8_t curMenu = infoMenu.cur;
    +
    +  loopProcess();
    +
    +  if (infoMenu.cur != curMenu)  // if a user interaction is needed (e.g. dialog box), handle it
    +  {
    +    (*infoMenu.menu[infoMenu.cur])();  // handle user interaction
    +
    +    if (MENU_IS_NOT(menuDummy))  // avoid to nest menuDummy menu type
    +      OPEN_MENU(menuDummy);      // load a dummy menu just to force the redraw of the underlying menu (caller menu)
    +  }
    +}
    +
    +void InfoHost_Init(bool isConnected)
    +{
    +  infoHost.target_tx_slots = infoSettings.tx_slots;
    +  infoHost.tx_slots = 1;  // set to 1 just to allow a soft start
    +  infoHost.tx_count = 0;
    +  infoHost.rx_timestamp = OS_GetTimeMs();
    +  infoHost.connected = isConnected;
    +  infoHost.listening_mode = false;  // temporary disable listening mode. It will be later set by InfoHost_UpdateListeningMode()
    +  infoHost.status = HOST_STATUS_IDLE;
    +
    +  if (!isConnected)
    +  {
    +    clearCmdQueue();
    +    resetPendingQueries();
    +    resetInfoQueries();
    +
    +    setReminderMsg(LABEL_UNCONNECTED, SYS_STATUS_DISCONNECTED);  // set the no printer attached reminder
    +  }
    +}
    +
    +void InfoHost_HandleAckOk(int16_t target_tx_slots)
    +{
    +  // the following check should always be matched unless:
    +  // - an ACK message not related to a gcode originated by the TFT is received
    +  // - an ACK message for an out of band gcode (e.g. emergency gcode) is received
    +  //
    +  if (infoHost.tx_count > 0)
    +    infoHost.tx_count--;
    +
    +  // NOTE: the following code always allows to align infoHost.tx_slots even in case of switching ON/OFF
    +  //       the ADVANCED_OK feature in TFT and/or in case infoHost.tx_slots is beeing also managed by
    +  //       Marlin (if ADVANCED_OK is enabled in Marlin firmware)
    +  //
    +
    +  // if ADVANCED_OK is disabled in TFT
    +  //
    +  if (GET_BIT(infoSettings.general_settings, INDEX_ADVANCED_OK) == 0)
    +  {
    +    infoHost.tx_slots = 1;
    +  }
    +  //
    +  // if ADVANCED_OK is enabled in TFT or Marlin
    +  //
    +  else if (target_tx_slots >= 0)
    +  {
    +    // UPPER LIMITER
    +    //
    +    // the following check is matched in case:
    +    // - ADVANCED_OK is enabled in TFT. infoSettings.tx_slots for static ADVANCED_OK configured in TFT is used
    +    // - ADVANCED_OK is enabled in Marlin but the mainboard reply (target_tx_slots) is out of sync (above) with the current
    +    //   pending gcodes (it happens sometimes). infoSettings.tx_slots for Marlin ADVANCED_OK detected at TFT boot is used
    +    //
    +    if (target_tx_slots + infoHost.tx_count >= infoSettings.tx_slots)
    +      infoHost.tx_slots = infoSettings.tx_slots - infoHost.tx_count;
    +    //
    +    // LOWER LIMITER (only for Marlin ADVANCED_OK)
    +    //
    +    // if printing from onboard media target_tx_slots is always reported as 0 by Marlin even if there are no pending gcodes
    +    // so just set infoHost.tx_slots to 1 to allow the transmission of one gcode per time avoiding a possible TFT freeze
    +    //
    +    else if (target_tx_slots != 0 || infoHost.tx_count != 0)  // if not printing from onboard media
    +      infoHost.tx_slots = target_tx_slots;
    +    else                                                      // if printing from onboard media
    +      infoHost.tx_slots = 1;
    +
    +    infoHost.target_tx_slots = infoHost.tx_slots;  // set new current target
    +  }
    +  //
    +  // if generic OK response handling (e.g. temperature response), increment the current value up to current target
    +  //
    +  else
    +  {
    +    // UPPER AND LOWER LIMITER
    +    //
    +    // limit the current value up to current target or to 1 if current target was set to 0 and there are no more pending gcodes
    +    //
    +    if (infoHost.tx_slots < infoHost.target_tx_slots || (infoHost.tx_slots == 0 && infoHost.tx_count == 0))
    +      infoHost.tx_slots++;
    +  }
    +}
    +
    +bool InfoHost_HandleAckTimeout(void)
    +{
    +  if (OS_GetTimeMs() - infoHost.rx_timestamp < ACK_TIMEOUT || infoHost.tx_count == 0)  // if no timeout or no pending gcode
    +    return false;
    +
    +  infoHost.rx_timestamp = OS_GetTimeMs();  // update timestamp
    +
    +  InfoHost_HandleAckOk(HOST_SLOTS_GENERIC_OK);  // release pending gcode
    +
    +  //addNotification(DIALOG_TYPE_ERROR, "ACK timed out", "Pending gcode released", true);
    +
    +  return true;
    +}
    +
    +void InfoHost_UpdateAckTimestamp(void)
    +{
    +  infoHost.rx_timestamp = OS_GetTimeMs();
    +}
    +
    +void InfoHost_UpdateListeningMode(void)
    +{
    +  infoHost.listening_mode = (GET_BIT(infoSettings.general_settings, INDEX_LISTENING_MODE) == 1);
    +
    +  if (infoHost.listening_mode)
    +    setReminderMsg(LABEL_LISTENING, SYS_STATUS_LISTENING);  // if TFT in listening mode, display a reminder message
    +}
    diff --git a/TFT/src/User/API/Mainboard_FlowControl.h b/TFT/src/User/API/Mainboard_FlowControl.h
    new file mode 100644
    index 0000000000..f643c7f690
    --- /dev/null
    +++ b/TFT/src/User/API/Mainboard_FlowControl.h
    @@ -0,0 +1,97 @@
    +#ifndef _MAINBOARD_FLOW_CONTROL_H_
    +#define _MAINBOARD_FLOW_CONTROL_H_
    +
    +#ifdef __cplusplus
    +extern "C" {
    +#endif
    +
    +#include 
    +#include 
    +#include "variants.h"  // for RCC_ClocksTypeDef
    +
    +#define BE_PRIORITY_DIVIDER 16     // a divider value of 16 -> run 6% of the time only. Use a power of 2 for performance reasons!
    +#define FE_PRIORITY_DIVIDER 16     // a divider value of 16 -> run 6% of the time only. Use a power of 2 for performance reasons!
    +#define ACK_TIMEOUT         15000  // 15 seconds (1 sec is 1000)
    +#define MAX_MENU_DEPTH      10     // max sub menu depth
    +
    +// menu macros
    +#define OPEN_MENU(x)    infoMenu.menu[++infoMenu.cur] = x
    +#define REPLACE_MENU(x) infoMenu.menu[infoMenu.cur] = x
    +#define CLOSE_MENU()    infoMenu.cur--
    +#define MENU_IS(x)      infoMenu.menu[infoMenu.cur] == x
    +#define MENU_IS_NOT(x)  infoMenu.menu[infoMenu.cur] != x
    +
    +typedef struct
    +{
    +  RCC_ClocksTypeDef rccClocks;
    +  uint32_t PCLK1_Timer_Frequency;
    +  uint32_t PCLK2_Timer_Frequency;
    +} CLOCKS;
    +
    +typedef struct
    +{
    +  uint32_t be;  // back end
    +  uint32_t fe;  // front end
    +} PRIORITY_COUNTER;
    +
    +typedef enum
    +{
    +  HOST_STATUS_IDLE = 0,
    +  HOST_STATUS_PRINTING,
    +  HOST_STATUS_RESUMING,
    +  HOST_STATUS_PAUSED,
    +  HOST_STATUS_PAUSING
    +} HOST_STATUS;
    +
    +typedef enum
    +{
    +  HOST_SLOTS_GENERIC_OK = -1,
    +} HOST_SLOTS;
    +
    +typedef struct
    +{
    +  uint8_t target_tx_slots;  // keep track of target gcode tx slots (e.g. if ADVANCED_OK feature is enabled on both mainboard and TFT)
    +  uint8_t tx_slots;         // keep track of available gcode tx slots (e.g. if ADVANCED_OK feature is enabled on both mainboard and TFT)
    +  uint8_t tx_count;         // keep track of pending gcode tx count
    +  uint32_t rx_timestamp;    // keep track of last received ACK message timestamp
    +  bool connected;           // TFT is connected to Marlin
    +  bool listening_mode;      // TFT is in listening mode from Marlin
    +  HOST_STATUS status;       // host is busy in printing execution. (USB serial printing and gcode print from onboard)
    +} HOST;
    +
    +typedef void (* FP_MENU)(void);
    +
    +typedef struct
    +{
    +  FP_MENU menu[MAX_MENU_DEPTH];  // menu function buffer
    +  uint8_t cur;                   // current menu index in buffer
    +} MENU;
    +
    +extern CLOCKS mcuClocks;                  // system clocks: SYSCLK, AHB, APB1, APB2, APB1_Timer, APB2_Timer2
    +extern PRIORITY_COUNTER priorityCounter;  // priority counter
    +extern HOST infoHost;                     // information interaction with Marlin
    +extern MENU infoMenu;                     // menu structure
    +
    +void resetPendingQueries(void);
    +void loopBackEnd(void);
    +void loopFrontEnd(void);
    +void loopProcess(void);
    +void loopProcessAndGUI(void);
    +
    +void InfoHost_Init(bool isConnected);
    +
    +// handle ACK message OK response:
    +//   - tx_slots (used/effective only in case "advanced_ok" configuration setting is also enabled in TFT):
    +//     - < 0 (HOST_SLOTS_GENERIC_OK): to increase infoHost.tx_slots up to current target and decrease infoHost.tx_count by 1
    +//     - >= 0: to handle static ADVANCED_OK and Marlin ADVANCED_OK
    +void InfoHost_HandleAckOk(int16_t tx_slots);
    +
    +bool InfoHost_HandleAckTimeout(void);     // handle ACK message timeout, if any. Return "true" if ACK message timed out
    +void InfoHost_UpdateAckTimestamp(void);   // update last received ACK message timestamp
    +void InfoHost_UpdateListeningMode(void);  // update listening mode
    +
    +#ifdef __cplusplus
    +}
    +#endif
    +
    +#endif
    diff --git a/TFT/src/User/API/ModeSwitching.c b/TFT/src/User/API/ModeSwitching.c
    index 4e8850c117..29269b95ff 100644
    --- a/TFT/src/User/API/ModeSwitching.c
    +++ b/TFT/src/User/API/ModeSwitching.c
    @@ -31,9 +31,9 @@ void Mode_Switch(void)
             {
               uint32_t startUpTime = OS_GetTimeMs();
     
    -          heatSetUpdateSeconds(TEMPERATURE_QUERY_FAST_SECONDS);
               LOGO_ReadDisplay();
    -          updateNextHeatCheckTime();  // send "M105" after a delay, because of mega2560 will be hanged when received data at startup
    +          heatSetUpdateSeconds(TEMPERATURE_QUERY_FAST_SECONDS);
    +          heatSetNextUpdateTime();  // send "M105" after a delay, because of mega2560 will be hanged when received data at startup
     
               TASK_LOOP_WHILE(OS_GetTimeMs() - startUpTime < BTT_BOOTSCREEN_TIME);  // display logo BTT_BOOTSCREEN_TIME ms
     
    @@ -46,7 +46,7 @@ void Mode_Switch(void)
         case MODE_MARLIN:
           #ifdef HAS_EMULATOR
             if (infoSettings.serial_always_on == ENABLED)
    -          updateNextHeatCheckTime();  // send "M105" after a delay, because of mega2560 will be hanged when received data at startup
    +          heatSetNextUpdateTime();  // send "M105" after a delay, because of mega2560 will be hanged when received data at startup
     
             REPLACE_MENU(menuMarlinMode);
           #endif
    diff --git a/TFT/src/User/API/Notification.c b/TFT/src/User/API/Notification.c
    index b16c61c9a3..8b5c09e19c 100644
    --- a/TFT/src/User/API/Notification.c
    +++ b/TFT/src/User/API/Notification.c
    @@ -14,17 +14,17 @@ typedef struct
     } TOAST;
     
     // toast notification variables
    -static TOAST toastlist[TOAST_MSG_COUNT];
    -
    -static uint8_t nextToastIndex = 0;   // next index to store new toast
    -static uint8_t curToastDisplay = 0;  // current toast notification being displayed
    -static uint32_t nextToastTime = 0;   // time to change to next notification
    -
    -static NOTIFICATION msglist[MAX_MSG_COUNT];
    -static uint8_t nextMsgIndex = 0;  // next index to store new message
    -static void (*notificationHandler)() = NULL;
    -
    -bool _toastRunning = false;
    +static TOAST toastlist[TOAST_MSG_COUNT];  // toast notification array
    +static bool _toastRunning = false;        // "true" when a toast notification is currently displayed
    +static bool _toastAvailable = false;      // "true" when a new toast is added. "false" when no more toasts to display are available
    +static uint8_t nextToastIndex = 0;        // next index to store new toast
    +static uint8_t curToastDisplay = 0;       // current toast notification being displayed
    +static uint32_t nextToastTime = 0;        // time to change to next toast notification
    +
    +// message notification variables
    +static NOTIFICATION msglist[MAX_MSG_COUNT];    // message notification array
    +static uint8_t nextMsgIndex = 0;               // next index to store new message
    +static void (* notificationHandler)() = NULL;  // message notification handler
     
     // add new message to toast notification queue
     void addToast(DIALOG_TYPE style, char * text)
    @@ -34,24 +34,9 @@ void addToast(DIALOG_TYPE style, char * text)
       strncpy_no_pad(toastlist[nextToastIndex].text, text, TOAST_MSG_LENGTH);
       toastlist[nextToastIndex].style = style;
       toastlist[nextToastIndex].isNew = true;
    -  nextToastIndex = (nextToastIndex + 1) % TOAST_MSG_COUNT;
    -}
     
    -// check if notification is currently displayed
    -bool toastRunning(void)
    -{
    -  return _toastRunning;
    -}
    -
    -// check if any new notification is available
    -static inline bool toastAvailable(void)
    -{
    -  for (int i = 0; i < TOAST_MSG_COUNT; i++)
    -  {
    -    if (toastlist[i].isNew == true)
    -      return true;
    -  }
    -  return false;
    +  _toastAvailable = true;
    +  nextToastIndex = (nextToastIndex + 1) % TOAST_MSG_COUNT;
     }
     
     // show next notification
    @@ -66,33 +51,33 @@ void drawToast(bool redraw)
         _toastRunning = true;
     
         // draw icon
    -    uint8_t *icon;
    -    SOUND cursound;
    +    uint8_t * icon;
    +    SOUND curSound;
     
         switch (toastlist[curToastDisplay].style)
         {
           case DIALOG_TYPE_ERROR:
             GUI_SetColor(NOTIF_ICON_ERROR_BG_COLOR);
             icon = IconCharSelect(CHARICON_ERROR);
    -        cursound = SOUND_ERROR;
    +        curSound = SOUND_ERROR;
             break;
     
           case DIALOG_TYPE_SUCCESS:
             GUI_SetColor(NOTIF_ICON_SUCCESS_BG_COLOR);
             icon = IconCharSelect(CHARICON_OK_ROUND);
    -        cursound = SOUND_SUCCESS;
    +        curSound = SOUND_SUCCESS;
             break;
     
           default:
             GUI_SetColor(NOTIF_ICON_INFO_BG_COLOR);
             icon = IconCharSelect(CHARICON_INFO);
    -        cursound = SOUND_TOAST;
    +        curSound = SOUND_TOAST;
             break;
         }
     
         if (!redraw)  // if notification is new
         {
    -      BUZZER_PLAY(cursound);  // play sound
    +      BUZZER_PLAY(curSound);  // play sound
           nextToastTime = OS_GetTimeMs() + SEC_TO_MS(TOAST_DURATION);  // set new timer
         }
     
    @@ -114,22 +99,42 @@ void drawToast(bool redraw)
       }
     }
     
    +// check if notification is currently displayed
    +bool toastRunning(void)
    +{
    +  return _toastRunning;
    +}
    +
    +// check if any new notification is available
    +static inline bool toastAvailable(void)
    +{
    +  for (int i = 0; i < TOAST_MSG_COUNT; i++)
    +  {
    +    if (toastlist[i].isNew == true)
    +      return true;
    +  }
    +
    +  return false;
    +}
    +
     // check and control toast notification display
     void loopToast(void)
     {
    -  if (getMenuType() != MENU_TYPE_FULLSCREEN && OS_GetTimeMs() > nextToastTime)
    +  // if no new toast is available or it is not yet expired on screen or in case a full screen menu is displayed, do nothing
    +  if (_toastAvailable == false || OS_GetTimeMs() < nextToastTime || getMenuType() == MENU_TYPE_FULLSCREEN)
    +    return;
    +
    +  if (toastAvailable())
       {
    -    if (toastAvailable())
    -    {
    -      drawToast(false);
    -    }
    -    else if (_toastRunning == true)
    -    {
    -      _toastRunning = false;
    -      GUI_ClearPrect(&toastIconRect);
    -      GUI_ClearPrect(&toastRect);
    -      menuDrawTitle();
    -    }
    +    drawToast(false);
    +  }
    +  else if (_toastRunning == true)
    +  {
    +    _toastRunning = false;
    +    _toastAvailable = false;
    +    GUI_ClearPrect(&toastIconRect);
    +    GUI_ClearPrect(&toastRect);
    +    menuDrawTitle();
       }
     }
     
    @@ -145,6 +150,7 @@ void addNotification(DIALOG_TYPE style, char * title, char * text, bool drawDial
         {
           memcpy(&msglist[i], &msglist[i + 1], sizeof(NOTIFICATION));
         }
    +
         nextMsgIndex = MAX_MSG_COUNT - 1;
       }
     
    @@ -173,7 +179,7 @@ void replayNotification(uint8_t index)
     }
     
     // retrieve a stored notification
    -NOTIFICATION *getNotification(uint8_t index)
    +NOTIFICATION * getNotification(uint8_t index)
     {
       if (msglist[index].title[0] != '\0' && msglist[index].text[0] != '\0')
         return &msglist[index];
    @@ -189,28 +195,28 @@ bool hasNotification(void)
     void clearNotification(void)
     {
       nextMsgIndex = 0;
    +
       for (int i = 0; i < MAX_MSG_COUNT; i++)
       {
         msglist[i].text[0] = '\0';
         msglist[i].title[0] = '\0';
       }
    +
       notificationDot();
       statusSetReady();
     }
     
    +void setNotificationHandler(void (* handler)())
    +{
    +  notificationHandler = handler;
    +}
    +
     // check if pressed on titlebar area
     void titleBarPress(void)
     {
       if (getMenuType() == MENU_TYPE_ICON || getMenuType() == MENU_TYPE_LISTVIEW)
       {
         if (MENU_IS_NOT(menuNotification))
    -    {
           OPEN_MENU(menuNotification);
    -    }
       }
     }
    -
    -void setNotificationHandler(void (*handler)())
    -{
    -  notificationHandler = handler;
    -}
    diff --git a/TFT/src/User/API/Notification.h b/TFT/src/User/API/Notification.h
    index 29ce457c90..7771cdf0bd 100644
    --- a/TFT/src/User/API/Notification.h
    +++ b/TFT/src/User/API/Notification.h
    @@ -12,11 +12,11 @@ extern "C" {
     #define TOAST_X_PAD          START_X
     #define TOAST_Y_PAD          3
     
    -#define TOAST_MSG_COUNT       3
    +#define TOAST_MSG_COUNT       5
     #define TOAST_MSG_LENGTH     35
     #define TOAST_DISPLAY_LENGTH TOAST_MSG_LENGTH
     
    -#define MAX_MSG_COUNT         3
    +#define MAX_MSG_COUNT         5
     #define MAX_MSG_TITLE_LENGTH 15
     #define MAX_MSG_LENGTH       70
     
    @@ -27,17 +27,18 @@ typedef struct
       char text[MAX_MSG_LENGTH];
     } NOTIFICATION;
     
    -bool toastRunning(void);
     void addToast(DIALOG_TYPE style, char * text);
     void drawToast(bool redraw);
    +bool toastRunning(void);
     void loopToast(void);
    +
     void addNotification(DIALOG_TYPE style, char * title, char * text, bool drawDialog);
     void replayNotification(uint8_t index);
     NOTIFICATION * getNotification(uint8_t index);
     bool hasNotification(void);
    -void titleBarPress(void);
     void clearNotification(void);
    -void setNotificationHandler(void (*handler)());
    +void setNotificationHandler(void (* handler)());
    +void titleBarPress(void);
     
     #ifdef __cplusplus
     }
    diff --git a/TFT/src/User/API/PowerFailed.c b/TFT/src/User/API/PowerFailed.c
    index 39b57ccd71..f1ab46cb45 100644
    --- a/TFT/src/User/API/PowerFailed.c
    +++ b/TFT/src/User/API/PowerFailed.c
    @@ -203,9 +203,7 @@ void powerFailedCreate(char *path)
           if (f_write(&fpPowerFailed, &infoBreakPoint, sizeof(infoBreakPoint), &br) == FR_OK)
           {
             if (f_sync(&fpPowerFailed) == FR_OK)
    -        {
               create_ok = true;
    -        }
           }
         }
       }
    diff --git a/TFT/src/User/API/Printing.c b/TFT/src/User/API/Printing.c
    index ac5ff3f05f..e3b2e72b1f 100644
    --- a/TFT/src/User/API/Printing.c
    +++ b/TFT/src/User/API/Printing.c
    @@ -24,11 +24,13 @@ typedef struct
     PRINTING infoPrinting = {0};
     PRINT_SUMMARY infoPrintSummary = {.name[0] = '\0', 0, 0, 0, 0, false};
     
    -static bool updateM27Waiting = false;
     static bool extrusionDuringPause = false;  // flag for extrusion during Print -> Pause
     static bool filamentRunoutAlarm = false;
     static float lastEPos = 0;                 // used only to update stats in infoPrintSummary
     
    +static uint32_t nextUpdateTime = 0;
    +static bool sendingWaiting = false;
    +
     void setExtrusionDuringPause(bool extruded)
     {
       extrusionDuringPause = extruded;
    @@ -52,9 +54,8 @@ bool getRunoutAlarm(void)
     void clearQueueAndMore(void)
     {
       clearCmdQueue();
    +  resetPendingQueries();
       setRunoutAlarmFalse();
    -  heatSetUpdateWaiting(false);
    -  setPrintUpdateWaiting(false);
     }
     
     void breakAndContinue(void)
    @@ -345,11 +346,6 @@ void sendPrintCodes(uint8_t index)
       }
     }
     
    -void setPrintUpdateWaiting(bool isWaiting)
    -{
    -  updateM27Waiting = isWaiting;
    -}
    -
     void updatePrintUsedFilament(void)
     {
       float ePos = coordinateGetAxis(E_AXIS);
    @@ -424,7 +420,7 @@ bool startPrintFromRemoteHost(const char * filename)
       {
         infoFile.source = FS_ONBOARD_MEDIA_REMOTE;  // set source first
         resetInfoFile();                            // then reset infoFile (source is restored)
    -    enterFolder(stripHead(filename));           // set path as last
    +    enterFolder(stripCmdHead(filename));        // set path as last
     
         request_M27(infoSettings.m27_refresh_time);  // use gcode M27 in case of a print running from remote onboard media
       }
    @@ -502,7 +498,7 @@ bool startPrint(void)
     
       if (infoFile.source == FS_ONBOARD_MEDIA)
       {
    -    // let setPrintResume() (that will be called in parseAck.c by parsing ACK message for M24 or M27)
    +    // let setPrintResume() (that will be called in Mainboard_AckHandler.c by parsing ACK message for M24 or M27)
         // notify the print as started (infoHost.status set to "HOST_STATUS_PRINTING")
         infoHost.status = HOST_STATUS_RESUMING;
     
    @@ -578,7 +574,7 @@ void abortPrint(void)
         case FS_REMOTE_HOST:
           // - forward a print cancel notification to all hosts (so also the one handling the print) asking to cancel the print
           // - the host handling the print should respond to this notification with "M118 P0 A1 action:cancel" that will
    -      //   trigger setPrintAbort() in parseACK() once the following loop does its job (stopping all blocking operations)
    +      //   trigger setPrintAbort() in parseAck() once the following loop does its job (stopping all blocking operations)
           //
           mustStoreCmd("M118 P0 A1 action:notification remote cancel\n");
           waitForAbort();
    @@ -588,9 +584,10 @@ void abortPrint(void)
       }
     
       // - forward a print cancel action to all hosts (also TFT) to notify the print cancelation
    -  // - the print cancel action received by the TFT always guarantees the invokation of setPrintAbort() in parseAck.c
    -  //   (e.g. to finalize the print (e.g. stats) in case the ACK messages "Not SD printing" and/or "//action:cancel"
    -  //   are not received from Marlin) once the following loop does its job (stopping all blocking operations)
    +  // - the print cancel action received by the TFT always guarantees the invokation of setPrintAbort()
    +  //   in Mainboard_AckHandler.c (e.g. to finalize the print (e.g. stats) in case the ACK messages
    +  //   "Not SD printing" and/or "//action:cancel" are not received from Marlin) once the following
    +  //   loop does its job (stopping all blocking operations)
       //
       mustStoreCmd("M118 P0 A1 action:cancel\n");
       waitForAbort();
    @@ -919,6 +916,16 @@ void loopPrintFromTFT(void)
       }
     }
     
    +void printSetNextUpdateTime(void)
    +{
    +  nextUpdateTime = OS_GetTimeMs() + SEC_TO_MS(infoSettings.m27_refresh_time);
    +}
    +
    +void printClearSendingWaiting(void)
    +{
    +  sendingWaiting = false;
    +}
    +
     void loopPrintFromOnboard(void)
     {
       #ifdef HAS_EMULATOR
    @@ -931,24 +938,18 @@ void loopPrintFromOnboard(void)
       if (!infoSettings.m27_active) return;
       if (MENU_IS(menuTerminal)) return;
     
    -  static uint32_t nextCheckPrintTime = 0;
    -  uint32_t update_M27_time = SEC_TO_MS(infoSettings.m27_refresh_time);
    -
       do
    -  { // WAIT FOR M27
    -    if (updateM27Waiting == true)
    -    {
    -      nextCheckPrintTime = OS_GetTimeMs() + update_M27_time;
    -      break;
    -    }
    +  { // send M27 to query SD print status continuously
     
    -    if (OS_GetTimeMs() < nextCheckPrintTime)
    +    if (OS_GetTimeMs() < nextUpdateTime)  // if next check time not yet elapsed, do nothing
           break;
     
    -    if (storeCmd("M27\n") == false)
    +    printSetNextUpdateTime();  // extend next check time
    +
    +    // if M27 previously enqueued and not yet sent, do nothing
    +    if (sendingWaiting)
           break;
     
    -    nextCheckPrintTime = OS_GetTimeMs() + update_M27_time;
    -    updateM27Waiting = true;
    +    sendingWaiting = storeCmd("M27\n");
       } while (0);
     }
    diff --git a/TFT/src/User/API/Printing.h b/TFT/src/User/API/Printing.h
    index 5b532e1e71..ba382b87b0 100644
    --- a/TFT/src/User/API/Printing.h
    +++ b/TFT/src/User/API/Printing.h
    @@ -7,8 +7,8 @@ extern "C" {
     
     #include 
     #include 
    -#include "variants.h"  // for RAPID_SERIAL_COMM
    -#include "main.h"      // for HOST_STATUS
    +#include "variants.h"               // for RAPID_SERIAL_COMM
    +#include "Mainboard_FlowControl.h"  // for HOST_STATUS
     
     #ifdef RAPID_SERIAL_COMM
       #define RAPID_SERIAL_LOOP() loopBackEnd()
    @@ -111,9 +111,8 @@ bool getPrintRunout(void);
     //void preparePrintSummary(void);
     //void sendPrintCodes(uint8_t index);
     
    -void setPrintUpdateWaiting(bool isWaiting);  // called in interfaceCmd.c
    -void updatePrintUsedFilament(void);          // called in PrintingMenu.c
    -void clearInfoPrint(void);                   // called in PrintingMenu.c
    +void updatePrintUsedFilament(void);  // called in PrintingMenu.c
    +void clearInfoPrint(void);           // called in PrintingMenu.c
     
     //
     // commented because NOT externally invoked
    @@ -147,8 +146,11 @@ void setPrintAbort(void);
     void setPrintPause(HOST_STATUS hostStatus, PAUSE_TYPE pauseType);
     void setPrintResume(HOST_STATUS hostStatus);
     
    -void loopPrintFromTFT(void);      // called in loopBackEnd(). It handles a print from TFT media, if any
    -void loopPrintFromOnboard(void);  // called in loopBackEnd(). It handles a print from (remote) onboard media, if any
    +void loopPrintFromTFT(void);          // called in loopBackEnd(). It handles a print from TFT media, if any
    +
    +void printSetNextUpdateTime(void);    // called in parseAck(). Set next printing query time or timeout
    +void printClearSendingWaiting(void);  // called in sendQueueCmd(). Clear sending waiting for printing query
    +void loopPrintFromOnboard(void);      // called in loopBackEnd(). It handles a print from (remote) onboard media, if any
     
     #ifdef __cplusplus
     }
    diff --git a/TFT/src/User/API/ProbeHeightControl.c b/TFT/src/User/API/ProbeHeightControl.c
    index 4d6349a84f..b7e58322ef 100644
    --- a/TFT/src/User/API/ProbeHeightControl.c
    +++ b/TFT/src/User/API/ProbeHeightControl.c
    @@ -5,9 +5,8 @@
     #define ENDSTOP_CMD_RRF "M564 S%d H%d\n"  // for RRF
     #define MOVE_Z_CMD      "G1 Z%.2f F%d\n"
     
    -#define PROBE_UPDATE_DELAY 200  // 1 seconds is 1000
    +#define PROBE_REFRESH_TIME 200  // 1 seconds is 1000
     
    -static uint32_t nextQueryTime = 0;
     static uint8_t origEndstopsState = DISABLED;
     static float origAblState = DISABLED;
     
    @@ -20,10 +19,10 @@ void probeHeightEnable(void)
     
       if (origEndstopsState == ENABLED)  // if software endstops is enabled, disable it temporary
       {
    -    if (infoMachineSettings.firmwareType == FW_REPRAPFW)
    -      mustStoreCmd(ENDSTOP_CMD_RRF, 0, 0);
    -    else
    +    if (infoMachineSettings.firmwareType != FW_REPRAPFW)
           mustStoreCmd(ENDSTOP_CMD, 0);  // disable software endstops to move nozzle lower than Z0 if necessary
    +    else
    +      mustStoreCmd(ENDSTOP_CMD_RRF, 0, 0);
       }
     }
     
    @@ -33,10 +32,10 @@ void probeHeightDisable(void)
     {
       if (origEndstopsState == ENABLED)  // if software endstops was originally enabled, enable it again
       {
    -    if (infoMachineSettings.firmwareType == FW_REPRAPFW)
    -      mustStoreCmd(ENDSTOP_CMD_RRF, 1, 1);
    -    else
    +    if (infoMachineSettings.firmwareType != FW_REPRAPFW)
           mustStoreCmd(ENDSTOP_CMD, 1);  // enable software endstops
    +    else
    +      mustStoreCmd(ENDSTOP_CMD_RRF, 1, 1);
       }
     
       if (origAblState == ENABLED)  // if ABL was originally enabled, enable it again
    @@ -110,9 +109,12 @@ void probeHeightMove(float unit)
     // Query for new coordinates
     void probeHeightQueryCoord(void)
     {
    -  if (OS_GetTimeMs() > nextQueryTime)
    -  {
    -    coordinateQuery(0);  // query position manually for delay less than 1 second
    -    nextQueryTime = OS_GetTimeMs() + PROBE_UPDATE_DELAY;
    -  }
    +  static uint32_t nextUpdateTime = 0;
    +
    +  if (OS_GetTimeMs() < nextUpdateTime)
    +    return;
    +
    +  nextUpdateTime = OS_GetTimeMs() + PROBE_REFRESH_TIME;
    +
    +  coordinateQuery(0);  // query position manually for delay less than 1 second
     }
    diff --git a/TFT/src/User/API/RRFParseACK.cpp b/TFT/src/User/API/RRFAckHandler.cpp
    similarity index 93%
    rename from TFT/src/User/API/RRFParseACK.cpp
    rename to TFT/src/User/API/RRFAckHandler.cpp
    index 0cfa7ad6d2..fa2e0ec177 100644
    --- a/TFT/src/User/API/RRFParseACK.cpp
    +++ b/TFT/src/User/API/RRFAckHandler.cpp
    @@ -1,5 +1,6 @@
    -#include "RRFParseACK.hpp"
    +#include "RRFAckHandler.hpp"
     #include "includes.h"
    +#include "RRFStatusControl.h"
     
     /*
      * Parses structures like follows:
    @@ -43,26 +44,33 @@ static uint32_t expire_time = 0;
     
     static void m291_confirm(void)
     {
    -  if (m291_mode >= 1) mustStoreCmd("M292 P0\n");
    +  if (m291_mode >= 1)
    +    mustStoreCmd("M292 P0\n");
    +
       if (rrfStatusIsMacroBusy())
         rrfShowRunningMacro();
     }
     
     static void m291_cancel(void)
     {
    -  if (m291_mode > 2) mustStoreCmd("M292 P1\n");
    -  if (m291_mode == 2) mustStoreCmd("M292 P0\n");
    +  if (m291_mode > 2)
    +    mustStoreCmd("M292 P1\n");
    +
    +  if (m291_mode == 2)
    +    mustStoreCmd("M292 P0\n");
    +
       if (rrfStatusIsMacroBusy())
         rrfShowRunningMacro();
     }
     
     static void m291_loop(void)
     {
    -  if (m291_mode == -1 || (expire_time > 0 && OS_GetTimeMs() > expire_time))
    +  if (m291_mode == -1 || (expire_time > 0 && OS_GetTimeMs() >= expire_time))
       {
    -    CLOSE_MENU();
         if (rrfStatusIsMacroBusy())
           rrfShowRunningMacro();
    +
    +    CLOSE_MENU();
       }
     }
     
    @@ -73,6 +81,7 @@ void ParseACKJsonParser::endDocument()
       if (show_m291 && m291_msg != NULL)
       {
         char M291[] = "M291";
    +
         // _Generic is not available in C++
         //setDialogText(m291_title, m291_msg, LABEL_CONFIRM, LABEL_CANCEL);
         _setDialogTitleStr((uint8_t *)(m291_title == NULL ? M291 : m291_title));
    @@ -80,8 +89,10 @@ void ParseACKJsonParser::endDocument()
         _setDialogOkTextLabel(LABEL_CONFIRM);
         _setDialogCancelTextLabel(m291_mode > 2 ? LABEL_CANCEL : LABEL_NULL);
         expire_time = m291_timeo > 0 ? OS_GetTimeMs() + m291_timeo : 0;
    +
         showDialog(m291_mode > 2 ? DIALOG_TYPE_QUESTION : DIALOG_TYPE_INFO, m291_confirm,
    -        m291_mode > 2 ? m291_cancel : NULL, m291_loop);
    +               m291_mode > 2 ? m291_cancel : NULL, m291_loop);
    +
         BUZZER_PLAY(SOUND_NOTIFY);
         show_m291 = false;
       }
    @@ -97,6 +108,7 @@ void ParseACKJsonParser::endDocument()
         free(m291_title);
         m291_title = NULL;
       }
    +
       need_parser_reset = true;
     }
     
    @@ -105,98 +117,103 @@ void ParseACKJsonParser::value(const char *value)
       uint32_t seq;
       char *string_end;
       char *string_start;
    +
       switch (state)
       {
         case status:
           rrfStatusSet(value[0]);
           break;
    +
         case heaters:
           if (index == 0)
    -      {
             heatSetCurrentTemp(BED, strtod((char *)value, NULL) + 0.5f);
    -      }
           else if (index <= INVALID_HEATER)
    -      {
             heatSetCurrentTemp(index - 1, strtod((char *)value, NULL) + 0.5f);
    -      }
           break;
    +
         case active:
           if (index == 0)
    -      {
             heatSetTargetTemp(BED, strtod((char *)value, NULL) + 0.5f, FROM_HOST);
    -      }
           else if (index <= INVALID_HEATER)
    -      {
             heatSetTargetTemp(index - 1, strtod((char *)value, NULL) + 0.5f, FROM_HOST);
    -      }
           break;
    +
         case standby:
           break;
    +
         case hstat:
           if (strtod((char *)value, NULL) == 3)
           {
             if (index == 0)
    -        {
               heatSetTargetTemp(BED, 0, FROM_HOST);
    -        }
             else if (index <= INVALID_HEATER)
    -        {
               heatSetTargetTemp(index - 1, 0, FROM_HOST);
    -        }
           }
           break;
    +
         case pos:
           coordinateSetAxisActual((AXIS)index, strtod((char *)value, NULL));
           break;
    +
         case sfactor:
           speedSetCurPercent(0, strtod((char *)value, NULL));
           break;
    +
         case efactor:
           if (index == heatGetToolIndex())
    -      {
             speedSetCurPercent(1, strtod((char *)value, NULL));
    -      }
           break;
    +
         case baby_step:
           babystepSetValue(strtod((char *)value, NULL));
           break;
    +
         case tool:
           break;
    +
         case probe:
           break;
    +
         case fan_percent:
           if (index != 0 && index <= infoSettings.fan_count)  // index 0 is an alias for default tool fan
    -      {
             fanSetPercent(index - 1, strtod((char *)value, NULL) + 0.5f);
    -      }
           break;
    +
         case fanRPM:
           break;
    +
         case fraction_printed:
           if (getPrintProgressSource() < PROG_RRF)
             setPrintProgressSource(PROG_RRF);
    +
           if (getPrintProgressSource() == PROG_RRF)
             setPrintProgressPercentage((value[0] - '0') * 100 + (value[2] - '0') * 10 + (value[3] - '0'));
           break;
    +
         case mbox_seq:
           seq = strtod((char *)value, NULL);
           show_m291 = seq != m291_seq;
           m291_seq = seq;
           break;
    +
         case mbox_mode:
           m291_mode = strtod((char *)value, NULL);
           break;
    +
         case mbox_msg:
           m291_msg = (char*)malloc(strlen(value) + 1);
           strcpy(m291_msg, value);
           break;
    +
         case mbox_title:
           m291_title = (char*)malloc(strlen(value) + 1);
           strcpy(m291_title, value);
           break;
    +
         case mbox_timeo:
           m291_timeo = SEC_TO_MS(strtod((char *)value, NULL));
           break;
    +
         case resp:
           if (strstr(value, (char *)"Steps/"))  // parse M92
           {
    @@ -237,8 +254,8 @@ void ParseACKJsonParser::value(const char *value)
           {
             pidUpdateStatus(PID_FAILED);
           }
    -
           break;
    +
         case result:
           if (starting_print)
           {
    @@ -246,22 +263,26 @@ void ParseACKJsonParser::value(const char *value)
             starting_print = false;
           }
           break;
    +
         case none:
           break;
       }
    +
       if (in_array)
         ++index;
     }
     
    -void rrfParseACK(const char *data)
    +void rrfParseAck(const char *data)
     {
       static ParseACKJsonParser handler;
     
       jsonStreamingParser.setListener(&handler);
    +
       while (*data != 0)
       {
         jsonStreamingParser.parse(*data++);
       }
    +
       if (handler.need_parser_reset)
       {
         jsonStreamingParser.reset();
    diff --git a/TFT/src/User/API/RRFParseACK.hpp b/TFT/src/User/API/RRFAckHandler.hpp
    similarity index 96%
    rename from TFT/src/User/API/RRFParseACK.hpp
    rename to TFT/src/User/API/RRFAckHandler.hpp
    index bd8de8c7c4..030e550904 100644
    --- a/TFT/src/User/API/RRFParseACK.hpp
    +++ b/TFT/src/User/API/RRFAckHandler.hpp
    @@ -1,11 +1,11 @@
    -#ifndef _PARSE_ACK_JSON_H_
    -#define _PARSE_ACK_JSON_H_
    +#ifndef _RRF_ACK_HANDLER_JSON_H_
    +#define _RRF_ACK_HANDLER_JSON_H_
     
     #ifdef __cplusplus
     extern "C"
     {
     #endif
    -  void rrfParseACK(const char *data);
    +  void rrfParseAck(const char *data);
     #ifdef __cplusplus
     }
     #endif
    @@ -79,11 +79,13 @@ class ParseACKJsonParser : public JsonListener
       inline void endObject() {}
       inline void whitespace(char c) {}
       virtual void endDocument();
    +
       inline void startArray()
       {
         in_array = true;
         index = 0;
       }
    +
       inline void endArray()
       {
         in_array = false;
    @@ -164,6 +166,7 @@ class ParseACKJsonParser : public JsonListener
           state = none;
         }
       }
    +
       virtual void value(const char *value);
     };
     #endif
    diff --git a/TFT/src/User/API/RRFM20Parser.cpp b/TFT/src/User/API/RRFM20Parser.cpp
    index eb18a290a0..1b4f3c7cbb 100644
    --- a/TFT/src/User/API/RRFM20Parser.cpp
    +++ b/TFT/src/User/API/RRFM20Parser.cpp
    @@ -36,6 +36,7 @@
         "next": 0
       }
     */
    +
     const TCHAR *skip_number(const TCHAR *value)
     {
       if (isdigit(*value))
    @@ -44,6 +45,7 @@ const TCHAR *skip_number(const TCHAR *value)
         {
           ++value;
         } while (isdigit(*value));
    +
         return (*value == '_') ? value + 1 : value;
       }
     
    @@ -53,6 +55,7 @@ const TCHAR *skip_number(const TCHAR *value)
     int compare_items(void *arg, const void *a, const void *b)
     {
       bool macro_sort = *(bool *)arg;
    +
       if (macro_sort)
         return strcasecmp(((M20_LIST_ITEM *)a)->file_name, ((M20_LIST_ITEM *)b)->file_name);
     
    @@ -61,13 +64,17 @@ int compare_items(void *arg, const void *a, const void *b)
         // if M20 S3 ever works, we can make use of this
         case SORT_DATE_NEW_FIRST:
           return ((M20_LIST_ITEM *)b)->timestamp - ((M20_LIST_ITEM *)a)->timestamp;
    +
         case SORT_DATE_OLD_FIRST:
           return ((M20_LIST_ITEM *)a)->timestamp - ((M20_LIST_ITEM *)b)->timestamp;
    +
         case SORT_NAME_ASCENDING:
           return strcasecmp(((M20_LIST_ITEM *)a)->file_name, ((M20_LIST_ITEM *)b)->file_name);
    +
         case SORT_NAME_DESCENDING:
           return strcasecmp(((M20_LIST_ITEM *)b)->file_name, ((M20_LIST_ITEM *)a)->file_name);
       }
    +
       return strcmp(((M20_LIST_ITEM *)a)->file_name, ((M20_LIST_ITEM *)b)->file_name);
     }
     
    @@ -79,6 +86,7 @@ void RRFM20Parser::startObject()
     void RRFM20Parser::endObject()
     {
       in_object = false;
    +
       if (in_files && fileCount < FILE_NUM)
         ++fileCount;
     }
    @@ -86,7 +94,9 @@ void RRFM20Parser::endObject()
     void RRFM20Parser::endDocument()
     {
       if (macro_sort)
    +  {
         qsort_r(fileList, fileCount, sizeof(M20_LIST_ITEM), ¯o_sort, compare_items);
    +  }
       else
       {
         switch (infoSettings.files_sort_by)
    @@ -95,6 +105,7 @@ void RRFM20Parser::endDocument()
             // TODO use this implicit sort until M20 S3 works
             // M20 appears to be sorted oldest first, implicitly, reverse it.
             int i, j;
    +
             for (i = 0, j = fileCount - 1; i < j; ++i, --j)
             {
               M20_LIST_ITEM tmp;
    @@ -103,9 +114,10 @@ void RRFM20Parser::endDocument()
               fileList[j] = tmp;
             }
             break;
    +
           case SORT_NAME_ASCENDING:
           case SORT_NAME_DESCENDING:
    -          qsort_r(fileList, fileCount, sizeof(M20_LIST_ITEM), ¯o_sort, compare_items);
    +        qsort_r(fileList, fileCount, sizeof(M20_LIST_ITEM), ¯o_sort, compare_items);
             break;
         }
       }
    @@ -124,6 +136,7 @@ void RRFM20Parser::endDocument()
           infoFile.file[infoFile.fileCount++] = fileList[i].display_name;
         }
       }
    +
       need_reset = true;
     }
     
    @@ -132,6 +145,7 @@ void RRFM20Parser::endDocument()
     void RRFM20Parser::key(const char *key)
     {
       state = none;
    +
       if (!in_array)
         in_files = strcmp(FILES, key) == 0;
     
    @@ -168,16 +182,14 @@ void RRFM20Parser::value(const char *value)
           {
             if ((fileList[fileCount].file_name = (TCHAR *)malloc(len)) != NULL)
               strcpy(fileList[fileCount].file_name, value);
    +
             const char *skipped = macro_sort ? skip_number(value) : value;
             len = strlen(skipped) + 1;
    +
             if (macro_sort && value != skipped && (fileList[fileCount].display_name = (TCHAR *)malloc(len)) != NULL)
    -        {
               strcpy(fileList[fileCount].display_name, skipped);
    -        }
             else
    -        {
               fileList[fileCount].display_name = fileList[fileCount].file_name;
    -        }
             break;
           }
     
    @@ -191,6 +203,7 @@ void RRFM20Parser::value(const char *value)
             uint8_t hour = strtol(out + 1, &out, 10);
             uint8_t mins = strtol(out + 1, &out, 10);
             uint8_t secs = strtol(out + 1, NULL, 10);
    +
             // uint32_t will allow about up until year 2098, 31 days in a month because I'm lazy
             fileList[fileCount].timestamp = secs + (mins * 60) + (hour * 60 * 60) +
               (date * 60 * 60 * 24) + (mnth * 31 * 60 * 60 * 24) + (year * 12 * 31 * 60 * 60 * 24);
    @@ -200,18 +213,19 @@ void RRFM20Parser::value(const char *value)
           case none:
             break;
         }
    +
         state = none;
       }
       else
       {
         uint16_t current = fileCount++;
    +
         if (current >= FILE_NUM)
           return;
     
         if ((fileList[current].is_directory = (*value == '*')))
    -    {
           ++value;
    -    }
    +
         uint16_t len = strlen(value) + 1;
     
         if ((fileList[current].file_name = (TCHAR *)malloc(len)) != NULL)
    @@ -219,6 +233,7 @@ void RRFM20Parser::value(const char *value)
     
         value = macro_sort ? skip_number(value) : value;
         len = strlen(value) + 1;
    +
         if ((fileList[current].display_name = (TCHAR *)malloc(len)) != NULL)
           strcpy(fileList[current].display_name, value);
       }
    @@ -229,12 +244,11 @@ void parseM20Response(const char *data, bool macro_sorting)
       static RRFM20Parser *handler = NULL;
     
       if (handler == NULL)
    -  {
         handler = new RRFM20Parser;
    -  }
     
       handler->macro_sort = macro_sorting;
       jsonStreamingParser.setListener(handler);
    +
       while (*data != 0)
       {
         jsonStreamingParser.parse(*data++);
    diff --git a/TFT/src/User/API/RRFM20Parser.hpp b/TFT/src/User/API/RRFM20Parser.hpp
    index 15563fb899..e0709f5754 100644
    --- a/TFT/src/User/API/RRFM20Parser.hpp
    +++ b/TFT/src/User/API/RRFM20Parser.hpp
    @@ -18,6 +18,7 @@ extern "C"
         TCHAR *file_name;
         uint32_t timestamp;
       } M20_LIST_ITEM;
    +
       void parseJobListResponse(const char *data);
       void parseMacroListResponse(const char *data);
     #ifdef __cplusplus
    @@ -32,6 +33,7 @@ extern "C"
     #define FILES_TYPE "type"
     #define FILES_NAME "name"
     #define FILES_DATE "date"
    +
     enum RRFM20ParserState { none, type, name, date };
     
     class RRFM20Parser : public JsonListener
    @@ -72,6 +74,7 @@ class RRFM20Parser : public JsonListener
       {
         in_array = in_files;
       }
    +
       inline void endArray()
       {
         in_array = false;
    diff --git a/TFT/src/User/API/RRFSendCmd.c b/TFT/src/User/API/RRFSendCmd.c
    deleted file mode 100644
    index 0f824888a3..0000000000
    --- a/TFT/src/User/API/RRFSendCmd.c
    +++ /dev/null
    @@ -1,25 +0,0 @@
    -#include "RRFM20Parser.hpp"
    -#include "includes.h"
    -
    -static uint32_t line_number = 0;
    -
    -void rrfSendCmd(const char * cmd_ptr)
    -{
    -  char rrfCmd[CMD_MAX_SIZE];
    -  char * rrfCmd_ptr = rrfCmd;
    -  uint8_t checksum = 0;
    -
    -  sprintf(rrfCmd, "N%lu %s", line_number++, cmd_ptr);
    -
    -  // calculate checksum
    -  while (*rrfCmd_ptr != '\n')
    -  {
    -    checksum ^= *rrfCmd_ptr++;
    -  }
    -
    -  // add checksum and finalize formatting the RRF command
    -  sprintf(rrfCmd_ptr, "*%u\n", checksum);
    -
    -  // send the command to the serial port
    -  Serial_Put(SERIAL_PORT, rrfCmd);
    -}
    diff --git a/TFT/src/User/API/RRFSendCmd.h b/TFT/src/User/API/RRFSendCmd.h
    deleted file mode 100644
    index 88250753ea..0000000000
    --- a/TFT/src/User/API/RRFSendCmd.h
    +++ /dev/null
    @@ -1,14 +0,0 @@
    -#ifndef _RRF_SEND_CMD_H_
    -#define _RRF_SEND_CMD_H_
    -
    -#ifdef __cplusplus
    -extern "C" {
    -#endif
    -
    -void rrfSendCmd(const char* cmd_ptr);
    -
    -#ifdef __cplusplus
    -}
    -#endif
    -
    -#endif
    diff --git a/TFT/src/User/API/RRFStatusControl.c b/TFT/src/User/API/RRFStatusControl.c
    index afeed2eee8..8313b2d3ac 100644
    --- a/TFT/src/User/API/RRFStatusControl.c
    +++ b/TFT/src/User/API/RRFStatusControl.c
    @@ -2,7 +2,7 @@
     #include "includes.h"
     
     #define RRF_NORMAL_STATUS_QUERY_MS 1000
    -#define RRF_FAST_STATUS_QUERY_MS 500
    +#define RRF_FAST_STATUS_QUERY_MS   500
     
     // available status: status:
     // I=idle, P=printing from SD card, S=stopped (i.e. needs a reset), C=running config file (i.e starting up),
    @@ -29,8 +29,9 @@ void rrfStatusSet(char status)
                 setHostDialog(false);
                 setPrintResume(HOST_STATUS_RESUMING);
                 break;
    +
               case 'I':
    -            // RRFParseACK will take care of going to the print screen
    +            // rrfParseAck will take care of going to the print screen
                 mustStoreCmd("M409 K\"job.file.fileName\"\n");
                 starting_print = true;
                 break;
    @@ -77,21 +78,21 @@ void rrfStatusSet(char status)
             break;
         }
       }
    +
       rrf_status = status;
    +
       if (status != 'B')
    -  {
         macro_busy = false;
    -  }
     }
     
    -inline void rrfStatusSetBusy(void)
    +inline bool rrfStatusIsBusy(void)
     {
    -  rrf_status = 'B';
    +  return rrf_status == 'B';
     }
     
    -inline bool rrfStatusIsBusy(void)
    +inline void rrfStatusSetBusy(void)
     {
    -  return rrf_status == 'B';
    +  rrf_status = 'B';
     }
     
     inline bool rrfStatusIsMacroBusy(void)
    @@ -116,16 +117,20 @@ inline void rrfStatusQueryNormal(void)
     }
     
     void rrfStatusQuery(void)
    -{ // following conditions ordered by importance
    -  if (infoMachineSettings.firmwareType == FW_REPRAPFW && infoHost.connected)
    +{
    +  if (infoHost.connected)
       {
         static uint32_t rrf_next_query_time = 0;
     
    +    if (OS_GetTimeMs() < rrf_next_query_time)
    +      return;
    +
    +    rrf_next_query_time = OS_GetTimeMs() + rrf_query_interval;
    +
         // don't send status queries while in the terminal menu to avoid flooding the console
    -    if (OS_GetTimeMs() > rrf_next_query_time && MENU_IS_NOT(menuTerminal))
    -    {
    -      rrf_next_query_time = OS_GetTimeMs() + rrf_query_interval;
    -      storeCmd("M408 S0\n");
    -    }
    +    if (MENU_IS(menuTerminal))
    +      return;
    +
    +    storeCmd("M408 S0\n");
       }
     }
    diff --git a/TFT/src/User/API/RRFStatusControl.h b/TFT/src/User/API/RRFStatusControl.h
    index 94e0ae35a2..9dc50bdc19 100644
    --- a/TFT/src/User/API/RRFStatusControl.h
    +++ b/TFT/src/User/API/RRFStatusControl.h
    @@ -6,16 +6,17 @@ extern "C" {
     #endif
     
     #include 
    +
     extern bool starting_print;
     
    -void rrfStatusQuery(void);
    -void rrfStatusQueryFast(void);
    -void rrfStatusQueryNormal(void);
     void rrfStatusSet(char status);
     bool rrfStatusIsBusy(void);
     void rrfStatusSetBusy(void);
     bool rrfStatusIsMacroBusy(void);
     void rrfStatusSetMacroBusy(void);
    +void rrfStatusQueryFast(void);
    +void rrfStatusQueryNormal(void);
    +void rrfStatusQuery(void);
     
     #ifdef __cplusplus
     }
    diff --git a/TFT/src/User/API/SerialConnection.c b/TFT/src/User/API/SerialConnection.c
    index 840816a9ab..9e435c9430 100644
    --- a/TFT/src/User/API/SerialConnection.c
    +++ b/TFT/src/User/API/SerialConnection.c
    @@ -38,7 +38,6 @@ void Serial_Init(SERIAL_PORT_INDEX portIndex)
       if (portIndex == PORT_1 || portIndex == ALL_PORTS)  // if primary or all serial ports, initialize the primary serial port first
       {
         InfoHost_Init(false);  // initialize infoHost when disconnected
    -    coordinateSetKnown(false);
     
         Serial_Config(serialPort[PORT_1].port, serialPort[PORT_1].cacheSizeRX, serialPort[PORT_1].cacheSizeTX, baudrateValues[infoSettings.serial_port[PORT_1]]);
       }
    @@ -113,7 +112,7 @@ void Serial_Forward(SERIAL_PORT_INDEX portIndex, const char * msg)
       }
     }
     
    -bool Serial_NewDataAvailable(uint8_t port)
    +bool Serial_DataAvailableRX(uint8_t port)
     {
       // NOTE: used 32 bit variables for performance reasons
     
    @@ -214,7 +213,7 @@ void Serial_GetFromUART(void)
             #endif
             )
         {
    -      while (Serial_NewDataAvailable(serialPort[portIndex].port) && Serial_Get(serialPort[portIndex].port, cmd, CMD_MAX_SIZE) != 0)
    +      while (Serial_DataAvailableRX(serialPort[portIndex].port) && Serial_Get(serialPort[portIndex].port, cmd, CMD_MAX_SIZE) != 0)
           {
             handleCmd(cmd, portIndex);
           }
    diff --git a/TFT/src/User/API/SerialConnection.h b/TFT/src/User/API/SerialConnection.h
    index 5c71575613..69104da579 100644
    --- a/TFT/src/User/API/SerialConnection.h
    +++ b/TFT/src/User/API/SerialConnection.h
    @@ -8,6 +8,7 @@ extern "C" {
     #include 
     #include 
     #include "variants.h"  // for SERIAL_PORT_2 etc.
    +#include "Serial.h"    // for dmaL1DataTX etc.
     #include "uart.h"      // for _UART_CNT etc.
     
     #define BAUDRATE_COUNT 12
    @@ -64,11 +65,20 @@ void Serial_DeInit(SERIAL_PORT_INDEX portIndex);
     //   - msg: message to send
     void Serial_Forward(SERIAL_PORT_INDEX portIndex, const char * msg);
     
    -// test if a new message is available in the message queue of the provided physical serial port:
    +// test if a message is available in the RX message queue of the provided physical serial port:
     //   - port: physical serial port where data availability is tested
     //
     //   - return value: "true" if a new message is available. "false" otherwise
    -bool Serial_NewDataAvailable(uint8_t port);
    +bool Serial_DataAvailableRX(uint8_t port);
    +
    +// test if a message is available in the TX message queue of the provided physical serial port:
    +//   - port: physical serial port where data availability is tested
    +//
    +//   - return value: "true" if a message is available. "false" otherwise
    +static inline bool Serial_DataAvailableTX(uint8_t port)
    +{
    +  return (dmaL1DataTX[port].wIndex != dmaL1DataTX[port].rIndex);  // is more data available?
    +}
     
     // retrieve a message from the provided physical serial port:
     //   - port: physical serial port where data are read from
    @@ -80,7 +90,7 @@ uint16_t Serial_Get(uint8_t port, char * buf, uint16_t bufSize);
     
     #ifdef SERIAL_PORT_2
       // retrieve messages from all the enabled supplementary ports storing them
    -  // in the command queue (in interfaceCmd.c) for further processing
    +  // in the command queue (in Mainboard_CmdHandler.c) for further processing
       void Serial_GetFromUART(void);
     #endif
     
    diff --git a/TFT/src/User/API/Settings.c b/TFT/src/User/API/Settings.c
    index 3c4ba19a14..6d01eb4dea 100644
    --- a/TFT/src/User/API/Settings.c
    +++ b/TFT/src/User/API/Settings.c
    @@ -26,6 +26,7 @@ void initSettings(void)
       infoSettings.tx_slots               = TX_SLOTS;
       infoSettings.general_settings       = ((0 << INDEX_LISTENING_MODE) |
                                              (ADVANCED_OK << INDEX_ADVANCED_OK) |
    +                                         (COMMAND_CHECKSUM << INDEX_COMMAND_CHECKSUM) |
                                              (EMULATED_M600 << INDEX_EMULATED_M600) |
                                              (EMULATED_M109_M190 << INDEX_EMULATED_M109_M190) |
                                              (EVENT_LED << INDEX_EVENT_LED) |
    @@ -173,12 +174,12 @@ void initSettings(void)
         infoSettings.pause_feedrate[i]    = default_pause_speed[i];  // XY, Z, E
       }
     
    -  for (int i = 0; i < FEEDRATE_COUNT - 1 ; i++)  // xy, z
    +  for (int i = 0; i < FEEDRATE_COUNT - 1; i++)  // xy, z
       {
         infoSettings.level_feedrate[i]    = default_level_speed[i];
       }
     
    -  for (int i = 0; i < LED_COLOR_COMPONENT_COUNT - 1 ; i++)
    +  for (int i = 0; i < LED_COLOR_COMPONENT_COUNT - 1; i++)
       {
         infoSettings.led_color[i]         = default_led_color[i];
       }
    @@ -226,10 +227,6 @@ void initMachineSettings(void)
       infoMachineSettings.babyStepping            = DISABLED;
       infoMachineSettings.buildPercent            = DISABLED;
       infoMachineSettings.softwareEndstops        = ENABLED;
    -
    -  // reset the state to restart the temperature polling process
    -  // needed by parseAck() function to establish the connection
    -  heatSetUpdateWaiting(false);
     }
     
     void setupMachine(FW_TYPE fwType)
    @@ -354,5 +351,6 @@ bool getFlashSignStatus(int index)
       uint32_t len = sizeof(flash_sign);
     
       W25Qxx_ReadBuffer((uint8_t*)&cur_flash_sign, addr, len);
    +
       return (flash_sign[index] == cur_flash_sign[index]);
     }
    diff --git a/TFT/src/User/API/Settings.h b/TFT/src/User/API/Settings.h
    index ad56be1b3b..d6393e57ed 100644
    --- a/TFT/src/User/API/Settings.h
    +++ b/TFT/src/User/API/Settings.h
    @@ -11,10 +11,10 @@ extern "C" {
     #include "coordinate.h"  // for TOTAL_AXIS
     #include "LED_Colors.h"  // for LED_COLOR_COMPONENT_COUNT
     
    -#define CONFIG_SUPPPORT       20231119  // (YYYYMMDD) change if any keyword(s) is Configuration.h is added, removed or changed.
    +#define CONFIG_SUPPPORT       20240203  // (YYYYMMDD) change if any keyword(s) in Configuration.h is added, removed or changed.
                                             // This number should match CONFIGURATION_H_VERSION in Configuration.h
    -#define CONFIG_FLASH_SIGN     20230929  // (YYYYMMDD) change if any keyword(s) in config.ini is added or removed
    -#define LANGUAGE_FLASH_SIGN   20230821  // (YYYYMMDD) change if any keyword(s) in language pack is added or removed
    +#define CONFIG_FLASH_SIGN     20240203  // (YYYYMMDD) change if any keyword(s) in config.ini is added or removed
    +#define LANGUAGE_FLASH_SIGN   20240203  // (YYYYMMDD) change if any keyword(s) in language pack is added or removed
     #define ICON_FLASH_SIGN       20230821  // (YYYYMMDD) change if any icon(s) is added or removed
     #define FONT_FLASH_SIGN       20230821  // (YYYYMMDD) change if fonts require updating
     
    @@ -69,6 +69,7 @@ typedef enum
     {
       INDEX_LISTENING_MODE = 0,
       INDEX_ADVANCED_OK,
    +  INDEX_COMMAND_CHECKSUM,
       INDEX_EMULATED_M600,
       INDEX_EMULATED_M109_M190,
       INDEX_EVENT_LED,
    diff --git a/TFT/src/User/API/SpeedControl.c b/TFT/src/User/API/SpeedControl.c
    index eb820aa440..d5727c495c 100644
    --- a/TFT/src/User/API/SpeedControl.c
    +++ b/TFT/src/User/API/SpeedControl.c
    @@ -1,75 +1,73 @@
     #include "SpeedControl.h"
     #include "includes.h"
     
    -#define NEXT_SPEED_WAIT 500  // 1 second is 1000
    +#define SPEED_REFRESH_TIME 500  // 1 second is 1000
     
    -const char *const speedCmd[SPEED_NUM] = {"M220", "M221"};
    +const char * const speedCmd[SPEED_NUM] = {"M220", "M221"};
     
     static uint16_t setPercent[SPEED_NUM] = {100, 100};
     static uint16_t curPercent[SPEED_NUM] = {100, 100};
    -static uint8_t  needSetPercent = 0;
    +static uint8_t needSetPercent = 0;
     
    -static bool speedQueryWait = false;
    -static uint32_t nextSpeedTime = 0;
    +static bool speedSendingWaiting = false;
     
    -void speedSetPercent(uint8_t tool, uint16_t per)
    +void speedSetPercent(const uint8_t tool, const uint16_t per)
     {
       uint16_t value = NOBEYOND(SPEED_MIN, per, SPEED_MAX);
    +
       SET_BIT_VALUE(needSetPercent, tool, value != curPercent[tool]);
       setPercent[tool] = value;
     }
     
    -uint16_t speedGetSetPercent(uint8_t tool)
    +uint16_t speedGetSetPercent(const uint8_t tool)
     {
       return setPercent[tool];
     }
     
    -void speedSetCurPercent(uint8_t tool, uint16_t per)
    +void speedSetCurPercent(const uint8_t tool, const uint16_t per)
     {
       curPercent[tool] = per;
     }
     
    -uint16_t speedGetCurPercent(uint8_t tool)
    +uint16_t speedGetCurPercent(const uint8_t tool)
     {
       return curPercent[tool];
     }
     
    -void loopSpeed(void)
    +void loopCheckSpeed(void)
     {
    +  static uint32_t nextUpdateTime = 0;
    +
    +  if (OS_GetTimeMs() < nextUpdateTime)  // avoid rapid fire, clogging the queue
    +    return;
    +
    +  nextUpdateTime = OS_GetTimeMs() + SPEED_REFRESH_TIME;  // extend next check time
    +
       for (uint8_t i = 0; i < SPEED_NUM; i++)
       {
    -    if (infoSettings.ext_count == 0 && i > 0)
    -    {
    -      // Don't poll M221 if there are no extruders
    +    if (infoSettings.ext_count == 0 && i > 0)  // don't poll M221 if there are no extruders
           continue;
    -    }
     
    -    if (GET_BIT(needSetPercent, i) && (OS_GetTimeMs() > nextSpeedTime))
    +    if (GET_BIT(needSetPercent, i))
         {
           if (storeCmd("%s S%d D%d\n", speedCmd[i], setPercent[i], heatGetToolIndex()))
    -      {
             SET_BIT_OFF(needSetPercent, i);
    -      }
    -
    -      nextSpeedTime = OS_GetTimeMs() + NEXT_SPEED_WAIT;  // avoid rapid fire, clogging the queue
         }
       }
     }
     
    -void speedQuerySetWait(bool wait)
    +void speedQueryClearSendingWaiting(void)
     {
    -  speedQueryWait = wait;
    +  speedSendingWaiting = false;
     }
     
     void speedQuery(void)
     { // following conditions ordered by importance
    -  if (!speedQueryWait && infoHost.tx_slots != 0 && infoHost.connected && infoMachineSettings.firmwareType != FW_REPRAPFW)
    +  if (!speedSendingWaiting && infoHost.tx_slots != 0 && infoHost.connected && infoMachineSettings.firmwareType != FW_REPRAPFW)
       {
    -    speedQueryWait = storeCmd("M220\n");
    +    speedSendingWaiting = storeCmd("M220\n");
     
         if (infoSettings.ext_count > 0)
    -    {
    -      speedQueryWait |= storeCmd("M221\n");  // speedQueryWait set to "true" if at least one command will be sent
    -    }
    +      speedSendingWaiting |= storeCmd("M221\n");  // speedSendingWaiting set to "true" if at least one command will be sent
       }
     }
    diff --git a/TFT/src/User/API/SpeedControl.h b/TFT/src/User/API/SpeedControl.h
    index 12eb46367c..8f3fc2df6c 100644
    --- a/TFT/src/User/API/SpeedControl.h
    +++ b/TFT/src/User/API/SpeedControl.h
    @@ -12,13 +12,14 @@ extern "C" {
     #define SPEED_MIN 10
     #define SPEED_MAX 999
     
    -void speedSetPercent(uint8_t tool, uint16_t per);
    -uint16_t speedGetSetPercent(uint8_t tool);
    -void speedSetCurPercent(uint8_t tool, uint16_t per);
    -uint16_t speedGetCurPercent(uint8_t tool);
    -void loopSpeed(void);
    -void speedQuerySetWait(bool wait);
    -void speedQuery(void);
    +void speedSetPercent(const uint8_t tool, const uint16_t per);
    +uint16_t speedGetSetPercent(const uint8_t tool);
    +void speedSetCurPercent(const uint8_t tool, const uint16_t per);
    +uint16_t speedGetCurPercent(const uint8_t tool);
    +
    +void loopCheckSpeed(void);                 // called in loopBackEnd(). Loop for check on speed
    +void speedQueryClearSendingWaiting(void);  // called in sendQueueCmd(). Clear sending waiting for speed query
    +void speedQuery(void);                     // query for speed
     
     #ifdef __cplusplus
     }
    diff --git a/TFT/src/User/API/Temperature.c b/TFT/src/User/API/Temperature.c
    index b46d98f627..dad45a8371 100644
    --- a/TFT/src/User/API/Temperature.c
    +++ b/TFT/src/User/API/Temperature.c
    @@ -9,36 +9,35 @@ const char * const heatWaitCmd[MAX_HEATER_COUNT]   = HEAT_WAIT_CMD;
     const char * const extruderDisplayID[]             = EXTRUDER_ID;
     const char * const toolChange[]                    = TOOL_CHANGE;
     
    -static HEATER  heater = {{}, NOZZLE0};
    -static uint8_t heat_update_seconds = TEMPERATURE_QUERY_SLOW_SECONDS;
    -static bool    heat_update_waiting = false;
    -static uint8_t heat_send_waiting = 0;
    -static uint8_t heat_feedback_waiting = 0;
    +static HEATER   heater = {{}, NOZZLE0};
    +static uint8_t  heat_send_waiting = 0;
    +static uint8_t  heat_feedback_waiting = 0;
     
    -uint32_t nextHeatCheckTime = 0;
    +static uint8_t  heat_update_seconds = TEMPERATURE_QUERY_SLOW_SECONDS;
    +static uint32_t heat_next_update_time = 0;
    +static bool     heat_sending_waiting = false;
     
    -#define AUTOREPORT_TIMEOUT (nextHeatCheckTime + 3000)  // update interval + 3 second grace period
    +#define AUTOREPORT_TIMEOUT 3000  // 3 second grace period
     
     // verify that the heater index is valid, and fix the index of multiple in and 1 out tool nozzles
     static uint8_t heaterIndexFix(uint8_t index)
     {
    -  if (index == BED && infoSettings.bed_en)  // Bed
    +  if (index == BED && infoSettings.bed_en)  // bed
         return index;
     
    -  if (index == CHAMBER && infoSettings.chamber_en)  // Chamber
    +  if (index == CHAMBER && infoSettings.chamber_en)  // chamber
         return index;
     
    -  if (index < infoSettings.hotend_count)  // Vaild tool nozzle
    +  if (index < infoSettings.hotend_count)  // vaild tool nozzle
         return index;
     
    -  if (index < infoSettings.ext_count && infoSettings.hotend_count == 1)  // "multi-extruder" that shares a single nozzle.
    +  if (index < infoSettings.ext_count && infoSettings.hotend_count == 1)  // "multi-extruder" that shares a single nozzle
         return NOZZLE0;
     
    -  return INVALID_HEATER;  // Invalid heater
    +  return INVALID_HEATER;  // invalid heater
     }
     
    -// set target temperature
    -void heatSetTargetTemp(uint8_t index, int16_t temp, TEMP_SOURCE tempSource)
    +void heatSetTargetTemp(uint8_t index, const int16_t temp, const TEMP_SOURCE tempSource)
     {
       index = heaterIndexFix(index);
     
    @@ -74,7 +73,6 @@ void heatSetTargetTemp(uint8_t index, int16_t temp, TEMP_SOURCE tempSource)
       }
     }
     
    -// get target temperature
     uint16_t heatGetTargetTemp(uint8_t index)
     {
       index = heaterIndexFix(index);
    @@ -85,8 +83,7 @@ uint16_t heatGetTargetTemp(uint8_t index)
       return heater.T[index].target;
     }
     
    -// set current temperature
    -void heatSetCurrentTemp(uint8_t index, int16_t temp)
    +void heatSetCurrentTemp(uint8_t index, const int16_t temp)
     {
       index = heaterIndexFix(index);
     
    @@ -96,10 +93,9 @@ void heatSetCurrentTemp(uint8_t index, int16_t temp)
       heater.T[index].current = NOBEYOND(-99, temp, 999);
     
       if (infoMachineSettings.autoReportTemp)
    -    updateNextHeatCheckTime();  // set next timeout for temperature auto-report
    +    heatSetNextUpdateTime();  // set next timeout for temperature auto-report
     }
     
    -// get current temperature
     int16_t heatGetCurrentTemp(uint8_t index)
     {
       index = heaterIndexFix(index);
    @@ -110,7 +106,6 @@ int16_t heatGetCurrentTemp(uint8_t index)
       return heater.T[index].current;
     }
     
    -// disable all heaters/hotends
     void heatCoolDown(void)
     {
       for (uint8_t i = 0; i < MAX_HEATER_COUNT; i++)
    @@ -119,13 +114,11 @@ void heatCoolDown(void)
       }
     }
     
    -// is heating waiting to heat up
    -bool heatGetIsWaiting(uint8_t index)
    +bool heatGetIsWaiting(const uint8_t index)
     {
       return (heater.T[index].waiting == true);
     }
     
    -// check all heater if there is a heater waiting to be waited
     bool heatHasWaiting(void)
     {
       for (uint8_t i = 0; i < MAX_HEATER_COUNT; i++)
    @@ -137,8 +130,7 @@ bool heatHasWaiting(void)
       return false;
     }
     
    -// set heater waiting status
    -void heatSetIsWaiting(uint8_t index, bool isWaiting)
    +void heatSetIsWaiting(uint8_t index, const bool isWaiting)
     {
       index = heaterIndexFix(index);
     
    @@ -163,8 +155,6 @@ void heatClearIsWaiting(void)
       heatSetUpdateSeconds(TEMPERATURE_QUERY_SLOW_SECONDS);
     }
     
    -// set current tool (extruder)
    -// used when tool change command is from TFT
     bool heatSetTool(const uint8_t toolIndex)
     {
       if (storeCmd("%s\n", toolChange[toolIndex]))
    @@ -181,20 +171,17 @@ void heatSetToolIndex(const uint8_t toolIndex)
       heater.toolIndex = toolIndex;
     }
     
    -// get current Tool (extruder)
     uint8_t heatGetToolIndex(void)
     {
       return heater.toolIndex;
     }
     
    -// get current hotend index in arry T[]
     uint8_t heatGetCurrentHotend(void)
     {
       return (infoSettings.hotend_count == 1) ? NOZZLE0 : heater.toolIndex;
     }
     
    -// check whether the index is a valid heater index.
    -bool heaterDisplayIsValid(uint8_t index)
    +bool heaterDisplayIsValid(const uint8_t index)
     {
       if (index >= infoSettings.hotend_count && index < MAX_HOTEND_COUNT)
         return false;
    @@ -208,78 +195,59 @@ bool heaterDisplayIsValid(uint8_t index)
       return true;
     }
     
    -// set temperature update time interval
    -void heatSetUpdateSeconds(uint8_t seconds)
    +void heatSetUpdateSeconds(const uint8_t seconds)
     {
       if (heat_update_seconds == seconds)
         return;
     
       heat_update_seconds = seconds;
     
    -  if (infoMachineSettings.autoReportTemp && !heat_update_waiting)
    -    heat_update_waiting = storeCmd("M155 S%u\n", heatGetUpdateSeconds());
    +  if (infoMachineSettings.autoReportTemp && !heat_sending_waiting)
    +    heat_sending_waiting = storeCmd("M155 S%u\n", heat_update_seconds);
     }
     
    -// get query temperature seconds
     uint8_t heatGetUpdateSeconds(void)
     {
       return heat_update_seconds;
     }
     
    -// set query temperature seconds
    -void heatSyncUpdateSeconds(uint8_t seconds)
    +void heatSyncUpdateSeconds(const uint8_t seconds)
     {
       heat_update_seconds = seconds;
     }
     
    -// set whether we need to query the current temperature
    -void heatSetUpdateWaiting(bool isWaiting)
    +void heatSetNextUpdateTime(void)
     {
    -  heat_update_waiting = isWaiting;
    +  heat_next_update_time = OS_GetTimeMs() + SEC_TO_MS(heat_update_seconds);
    +
    +  if (infoMachineSettings.autoReportTemp)
    +    heat_next_update_time += AUTOREPORT_TIMEOUT;
     }
     
    -void updateNextHeatCheckTime(void)
    +void heatClearSendingWaiting(void)
     {
    -  nextHeatCheckTime = OS_GetTimeMs() + SEC_TO_MS(heat_update_seconds);
    +  heat_sending_waiting = false;
     }
     
     void loopCheckHeater(void)
     {
    -  // Send M105 to query the temperatures, if motherboard does not supports M155 (AUTO_REPORT_TEMPERATURES) feature
    -  // to automatically report the temperatures.
    -  if (!infoMachineSettings.autoReportTemp)
    -  {
    -    do
    -    {
    -      // Send M105 query temperature continuously
    -      if (heat_update_waiting == true)
    -      {
    -        updateNextHeatCheckTime();
    -        break;
    -      }
    +  do
    +  { // periodically send M105 to query the temperatures, if motherboard does not supports M155 (AUTO_REPORT_TEMPERATURES)
    +    // feature to automatically report the temperatures or (if M155 is supported) check temperature auto-report timeout
    +    // and resend M155 command in case of timeout expired
     
    -      if (OS_GetTimeMs() < nextHeatCheckTime)
    -        break;
    +    if (OS_GetTimeMs() < heat_next_update_time)  // if next check time not yet elapsed, do nothing
    +      break;
     
    -      if (requestCommandInfoIsRunning())  // To avoid collision in gcode response processing
    -        break;
    +    heatSetNextUpdateTime();  // extend next check time
     
    -      if ((infoMachineSettings.firmwareType != FW_REPRAPFW) && !storeCmd("M105\n"))
    -        break;
    +    // if M105/M155 previously enqueued and not yet sent or pending command
    +    // (to avoid collision in gcode response processing), do nothing
    +    if (heat_sending_waiting || requestCommandInfoIsRunning())
    +      break;
     
    -      updateNextHeatCheckTime();
    -      heat_update_waiting = true;
    -    } while (0);
    -  }
    -  else  // check temperature auto-report timout and resend M155 command
    -  {
    -    if (OS_GetTimeMs() > AUTOREPORT_TIMEOUT && !heat_update_waiting)
    -    {
    -      heat_update_waiting = storeCmd("M155 S%u\n", heatGetUpdateSeconds());
    -      if (heat_update_waiting)
    -        updateNextHeatCheckTime();  // set next timeout for temperature auto-report
    -    }
    -  }
    +    heat_sending_waiting = !infoMachineSettings.autoReportTemp ? storeCmd("M105\n") : storeCmd("M155 S%u\n", heat_update_seconds);
    +  } while (0);
     
       for (uint8_t i = 0; i < MAX_HEATER_COUNT; i++)
       {
    diff --git a/TFT/src/User/API/Temperature.h b/TFT/src/User/API/Temperature.h
    index 8646694642..c65e9e73dd 100644
    --- a/TFT/src/User/API/Temperature.h
    +++ b/TFT/src/User/API/Temperature.h
    @@ -23,7 +23,7 @@ typedef enum
     
     typedef enum
     {
    -  FROM_HOST = 0,  // temperature status (actual/requested) from host (Marlin, Reprap, etc.)
    +  FROM_HOST = 0,  // temperature status (actual/requested) from host (Marlin, RepRap, etc.)
       FROM_GUI,       // temperature requested from the TFT's GUI
       FROM_CMD,       // temperature requested in the command queue (from gcode or external source connected to the TFT)
     } TEMP_SOURCE;
    @@ -73,30 +73,30 @@ extern const char * const heatWaitCmd[];
     extern const char * const extruderDisplayID[];
     extern const char * const toolChange[];
     
    -void heatSetTargetTemp(uint8_t index, int16_t temp, TEMP_SOURCE tempSource);
    -uint16_t heatGetTargetTemp(uint8_t index);
    -void heatSetCurrentTemp(uint8_t index, int16_t temp);
    -int16_t heatGetCurrentTemp(uint8_t index);
    -void heatCoolDown(void);
    +void heatSetTargetTemp(uint8_t index, const int16_t temp, const TEMP_SOURCE tempSource);  // set target temperature
    +uint16_t heatGetTargetTemp(uint8_t index);                   // get target temperature
    +void heatSetCurrentTemp(uint8_t index, const int16_t temp);  // set current temperature
    +int16_t heatGetCurrentTemp(uint8_t index);                   // get current temperature
    +void heatCoolDown(void);                                     // disable all heaters/hotends
     
    -bool heatGetIsWaiting(uint8_t index);
    -bool heatHasWaiting(void);
    -void heatSetIsWaiting(uint8_t index, bool isWaiting);
    +bool heatGetIsWaiting(const uint8_t index);                  // is heating waiting to heat up
    +bool heatHasWaiting(void);                                   // check all heater if there is a heater waiting to be waited
    +void heatSetIsWaiting(uint8_t index, const bool isWaiting);  // set heater waiting status
     void heatClearIsWaiting(void);
     
    -bool heatSetTool(const uint8_t tool);
    -void heatSetToolIndex(const uint8_t toolIndex);
    -uint8_t heatGetToolIndex(void);
    -uint8_t heatGetCurrentHotend(void);
    -bool heaterDisplayIsValid(uint8_t index);
    +bool heatSetTool(const uint8_t tool);               // set current tool (extruder). Used when tool change command is from TFT
    +void heatSetToolIndex(const uint8_t toolIndex);     // set current Tool (extruder)
    +uint8_t heatGetToolIndex(void);                     // get current Tool (extruder)
    +uint8_t heatGetCurrentHotend(void);                 // get current hotend index in arry T[]
    +bool heaterDisplayIsValid(const uint8_t index);     // check whether the index is a valid heater index
     
    -void heatSetUpdateSeconds(uint8_t seconds);
    -uint8_t heatGetUpdateSeconds(void);
    -void heatSyncUpdateSeconds(uint8_t seconds);
    -void heatSetUpdateWaiting(bool isWaiting);
    +void heatSetUpdateSeconds(const uint8_t seconds);   // set temperature query update time interval
    +uint8_t heatGetUpdateSeconds(void);                 // get temperature query seconds
    +void heatSyncUpdateSeconds(const uint8_t seconds);  // set temperature query seconds
     
    -void updateNextHeatCheckTime(void);
    -void loopCheckHeater(void);
    +void heatSetNextUpdateTime(void);                   // called in parseAck(). Set next temperature query time or timeout
    +void heatClearSendingWaiting(void);                 // called in sendQueueCmd(). Clear sending waiting for temperature query
    +void loopCheckHeater(void);                         // called in loopBackEnd(). Loop for check on Heater
     
     #ifdef __cplusplus
     }
    diff --git a/TFT/src/User/API/Touch_Encoder.c b/TFT/src/User/API/Touch_Encoder.c
    index 44e1e14841..722c59aa7e 100644
    --- a/TFT/src/User/API/Touch_Encoder.c
    +++ b/TFT/src/User/API/Touch_Encoder.c
    @@ -1,25 +1,25 @@
     #include "Touch_Encoder.h"
     #include "includes.h"
     
    -bool Touch_Enc_ReadPen(uint16_t interval)
    +bool Touch_Enc_ReadPen(uint16_t duration)
     {
    -  static uint32_t nowTime = 0;
    +  static uint32_t lastTime = 0;
     
    -  if (!XPT2046_Read_Pen())
    +  if (XPT2046_Read_Pen())  // if touch screen not pressed
       {
    -    if (OS_GetTimeMs() - nowTime >= interval)
    -    {
    -      nowTime = OS_GetTimeMs();
    +    lastTime = OS_GetTimeMs();
     
    -      return true;
    -    }
    -  }
    -  else
    -  {
    -    nowTime = OS_GetTimeMs();
    +    return false;
       }
     
    -  return false;
    +  if (OS_GetTimeMs() - lastTime < duration)  // if touch screen held pressed but provided duration not yet reached
    +    return false;
    +
    +  // touch screen held pressed for the provided duration
    +
    +  lastTime = OS_GetTimeMs();
    +
    +  return true;
     }
     
     #if LCD_ENCODER_SUPPORT
    @@ -27,79 +27,83 @@ bool Touch_Enc_ReadPen(uint16_t interval)
     #define LCD_FREE_WIDTH  (LCD_WIDTH - LCD_WIDTH / 5)
     #define LCD_FREE_HEIGHT (LCD_HEIGHT / 5)
     
    -bool Touch_Enc_ReadBtn(uint16_t interval)
    +bool Touch_Enc_ReadBtn(uint16_t duration)
     {
    -  static uint32_t nowTime = 0;
    -  uint16_t tx, ty;
    +  static uint32_t lastTime = 0;
     
    -  if (!XPT2046_Read_Pen())
    +  if (XPT2046_Read_Pen())  // if touch screen not pressed
       {
    -    TS_GetCoordinates(&tx, &ty);
    +    lastTime = OS_GetTimeMs();
     
    -    if (OS_GetTimeMs() - nowTime >= interval)
    -    {
    -      if (tx > LCD_FREE_WIDTH && ty < LCD_FREE_HEIGHT)
    -        return true;
    -    }
    -  }
    -  else
    -  {
    -    nowTime = OS_GetTimeMs();
    +    return false;
       }
     
    +  if (OS_GetTimeMs() - lastTime < duration)  // if touch screen held pressed but provided duration not yet reached
    +    return false;
    +
    +  // touch screen held pressed for the provided duration
    +
    +  uint16_t tx, ty;
    +
    +  TS_GetCoordinates(&tx, &ty);
    +
    +  if (tx > LCD_FREE_WIDTH && ty < LCD_FREE_HEIGHT)
    +    return true;
    +
       return false;
     }
     
     uint8_t Touch_Enc_ReadPos(void)
     {
       static bool move = false;
    -  static uint16_t sy;
    +  static uint16_t sy = 0;
    +
    +  if (XPT2046_Read_Pen())  // if touch screen not pressed
    +  {
    +    move = false;
    +    sy = 0;
    +    modeSwitching = false;  // resume mode switching
    +
    +    return 0;
    +  }
    +
       uint16_t ex, ey;
     
       ex = ey = 0;
     
    -  if (!XPT2046_Read_Pen())
    -  {
    -    TS_GetCoordinates(&ex, &ey);
    +  TS_GetCoordinates(&ex, &ey);
     
    -    if (!move)
    -      sy = ey;
    +  if (!move)
    +    sy = ey;
     
    -    move = true;
    +  move = true;
     
    -    if (ex > LCD_FREE_WIDTH)  // if touched navigation area, stop mode switching
    -      modeSwitching = true;
    -    else
    -      modeSwitching = false;
    +  if (ex > LCD_FREE_WIDTH)  // if touched navigation area, stop mode switching
    +    modeSwitching = true;
    +  else
    +    modeSwitching = false;
     
    -    if (ex > LCD_FREE_WIDTH)
    +  if (ex > LCD_FREE_WIDTH)
    +  {
    +    if (sy > ey && ey != 0)
         {
    -      if (sy > ey && ey != 0)
    +      if (sy - ey > LCD_HEIGHT / 9 && sy - ey < LCD_HEIGHT / 7)  // 7 - 5
           {
    -        if (sy - ey > LCD_HEIGHT / 9 && sy - ey < LCD_HEIGHT / 7)  // 7 - 5
    -        {
    -          sy = ey;
    +        sy = ey;
     
    -          return 2;
    -        }
    +        return 2;
           }
    -      else
    +    }
    +    else
    +    {
    +      if (ey - sy > LCD_HEIGHT / 9 && ey - sy < LCD_HEIGHT / 7)
           {
    -        if (ey - sy > LCD_HEIGHT / 9 && ey - sy < LCD_HEIGHT / 7)
    -        {
    -          sy = ey;
    +        sy = ey;
     
    -          return 3;
    -        }
    +        return 3;
           }
         }
       }
    -  else
    -  {
    -    move = false;
    -    sy = ey = 0;
    -    modeSwitching = false;  // resume mode switching
    -  }
     
       return 0;
     }
    diff --git a/TFT/src/User/API/Touch_Encoder.h b/TFT/src/User/API/Touch_Encoder.h
    index 31e774e8c1..b4123ef9fa 100644
    --- a/TFT/src/User/API/Touch_Encoder.h
    +++ b/TFT/src/User/API/Touch_Encoder.h
    @@ -9,10 +9,10 @@ extern "C" {
     #include 
     #include "variants.h"  // for LCD_ENCODER_SUPPORT
     
    -bool Touch_Enc_ReadPen(uint16_t interval);  // return the button press state resetting internal timer when reached. Interval is in milli seconds
    -
    +bool Touch_Enc_ReadPen(uint16_t duration);  // return the button press state resetting internal timer when reached.
    +                                            // Duration is in milli seconds
     #if LCD_ENCODER_SUPPORT
    -  bool Touch_Enc_ReadBtn(uint16_t interval);  // return the button press state to send to encoder. Interval is in milli seconds
    +  bool Touch_Enc_ReadBtn(uint16_t duration);  // return the button press state to send to encoder. Duration is in milli seconds
       uint8_t Touch_Enc_ReadPos(void);            // return the position to send to encoder
     #endif
     
    diff --git a/TFT/src/User/API/UI/GUI.c b/TFT/src/User/API/UI/GUI.c
    index ca9c3a7947..0509d5fb87 100644
    --- a/TFT/src/User/API/UI/GUI.c
    +++ b/TFT/src/User/API/UI/GUI.c
    @@ -87,7 +87,7 @@ void GUI_DrawPixel(int16_t x, int16_t y, uint16_t color)
          ||x >= pixel_limit_rect.x1
          ||y <  pixel_limit_rect.y0
          ||y >= pixel_limit_rect.y1))
    -    return ;
    +    return;
     
       LCD_SetWindow(x, y, x, y);
       LCD_WR_16BITS_DATA(color);
    @@ -1132,7 +1132,7 @@ void Scroll_DispString(SCROLL * para, uint8_t align)
       if (para->text == NULL) return;
       if (para->totalPixelWidth > para->maxPixelWidth)
       {
    -    if (OS_GetTimeMs() > para->time)
    +    if (OS_GetTimeMs() >= para->time)
         {
           para->time = OS_GetTimeMs() + 50;  // 50ms
           GUI_SetRange(para->rect.x0, para->rect.y0, para->rect.x1, para->rect.y1);
    diff --git a/TFT/src/User/API/UI/HD44780_Emulator.c b/TFT/src/User/API/UI/HD44780_Emulator.c
    index 2cd12e9cf1..5d356e5c8d 100644
    --- a/TFT/src/User/API/UI/HD44780_Emulator.c
    +++ b/TFT/src/User/API/UI/HD44780_Emulator.c
    @@ -176,7 +176,7 @@ void HD44780_DispDDRAM(uint8_t data)
             temp |= font[i++];
           }
     
    -      for (y = HD44780.y * BYTE_HEIGHT; y < ey ;y++)
    +      for (y = HD44780.y * BYTE_HEIGHT; y < ey; y++)
           {
             HD44780_DrawPixel(x, y, temp & (1 << (BYTE_HEIGHT - 1)), 1);
             temp <<= 1;
    diff --git a/TFT/src/User/API/UI/Numpad.c b/TFT/src/User/API/UI/Numpad.c
    index 6dfeca2d4d..2f631e6b96 100644
    --- a/TFT/src/User/API/UI/Numpad.c
    +++ b/TFT/src/User/API/UI/Numpad.c
    @@ -162,7 +162,7 @@ void Draw_keyboard(uint8_t * title, bool numberOnly, bool negative)
     
         setFontSize(FONT_SIZE_LARGE);
     
    -    for (uint8_t i = 0; i < KEY_COUNT ;i++)
    +    for (uint8_t i = 0; i < KEY_COUNT; i++)
         {
           if (!(i == NUM_KEY_DEC || i == NUM_KEY_MINUS || (i % 4) == 3))  // || i == NUM_KEY_DEL || i == NUM_KEY_EXIT || i == NUM_KEY_RESET))
             GUI_DispStringInPrect(&rect_of_numkey[i], (uint8_t *)numPadKeyChar[i]);
    diff --git a/TFT/src/User/API/Vfs/vfs.c b/TFT/src/User/API/Vfs/vfs.c
    index 6df3ec9076..986f5cbd57 100644
    --- a/TFT/src/User/API/Vfs/vfs.c
    +++ b/TFT/src/User/API/Vfs/vfs.c
    @@ -26,7 +26,7 @@ TCHAR * getFS(void)
     
         case FS_ONBOARD_MEDIA:
         case FS_ONBOARD_MEDIA_REMOTE:
    -      return infoMachineSettings.firmwareType == FW_REPRAPFW ? "gcodes" : "oMD:";
    +      return infoMachineSettings.firmwareType != FW_REPRAPFW ? "oMD:" : "gcodes";
     
         case FS_REMOTE_HOST:
           return "Remote printing...";
    diff --git a/TFT/src/User/API/config.c b/TFT/src/User/API/config.c
    index a5a845fe9e..d592763620 100644
    --- a/TFT/src/User/API/config.c
    +++ b/TFT/src/User/API/config.c
    @@ -7,6 +7,7 @@
     #define CONFIG_SERIAL_PORT            "serial_port:"
     #define CONFIG_TX_SLOTS               "tx_slots:"
     #define CONFIG_ADVANCED_OK            "advanced_ok:"
    +#define CONFIG_COMMAND_CHECKSUM       "command_checksum:"
     #define CONFIG_EMULATED_M600          "emulated_m600:"
     #define CONFIG_EMULATED_M109_M190     "emulated_m109_m190:"
     #define CONFIG_EVENT_LED              "event_led:"
    @@ -428,10 +429,11 @@ void parseConfigKey(uint16_t index)
           break;
     
         case C_INDEX_TX_SLOTS:
    -      SET_VALID_INT_VALUE(infoSettings.tx_slots, 2, 16);
    +      SET_VALID_INT_VALUE(infoSettings.tx_slots, MIN_TX_SLOTS, MAX_TX_SLOTS);
           break;
     
         case C_INDEX_ADVANCED_OK:
    +    case C_INDEX_COMMAND_CHECKSUM:
         case C_INDEX_EMULATED_M600:
         case C_INDEX_EMULATED_M109_M190:
         case C_INDEX_EVENT_LED:
    diff --git a/TFT/src/User/API/config.h b/TFT/src/User/API/config.h
    index 54ad0c02ea..88fa2d72af 100644
    --- a/TFT/src/User/API/config.h
    +++ b/TFT/src/User/API/config.h
    @@ -11,6 +11,7 @@ extern "C" {
     #define LINE_MAX_CHAR    200
     
     //-----------------------------Limits
    +#define MAX_TX_SLOTS             16  // tx slots over this will not be parsed
     #define MAX_SIZE_LIMIT         2000  // machine size over this will not be parsed
     #define MAX_EXT_SPEED_LIMIT    5000  // Extruder speed over this will not be parsed
     #define MAX_TOOL_TEMP          1000  // extruder temp over this will not be parsed
    @@ -25,6 +26,7 @@ extern "C" {
     #define MAX_LED_COLOR_COMP      255  // (neopixel) LED color component over this will not be parsed
     #define MAX_NEOPIXEL_PIXELS     200  // neopixel pixels over this will not be parsed
     
    +#define MIN_TX_SLOTS              2  // tx slots less than this will not be parsed
     #define MIN_SIZE_LIMIT        -2000  // machine size less than this will not be parsed
     #define NAME_MIN_LENGTH           3  // minimum name length
     #define GCODE_MIN_LENGTH          3  // gcode length less than this will not be parsed
    diff --git a/TFT/src/User/API/config.inc b/TFT/src/User/API/config.inc
    index 35b9f35f65..2b0d84e5ca 100644
    --- a/TFT/src/User/API/config.inc
    +++ b/TFT/src/User/API/config.inc
    @@ -12,6 +12,7 @@
     X_CONFIG (SERIAL_PORT)
     X_CONFIG (TX_SLOTS)
     X_CONFIG (ADVANCED_OK)
    +X_CONFIG (COMMAND_CHECKSUM)
     X_CONFIG (EMULATED_M600)
     X_CONFIG (EMULATED_M109_M190)
     X_CONFIG (EVENT_LED)
    diff --git a/TFT/src/User/API/coordinate.c b/TFT/src/User/API/coordinate.c
    index 943f74aeb3..1ad3f37d0a 100644
    --- a/TFT/src/User/API/coordinate.c
    +++ b/TFT/src/User/API/coordinate.c
    @@ -18,8 +18,9 @@ static bool relative_e = false;
     // false means current position is unknown
     // false after M18/M84 disable stepper or power up, true after G28
     static bool position_known = false;
    -static bool coordinateQueryWait = false;
    -static uint8_t curQuerySeconds = 0;
    +
    +static uint8_t coordUpdateSeconds = 0;
    +static bool coordSendingWaiting = false;
     
     bool coorGetRelative(void)
     {
    @@ -108,9 +109,17 @@ void coordinateGetAllActual(COORDINATE *tmp)
       memcpy(tmp, &curPosition, sizeof(curPosition));
     }
     
    -void coordinateQuerySetWait(bool wait)
    +float coordinateGetAxis(AXIS axis)
    +{
    +  if (infoFile.source >= FS_ONBOARD_MEDIA)
    +    return coordinateGetAxisActual(axis);
    +  else
    +    return coordinateGetAxisTarget(axis);
    +}
    +
    +void coordinateQueryClearSendingWaiting(void)
     {
    -  coordinateQueryWait = wait;
    +  coordSendingWaiting = false;
     }
     
     /**
    @@ -120,41 +129,33 @@ void coordinateQuerySetWait(bool wait)
      */
     void coordinateQuery(uint8_t seconds)
     { // following conditions ordered by importance
    -  if (!coordinateQueryWait && infoHost.tx_slots != 0 && infoHost.connected)
    +  if (!coordSendingWaiting && infoHost.tx_slots != 0 && infoHost.connected && infoMachineSettings.firmwareType != FW_REPRAPFW)
       {
         if (infoMachineSettings.autoReportPos == 1)  // if auto report is enabled
         {
           if (seconds == 0)  // if manual querying is requested (if query interval is 0)
    -        coordinateQueryWait = storeCmd("M114\n");
    +        coordSendingWaiting = storeCmd("M114\n");
     
    -      if (seconds != curQuerySeconds)  // if query interval is changed
    +      if (seconds != coordUpdateSeconds)  // if query interval is changed
           {
             if (storeCmd("M154 S%d\n", seconds))  // turn on or off (if query interval is 0) auto report
    -          curQuerySeconds = seconds;          // if gcode will be sent, avoid to enable auto report again on next
    +          coordUpdateSeconds = seconds;       // if gcode will be sent, avoid to enable auto report again on next
           }                                       // function call if already enabled for that query interval
         }
         else  // if auto report is disabled
         {
    -      coordinateQueryWait = storeCmd("M114\n");
    +      coordSendingWaiting = storeCmd("M114\n");
         }
       }
     }
     
     void coordinateQueryTurnOff(void)
     {
    -  coordinateQueryWait = false;
    +  coordSendingWaiting = false;
     
       if (infoMachineSettings.autoReportPos == 1)  // if auto report is enabled, turn it off
       {
         storeCmd("M154 S0\n");
    -    curQuerySeconds = 0;
    +    coordUpdateSeconds = 0;
       }
     }
    -
    -float coordinateGetAxis(AXIS axis)
    -{
    -  if (infoFile.source >= FS_ONBOARD_MEDIA)
    -    return coordinateGetAxisActual(axis);
    -  else
    -    return coordinateGetAxisTarget(axis);
    -}
    diff --git a/TFT/src/User/API/coordinate.h b/TFT/src/User/API/coordinate.h
    index 3d4c5302a3..0e9c5d01ec 100644
    --- a/TFT/src/User/API/coordinate.h
    +++ b/TFT/src/User/API/coordinate.h
    @@ -50,11 +50,12 @@ void coordinateSetExtruderActualSteps(float steps);
     float coordinateGetAxisActual(AXIS axis);
     void coordinateSetAxisActual(AXIS axis, float position);
     void coordinateGetAllActual(COORDINATE *tmp);
    -void coordinateQuerySetWait(bool wait);
    -void coordinateQuery(uint8_t delay);
    -void coordinateQueryTurnOff(void);
     float coordinateGetAxis(AXIS axis);
     
    +void coordinateQueryClearSendingWaiting(void);  // called in sendQueueCmd(). Clear sending waiting for coordinate query
    +void coordinateQuery(uint8_t delay);            // query for coordinate
    +void coordinateQueryTurnOff(void);
    +
     #ifdef __cplusplus
     }
     #endif
    diff --git a/TFT/src/User/API/menu.c b/TFT/src/User/API/menu.c
    index 625e0c95b9..892cbc1251 100644
    --- a/TFT/src/User/API/menu.c
    +++ b/TFT/src/User/API/menu.c
    @@ -536,15 +536,6 @@ void menuClearGaps(void)
     #endif
     }
     
    -void GUI_RestoreColorDefault(void)
    -{
    -  GUI_SetBkColor(infoSettings.bg_color);
    -  GUI_SetColor(infoSettings.font_color);
    -  GUI_SetTextMode(GUI_TEXTMODE_NORMAL);
    -  GUI_SetNumMode(GUI_NUMMODE_SPACE);
    -  setFontSize(FONT_SIZE_NORMAL);
    -}
    -
     static const MENUITEMS *curMenuItems = NULL;  // current menu
     static const LISTITEMS *curListItems = NULL;  // current listmenu
     
    @@ -568,105 +559,6 @@ static struct { uint16_t x;
                     uint32_t time;
                   } busySign = {LCD_WIDTH - 3, 3, 3, SYS_STATUS_BUSY, 0};
     
    -MENUITEMS *getCurMenuItems(void)
    -{
    -  return (MENUITEMS *)curMenuItems;
    -}
    -
    -LISTITEMS *getCurListItems(void)
    -{
    -  return (LISTITEMS *)curListItems;
    -}
    -
    -// Get the top left point of the corresponding icon position)
    -GUI_POINT getIconStartPoint(int index)
    -{
    -  GUI_POINT p = {curRect[index].x0, curRect[index].y0};
    -  return p;
    -}
    -
    -uint8_t *labelGetAddress(const LABEL *label)
    -{
    -  if (label == NULL || label->index == LABEL_NULL)  // No content in label
    -    return NULL;
    -  if (label->index < LABEL_NUM)  // Index of language
    -    return textSelect(label->index);
    -  else  // Address of string
    -    return label->address;
    -}
    -
    -void menuDrawItem(const ITEM *item, uint8_t position)
    -{
    -  menuDrawIconOnly(item, position);
    -  menuDrawIconText(item, position);
    -}
    -
    -void menuDrawIconOnly(const ITEM *item, uint8_t position)
    -{
    -  const GUI_RECT *rect = curRect + position;
    -  if (item->icon != ICON_NULL)
    -    ICON_ReadDisplay(rect->x0, rect->y0, item->icon);
    -  else
    -    GUI_ClearPrect(rect);
    -}
    -
    -void menuDrawIconText(const ITEM *item, uint8_t position)
    -{
    -  uint8_t *content = labelGetAddress(&item->label);
    -  const GUI_RECT *rect = curRect + ITEM_PER_PAGE + position;
    -  GUI_ClearPrect(rect);
    -  if (content)
    -    GUI_DispStringInPrect(rect, content);
    -}
    -
    -void menuDrawListItem(const LISTITEM *item, uint8_t position)
    -{
    -  const GUI_RECT *rect = rect_of_keyListView + position;
    -  if (item->icon == CHARICON_NULL)
    -  {
    -    GUI_ClearPrect(rect);
    -  }
    -  else
    -  {
    -    ListItem_Display(rect, position, item, false);
    -  }
    -}
    -
    -void menuRefreshListPage(void)
    -{
    -  for (uint8_t i = 0; i < ITEM_PER_PAGE; i++)
    -  {
    -    RAPID_PRINTING_COMM()  // perform backend printing loop between drawing icons to avoid printer idling
    -    menuDrawListItem(&curListItems->items[i], i);
    -  }
    -}
    -
    -void setMenuType(MENU_TYPE type)
    -{
    -  menuType = type;
    -}
    -
    -MENU_TYPE getMenuType(void)
    -{
    -  return menuType;
    -}
    -
    -void setMenu(MENU_TYPE menu_type, LABEL * title, uint16_t rectCount, const GUI_RECT * menuRect,
    -             void (*action_redraw)(uint8_t position, uint8_t is_press),
    -             void (*menu_redraw)(void))
    -{
    -  menuType = menu_type;
    -  curRect = menuRect;
    -  curRectCount = rectCount;
    -  curTitle = title;
    -  curMenuRedrawHandle = menu_redraw;
    -  TS_ReDrawIcon = action_redraw;
    -
    -  #if LCD_ENCODER_SUPPORT
    -    encoderPosition = 0;
    -  #endif
    -}
    -
     SYS_STATUS getReminderStatus(void)
     {
       return reminder.status;
    @@ -775,6 +667,114 @@ void notificationDot(void)
       GUI_RestoreColorDefault();
     }
     
    +void GUI_RestoreColorDefault(void)
    +{
    +  GUI_SetBkColor(infoSettings.bg_color);
    +  GUI_SetColor(infoSettings.font_color);
    +  GUI_SetTextMode(GUI_TEXTMODE_NORMAL);
    +  GUI_SetNumMode(GUI_NUMMODE_SPACE);
    +  setFontSize(FONT_SIZE_NORMAL);
    +}
    +
    +MENUITEMS *getCurMenuItems(void)
    +{
    +  return (MENUITEMS *)curMenuItems;
    +}
    +
    +LISTITEMS *getCurListItems(void)
    +{
    +  return (LISTITEMS *)curListItems;
    +}
    +
    +// Get the top left point of the corresponding icon position)
    +GUI_POINT getIconStartPoint(int index)
    +{
    +  GUI_POINT p = {curRect[index].x0, curRect[index].y0};
    +  return p;
    +}
    +
    +uint8_t *labelGetAddress(const LABEL *label)
    +{
    +  if (label == NULL || label->index == LABEL_NULL)  // No content in label
    +    return NULL;
    +  if (label->index < LABEL_NUM)  // Index of language
    +    return textSelect(label->index);
    +  else  // Address of string
    +    return label->address;
    +}
    +
    +void menuDrawItem(const ITEM *item, uint8_t position)
    +{
    +  menuDrawIconOnly(item, position);
    +  menuDrawIconText(item, position);
    +}
    +
    +void menuDrawIconOnly(const ITEM *item, uint8_t position)
    +{
    +  const GUI_RECT *rect = curRect + position;
    +  if (item->icon != ICON_NULL)
    +    ICON_ReadDisplay(rect->x0, rect->y0, item->icon);
    +  else
    +    GUI_ClearPrect(rect);
    +}
    +
    +void menuDrawIconText(const ITEM *item, uint8_t position)
    +{
    +  uint8_t *content = labelGetAddress(&item->label);
    +  const GUI_RECT *rect = curRect + ITEM_PER_PAGE + position;
    +  GUI_ClearPrect(rect);
    +  if (content)
    +    GUI_DispStringInPrect(rect, content);
    +}
    +
    +void menuDrawListItem(const LISTITEM *item, uint8_t position)
    +{
    +  const GUI_RECT *rect = rect_of_keyListView + position;
    +  if (item->icon == CHARICON_NULL)
    +  {
    +    GUI_ClearPrect(rect);
    +  }
    +  else
    +  {
    +    ListItem_Display(rect, position, item, false);
    +  }
    +}
    +
    +void menuRefreshListPage(void)
    +{
    +  for (uint8_t i = 0; i < ITEM_PER_PAGE; i++)
    +  {
    +    RAPID_PRINTING_COMM()  // perform backend printing loop between drawing icons to avoid printer idling
    +    menuDrawListItem(&curListItems->items[i], i);
    +  }
    +}
    +
    +void setMenuType(MENU_TYPE type)
    +{
    +  menuType = type;
    +}
    +
    +MENU_TYPE getMenuType(void)
    +{
    +  return menuType;
    +}
    +
    +void setMenu(MENU_TYPE menu_type, LABEL * title, uint16_t rectCount, const GUI_RECT * menuRect,
    +             void (*action_redraw)(uint8_t position, uint8_t is_press),
    +             void (*menu_redraw)(void))
    +{
    +  menuType = menu_type;
    +  curRect = menuRect;
    +  curRectCount = rectCount;
    +  curTitle = title;
    +  curMenuRedrawHandle = menu_redraw;
    +  TS_ReDrawIcon = action_redraw;
    +
    +  #if LCD_ENCODER_SUPPORT
    +    encoderPosition = 0;
    +  #endif
    +}
    +
     void menuSetTitle(const LABEL *title)
     {
       curTitle = title;
    @@ -1023,7 +1023,7 @@ void showLiveInfo(uint8_t index, const LIVE_INFO * liveicon, bool redrawIcon)
       }
     
       GUI_RestoreColorDefault();
    -}  // showLiveInfo
    +} // showLiveInfo
     
     void displayExhibitHeader(const char * titleStr, const char * unitStr)
     {
    @@ -1137,7 +1137,7 @@ KEY_VALUES menuKeyGetValue(void)
     // Smart home (long press on back button to go to status screen)
     #ifdef SMART_HOME
     
    -static inline void loopCheckBackPress(void)
    +void loopCheckBackPress(void)
     {
       static bool longPress = false;
     
    @@ -1207,148 +1207,3 @@ static inline void loopCheckBackPress(void)
     }
     
     #endif  // SMART_HOME
    -
    -// Non-UI background loop tasks
    -void loopBackEnd(void)
    -{
    -  UPD_SCAN_RATE();  // debug monitoring KPI
    -
    -  // Handle a print from TFT media, if any
    -  loopPrintFromTFT();
    -
    -  // Parse and send gcode commands in the queue
    -  sendQueueCmd();
    -
    -  // Parse the received slave response information
    -  parseACK();
    -
    -  // Retrieve and store (in command queue) the gcodes received from other UART, such as ESP3D etc...
    -  #ifdef SERIAL_PORT_2
    -    Serial_GetFromUART();
    -  #endif
    -
    -  // Handle USB communication
    -  #ifdef USB_FLASH_DRIVE_SUPPORT
    -    USB_LoopProcess();
    -  #endif
    -
    -  if ((priorityCounter.be++ % BE_PRIORITY_DIVIDER) != 0)  // a divider value of 16 -> run 6% of the time only
    -    return;
    -
    -  // Temperature monitor
    -  loopCheckHeater();
    -
    -  // Fan speed monitor
    -  loopFan();
    -
    -  // Speed & flow monitor
    -  loopSpeed();
    -
    -  // Buzzer handling
    -  #ifdef BUZZER_PIN
    -    loopBuzzer();
    -  #endif
    -
    -  // Handle a print from (remote) onboard media, if any
    -  if (infoMachineSettings.onboardSD == ENABLED)
    -    loopPrintFromOnboard();
    -
    -  // Check filament runout status
    -  #ifdef FIL_RUNOUT_PIN
    -    FIL_BE_CheckRunout();
    -  #endif
    -
    -  // Check changes in encoder steps
    -  #if LCD_ENCODER_SUPPORT
    -    #ifdef HAS_EMULATOR
    -      if (MENU_IS_NOT(menuMarlinMode))
    -    #endif
    -    {
    -      LCD_Enc_CheckSteps();
    -    }
    -  #endif
    -
    -  // Check mode switching
    -  #ifdef HAS_EMULATOR
    -    Mode_CheckSwitching();
    -  #endif
    -
    -  // Handle screenshot capture
    -  #ifdef SCREEN_SHOT_TO_SD
    -    loopScreenShot();
    -  #endif
    -
    -  // Check if Back is pressed and held
    -  #ifdef SMART_HOME
    -    loopCheckBackPress();
    -  #endif
    -
    -  // Check LCD screen dimming
    -  #ifdef LCD_LED_PWM_CHANNEL
    -    LCD_CheckDimming();
    -  #endif
    -
    -  // Check LED Event
    -  if (GET_BIT(infoSettings.general_settings, INDEX_EVENT_LED) == 1)
    -    LED_CheckEvent();
    -
    -  // Query RRF status
    -  rrfStatusQuery();
    -}
    -
    -// UI-related background loop tasks
    -void loopFrontEnd(void)
    -{
    -  // Check if volume source (SD/USB) insert
    -  loopVolumeSource();
    -
    -  // Loop to check and run toast messages
    -  loopToast();
    -
    -  // If there is a message in the status bar, timed clear
    -  loopReminderManage();
    -
    -  // Busy Indicator clear
    -  loopBusySignClear();
    -
    -  // Check update temperature status
    -  loopTemperatureStatus();
    -
    -  // Loop for filament runout detection
    -  #ifdef FIL_RUNOUT_PIN
    -    FIL_FE_CheckRunout();
    -  #endif
    -
    -  // Loop for popup menu
    -  loopPopup();
    -}
    -
    -void loopProcess(void)
    -{
    -  loopBackEnd();
    -
    -  if ((priorityCounter.fe++ % FE_PRIORITY_DIVIDER) != 0)  // a divider value of 16 -> run 6% of the time only
    -    return;
    -
    -  loopFrontEnd();
    -}
    -
    -void menuDummy(void)
    -{
    -  CLOSE_MENU();
    -}
    -
    -void loopProcessAndGUI(void)
    -{
    -  uint8_t curMenu = infoMenu.cur;
    -
    -  loopProcess();
    -
    -  if (infoMenu.cur != curMenu)  // if a user interaction is needed (e.g. dialog box), handle it
    -  {
    -    (*infoMenu.menu[infoMenu.cur])();  // handle user interaction
    -
    -    if (MENU_IS_NOT(menuDummy))  // avoid to nest menuDummy menu type
    -      OPEN_MENU(menuDummy);      // load a dummy menu just to force the redraw of the underlying menu (caller menu)
    -  }
    -}
    diff --git a/TFT/src/User/API/menu.h b/TFT/src/User/API/menu.h
    index 32e5beb66a..0d78934cd9 100644
    --- a/TFT/src/User/API/menu.h
    +++ b/TFT/src/User/API/menu.h
    @@ -106,15 +106,6 @@ typedef struct
       ITEM  items[ITEM_PER_PAGE];
     } MENUITEMS;
     
    -typedef enum
    -{
    -  SYS_STATUS_IDLE = 0,
    -  SYS_STATUS_BUSY,
    -  SYS_STATUS_DISCONNECTED,
    -  SYS_STATUS_LISTENING,
    -  SYS_STATUS_VOL_CHANGE
    -} SYS_STATUS;
    -
     typedef enum
     {
       LIST_LABEL = 0,
    @@ -158,7 +149,14 @@ typedef struct
       LIVE_DATA lines[LIVEICON_LINES];
     } LIVE_INFO;
     
    -typedef bool (* CONDITION_CALLBACK)(void);
    +typedef enum
    +{
    +  SYS_STATUS_IDLE = 0,
    +  SYS_STATUS_BUSY,
    +  SYS_STATUS_DISCONNECTED,
    +  SYS_STATUS_LISTENING,
    +  SYS_STATUS_VOL_CHANGE
    +} SYS_STATUS;
     
     extern const GUI_RECT exhibitRect;
     extern const GUI_RECT rect_of_key[MENU_RECT_COUNT];
    @@ -166,32 +164,32 @@ extern const GUI_RECT rect_of_keySS[SS_RECT_COUNT];
     extern const GUI_RECT rect_of_keyPS[];
     extern const GUI_RECT rect_of_keyPS_end[];
     extern const GUI_RECT rect_of_keyPS_draw[];  // used to draw VERTICAL GUI Printing menu
    -
     extern const GUI_RECT rect_of_titleBar[1];
     
    -void setMenuType(MENU_TYPE type);
    -MENU_TYPE getMenuType(void);
    -
     SYS_STATUS getReminderStatus(void);
     void setReminderMsg(int16_t inf, SYS_STATUS status);
    -void notificationDot(void);
    +void loopReminderManage(void);
     
     void drawBusySign(void);
    +void loopBusySignClear(void);
    +void notificationDot(void);
     
    +void GUI_RestoreColorDefault(void);
     MENUITEMS *getCurMenuItems(void);
     LISTITEMS *getCurListItems(void);
     GUI_POINT getIconStartPoint(int index);
    -
    -void GUI_RestoreColorDefault(void);
     uint8_t *labelGetAddress(const LABEL * label);
    -void setMenu(MENU_TYPE menu_type, LABEL * title, uint16_t rectCount, const GUI_RECT * menuRect,
    -             void (*action_redraw)(uint8_t position, uint8_t is_press),
    -             void (*menu_redraw)(void));
     void menuDrawItem (const ITEM * menuItem, uint8_t position);
     void menuDrawIconOnly(const ITEM *item, uint8_t position);
     void menuDrawIconText(const ITEM *item, uint8_t position);
     void menuDrawListItem(const LISTITEM *item, uint8_t position);
     void menuRefreshListPage(void);
    +
    +void setMenuType(MENU_TYPE type);
    +MENU_TYPE getMenuType(void);
    +void setMenu(MENU_TYPE menu_type, LABEL * title, uint16_t rectCount, const GUI_RECT * menuRect,
    +             void (*action_redraw)(uint8_t position, uint8_t is_press),
    +             void (*menu_redraw)(void));
     void menuSetTitle(const LABEL *title);
     void menuDrawTitle(void);
     void menuDrawPage(const MENUITEMS * menuItems);
    @@ -203,16 +201,12 @@ void displayExhibitValue(const char * valueStr);
     
     KEY_VALUES menuKeyGetValue(void);
     
    -// Smart home
    +// smart home
     #ifdef SMART_HOME
       #define LONG_TOUCH (MODE_SWITCHING_INTERVAL / 3)  // keep it lower than MODE_SWITCHING_INTERVAL
    -#endif
     
    -void menuDummy(void);
    -void loopBackEnd(void);
    -void loopFrontEnd(void);
    -void loopProcess(void);
    -void loopProcessAndGUI(void);
    +  void loopCheckBackPress(void);
    +#endif
     
     #ifdef __cplusplus
     }
    diff --git a/TFT/src/User/Configuration.h b/TFT/src/User/Configuration.h
    index 171f7b7f19..8fb168dfe7 100644
    --- a/TFT/src/User/Configuration.h
    +++ b/TFT/src/User/Configuration.h
    @@ -1,7 +1,7 @@
     #ifndef _CONFIGURATION_H_
     #define _CONFIGURATION_H_
     
    -#define CONFIG_VERSION 20231119
    +#define CONFIG_VERSION 20240203
     
     //====================================================================================================
     //=============================== Settings Configurable On config.ini ================================
    @@ -74,12 +74,50 @@
      *   support the transmission of G-codes according to the configured "TX_SLOTS" setting.
      * If disabled, the TFT will provide the standard transmission logic based on one G-code per time.
      *
    - * NOTE: Disable it in case no ADVANCED_OK feature is requested/needed by the user.
    + * NOTE: Disable it in case:
    + *       - no ADVANCED_OK feature is requested/needed by the user.
    + *       - ADVANCED_OK feature is not providing good printing results or if the mainboard notifies
    + *         frequent error ACK messages (e.g. unknown command) to the TFT during printing.
    + *       - COMMAND_CHECKSUM feature (see description of next setting "COMMAND_CHECKSUM") is
    + *         requested/needed by the user.
      *
      *   Options: [disable: 0, enable: 1]
      */
     #define ADVANCED_OK 0  // Default: 0
     
    +/**
    + * Command Checksum
    + * The TFT enriches each G-code to be sent to the mainboard adding a leading sequential line number
    + * and a trailing checksum appended after an "*" character used as separator.
    + * The checksum is based on algorithm "CheckSum8 Xor" and it is calculated on the G-code with the
    + * applied line number. E.g. "G28" is firstly enriched with a line number (e.g. "N1 G28") and finally
    + * a checksum calculated on that enriched G-code is appended (e.g. "N1 G28*18").
    + * A data integrity check (sequential line number check and checksum check) will be performed on the
    + * mainboard. In case of data mismatch (e.g. data corruption due to EMI on communication serial line):
    + * - the mainboard will send to the TFT an error ACK message followed by a "Resend: " ACK message to
    + *   ask TFT to resend the G-code with the requested line number.
    + * - the TFT will check the presence on an internal buffer of the G-code with the requested line number:
    + *   - if found, the G-code is resent for a maximum of 3 attempts.
    + *   - if not found or the maximum number of attempts has been reached, the TFT will reset the line
    + *     number with an "M110" G-code (immediately sent bypassing any other enqueued G-code) to the
    + *     requested line number just to try to avoid further retransmission requests for the same line
    + *     number or for any out of synch command already sent to the mainboard (e.g. in case ADVANCED_OK
    + *     feature is enabled in TFT).
    + *
    + * NOTE: Disable it in case:
    + *       - printing is controlled by a remote host (e.g. ESP3D, OctoPrint etc.) and a COMMAND_CHECKSUM
    + *         feature is enabled and managed by the remote host. Otherwise (COMMAND_CHECKSUM feature also
    + *         enabled in TFT), the TFT's COMMAND_CHECKSUM feature will always replace the one provided by
    + *         the remote host causing conflicts in case data mismatch will be notified by the mainboard.
    + *       - ADVANCED_OK feature is enabled in TFT. Otherwise, any out of synch command already sent to
    + *         the mainboard will be discarded by the mainboard and not resent by the TFT due the current
    + *         implementation of COMMAND_CHECKSUM feature on the TFT buffers only the last sent command
    + *         and not all the pending commands.
    + *
    + *   Options: [disable: 0, enable: 1]
    + */
    +#define COMMAND_CHECKSUM 1  // Default: 1
    +
     /**
      * Emulated M600
      * The TFT intercepts the M600 G-code (filament change) and emulates the handling logic
    @@ -209,7 +247,7 @@
      *   Options: [OFF: 0, POPUP: 1, TOAST: 2]
      *     OFF:   No notification. The message is ignored.
      *     POPUP: Display a popup window for user confirmation.
    - *     TOAST: A non-blocking Toast notification is displayed for few seconds. No user interaction is needed.
    + *     TOAST: A non-blocking toast notification is displayed for few seconds. No user interaction is needed.
      */
     #define ACK_NOTIFICATION 1  // Default: 1
     
    @@ -802,6 +840,10 @@
     /**
      * Filament Runout Sensor
      * Select the type of filament runout sensor and its default enabled/disabled state.
    + *
    + * NOTE: Smart Filament Sensor (SFS) (value 2 or 3) is a sensor based on an encoder disc that
    + *       toggles runout pin as filament moves (e.g. the BigTreeTech SFS).
    + *
      *   Options: [Normal Disabled: 0, Normal Enabled: 1, Smart Disabled: 2, Smart Enabled: 3]
      */
     #define FIL_RUNOUT 0  // Default: 0
    @@ -834,6 +876,10 @@
      * Smart Filament Runout Detection
      * Used in conjunction with an SFS (Smart Filament Sensor) based on an encoder disc that
      * toggles runout pin as filament moves.
    + *
    + * NOTE: This setting is taken into account by the TFT only in case "FIL_RUNOUT" setting is
    + *       set to 2 or 3 (an SFS is used).
    + *
      *   Unit: [distance in mm]
      *   Value range: [min: 1, max: 50]
      */
    diff --git a/TFT/src/User/Hal/HD44780.c b/TFT/src/User/Hal/HD44780.c
    index 9ed1f63b59..933decb9c5 100644
    --- a/TFT/src/User/Hal/HD44780.c
    +++ b/TFT/src/User/Hal/HD44780.c
    @@ -112,7 +112,7 @@ bool HD44780_writeData(void)
         uint8_t temp = ((LCD_D7_PORT->IDR & LCD_D7_PIN) >> 3 ) +         // D7
                        ((LCD_D6_PORT->IDR & LCD_D6_PIN) >> 5 ) +         // D6
                        ((LCD_D5_PORT->IDR & LCD_D5_PIN) >> 13) +         // D5
    -                   ((LCD_D4_PORT->IDR & LCD_D4_PIN) >> 13) ;         // D4
    +                   ((LCD_D4_PORT->IDR & LCD_D4_PIN) >> 13);          // D4
     
         if ((GPIOB->IDR & (1 << 12)) == 0)
         { //Command received
    diff --git a/TFT/src/User/Hal/LCD_Encoder.c b/TFT/src/User/Hal/LCD_Encoder.c
    index 5b33b47d94..09cef9aea8 100644
    --- a/TFT/src/User/Hal/LCD_Encoder.c
    +++ b/TFT/src/User/Hal/LCD_Encoder.c
    @@ -48,21 +48,23 @@ void LCD_Enc_Init(void)
       encoderLastState = encoderLastSteps = LCD_Enc_ReadPos();
     }
     
    -bool LCD_Enc_ReadBtn(uint16_t interval)
    +bool LCD_Enc_ReadBtn(uint16_t duration)
     {
    -  static uint32_t nowTime = 0;
    +  static uint32_t lastTime = 0;
     
    -  if (!GPIO_GetLevel(LCD_BTN_PIN))
    +  if (GPIO_GetLevel(LCD_BTN_PIN))  // if rotary encoder button not pressed
       {
    -    if (OS_GetTimeMs() - nowTime >= interval)
    -      return true;
    -  }
    -  else
    -  {
    -    nowTime = OS_GetTimeMs();
    +    lastTime = OS_GetTimeMs();
    +
    +    return false;
       }
     
    -  return false;
    +  if (OS_GetTimeMs() - lastTime < duration)  // if rotary encoder button held pressed but provided duration not yet reached
    +    return false;
    +
    +  // rotary encoder button held pressed for the provided duration
    +
    +  return true;
     }
     
     uint8_t LCD_Enc_ReadPos(void)
    @@ -158,8 +160,12 @@ void LCD_Enc_CheckSteps(void)
       #define encrot3 1
     
       // manage encoder rotation
    -  #define ENCODER_SPIN(_E1, _E2) switch (encoderLastSteps) { case _E1: encoderDiff += encoderDirection; break; \
    -                                                             case _E2: encoderDiff -= encoderDirection; }
    +  #define ENCODER_SPIN(_E1, _E2)                        \
    +    switch (encoderLastSteps)                           \
    +    {                                                   \
    +      case _E1: encoderDiff += encoderDirection; break; \
    +      case _E2: encoderDiff -= encoderDirection; break; \
    +    }
     
       if (pos != encoderLastSteps)
       {
    @@ -190,7 +196,9 @@ KEY_VALUES LCD_Enc_KeyValue(void)
       else
       {
         int16_t encPosTemp = encoderPosition;
    +
         encoderPosition = 0;
    +
         return (encPosTemp > 0) ? KEY_INCREASE : KEY_DECREASE;
       }
     }
    diff --git a/TFT/src/User/Hal/LCD_Encoder.h b/TFT/src/User/Hal/LCD_Encoder.h
    index 94f7812e43..3415f94c34 100644
    --- a/TFT/src/User/Hal/LCD_Encoder.h
    +++ b/TFT/src/User/Hal/LCD_Encoder.h
    @@ -19,7 +19,7 @@ extern "C" {
       extern int16_t encoderPosition;  // make it available for reading the current rotation value
     
       void LCD_Enc_Init(void);
    -  bool LCD_Enc_ReadBtn(uint16_t interval);  // return the button press state. Interval is in milli seconds
    +  bool LCD_Enc_ReadBtn(uint16_t duration);  // return the button press state. Duration is in milli seconds
       uint8_t LCD_Enc_ReadPos(void);            // return the position pins state
       void LCD_Enc_SendPulse(uint8_t num);      // send a pulse to the encoder
       bool LCD_Enc_CheckState(void);
    diff --git a/TFT/src/User/Hal/gd32f20x/Serial.h b/TFT/src/User/Hal/gd32f20x/Serial.h
    index d66960ba5a..43041be815 100644
    --- a/TFT/src/User/Hal/gd32f20x/Serial.h
    +++ b/TFT/src/User/Hal/gd32f20x/Serial.h
    @@ -30,6 +30,7 @@ typedef struct
     } SERIAL_CFG;
     
     extern DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT];
    +extern DMA_CIRCULAR_BUFFER dmaL1DataTX[_UART_CNT];
     extern const SERIAL_CFG Serial[_UART_CNT];
     
     void Serial_Config(uint8_t port, uint32_t cacheSizeRX, uint32_t cacheSizeTX, uint32_t baudrate);
    diff --git a/TFT/src/User/Hal/gd32f20x/lcd.c b/TFT/src/User/Hal/gd32f20x/lcd.c
    index 5afacb4cc7..4091bdf230 100644
    --- a/TFT/src/User/Hal/gd32f20x/lcd.c
    +++ b/TFT/src/User/Hal/gd32f20x/lcd.c
    @@ -59,8 +59,8 @@ void LCD_GPIO_Config(void)
     
     void LCD_EXMC_Config(void)
     {
    -  exmc_norsram_parameter_struct    EXMC_NORSRAMInitStructure;
    -  exmc_norsram_timing_parameter_struct   readWriteTiming,writeTiming; ;
    +  exmc_norsram_parameter_struct EXMC_NORSRAMInitStructure;
    +  exmc_norsram_timing_parameter_struct readWriteTiming,writeTiming;
     
       /* EXMC configuration */
       readWriteTiming.asyn_address_setuptime = 1U;
    diff --git a/TFT/src/User/Hal/stm32f10x/Serial.h b/TFT/src/User/Hal/stm32f10x/Serial.h
    index bd51bf52a0..7d5fd491a3 100644
    --- a/TFT/src/User/Hal/stm32f10x/Serial.h
    +++ b/TFT/src/User/Hal/stm32f10x/Serial.h
    @@ -30,6 +30,7 @@ typedef struct
     } SERIAL_CFG;
     
     extern DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT];
    +extern DMA_CIRCULAR_BUFFER dmaL1DataTX[_UART_CNT];
     extern const SERIAL_CFG Serial[_UART_CNT];
     
     void Serial_Config(uint8_t port, uint32_t cacheSizeRX, uint32_t cacheSizeTX, uint32_t baudrate);
    diff --git a/TFT/src/User/Hal/stm32f2_f4xx/Serial.h b/TFT/src/User/Hal/stm32f2_f4xx/Serial.h
    index a1bdbdc6c8..e7b9a2fd33 100644
    --- a/TFT/src/User/Hal/stm32f2_f4xx/Serial.h
    +++ b/TFT/src/User/Hal/stm32f2_f4xx/Serial.h
    @@ -30,6 +30,7 @@ typedef struct
     } SERIAL_CFG;
     
     extern DMA_CIRCULAR_BUFFER dmaL1DataRX[_UART_CNT];
    +extern DMA_CIRCULAR_BUFFER dmaL1DataTX[_UART_CNT];
     extern const SERIAL_CFG Serial[_UART_CNT];
     
     void Serial_Config(uint8_t port, uint32_t cacheSizeRX, uint32_t cacheSizeTX, uint32_t baudrate);
    diff --git a/TFT/src/User/Hal/stm32f2_f4xx/sdio_sdcard.c b/TFT/src/User/Hal/stm32f2_f4xx/sdio_sdcard.c
    index cbb110bb81..294a51c1ed 100644
    --- a/TFT/src/User/Hal/stm32f2_f4xx/sdio_sdcard.c
    +++ b/TFT/src/User/Hal/stm32f2_f4xx/sdio_sdcard.c
    @@ -1126,7 +1126,7 @@ SD_Error SD_GetCardInfo(SD_CardInfo *cardinfo)
         tmp = (uint8_t)((CSD_Tab[2] & 0x0000FF00) >> 8);
         cardinfo->SD_csd.DeviceSizeMul |= (tmp & 0x80) >> 7;
     
    -    cardinfo->CardCapacity = (cardinfo->SD_csd.DeviceSize + 1) ;
    +    cardinfo->CardCapacity = (cardinfo->SD_csd.DeviceSize + 1);
         cardinfo->CardCapacity *= (1 << (cardinfo->SD_csd.DeviceSizeMul + 2));
         cardinfo->CardBlockSize = 1 << (cardinfo->SD_csd.RdBlockLen);
         cardinfo->CardCapacity *= cardinfo->CardBlockSize;
    diff --git a/TFT/src/User/Menu/BLTouch.c b/TFT/src/User/Menu/BLTouch.c
    index 431ee3e438..12f5f2df44 100644
    --- a/TFT/src/User/Menu/BLTouch.c
    +++ b/TFT/src/User/Menu/BLTouch.c
    @@ -33,7 +33,7 @@ void menuBLTouch(void)
     
       if (infoMachineSettings.firmwareType == FW_MARLIN)
       {
    -    mustStoreCmd("M401 H\n");       // get BLTouch HS Mode state (bltHSmode will be updated in parseACK())
    +    mustStoreCmd("M401 H\n");       // get BLTouch HS Mode state (bltHSmode will be updated in parseAck())
         mustStoreCmd(SERVO_GCODE, 90);  // if "M401 H" is not supported the probe will be deployed so it needs to be stowed back
       }
     
    @@ -67,7 +67,7 @@ void menuBLTouch(void)
     
           case KEY_ICON_5:
             if (bltHSmode != HS_DISABLED)
    -          storeCmd("M401 S%u\n", HS_ON - bltHSmode);  // switch BLTouch HS Mode state (bltHSmode will be updated in parseACK())
    +          storeCmd("M401 S%u\n", HS_ON - bltHSmode);  // switch BLTouch HS Mode state (bltHSmode will be updated in parseAck())
             break;
     
           case KEY_ICON_7:
    diff --git a/TFT/src/User/Menu/BedLeveling.c b/TFT/src/User/Menu/BedLeveling.c
    index 3f69099e07..e6b830c7fb 100644
    --- a/TFT/src/User/Menu/BedLeveling.c
    +++ b/TFT/src/User/Menu/BedLeveling.c
    @@ -168,7 +168,7 @@ void menuBedLeveling(void)
             break;
         }
     
    -    if (levelStateNew != UNDEFINED)  // it's Marlin or Reprap FW
    +    if (levelStateNew != UNDEFINED)  // it's Marlin or RepRap firmware
         {
           levelStateNew = getParameter(P_ABL_STATE, 0);
     
    diff --git a/TFT/src/User/Menu/CaseLight.c b/TFT/src/User/Menu/CaseLight.c
    index 93d9f3de82..587c9006b3 100644
    --- a/TFT/src/User/Menu/CaseLight.c
    +++ b/TFT/src/User/Menu/CaseLight.c
    @@ -1,8 +1,6 @@
     #include "CaseLight.h"
     #include "includes.h"
     
    -#define CASE_LIGHT_UPDATE_TIME 1000  // 1 seconds is 1000
    -
     static uint8_t caseLightPercent = 0;
     static bool caseLightState;
     
    @@ -87,8 +85,8 @@ void menuCaseLight(void)
           case KEY_ICON_3:
           case KEY_INCREASE:
             requestedCLpercent = (key_num == KEY_ICON_3 || key_num == KEY_INCREASE) ?
    -                            NOBEYOND(0, requestedCLpercent + percentSteps[percent_index], 100) :
    -                            NOBEYOND(0, requestedCLpercent - percentSteps[percent_index], 100);
    +                             NOBEYOND(0, requestedCLpercent + percentSteps[percent_index], 100) :
    +                             NOBEYOND(0, requestedCLpercent - percentSteps[percent_index], 100);
             sendingNeeded |= DO_SEND_PERCENT;
             break;
     
    diff --git a/TFT/src/User/Menu/FeatureSettings.c b/TFT/src/User/Menu/FeatureSettings.c
    index 8388fc35be..95f0d18882 100644
    --- a/TFT/src/User/Menu/FeatureSettings.c
    +++ b/TFT/src/User/Menu/FeatureSettings.c
    @@ -24,6 +24,7 @@ const LABEL itemToggleSmart[ITEM_TOGGLE_SMART_NUM] =
     typedef enum
     {
       SKEY_ADVANCED_OK = 0,
    +  SKEY_COMMAND_CHECKSUM,
       SKEY_EMULATED_M600,
       SKEY_EMULATED_M109_M190,
       SKEY_EVENT_LED,
    @@ -65,6 +66,7 @@ static inline void updateFeatureSettings(uint8_t item_index)
       switch (item_index)
       {
         case SKEY_ADVANCED_OK:
    +    case SKEY_COMMAND_CHECKSUM:
         case SKEY_EMULATED_M600:
         case SKEY_EMULATED_M109_M190:
         case SKEY_EVENT_LED:
    @@ -139,6 +141,7 @@ void loadFeatureSettings(LISTITEM * item, uint16_t item_index, uint8_t itemPos)
         switch (item_index)
         {
           case SKEY_ADVANCED_OK:
    +      case SKEY_COMMAND_CHECKSUM:
           case SKEY_EMULATED_M600:
           case SKEY_EMULATED_M109_M190:
           case SKEY_EVENT_LED:
    @@ -176,7 +179,7 @@ void loadFeatureSettings(LISTITEM * item, uint16_t item_index, uint8_t itemPos)
             case SKEY_FIL_RUNOUT:
             {
               LABEL sensorLabel = itemToggleSmart[GET_BIT(infoSettings.runout, 1)];
    -          item->valueLabel.index = (GET_BIT(infoSettings.runout, 0)) ? sensorLabel.index : LABEL_OFF ;
    +          item->valueLabel.index = (GET_BIT(infoSettings.runout, 0)) ? sensorLabel.index : LABEL_OFF;
               break;
             }
           #endif
    @@ -215,6 +218,7 @@ void menuFeatureSettings(void)
       // set item types
       LISTITEM settingPage[SKEY_COUNT] = {
         {CHARICON_TOGGLE_ON,   LIST_TOGGLE,        LABEL_ADVANCED_OK,            LABEL_NULL},
    +    {CHARICON_TOGGLE_ON,   LIST_TOGGLE,        LABEL_COMMAND_CHECKSUM,       LABEL_NULL},
         {CHARICON_TOGGLE_ON,   LIST_TOGGLE,        LABEL_EMULATED_M600,          LABEL_NULL},
         {CHARICON_TOGGLE_ON,   LIST_TOGGLE,        LABEL_EMULATED_M109_M190,     LABEL_NULL},
         {CHARICON_TOGGLE_ON,   LIST_TOGGLE,        LABEL_EVENT_LED,              LABEL_NULL},
    diff --git a/TFT/src/User/Menu/LEDColor.c b/TFT/src/User/Menu/LEDColor.c
    index 41399cd92a..55bea976f9 100644
    --- a/TFT/src/User/Menu/LEDColor.c
    +++ b/TFT/src/User/Menu/LEDColor.c
    @@ -2,9 +2,9 @@
     #include "includes.h"
     
     // value ranges
    -#define LED_UPDATE_TIME 1000  // 1 seconds is 1000
    -#define LED_MIN_VALUE   0
    -#define LED_MAX_VALUE   255
    +#define LED_REFRESH_TIME 1000  // 1 seconds is 1000
    +#define LED_MIN_VALUE    0
    +#define LED_MAX_VALUE    255
     
     // key button enumeration
     typedef enum
    @@ -444,7 +444,7 @@ void menuLEDColorCustom(void)
           sendingNeeded = true;
         }
     
    -    if ((sendingNeeded && nextScreenUpdate(LED_UPDATE_TIME)) || updateForced)
    +    if ((sendingNeeded && nextScreenUpdate(LED_REFRESH_TIME)) || updateForced)
         {
           LED_SendColor(&ledColor);
     
    diff --git a/TFT/src/User/Menu/LevelCorner.c b/TFT/src/User/Menu/LevelCorner.c
    index f3c1f17a89..f0f4071d66 100644
    --- a/TFT/src/User/Menu/LevelCorner.c
    +++ b/TFT/src/User/Menu/LevelCorner.c
    @@ -11,7 +11,7 @@ int16_t origLevelEdge = -1;
     
     uint8_t getLevelEdgeMin(void)
     {
    -  // min edge limit for the probe with probe offset set in parseACK.c
    +  // min edge limit for the probe with probe offset set in Mainboard_AckHandler.c
       int16_t maxXedge = getParameter(P_PROBE_OFFSET, AXIS_INDEX_X) + getParameter(P_HOME_OFFSET, AXIS_INDEX_X);
       int16_t maxYedge = getParameter(P_PROBE_OFFSET, AXIS_INDEX_Y) + getParameter(P_HOME_OFFSET, AXIS_INDEX_Y);
     
    diff --git a/TFT/src/User/Menu/MainPage.c b/TFT/src/User/Menu/MainPage.c
    index d12405c7b5..189d2074eb 100644
    --- a/TFT/src/User/Menu/MainPage.c
    +++ b/TFT/src/User/Menu/MainPage.c
    @@ -27,9 +27,7 @@ void menuMain(void)
       KEY_VALUES key_num = KEY_IDLE;
     
       if (infoMachineSettings.firmwareType == FW_REPRAPFW)
    -  {
         mainPageItems.items[5].label.index = LABEL_MACROS;
    -  }
     
       if (infoSettings.status_screen != 1)
       {
    @@ -72,14 +70,14 @@ void menuMain(void)
             break;
     
           case KEY_ICON_5:
    -        if (infoMachineSettings.firmwareType == FW_REPRAPFW)
    +        if (infoMachineSettings.firmwareType != FW_REPRAPFW)
             {
    -          strcpy(infoFile.path, "Macros");
    -          OPEN_MENU(menuCallMacro);
    +          OPEN_MENU(menuCustom);
             }
             else
             {
    -          OPEN_MENU(menuCustom);
    +          strcpy(infoFile.path, "Macros");
    +          OPEN_MENU(menuCallMacro);
             }
             break;
     
    diff --git a/TFT/src/User/Menu/Move.c b/TFT/src/User/Menu/Move.c
    index 31a9055678..1f62ff034f 100644
    --- a/TFT/src/User/Menu/Move.c
    +++ b/TFT/src/User/Menu/Move.c
    @@ -105,7 +105,7 @@ void menuMove(void)
            | X-(4) | Y+(5) | X+(6) | back(7) |
            *-------*-------*-------*---------*
            |X+ X-  |Y+ Y-  |Z+ Z-            */
    -      {{6, 4}, {5, 1}, {2, 0}}
    +      {{6, 4}, {5, 1}, {2, 0}};
         #else
           /*-------*-------*-------*---------*
            | X+(0) | Y+(1) | Z+(2) | unit(3) |
    @@ -113,9 +113,8 @@ void menuMove(void)
            | X-(4) | Y-(5) | Z-(6) | back(7) |
            *-------*-------*-------*---------*
            |X+ X-  |Y+ Y-  |Z+ Z-            */
    -      {{0, 4}, {1, 5}, {2, 6}}
    +      {{0, 4}, {1, 5}, {2, 6}};
         #endif
    -    ;
     
       if (!GET_BIT(infoSettings.inverted_axis, X_AXIS))
         LOAD_XYZ_LABEL_INDEX(table[X_AXIS][0], INC, table[X_AXIS][1], DEC, X);  // table[0] <--> INC(+) table[1] <--> DEC(+) if not inverted
    diff --git a/TFT/src/User/Menu/NotificationMenu.c b/TFT/src/User/Menu/NotificationMenu.c
    index 77f6694873..d1c9549ec6 100644
    --- a/TFT/src/User/Menu/NotificationMenu.c
    +++ b/TFT/src/User/Menu/NotificationMenu.c
    @@ -72,6 +72,8 @@ void menuNotification(void)
           case KEY_ICON_0:
           case KEY_ICON_1:
           case KEY_ICON_2:
    +      case KEY_ICON_3:
    +      case KEY_ICON_4:
             replayNotification(key_num);
             break;
     
    diff --git a/TFT/src/User/Menu/Pid.c b/TFT/src/User/Menu/Pid.c
    index d7ac99394c..902196c096 100644
    --- a/TFT/src/User/Menu/Pid.c
    +++ b/TFT/src/User/Menu/Pid.c
    @@ -42,7 +42,7 @@ void pidRun(void)
     
       if (tool < MAX_HEATER_PID_COUNT)
       {
    -    mustStoreCmd("%s S%d\n", (infoMachineSettings.firmwareType == FW_REPRAPFW) ? pidCmdRRF[tool] : pidCmdMarlin[tool], (int)pidHeaterTarget[tool]);  // start PID autotune
    +    mustStoreCmd("%s S%d\n", (infoMachineSettings.firmwareType != FW_REPRAPFW) ? pidCmdMarlin[tool] : pidCmdRRF[tool], (int)pidHeaterTarget[tool]);  // start PID autotune
         pidStatus = PID_RUNNING;
       }
     }
    @@ -235,7 +235,7 @@ void menuPid(void)
           if (getMenuType() != MENU_TYPE_SPLASH)
             popupSplash(DIALOG_TYPE_INFO, LABEL_SCREEN_INFO, LABEL_BUSY);
     
    -      if (OS_GetTimeMs() > pidTimeout)
    +      if (OS_GetTimeMs() >= pidTimeout)
             pidUpdateStatus(PID_TIMEOUT);
     
           if (pidStatus != PID_RUNNING)
    diff --git a/TFT/src/User/Menu/SelectMode.c b/TFT/src/User/Menu/SelectMode.c
    index fa1ec5ee77..32a9d5571a 100644
    --- a/TFT/src/User/Menu/SelectMode.c
    +++ b/TFT/src/User/Menu/SelectMode.c
    @@ -66,11 +66,13 @@ void menuMode(void)
       drawSelectedMode(nowMode);
     
       #if LCD_ENCODER_SUPPORT
    -    while (!XPT2046_Read_Pen() || LCD_Enc_ReadBtn(LCD_ENC_BUTTON_INTERVAL))
    -      ;  // wait for button release
    +    while (!XPT2046_Read_Pen() || LCD_Enc_ReadBtn(LCD_ENC_BUTTON_INTERVAL))  // wait for button release
    +    {
    +    }
       #else
    -    while (!XPT2046_Read_Pen())
    -      ;  // wait for touch release
    +    while (!XPT2046_Read_Pen())  // wait for touch release
    +    {
    +    }
       #endif
     
       while (MENU_IS(menuMode))
    diff --git a/TFT/src/User/Menu/StatusScreen.c b/TFT/src/User/Menu/StatusScreen.c
    index a0c0d17436..c96e7f0280 100644
    --- a/TFT/src/User/Menu/StatusScreen.c
    +++ b/TFT/src/User/Menu/StatusScreen.c
    @@ -12,7 +12,7 @@
       #define SET_SPEEDMENUINDEX(x)
     #endif
     
    -#define UPDATE_TOOL_TIME 2000  // 1 seconds is 1000
    +#define TOOL_TOGGLE_TIME 2000  // 1 seconds is 1000
     
     #ifdef PORTRAIT_MODE
       #define XYZ_STATUS "X:%.2f Y:%.2f Z:%.2f"
    @@ -251,7 +251,7 @@ static inline void statusScrollMsg(void)
     
     static inline void statusToggleTool(void)
     {
    -  if (nextScreenUpdate(UPDATE_TOOL_TIME))
    +  if (nextScreenUpdate(TOOL_TOGGLE_TIME))
       {
         // increment hotend index
         if (infoSettings.hotend_count > 1)
    @@ -275,7 +275,7 @@ static inline void statusToggleTool(void)
         statusDraw();
     
         // gcode queries must be call after drawStatus
    -    coordinateQuery(MS_TO_SEC(UPDATE_TOOL_TIME));
    +    coordinateQuery(MS_TO_SEC(TOOL_TOGGLE_TIME));
         speedQuery();
         ctrlFanQuery();
       }
    diff --git a/TFT/src/User/Menu/common.c b/TFT/src/User/Menu/common.c
    index babf2c3e8a..66365665e3 100644
    --- a/TFT/src/User/Menu/common.c
    +++ b/TFT/src/User/Menu/common.c
    @@ -102,20 +102,16 @@ const uint16_t iconToggle[ITEM_TOGGLE_NUM] =
     
     // Check time elapsed against the time specified in milliseconds for displaying/updating info on screen
     // Use this for timed screen updates in menu loops only
    -bool nextScreenUpdate(uint32_t duration)
    +bool nextScreenUpdate(uint32_t refreshTime)
     {
       static uint32_t lastTime = 0;
    -  uint32_t curTime = OS_GetTimeMs();
     
    -  if (curTime > (lastTime + duration))
    -  {
    -    lastTime = curTime;
    -    return true;
    -  }
    -  else
    -  {
    +  if (OS_GetTimeMs() - lastTime < refreshTime)
         return false;
    -  }
    +
    +  lastTime = OS_GetTimeMs();
    +
    +  return true;
     }
     
     #ifdef FRIENDLY_Z_OFFSET_LANGUAGE
    diff --git a/TFT/src/User/Menu/common.h b/TFT/src/User/Menu/common.h
    index 7f7cac4b61..09bdfb5308 100644
    --- a/TFT/src/User/Menu/common.h
    +++ b/TFT/src/User/Menu/common.h
    @@ -58,7 +58,7 @@ extern const LABEL itemToggle[ITEM_TOGGLE_NUM];
     extern const uint16_t iconToggle[ITEM_TOGGLE_NUM];
     
     // Check if next screen update is due
    -bool nextScreenUpdate(uint32_t duration);
    +bool nextScreenUpdate(uint32_t refreshTime);
     
     #ifdef FRIENDLY_Z_OFFSET_LANGUAGE
       void invertZAxisIcons(MENUITEMS * menuItems);
    diff --git a/TFT/src/User/Variants/pin_GD_TFT35_V3_0.h b/TFT/src/User/Variants/pin_GD_TFT35_V3_0.h
    index 91f9659dd0..fae81c57c7 100644
    --- a/TFT/src/User/Variants/pin_GD_TFT35_V3_0.h
    +++ b/TFT/src/User/Variants/pin_GD_TFT35_V3_0.h
    @@ -1,7 +1,7 @@
     #ifndef _PIN_GD_TFT35_V3_0_H_  // modify to actual filename !!!
     #define _PIN_GD_TFT35_V3_0_H_  // modify to actual filename !!!
     
    -// MCU type (STM32F10x, STM32F2xx, STM32F4xx)
    +// MCU type (STM32F10x, STM32F2xx, STM32F4xx, gd32f20x, gd32f30x)
     #ifndef MCU_TYPE
       #define MCU_TYPE
       #include "gd32f20x.h"
    diff --git a/TFT/src/User/Variants/pin_MKS_TFT32_V1_4.h b/TFT/src/User/Variants/pin_MKS_TFT32_V1_4.h
    index 980c24ce2e..7cb637f435 100644
    --- a/TFT/src/User/Variants/pin_MKS_TFT32_V1_4.h
    +++ b/TFT/src/User/Variants/pin_MKS_TFT32_V1_4.h
    @@ -1,7 +1,7 @@
     #ifndef _PIN_MKS_TFT32_V1_4_H_  // modify to actual filename !!!
     #define _PIN_MKS_TFT32_V1_4_H_  // modify to actual filename !!!
     
    -// MCU type (STM32F10x, STM32F2xx, STM32F4xx)
    +// MCU type (STM32F10x, STM32F2xx, STM32F4xx, GD32F20x, GD32F30x)
     #ifndef MCU_TYPE
       #define MCU_TYPE
       #include "stm32f10x.h"
    diff --git a/TFT/src/User/Variants/pin_MKS_TFT35_V1_0.h b/TFT/src/User/Variants/pin_MKS_TFT35_V1_0.h
    index bae8e37a02..6076080a5a 100644
    --- a/TFT/src/User/Variants/pin_MKS_TFT35_V1_0.h
    +++ b/TFT/src/User/Variants/pin_MKS_TFT35_V1_0.h
    @@ -1,8 +1,11 @@
     #ifndef _PIN_TFT35_V1_0_H_  // modify to actual filename !!!
     #define _PIN_TFT35_V1_0_H_  // modify to actual filename !!!
     
    -// MCU type (STM32F10x, STM32F2xx, STM32F4xx)
    -#include "stm32f4xx.h"
    +// MCU type (STM32F10x, STM32F2xx, STM32F4xx, gd32f20x, gd32f30x)
    +#ifndef MCU_TYPE
    +  #define MCU_TYPE
    +  #include "stm32f4xx.h"
    +#endif
     
     // Portrait Mode support
     // Comment the following line in case the TFT variant supports Portrait Mode
    diff --git a/TFT/src/User/Variants/pin_TFT24_V1_1.h b/TFT/src/User/Variants/pin_TFT24_V1_1.h
    index 19f26d962f..279f614f0d 100644
    --- a/TFT/src/User/Variants/pin_TFT24_V1_1.h
    +++ b/TFT/src/User/Variants/pin_TFT24_V1_1.h
    @@ -1,8 +1,11 @@
     #ifndef _PIN_TFT24_V1_1_H_  // modify to actual filename !!!
     #define _PIN_TFT24_V1_1_H_  // modify to actual filename !!!
     
    -// MCU type (STM32F10x, STM32F2xx, STM32F4xx)
    -#include "stm32f10x.h"
    +// MCU type (STM32F10x, STM32F2xx, STM32F4xx, gd32f20x, gd32f30x)
    +#ifndef MCU_TYPE
    +  #define MCU_TYPE
    +  #include "stm32f10x.h"
    +#endif
     
     // Portrait Mode support
     // Comment the following line in case the TFT variant supports Portrait Mode
    diff --git a/TFT/src/User/Variants/pin_TFT35_V1_0.h b/TFT/src/User/Variants/pin_TFT35_V1_0.h
    index 96ea757d1a..f678d6ed0e 100644
    --- a/TFT/src/User/Variants/pin_TFT35_V1_0.h
    +++ b/TFT/src/User/Variants/pin_TFT35_V1_0.h
    @@ -1,8 +1,11 @@
     #ifndef _PIN_TFT35_V1_0_H_  // modify to actual filename !!!
     #define _PIN_TFT35_V1_0_H_  // modify to actual filename !!!
     
    -// MCU type (STM32F10x, STM32F2xx, STM32F4xx)
    -#include "stm32f10x.h"
    +// MCU type (STM32F10x, STM32F2xx, STM32F4xx, gd32f20x, gd32f30x)
    +#ifndef MCU_TYPE
    +  #define MCU_TYPE
    +  #include "stm32f10x.h"
    +#endif
     
     // Portrait Mode support
     // Comment the following line in case the TFT variant supports Portrait Mode
    diff --git a/TFT/src/User/Variants/pin_TFT35_V2_0.h b/TFT/src/User/Variants/pin_TFT35_V2_0.h
    index 00d8d69dd2..0f8a668c3e 100644
    --- a/TFT/src/User/Variants/pin_TFT35_V2_0.h
    +++ b/TFT/src/User/Variants/pin_TFT35_V2_0.h
    @@ -1,8 +1,11 @@
     #ifndef _PIN_TFT35_V2_0_H_  // modify to actual filename !!!
     #define _PIN_TFT35_V2_0_H_  // modify to actual filename !!!
     
    -// MCU type (STM32F10x, STM32F2xx, STM32F4xx)
    -#include "stm32f10x.h"
    +// MCU type (STM32F10x, STM32F2xx, STM32F4xx, gd32f20x, gd32f30x)
    +#ifndef MCU_TYPE
    +  #define MCU_TYPE
    +  #include "stm32f10x.h"
    +#endif
     
     // Portrait Mode support
     // Comment the following line in case the TFT variant supports Portrait Mode
    diff --git a/TFT/src/User/Variants/pin_TFT35_V3_0.h b/TFT/src/User/Variants/pin_TFT35_V3_0.h
    index 186f94b962..025a1d2cc0 100644
    --- a/TFT/src/User/Variants/pin_TFT35_V3_0.h
    +++ b/TFT/src/User/Variants/pin_TFT35_V3_0.h
    @@ -1,7 +1,7 @@
     #ifndef _PIN_TFT35_V3_0_H_  // modify to actual filename !!!
     #define _PIN_TFT35_V3_0_H_  // modify to actual filename !!!
     
    -// MCU type (STM32F10x, STM32F2xx, STM32F4xx)
    +// MCU type (STM32F10x, STM32F2xx, STM32F4xx, gd32f20x, gd32f30x)
     #ifndef MCU_TYPE
       #define MCU_TYPE
       #include "stm32f2xx.h"
    diff --git a/TFT/src/User/Variants/pin_TFT70_V3_0.h b/TFT/src/User/Variants/pin_TFT70_V3_0.h
    index 365f6be1ec..c8b2bd4140 100644
    --- a/TFT/src/User/Variants/pin_TFT70_V3_0.h
    +++ b/TFT/src/User/Variants/pin_TFT70_V3_0.h
    @@ -1,8 +1,11 @@
     #ifndef _PIN_TFT70_V3_0_H_  // modify to actual filename !!!
     #define _PIN_TFT70_V3_0_H_  // modify to actual filename !!!
     
    -// MCU type (STM32F10x, STM32F2xx, STM32F4xx)
    -#include "stm32f4xx.h"
    +// MCU type (STM32F10x, STM32F2xx, STM32F4xx, gd32f20x, gd32f30x)
    +#ifndef MCU_TYPE
    +  #define MCU_TYPE
    +  #include "stm32f4xx.h"
    +#endif
     
     // Portrait Mode support
     // Comment the following line in case the TFT variant supports Portrait Mode
    diff --git a/TFT/src/User/Variants/pin_Template.h b/TFT/src/User/Variants/pin_Template.h
    index e11e59f81d..a4f95ed102 100644
    --- a/TFT/src/User/Variants/pin_Template.h
    +++ b/TFT/src/User/Variants/pin_Template.h
    @@ -1,8 +1,11 @@
     #ifndef _PIN_TEMPLATE_H_  // modify to actual filename !!!
     #define _PIN_TEMPLATE_H_  // modify to actual filename !!!
     
    -// MCU type (STM32F10x, STM32F2xx, STM32F4xx)
    -//#include "stm32fxxx.h"
    +// MCU type (STM32F10x, STM32F2xx, STM32F4xx, gd32f20x, gd32f30x)
    +#ifndef MCU_TYPE
    +  #define MCU_TYPE
    +  //#include "stm32fxxx.h"
    +#endif
     
     // Portrait Mode support
     // Comment the following line in case the TFT variant supports Portrait Mode
    diff --git a/TFT/src/User/config.ini b/TFT/src/User/config.ini
    index 3e6c94bd00..02b6ebd4e6 100644
    --- a/TFT/src/User/config.ini
    +++ b/TFT/src/User/config.ini
    @@ -142,11 +142,47 @@ tx_slots:2
     #   support the transmission of G-codes according to the configured "tx_slots" setting.
     # If disabled, the TFT will provide the standard transmission logic based on one G-code per time.
     #
    -# NOTE: Disable it in case no ADVANCED_OK feature is requested/needed by the user.
    +# NOTE: Disable it in case:
    +#       - no ADVANCED_OK feature is requested/needed by the user.
    +#       - ADVANCED_OK feature is not providing good printing results or if the mainboard notifies
    +#         frequent error ACK messages (e.g. unknown command) to the TFT during printing.
    +#       - COMMAND_CHECKSUM feature (see description of next setting "command_checksum") is
    +#         requested/needed by the user.
     #
     #   Options: [disable: 0, enable: 1]
     advanced_ok:0
     
    +#### Command Checksum
    +# The TFT enriches each G-code to be sent to the mainboard adding a leading sequential line number
    +# and a trailing checksum appended after an "*" character used as separator.
    +# The checksum is based on algorithm "CheckSum8 Xor" and it is calculated on the G-code with the
    +# applied line number. E.g. "G28" is firstly enriched with a line number (e.g. "N1 G28") and finally
    +# a checksum calculated on that enriched G-code is appended (e.g. "N1 G28*18").
    +# A data integrity check (sequential line number check and checksum check) will be performed on the
    +# mainboard. In case of data mismatch (e.g. data corruption due to EMI on communication serial line):
    +# - the mainboard will send to the TFT an error ACK message followed by a "Resend: " ACK message to
    +#   ask TFT to resend the G-code with the requested line number.
    +# - the TFT will check the presence on an internal buffer of the G-code with the requested line number:
    +#   - if found, the G-code is resent for a maximum of 3 attempts.
    +#   - if not found or the maximum number of attempts has been reached, the TFT will reset the line
    +#     number with an "M110" G-code (immediately sent bypassing any other enqueued G-code) to the
    +#     requested line number just to try to avoid further retransmission requests for the same line
    +#     number or for any out of synch command already sent to the mainboard (e.g. in case ADVANCED_OK
    +#     feature is enabled in TFT).
    +#
    +# NOTE: Disable it in case:
    +#       - printing is controlled by a remote host (e.g. ESP3D, OctoPrint etc.) and a COMMAND_CHECKSUM
    +#         feature is enabled and managed by the remote host. Otherwise (COMMAND_CHECKSUM feature also
    +#         enabled in TFT), the TFT's COMMAND_CHECKSUM feature will always replace the one provided by
    +#         the remote host causing conflicts in case data mismatch will be notified by the mainboard.
    +#       - ADVANCED_OK feature is enabled in TFT. Otherwise, any out of synch command already sent to
    +#         the mainboard will be discarded by the mainboard and not resent by the TFT due the current
    +#         implementation of COMMAND_CHECKSUM feature on the TFT buffers only the last sent command
    +#         and not all the pending commands.
    +#
    +#   Options: [disable: 0, enable: 1]
    +command_checksum:1
    +
     #### Emulated M600
     # The TFT intercepts the M600 G-code (filament change) and emulates the handling logic
     # otherwise provided by Marlin firmware.
    @@ -275,7 +311,7 @@ terminal_color_scheme:0
     #   Options: [OFF: 0, POPUP: 1, TOAST: 2]
     #     OFF:   No notification. The message is ignored.
     #     POPUP: Display a popup window for user confirmation.
    -#     TOAST: A non-blocking Toast notification is displayed for few seconds. No user interaction is needed.
    +#     TOAST: A non-blocking toast notification is displayed for few seconds. No user interaction is needed.
     ack_notification:1
     
     #### Files Sorting
    @@ -763,6 +799,10 @@ ps_auto_shutdown_temp:50
     
     #### Filament Runout Sensor
     # Select the type of filament runout sensor and its default enabled/disabled state.
    +#
    +# NOTE: Smart Filament Sensor (SFS) (value 2 or 3) is a sensor based on an encoder disc that
    +#       toggles runout pin as filament moves (e.g. the BigTreeTech SFS).
    +#
     #   Options: [Normal Disabled: 0, Normal Enabled: 1, Smart Disabled: 2, Smart Enabled: 3]
     fil_runout:0
     
    @@ -787,6 +827,10 @@ fil_runout_noise_threshold:100
     #### Smart Filament Runout Detection
     # Used in conjunction with an SFS (Smart Filament Sensor) based on an encoder disc that
     # toggles runout pin as filament moves.
    +#
    +# NOTE: This setting is taken into account by the TFT only in case "fil_runout" setting is
    +#       set to 2 or 3 (an SFS is used).
    +#
     #   Unit: [distance in mm]
     #   Value range: [min: 1, max: 50]
     fil_runout_distance:7
    diff --git a/TFT/src/User/config_rrf.ini b/TFT/src/User/config_rrf.ini
    index f51b3f96b2..8fe12cfe07 100644
    --- a/TFT/src/User/config_rrf.ini
    +++ b/TFT/src/User/config_rrf.ini
    @@ -105,11 +105,47 @@ tx_slots:2
     #   support the transmission of G-codes according to the configured "tx_slots" setting.
     # If disabled, the TFT will provide the standard transmission logic based on one G-code per time.
     #
    -# NOTE: Disable it in case no ADVANCED_OK feature is requested/needed by the user.
    +# NOTE: Disable it in case:
    +#       - no ADVANCED_OK feature is requested/needed by the user.
    +#       - ADVANCED_OK feature is not providing good printing results or if the mainboard notifies
    +#         frequent error ACK messages (e.g. unknown command) to the TFT during printing.
    +#       - COMMAND_CHECKSUM feature (see description of next setting "command_checksum") is
    +#         requested/needed by the user.
     #
     #   Options: [disable: 0, enable: 1]
     advanced_ok:0
     
    +#### Command Checksum
    +# The TFT enriches each G-code to be sent to the mainboard adding a leading sequential line number
    +# and a trailing checksum appended after an "*" character used as separator.
    +# The checksum is based on algorithm "CheckSum8 Xor" and it is calculated on the G-code with the
    +# applied line number. E.g. "G28" is firstly enriched with a line number (e.g. "N1 G28") and finally
    +# a checksum calculated on that enriched G-code is appended (e.g. "N1 G28*18").
    +# A data integrity check (sequential line number check and checksum check) will be performed on the
    +# mainboard. In case of data mismatch (e.g. data corruption due to EMI on communication serial line):
    +# - the mainboard will send to the TFT an error ACK message followed by a "Resend: " ACK message to
    +#   ask TFT to resend the G-code with the requested line number.
    +# - the TFT will check the presence on an internal buffer of the G-code with the requested line number:
    +#   - if found, the G-code is resent for a maximum of 3 attempts.
    +#   - if not found or the maximum number of attempts has been reached, the TFT will reset the line
    +#     number with an "M110" G-code (immediately sent bypassing any other enqueued G-code) to the
    +#     requested line number just to try to avoid further retransmission requests for the same line
    +#     number or for any out of synch command already sent to the mainboard (e.g. in case ADVANCED_OK
    +#     feature is enabled in TFT).
    +#
    +# NOTE: Disable it in case:
    +#       - printing is controlled by a remote host (e.g. ESP3D, OctoPrint etc.) and a COMMAND_CHECKSUM
    +#         feature is enabled and managed by the remote host. Otherwise (COMMAND_CHECKSUM feature also
    +#         enabled in TFT), the TFT's COMMAND_CHECKSUM feature will always replace the one provided by
    +#         the remote host causing conflicts in case data mismatch will be notified by the mainboard.
    +#       - ADVANCED_OK feature is enabled in TFT. Otherwise, any out of synch command already sent to
    +#         the mainboard will be discarded by the mainboard and not resent by the TFT due the current
    +#         implementation of COMMAND_CHECKSUM feature on the TFT buffers only the last sent command
    +#         and not all the pending commands.
    +#
    +#   Options: [disable: 0, enable: 1]
    +command_checksum:1
    +
     #### Emulated M600
     # The TFT intercepts the M600 G-code (filament change) and emulates the handling logic
     # otherwise provided by Marlin firmware.
    @@ -238,7 +274,7 @@ terminal_color_scheme:0
     #   Options: [OFF: 0, POPUP: 1, TOAST: 2]
     #     OFF:   No notification. The message is ignored.
     #     POPUP: Display a popup window for user confirmation.
    -#     TOAST: A non-blocking Toast notification is displayed for few seconds. No user interaction is needed.
    +#     TOAST: A non-blocking toast notification is displayed for few seconds. No user interaction is needed.
     ack_notification:1
     
     #### Files Sorting
    diff --git a/TFT/src/User/includes.h b/TFT/src/User/includes.h
    index 64852508b2..36f33b407e 100644
    --- a/TFT/src/User/includes.h
    +++ b/TFT/src/User/includes.h
    @@ -79,22 +79,23 @@
     #include "FlashStore.h"
     #include "HomeOffsetControl.h"
     #include "HW_Init.h"
    -#include "interfaceCmd.h"
     #include "LCD_Colors.h"
     #include "LCD_Dimming.h"
     #include "LED_Colors.h"
     #include "LED_Event.h"
     #include "LevelingControl.h"
     #include "MachineParameters.h"
    +#include "Mainboard_AckHandler.h"
    +#include "Mainboard_CmdControl.h"
    +#include "Mainboard_CmdHandler.h"
    +#include "Mainboard_FlowControl.h"
     #include "menu.h"
     #include "ModeSwitching.h"
     #include "Notification.h"
    -#include "parseACK.h"
     #include "PowerFailed.h"
     #include "Printing.h"
     #include "ProbeHeightControl.h"
     #include "ProbeOffsetControl.h"
    -#include "RRFStatusControl.h"
     #include "ScreenShot.h"
     #include "SerialConnection.h"
     #include "Settings.h"
    diff --git a/TFT/src/User/main.c b/TFT/src/User/main.c
    index c7778b415e..1e397debb7 100644
    --- a/TFT/src/User/main.c
    +++ b/TFT/src/User/main.c
    @@ -1,93 +1,6 @@
     #include "main.h"
     #include "includes.h"
     
    -MENU infoMenu;
    -HOST infoHost;
    -CLOCKS mcuClocks;
    -PRIORITY_COUNTER priorityCounter;
    -
    -void InfoHost_Init(bool isConnected)
    -{
    -  infoHost.target_tx_slots = infoSettings.tx_slots;
    -  infoHost.tx_slots = 1;  // set to 1 just to allow a soft start
    -  infoHost.tx_count = 0;
    -  infoHost.connected = isConnected;
    -  infoHost.listening_mode = false;  // temporary disable listening mode. It will be later set by InfoHost_UpdateListeningMode()
    -  infoHost.status = HOST_STATUS_IDLE;
    -
    -  if (!isConnected)
    -    setReminderMsg(LABEL_UNCONNECTED, SYS_STATUS_DISCONNECTED);  // set the no printer attached reminder
    -}
    -
    -void InfoHost_UpdateListeningMode(void)
    -{
    -  infoHost.listening_mode = (GET_BIT(infoSettings.general_settings, INDEX_LISTENING_MODE) == 1);
    -
    -  if (infoHost.listening_mode)
    -    setReminderMsg(LABEL_LISTENING, SYS_STATUS_LISTENING);  // if TFT in listening mode, display a reminder message
    -}
    -
    -void InfoHost_HandleOkAck(int16_t target_tx_slots)
    -{
    -  // the following check should always be matched unless:
    -  // - an ACK message not related to a gcode originated by the TFT is received
    -  // - an ACK message for an out of band gcode (e.g. emergency gcode) is received
    -  //
    -  if (infoHost.tx_count > 0)
    -    infoHost.tx_count--;
    -
    -  // NOTE: the following code always allows to align infoHost.tx_slots even in case of switching ON/OFF
    -  //       the ADVANCED_OK feature in TFT and/or in case infoHost.tx_slots is beeing also managed by
    -  //       Marlin (if ADVANCED_OK is enabled in Marlin firmware)
    -  //
    -
    -  // if ADVANCED_OK is disabled in TFT
    -  //
    -  if (GET_BIT(infoSettings.general_settings, INDEX_ADVANCED_OK) == 0)
    -  {
    -    infoHost.tx_slots = 1;
    -  }
    -  //
    -  // if ADVANCED_OK is enabled in TFT or Marlin
    -  //
    -  else if (target_tx_slots >= 0)
    -  {
    -    // UPPER LIMITER
    -    //
    -    // the following check is matched in case:
    -    // - ADVANCED_OK is enabled in TFT. infoSettings.tx_slots for static ADVANCED_OK configured in TFT is used
    -    // - ADVANCED_OK is enabled in Marlin but the mainboard reply (target_tx_slots) is out of sync (above) with the current
    -    //   pending gcodes (it happens sometimes). infoSettings.tx_slots for Marlin ADVANCED_OK detected at TFT boot is used
    -    //
    -    if (target_tx_slots + infoHost.tx_count >= infoSettings.tx_slots)
    -      infoHost.tx_slots = infoSettings.tx_slots - infoHost.tx_count;
    -    //
    -    // LOWER LIMITER (only for Marlin ADVANCED_OK)
    -    //
    -    // if printing from onboard media target_tx_slots is always reported as 0 by Marlin even if there are no pending gcodes
    -    // so just set infoHost.tx_slots to 1 to allow the transmission of one gcode per time avoiding a possible TFT freeze
    -    //
    -    else if (target_tx_slots != 0 || infoHost.tx_count != 0)  // if not printing from onboard media
    -      infoHost.tx_slots = target_tx_slots;
    -    else                                                      // if printing from onboard media
    -      infoHost.tx_slots = 1;
    -
    -    infoHost.target_tx_slots = infoHost.tx_slots;  // set new current target
    -  }
    -  //
    -  // if generic OK response handling (e.g. temperature response), increment the current value up to current target
    -  //
    -  else
    -  {
    -    // UPPER AND LOWER LIMITER
    -    //
    -    // limit the current value up to current target or to 1 if current target was set to 0 and there are no more pending gcodes
    -    //
    -    if (infoHost.tx_slots < infoHost.target_tx_slots || (infoHost.tx_slots == 0 && infoHost.tx_count == 0))
    -      infoHost.tx_slots++;
    -  }
    -}
    -
     int main(void)
     {
       #if defined GD32F3XX
    @@ -95,7 +8,7 @@ int main(void)
         __enable_irq();
       #endif
     
    -  SystemClockInit();
    +  SystemClockInit();  // it depends on "variants.h" included in "includes.h"
     
       SCB->VTOR = VECT_TAB_FLASH;
     
    diff --git a/TFT/src/User/main.h b/TFT/src/User/main.h
    index 0a2f831d58..391cea7165 100644
    --- a/TFT/src/User/main.h
    +++ b/TFT/src/User/main.h
    @@ -5,73 +5,7 @@
     extern "C" {
     #endif
     
    -#include 
    -#include 
    -#include "variants.h"  // for RCC_ClocksTypeDef
    -#include "uart.h"      // for _UART_CNT
    -
    -#define MAX_MENU_DEPTH      10  // max sub menu depth
    -#define BE_PRIORITY_DIVIDER 16  // a divider value of 16 -> run 6% of the time only. Use a power of 2 for performance reasons!
    -#define FE_PRIORITY_DIVIDER 16  // a divider value of 16 -> run 6% of the time only. Use a power of 2 for performance reasons!
    -
    -typedef void (* FP_MENU)(void);
    -
    -typedef struct
    -{
    -  FP_MENU menu[MAX_MENU_DEPTH];  // menu function buffer
    -  uint8_t cur;                   // current menu index in buffer
    -} MENU;
    -
    -typedef enum
    -{
    -  HOST_STATUS_IDLE = 0,
    -  HOST_STATUS_PRINTING,
    -  HOST_STATUS_RESUMING,
    -  HOST_STATUS_PAUSED,
    -  HOST_STATUS_PAUSING
    -} HOST_STATUS;
    -
    -typedef enum
    -{
    -  HOST_SLOTS_GENERIC_OK = -1,
    -} HOST_SLOTS;
    -
    -typedef struct
    -{
    -  uint8_t target_tx_slots;  // keep track of target gcode tx slots (e.g. if ADVANCED_OK feature is enabled on both mainboard and TFT)
    -  uint8_t tx_slots;         // keep track of available gcode tx slots (e.g. if ADVANCED_OK feature is enabled on both mainboard and TFT)
    -  uint8_t tx_count;         // keep track of pending gcode tx count
    -  bool connected;           // TFT is connected to Marlin
    -  bool listening_mode;      // TFT is in listening mode from Marlin
    -  HOST_STATUS status;       // host is busy in printing execution. (USB serial printing and gcode print from onboard)
    -} HOST;
    -
    -typedef struct
    -{
    -  RCC_ClocksTypeDef rccClocks;
    -  uint32_t PCLK1_Timer_Frequency;
    -  uint32_t PCLK2_Timer_Frequency;
    -} CLOCKS;
    -
    -typedef struct
    -{
    -  uint32_t be;  // back end
    -  uint32_t fe;  // front end
    -} PRIORITY_COUNTER;
    -
    -extern MENU infoMenu;                     // menu structure
    -extern HOST infoHost;                     // information interaction with Marlin
    -extern CLOCKS mcuClocks;                  // system clocks: SYSCLK, AHB, APB1, APB2, APB1_Timer, APB2_Timer2
    -extern PRIORITY_COUNTER priorityCounter;  // priority counter
    -
    -void InfoHost_Init(bool isConnected);
    -void InfoHost_UpdateListeningMode(void);
    -
    -// handle OK response:
    -//   - tx_slots (used/effective only in case "advanced_ok" configuration setting is also enabled in TFT):
    -//     - < 0 (HOST_SLOTS_GENERIC_OK): to increase infoHost.tx_slots up to current target and decrease infoHost.tx_count by 1
    -//     - >= 0: to handle static ADVANCED_OK and Marlin ADVANCED_OK
    -void InfoHost_HandleOkAck(int16_t tx_slots);
    +int main(void);
     
     #ifdef __cplusplus
     }
    diff --git a/TFT/src/User/my_misc.c b/TFT/src/User/my_misc.c
    index 3aa686c38e..0aa7d580e1 100644
    --- a/TFT/src/User/my_misc.c
    +++ b/TFT/src/User/my_misc.c
    @@ -24,21 +24,20 @@ uint32_t calculateCRC16(const uint8_t *data, uint32_t length)
     {
       uint16_t crc = 0xFFFF;
       uint32_t i;
    +
       for (i = 0; i < length; i++)
       {
         crc = (crc ^ data[i]) & 0xFFFF;
    +
         for (uint8_t j = 0; j < 8; j++)
         {
           if (crc & 1)
    -      {
             crc = (crc >> 1) ^ CRC_POLY;
    -      }
           else
    -      {
             crc = crc >> 1;
    -      }
         }
       }
    +
       return crc;
     }
     
    @@ -213,115 +212,3 @@ void strncpy_no_pad(char *dest, const char *src, size_t n)
       if (n != 0)  // safe in case value 0 was passed for "n"
         *dest = '\0';
     }
    -
    -// strip out any leading " ", "/" or ":" character that might be in the string
    -const char *stripHead(const char *str)
    -{
    -  // example: ":    /test/cap2.gcode\n" -> "test/cap2.gcode\n"
    -
    -  while (*str == ' ' || *str == '/' || *str == ':')
    -  {
    -    str++;
    -  }
    -
    -  return str;
    -}
    -
    -// strip out any trailing checksum that might be in the string
    -void stripChecksum(char *str)
    -{
    -  // examples:
    -  //
    -  // "/test/cap2.gcode  *36\n\0" -> "/test/cap2.gcode"
    -  // "/test/cap2.gcode  \n\0" -> "/test/cap2.gcode"
    -
    -  char *strPtr = strrchr(str, '*');  // e.g. "/test/cap2.gcode  *36\n\0" -> "*36\n\0"
    -
    -  if (strPtr == NULL)
    -    strPtr = str + strlen(str);      // e.g. "/test/cap2.gcode  \n\0" -> "\0"
    -
    -  while (strPtr != str)
    -  {
    -    // e.g. "*36\n\0" -> " *36\n\0"
    -    // e.g. "\0" -> "\n\0"
    -    //
    -    --strPtr;
    -
    -    if (*strPtr != ' ' && *strPtr != '\t' && *strPtr != '\n' && *strPtr != '\r')
    -    {
    -      strPtr++;  // next char has to be set to "\0"
    -      break;
    -    }
    -  }
    -
    -  // e.g. "  *36\n\0" -> "\0 *36\n\0"
    -  // e.g. "  \n\0" -> "\0 \n\0"
    -  //
    -  *strPtr = '\0';
    -}
    -
    -uint8_t getChecksum(char *str)
    -{
    -  uint8_t checksum = 0;
    -
    -  while (*str != '\0')
    -  {
    -    checksum ^= *(str++);
    -  }
    -
    -  return checksum;
    -}
    -
    -bool validateChecksum(char *str)
    -{
    -  char *strPtr = strrchr(str, '*');  // e.g. "N1 G28*18\n\0" -> "*18\n\0"
    -
    -  if (strPtr == NULL)
    -    return false;
    -
    -  uint8_t checksum = 0;
    -  uint8_t value = strtol(&strPtr[1], NULL, 10);
    -
    -  while (strPtr != str)
    -  {
    -    checksum ^= *(--strPtr);
    -  }
    -
    -  return (checksum == value ? true : false);
    -}
    -
    -const char *parseM118(char *str, bool *hasE, bool *hasA)
    -{
    -  stripChecksum(str);
    -  str = (char *) stripHead(str);
    -
    -  *hasE = false;
    -  *hasA = false;
    -
    -  for (uint8_t i = 3; i--;)
    -  {
    -    // A1, E1 and Pn are always parsed out
    -    if (!(((str[0] == 'A' || str[0] == 'E') && str[1] == '1') || (str[0] == 'P' && NUMERIC(str[1]))))
    -      break;
    -
    -    switch (str[0])
    -    {
    -      case 'A':
    -        *hasA = true;
    -        break;
    -
    -      case 'E':
    -        *hasE = true;
    -        break;
    -    }
    -
    -    str += 2;
    -
    -    while (*str == ' ')
    -    {
    -      ++str;
    -    }
    -  }
    -
    -  return str;
    -}
    diff --git a/TFT/src/User/my_misc.h b/TFT/src/User/my_misc.h
    index 2ce0a59e49..f60d5cdfff 100644
    --- a/TFT/src/User/my_misc.h
    +++ b/TFT/src/User/my_misc.h
    @@ -9,13 +9,6 @@ extern "C" {
     #include 
     #include   // for size_t
     
    -// Menu Macros
    -#define OPEN_MENU(x)    infoMenu.menu[++infoMenu.cur] = x
    -#define REPLACE_MENU(x) infoMenu.menu[infoMenu.cur] = x
    -#define CLOSE_MENU()    infoMenu.cur--
    -#define MENU_IS(x)      infoMenu.menu[infoMenu.cur] == x
    -#define MENU_IS_NOT(x)  infoMenu.menu[infoMenu.cur] != x
    -
     // Macros to make a string from a macro
     #define STRINGIFY_(M) #M
     #define STRINGIFY(M)  STRINGIFY_(M)
    @@ -91,13 +84,6 @@ double strtod_ligth(char *str, char **endptr);               // light weight str
     void strncpy_pad(char *dest, const char *src, size_t n);     // light weight and safe strncpy() function with padding
     void strncpy_no_pad(char *dest, const char *src, size_t n);  // light weight and safe strncpy() function without padding
     
    -const char *stripHead(const char *str);  // strip out any leading " ", "/" or ":" character that might be in the string
    -void stripChecksum(char *str);           // strip out any trailing checksum that might be in the string
    -uint8_t getChecksum(char *str);
    -bool validateChecksum(char *str);
    -
    -const char *parseM118(char *str, bool *hasE, bool *hasA);
    -
     #ifdef __cplusplus
     }
     #endif
    diff --git a/TFT/src/User/os_timer.c b/TFT/src/User/os_timer.c
    index d3aaa1f28b..6a60b4125b 100644
    --- a/TFT/src/User/os_timer.c
    +++ b/TFT/src/User/os_timer.c
    @@ -1,13 +1,7 @@
     #include "os_timer.h"
     #include "includes.h"
     
    -typedef struct
    -{
    -  uint32_t ms;   // milliseconds
    -  uint16_t sec;  // seconds
    -} OS_COUNTER;
    -
    -volatile static OS_COUNTER os_counter = {0, 0};
    +OS_COUNTER os_counter = {0, 0};
     
     void OS_InitTimerMs(void)
     {
    @@ -84,12 +78,6 @@ void TIM7_IRQHandler(void)
     }
     #endif
     
    -// 1 ms
    -uint32_t OS_GetTimeMs(void)
    -{
    -  return os_counter.ms;
    -}
    -
     // task: task structure to be filled
     // time_ms:
     //
    diff --git a/TFT/src/User/os_timer.h b/TFT/src/User/os_timer.h
    index 570c994fc6..e1bd01d9c6 100644
    --- a/TFT/src/User/os_timer.h
    +++ b/TFT/src/User/os_timer.h
    @@ -7,6 +7,13 @@ extern "C" {
     
     #include 
     
    +// NOTE: not needed to define "ms" attribute as "volatile"
    +typedef struct
    +{
    +  uint32_t ms;   // milliseconds
    +  uint16_t sec;  // seconds
    +} OS_COUNTER;
    +
     typedef void (*FP_TASK)(void *);
     
     typedef struct
    @@ -19,8 +26,14 @@ typedef struct
       uint8_t  is_repeat;
     } OS_TASK;
     
    +extern OS_COUNTER os_counter;
    +
     void OS_InitTimerMs(void);
    -uint32_t OS_GetTimeMs(void);
    +
    +static inline uint32_t OS_GetTimeMs(void)
    +{
    +  return os_counter.ms;
    +}
     
     void OS_TaskInit(OS_TASK *task, uint32_t time_ms, FP_TASK function, void *para);
     void OS_TaskLoop(OS_TASK *task);
    diff --git a/platformio.ini b/platformio.ini
    index 10627f8b82..6a1820306e 100644
    --- a/platformio.ini
    +++ b/platformio.ini
    @@ -186,6 +186,12 @@ platform_packages = framework-spl-gd32@https://github.com/bigtreetech/gd32-pio-s
     framework         = spl
     upload_protocol   = cmsis-dap
     
    +
    +
    +#
    +# BIGTREE-TECH STM32 Series
    +#
    +
     #
     # BIGTREE TFT24 V1.1
     #
    @@ -390,7 +396,10 @@ build_flags      = ${stm32f4xx.build_flags} ${base64_png.build_flags}
     
     
     
    -# GD32 Series
    +#
    +# BIGTREE-TECH GD32 Series
    +#
    +
     #
     # BIGTREE TFT24 V1.1
     #
    @@ -522,7 +531,9 @@ build_flags      = ${stm32f4xx.build_flags} ${base64_png.build_flags}
     
     
     #
    -# MKS Series, Maintained by open source contributors
    +# MKS STM32 Series. Maintained by open source contributors
    +#
    +
     #
     # MKS TFT28 V3.0
     #