diff --git a/Marlin/src/HAL/AVR/HAL.h b/Marlin/src/HAL/AVR/HAL.h index a5896a0e97..41263b6cd6 100644 --- a/Marlin/src/HAL/AVR/HAL.h +++ b/Marlin/src/HAL/AVR/HAL.h @@ -124,7 +124,7 @@ typedef int8_t pin_t; #error "LCD_SERIAL_PORT must be from 0 to 3, or -1 for USB Serial." #endif #define LCD_SERIAL lcdSerial - #if HAS_DGUS_LCD + #if HAS_DGUS_LCD || ENABLED(DGUS_LCD_UI_CREALITY_TOUCH) #define SERIAL_GET_TX_BUFFER_FREE() LCD_SERIAL.get_tx_buffer_free() #endif #endif diff --git a/Marlin/src/HAL/AVR/MarlinSerial.cpp b/Marlin/src/HAL/AVR/MarlinSerial.cpp index cd8bf5e690..2d5c5d52ac 100644 --- a/Marlin/src/HAL/AVR/MarlinSerial.cpp +++ b/Marlin/src/HAL/AVR/MarlinSerial.cpp @@ -629,7 +629,7 @@ MSerialT1 customizedSerial1(MSerialT1::HasEmergencyParser); template class MarlinSerial< LCDSerialCfg >; MSerialLCD lcdSerial(MSerialLCD::HasEmergencyParser); - #if HAS_DGUS_LCD + #if HAS_DGUS_LCD || ENABLED(DGUS_LCD_UI_CREALITY_TOUCH) template typename MarlinSerial::ring_buffer_pos_t MarlinSerial::get_tx_buffer_free() { const ring_buffer_pos_t t = tx_buffer.tail, // next byte to send. diff --git a/Marlin/src/HAL/AVR/MarlinSerial.h b/Marlin/src/HAL/AVR/MarlinSerial.h index 0565c7b9db..56462da716 100644 --- a/Marlin/src/HAL/AVR/MarlinSerial.h +++ b/Marlin/src/HAL/AVR/MarlinSerial.h @@ -212,7 +212,7 @@ static ring_buffer_pos_t available(); static void write(const uint8_t c); static void flushTX(); - #if HAS_DGUS_LCD + #if HAS_DGUS_LCD || ENABLED(DGUS_LCD_UI_CREALITY_TOUCH) static ring_buffer_pos_t get_tx_buffer_free(); #endif diff --git a/Marlin/src/HAL/STM32/HAL.h b/Marlin/src/HAL/STM32/HAL.h index 2441c46eab..0b56e2e773 100644 --- a/Marlin/src/HAL/STM32/HAL.h +++ b/Marlin/src/HAL/STM32/HAL.h @@ -96,7 +96,7 @@ #else #error "LCD_SERIAL_PORT must be from 1 to 6. You can also use -1 if the board supports Native USB." #endif - #if HAS_DGUS_LCD + #if HAS_DGUS_LCD || ENABLED(DGUS_LCD_UI_CREALITY_TOUCH) #define SERIAL_GET_TX_BUFFER_FREE() LCD_SERIAL.availableForWrite() #endif #endif diff --git a/Marlin/src/HAL/STM32F1/HAL.h b/Marlin/src/HAL/STM32F1/HAL.h index b3d8dc9d0b..d9beae77f5 100644 --- a/Marlin/src/HAL/STM32F1/HAL.h +++ b/Marlin/src/HAL/STM32F1/HAL.h @@ -129,7 +129,7 @@ #define LCD_SERIAL MSERIAL(1) // dummy port static_assert(false, "LCD_SERIAL_PORT must be from 1 to " STRINGIFY(NUM_UARTS) ". You can also use -1 if the board supports Native USB.") #endif - #if HAS_DGUS_LCD + #if HAS_DGUS_LCD || ENABLED(DGUS_LCD_UI_CREALITY_TOUCH) #define SERIAL_GET_TX_BUFFER_FREE() LCD_SERIAL.availableForWrite() #endif #endif diff --git a/Marlin/src/gcode/bedlevel/abl/G29.cpp b/Marlin/src/gcode/bedlevel/abl/G29.cpp index 729bca93a6..ab6adeb1cf 100644 --- a/Marlin/src/gcode/bedlevel/abl/G29.cpp +++ b/Marlin/src/gcode/bedlevel/abl/G29.cpp @@ -82,6 +82,12 @@ #endif #endif +#if ENABLED(PROBING_HEATERS_OFF) + #include "../../../module/temperature.h" + #include "../../../module/printcounter.h" +#endif + + #define G29_RETURN(b) return TERN_(G29_RETRY_AND_RECOVER, b) // For manual probing values persist over multiple G29 diff --git a/Marlin/src/inc/Conditionals_LCD.h b/Marlin/src/inc/Conditionals_LCD.h index c5c878a80d..0da53fd53b 100644 --- a/Marlin/src/inc/Conditionals_LCD.h +++ b/Marlin/src/inc/Conditionals_LCD.h @@ -464,7 +464,7 @@ #endif // Extensible UI serial touch screens. (See src/lcd/extui) -#if ANY(HAS_DGUS_LCD, MALYAN_LCD, TOUCH_UI_FTDI_EVE, ANYCUBIC_LCD_I3MEGA, ANYCUBIC_LCD_CHIRON, NEXTION_TFT) +#if ANY(HAS_DGUS_LCD, MALYAN_LCD, TOUCH_UI_FTDI_EVE, ANYCUBIC_LCD_I3MEGA, ANYCUBIC_LCD_CHIRON, NEXTION_TFT, DGUS_LCD_UI_CREALITY_TOUCH) #define IS_EXTUI 1 #define EXTENSIBLE_UI #endif diff --git a/Marlin/src/inc/Conditionals_post.h b/Marlin/src/inc/Conditionals_post.h index d28822cf38..59d8292b86 100644 --- a/Marlin/src/inc/Conditionals_post.h +++ b/Marlin/src/inc/Conditionals_post.h @@ -2830,7 +2830,7 @@ #define HAS_TEMPERATURE 1 #endif -#if HAS_TEMPERATURE && EITHER(HAS_LCD_MENU, DWIN_CREALITY_LCD) +#if HAS_TEMPERATURE && ANY(HAS_LCD_MENU, DWIN_CREALITY_LCD, DGUS_LCD_UI_CREALITY_TOUCH) #ifdef PREHEAT_6_LABEL #define PREHEAT_COUNT 6 #elif defined(PREHEAT_5_LABEL) diff --git a/Marlin/src/inc/SanityCheck.h b/Marlin/src/inc/SanityCheck.h index d5f763ce9d..eea5307f59 100644 --- a/Marlin/src/inc/SanityCheck.h +++ b/Marlin/src/inc/SanityCheck.h @@ -2438,7 +2438,7 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS + (DISABLED(IS_LEGACY_TFT) && ENABLED(TFT_GENERIC)) \ + (ENABLED(IS_LEGACY_TFT) && COUNT_ENABLED(TFT_320x240, TFT_320x240_SPI, TFT_480x320, TFT_480x320_SPI)) \ + COUNT_ENABLED(ANYCUBIC_LCD_I3MEGA, ANYCUBIC_LCD_CHIRON, ANYCUBIC_TFT35) \ - + COUNT_ENABLED(DGUS_LCD_UI_ORIGIN, DGUS_LCD_UI_FYSETC, DGUS_LCD_UI_HIPRECY, DGUS_LCD_UI_MKS) \ + + COUNT_ENABLED(DGUS_LCD_UI_ORIGIN, DGUS_LCD_UI_FYSETC, DGUS_LCD_UI_HIPRECY, DGUS_LCD_UI_MKS, DGUS_LCD_UI_CREALITY_TOUCH) \ + COUNT_ENABLED(ENDER2_STOCKDISPLAY, CR10_STOCKDISPLAY, DWIN_CREALITY_LCD) \ + COUNT_ENABLED(FYSETC_MINI_12864_X_X, FYSETC_MINI_12864_1_2, FYSETC_MINI_12864_2_0, FYSETC_MINI_12864_2_1, FYSETC_GENERIC_12864_1_1) \ + COUNT_ENABLED(LCD_SAINSMART_I2C_1602, LCD_SAINSMART_I2C_2004) \ diff --git a/Marlin/src/lcd/extui/dgus_creality/DGUSDisplay.cpp b/Marlin/src/lcd/extui/dgus_creality/DGUSDisplay.cpp new file mode 100644 index 0000000000..c12925feab --- /dev/null +++ b/Marlin/src/lcd/extui/dgus_creality/DGUSDisplay.cpp @@ -0,0 +1,352 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/* DGUS implementation written by coldtobi in 2019 for Marlin */ + +#include "../../../inc/MarlinConfigPre.h" + +#if ENABLED(DGUS_LCD_UI_CREALITY_TOUCH) + +#if HOTENDS > 2 + #error "More than 2 hotends not implemented on the Display UI design." +#endif + +#include "../ui_api.h" + +#include "../../../MarlinCore.h" +#include "../../../module/temperature.h" +#include "../../../module/motion.h" +#include "../../../gcode/queue.h" +#include "../../../module/planner.h" +#include "../../../sd/cardreader.h" +#include "../../../libs/duration_t.h" +#include "../../../module/printcounter.h" +#if ENABLED(POWER_LOSS_RECOVERY) + #include "../../../feature/powerloss.h" +#endif + +#include "DGUSDisplay.h" +#include "DGUSVPVariable.h" +#include "DGUSDisplayDef.h" + +// Preamble... 2 Bytes, usually 0x5A 0xA5, but configurable +constexpr uint8_t DGUS_HEADER1 = 0x5A; +constexpr uint8_t DGUS_HEADER2 = 0xA5; + +constexpr uint8_t DGUS_CMD_WRITEVAR = 0x82; +constexpr uint8_t DGUS_CMD_READVAR = 0x83; + +#if ENABLED(DEBUG_DGUSLCD) + bool dguslcd_local_debug; // = false; +#endif + +#define dgusserial LCD_SERIAL + +void DGUSDisplay::InitDisplay() { + dgusserial.begin(LCD_BAUDRATE); + + /*delay(500); // Attempt to fix possible handshake error + + ResetDisplay(); // Reset for firmware update + + delay(500); // Attempt to fix possible handshake error +*/ + if (true + #if ENABLED(POWER_LOSS_RECOVERY) + && !recovery.valid() + #endif + ) + RequestScreen( + #if ENABLED(SHOW_BOOTSCREEN) + DGUSLCD_SCREEN_BOOT + #else + DGUSLCD_SCREEN_MAIN + #endif + ); +} + +void DGUSDisplay::ResetDisplay() { + SERIAL_ECHOLN("ResetDisplay"); + const unsigned char resetCommand[] = { 0x55, 0xAA, 0x5A, 0xA5 }; + WriteVariable(0x04, resetCommand, sizeof(resetCommand)); +} + +void DGUSDisplay::ReadVariable(uint16_t adr) { + WriteHeader(adr, DGUS_CMD_READVAR, sizeof(uint8_t)); + + // Specify to read one byte + dgusserial.write(static_cast(1)); +} + +void DGUSDisplay::WriteVariable(uint16_t adr, const void* values, uint8_t valueslen, bool isstr, char fillChar) { + const char* myvalues = static_cast(values); + bool strend = !myvalues; + WriteHeader(adr, DGUS_CMD_WRITEVAR, valueslen); + while (valueslen--) { + char x; + if (!strend) x = *myvalues++; + if ((isstr && !x) || strend) { + strend = true; + x = fillChar; + } + dgusserial.write(x); + } +} + +void DGUSDisplay::WriteVariable(uint16_t adr, uint16_t value) { + value = (value & 0xffU) << 8U | (value >> 8U); + WriteVariable(adr, static_cast(&value), sizeof(uint16_t)); +} + +void DGUSDisplay::WriteVariable(uint16_t adr, int16_t value) { + union { int16_t l; char lb[2]; } endian; + char tmp[2]; + endian.l = value; + tmp[0] = endian.lb[1]; + tmp[1] = endian.lb[0]; + + WriteVariable(adr, static_cast(&tmp), sizeof(int16_t)); +} + +void DGUSDisplay::WriteVariable(uint16_t adr, uint8_t value) { + WriteVariable(adr, static_cast(&value), sizeof(uint8_t)); +} + +void DGUSDisplay::WriteVariable(uint16_t adr, int8_t value) { + WriteVariable(adr, static_cast(&value), sizeof(int8_t)); +} + +void DGUSDisplay::WriteVariable(uint16_t adr, long value) { + union { long l; char lb[4]; } endian; + char tmp[4]; + endian.l = value; + tmp[0] = endian.lb[3]; + tmp[1] = endian.lb[2]; + tmp[2] = endian.lb[1]; + tmp[3] = endian.lb[0]; + WriteVariable(adr, static_cast(&tmp), sizeof(long)); +} + +void DGUSDisplay::WriteVariable(uint16_t adr, float value) { + static_assert(sizeof(float) == 4); + + union { float l; char lb[4]; } endian; + char tmp[4]; + endian.l = value; + tmp[0] = endian.lb[3]; + tmp[1] = endian.lb[2]; + tmp[2] = endian.lb[1]; + tmp[3] = endian.lb[0]; + WriteVariable(adr, static_cast(&tmp), sizeof(float)); +} + +void DGUSDisplay::WriteVariablePGM(uint16_t adr, const void* values, uint8_t valueslen, bool isstr, char fillChar) { + const char* myvalues = static_cast(values); + bool strend = !myvalues; + WriteHeader(adr, DGUS_CMD_WRITEVAR, valueslen); + while (valueslen--) { + char x; + if (!strend) x = pgm_read_byte(myvalues++); + if ((isstr && !x) || strend) { + strend = true; + x = fillChar; + } + dgusserial.write(x); + } +} + +void DGUSDisplay::SetVariableDisplayColor(uint16_t sp, uint16_t color) { + WriteVariable(sp + 0x03, color); +} + +void DGUSDisplay::ProcessRx() { + + #if ENABLED(DGUS_SERIAL_STATS_RX_BUFFER_OVERRUNS) + if (!dgusserial.available() && dgusserial.buffer_overruns()) { + // Overrun, but reset the flag only when the buffer is empty + // We want to extract as many as valid datagrams possible... + DEBUG_ECHOPGM("OVFL"); + rx_datagram_state = DGUS_IDLE; + //dgusserial.reset_rx_overun(); + dgusserial.flush(); + } + #endif + + uint8_t receivedbyte; + while (dgusserial.available()) { + switch (rx_datagram_state) { + + case DGUS_IDLE: // Waiting for the first header byte + receivedbyte = dgusserial.read(); + //DEBUGLCDCOMM_ECHOPAIR("< ",receivedbyte); + if (DGUS_HEADER1 == receivedbyte) rx_datagram_state = DGUS_HEADER1_SEEN; + break; + + case DGUS_HEADER1_SEEN: // Waiting for the second header byte + receivedbyte = dgusserial.read(); + //DEBUGLCDCOMM_ECHOPAIR(" ", receivedbyte); + rx_datagram_state = (DGUS_HEADER2 == receivedbyte) ? DGUS_HEADER2_SEEN : DGUS_IDLE; + break; + + case DGUS_HEADER2_SEEN: // Waiting for the length byte + rx_datagram_len = dgusserial.read(); + //DEBUGLCDCOMM_ECHOPAIR(" (", rx_datagram_len, ") "); + + // Telegram min len is 3 (command and one word of payload) + rx_datagram_state = WITHIN(rx_datagram_len, 3, DGUS_RX_BUFFER_SIZE) ? DGUS_WAIT_TELEGRAM : DGUS_IDLE; + break; + + case DGUS_WAIT_TELEGRAM: // wait for complete datagram to arrive. + if (dgusserial.available() < rx_datagram_len) return; + + Initialized = true; // We've talked to it, so we defined it as initialized. + uint8_t command = dgusserial.read(); + + // DEBUGLCDCOMM_ECHOPAIR("# ", command); + + uint8_t readlen = rx_datagram_len - 1; // command is part of len. + unsigned char tmp[rx_datagram_len - 1]; + unsigned char *ptmp = tmp; + while (readlen--) { + receivedbyte = dgusserial.read(); + //DEBUGLCDCOMM_ECHOPAIR(" ", receivedbyte); + *ptmp++ = receivedbyte; + } + //DEBUGLCDCOMM_ECHOPGM(" # "); + // mostly we'll get this: 5A A5 03 82 4F 4B -- ACK on 0x82, so discard it. + if (command == DGUS_CMD_WRITEVAR && 'O' == tmp[0] && 'K' == tmp[1]) { + //DEBUG_ECHOLNPGM(">"); + rx_datagram_state = DGUS_IDLE; + break; + } + + /* AutoUpload, (and answer to) Command 0x83 : + | tmp[0 1 2 3 4 ... ] + | Example 5A A5 06 83 20 01 01 78 01 …… + | / / | | \ / | \ \ + | Header | | | | \_____\_ DATA (Words!) + | DatagramLen / VPAdr | + | Command DataLen (in Words) */ + if (command == DGUS_CMD_READVAR) { + const uint16_t vp = tmp[0] << 8 | tmp[1]; + + //const uint8_t dlen = tmp[2] << 1; // Convert to Bytes. (Display works with words) + //DEBUG_ECHOPAIR(" vp=", vp, " dlen=", dlen); + DGUS_VP_Variable ramcopy; + DEBUG_ECHOLNPAIR("VP received: ", vp , " - val ", tmp[3]); + if (populate_VPVar(vp, &ramcopy)) { + if (ramcopy.set_by_display_handler) + ramcopy.set_by_display_handler(ramcopy, &tmp[3]); + else + DEBUG_ECHOLNPGM(" VPVar found, no handler."); + } + else + DEBUG_ECHOLNPAIR(" VPVar not found:", vp); + + rx_datagram_state = DGUS_IDLE; + break; + } + + // discard anything else + rx_datagram_state = DGUS_IDLE; + } + } +} + +size_t DGUSDisplay::GetFreeTxBuffer() { return SERIAL_GET_TX_BUFFER_FREE(); } + +void DGUSDisplay::WriteHeader(uint16_t adr, uint8_t cmd, uint8_t payloadlen) { + dgusserial.write(DGUS_HEADER1); + dgusserial.write(DGUS_HEADER2); + dgusserial.write(payloadlen + 3); + dgusserial.write(cmd); + dgusserial.write(adr >> 8); + dgusserial.write(adr & 0xFF); +} + +void DGUSDisplay::WritePGM(const char str[], uint8_t len) { + while (len--) dgusserial.write(pgm_read_byte(str++)); +} + +void DGUSDisplay::loop() { + // protect against recursion… ProcessRx() may indirectly call idle() when injecting gcode commands. + if (!no_reentrance) { + no_reentrance = true; + ProcessRx(); + no_reentrance = false; + } +} + +void DGUSDisplay::RequestScreen(DGUSLCD_Screens screen) { + displayRequest = screen; + + DEBUG_ECHOLNPAIR("GotoScreen ", screen); + const unsigned char gotoscreen[] = { 0x5A, 0x01, (unsigned char) (screen >> 8U), (unsigned char) (screen & 0xFFU) }; + WriteVariable(0x84, gotoscreen, sizeof(gotoscreen)); +} + +void DGUSDisplay::SetTouchScreenConfiguration(bool enable_standby, bool enable_sound, uint8_t standby_brightness, uint8_t brightness, uint16_t standbyTimeSeconds) { + // Main configuration (System_Config) + unsigned char cfg_bits = 0x0; + cfg_bits |= 1UL << 5; // 5: load 22 touch file + cfg_bits |= 1UL << 4; // 4: auto-upload should always be enabled + if (enable_sound) cfg_bits |= 1UL << 3; // 3: audio + if (enable_standby) cfg_bits |= 1UL << 2; // 2: backlight on standby + cfg_bits |= 1UL << 1; // 1 & 0: 270 degrees orientation of display + cfg_bits |= 1UL << 0; + + DEBUG_ECHOLNPAIR("Update touch screen config - standby ", enable_standby); + DEBUG_ECHOLNPAIR("Update touch screen config - sound ", enable_sound); + + const unsigned char config_set[] = { 0x5A, 0x00, (unsigned char) (cfg_bits >> 8U), (unsigned char) (cfg_bits & 0xFFU) }; + WriteVariable(0x80 /*System_Config*/, config_set, sizeof(config_set)); + + // Standby brightness (LED_Config) + uint16_t dwinStandbyTimeSeconds = 100 * standbyTimeSeconds; /* milliseconds, but divided by 10 (not 5 like the docs say) */ + const unsigned char brightness_set[] = { + brightness /*% active*/, + standby_brightness /*% standby*/, + static_cast(dwinStandbyTimeSeconds >> 8), + static_cast(dwinStandbyTimeSeconds) + }; + WriteVariable(0x82 /*LED_Config*/, brightness_set, sizeof(brightness_set)); +} + +rx_datagram_state_t DGUSDisplay::rx_datagram_state = DGUS_IDLE; +uint8_t DGUSDisplay::rx_datagram_len = 0; +bool DGUSDisplay::Initialized = false; +bool DGUSDisplay::no_reentrance = false; +DGUSLCD_Screens DGUSDisplay::displayRequest = DGUSLCD_SCREEN_BOOT; + +// A SW memory barrier, to ensure GCC does not overoptimize loops +#define sw_barrier() asm volatile("": : :"memory"); + +bool populate_VPVar(const uint16_t VP, DGUS_VP_Variable * const ramcopy) { + //DEBUG_ECHOLNPAIR("populate_VPVar ", VP); + const DGUS_VP_Variable *pvp = DGUSLCD_FindVPVar(VP); + // DEBUG_ECHOLNPAIR(" pvp ", (uint16_t )pvp); + if (!pvp) return false; + memcpy_P(ramcopy, pvp, sizeof(DGUS_VP_Variable)); + return true; +} + +#endif // HAS_DGUS_LCD diff --git a/Marlin/src/lcd/extui/dgus_creality/DGUSDisplay.h b/Marlin/src/lcd/extui/dgus_creality/DGUSDisplay.h new file mode 100644 index 0000000000..849b685fb6 --- /dev/null +++ b/Marlin/src/lcd/extui/dgus_creality/DGUSDisplay.h @@ -0,0 +1,136 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +#pragma once + +//#define DEBUG_DGUSLCD +//#define DEBUG_DGUSLCD_OUTGOING_COMM + + +/* Creality DGUS implementation written by Sebastiaan Dammann in 2020 for Marlin */ + +#include "../../../inc/MarlinConfigPre.h" + +#include "../../../MarlinCore.h" +#if HAS_BED_PROBE + #include "../../../module/probe.h" +#endif + +#include "DGUSVPVariable.h" + +enum DGUSLCD_Screens : uint8_t; + +#ifndef DEBUG_OUT +#define DEBUG_OUT ENABLED(DEBUG_DGUSLCD) +#endif + +#include "../../../core/debug_out.h" + + +typedef enum : uint8_t { + DGUS_IDLE, //< waiting for DGUS_HEADER1. + DGUS_HEADER1_SEEN, //< DGUS_HEADER1 received + DGUS_HEADER2_SEEN, //< DGUS_HEADER2 received + DGUS_WAIT_TELEGRAM, //< LEN received, Waiting for to receive all bytes. +} rx_datagram_state_t; + +typedef void (*UPDATE_CURRENT_SCREEN_CALLBACK)(DGUSLCD_Screens screen); + +// Low-Level access to the display. +class DGUSDisplay { +public: + + DGUSDisplay() = default; + + static void InitDisplay(); + static void ResetDisplay(); + + // Variable access. + static void WriteVariable(uint16_t adr, const void* values, uint8_t valueslen, bool isstr=false, char fillChar = ' '); + static void WriteVariablePGM(uint16_t adr, const void* values, uint8_t valueslen, bool isstr=false, char fillChar = ' '); + static void WriteVariable(uint16_t adr, int16_t value); + static void WriteVariable(uint16_t adr, uint16_t value); + static void WriteVariable(uint16_t adr, uint8_t value); + static void WriteVariable(uint16_t adr, int8_t value); + static void WriteVariable(uint16_t adr, long value); + static void WriteVariable(uint16_t adr, float value); + + static void SetVariableDisplayColor(uint16_t sp, uint16_t color); + + static void ReadVariable(uint16_t adr); + + // Utility functions for bridging ui_api and dgus + template + static void SetVariable(DGUS_VP_Variable &var) { + WriteVariable(var.VP, (WireType)Getter(selector)); + } + + template + static void GetVariable(DGUS_VP_Variable &var, void *val_ptr) { + uint16_t newvalue = swap16(*(uint16_t*)val_ptr); + Setter(newvalue, selector); + } + + // Force display into another screen. + // (And trigger update of containing VPs) + // (to implement a pop up message, which may not be nested) + static void RequestScreen(DGUSLCD_Screens screen); + + static void SetTouchScreenConfiguration(bool enable_standby, bool enable_sound, uint8_t standby_brightness, uint8_t active_brightness, uint16_t standbyTimeSeconds); + + // Periodic tasks, eg. Rx-Queue handling. + static void loop(); + +public: + // Helper for users of this class to estimate if an interaction would be blocking. + static size_t GetFreeTxBuffer(); + + // Checks two things: Can we confirm the presence of the display and has we initiliazed it. + // (both boils down that the display answered to our chatting) + static inline bool isInitialized() { return Initialized; } + +private: + static void WriteHeader(uint16_t adr, uint8_t cmd, uint8_t payloadlen); + static void WritePGM(const char str[], uint8_t len); + static void ProcessRx(); + + static inline uint16_t swap16(const uint16_t value) { return (value & 0xffU) << 8U | (value >> 8U); } + static rx_datagram_state_t rx_datagram_state; + static uint8_t rx_datagram_len; + static bool Initialized, no_reentrance; + + static DGUSLCD_Screens displayRequest; +}; + +#define GET_VARIABLE(f, t, V...) (&DGUSDisplay::GetVariable) +#define SET_VARIABLE(f, t, V...) (&DGUSDisplay::SetVariable) + +extern DGUSDisplay dgusdisplay; + +// compile-time x^y +template +constexpr T cpow(const T x, const int y) { return y == 0 ? 1.0 : x * cpow(x, y - 1); } + +/// Find the flash address of a DGUS_VP_Variable for the VP. +const DGUS_VP_Variable* DGUSLCD_FindVPVar(const uint16_t vp); + +/// Helper to populate a DGUS_VP_Variable for a given VP. Return false if not found. +bool populate_VPVar(const uint16_t VP, DGUS_VP_Variable * const ramcopy); diff --git a/Marlin/src/lcd/extui/dgus_creality/DGUSDisplayDef.h b/Marlin/src/lcd/extui/dgus_creality/DGUSDisplayDef.h new file mode 100644 index 0000000000..c9f5b6d1da --- /dev/null +++ b/Marlin/src/lcd/extui/dgus_creality/DGUSDisplayDef.h @@ -0,0 +1,53 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +#pragma once + +/* DGUS implementation written by Sebastiaan Dammann in 2020 for Marlin */ + +#include "DGUSVPVariable.h" + +#include + +// This file defines the interaction between Marlin and the display firmware. + +// information on which screen which VP is displayed +// As this is a sparse table, two arrays are needed: +// one to list the VPs of one screen and one to map screens to the lists. +// (Strictly this would not be necessary, but allows to only send data the display needs and reducing load on Marlin) +struct VPMapping { + const uint8_t screen; + const uint16_t *VPList; // The list is null-terminated. +}; + +extern const struct VPMapping VPMap[]; + +// List of VPs handled by Marlin / The Display. +extern const struct DGUS_VP_Variable ListOfVP[]; + +#define DWIN_DEFAULT_FILLER_CHAR ' ' +#define DWIN_SCROLLER_FILLER_CHAR 0x0 + +#include "../../../inc/MarlinConfig.h" + +#if ENABLED(DGUS_LCD_UI_CREALITY_TOUCH) + #include "creality_touch/DGUSDisplayDef.h" +#endif diff --git a/Marlin/src/lcd/extui/dgus_creality/DGUSScreenHandler.cpp b/Marlin/src/lcd/extui/dgus_creality/DGUSScreenHandler.cpp new file mode 100644 index 0000000000..0af839bf52 --- /dev/null +++ b/Marlin/src/lcd/extui/dgus_creality/DGUSScreenHandler.cpp @@ -0,0 +1,1738 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "../../../inc/MarlinConfigPre.h" + +#define DEBUG_ECHOLNPAIR DEBUG_ECHOLNPAIR + +#if ENABLED(DGUS_LCD_UI_CREALITY_TOUCH) + +#include "DGUSScreenHandler.h" +#include "DGUSDisplay.h" +#include "DGUSVPVariable.h" +#include "DGUSDisplayDef.h" + +#include "../ui_api.h" +#include "../../../MarlinCore.h" +#include "../../../module/temperature.h" +#include "../../../module/motion.h" +#include "../../../module/settings.h" +#include "../../../gcode/queue.h" +#include "../../../module/planner.h" +#include "../../../sd/cardreader.h" +#include "../../../libs/duration_t.h" +#include "../../../module/printcounter.h" +#include "../../../feature/caselight.h" + +#if ENABLED(POWER_LOSS_RECOVERY) + #include "../../../feature/powerloss.h" +#endif + +#if HAS_COLOR_LEDS + #include "../../../feature/leds/leds.h" +#endif + +uint16_t DGUSScreenHandler::ConfirmVP; + +#if ENABLED(SDSUPPORT) + int16_t DGUSScreenHandler::top_file = 0; + int16_t DGUSScreenHandler::file_to_print = 0; + static ExtUI::FileList filelist; +#endif + +// Storage initialization +constexpr uint8_t dwin_settings_version = 6; // Increase when properties are added or removed +creality_dwin_settings_t DGUSScreenHandler::Settings = {.settings_size = sizeof(creality_dwin_settings_t)}; +DGUSLCD_Screens DGUSScreenHandler::current_screen; +DGUSLCD_Screens DGUSScreenHandler::past_screens[NUM_PAST_SCREENS] = {DGUSLCD_SCREEN_MAIN}; +uint8_t DGUSScreenHandler::update_ptr; +uint16_t DGUSScreenHandler::skipVP; +bool DGUSScreenHandler::ScreenComplete; +bool DGUSScreenHandler::SaveSettingsRequested; +bool DGUSScreenHandler::HasSynchronousOperation; +bool DGUSScreenHandler::HasScreenVersionMismatch; +uint8_t DGUSScreenHandler::MeshLevelIndex = -1; +uint8_t DGUSScreenHandler::MeshLevelIconIndex = -1; +bool DGUSScreenHandler::fwretract_available = TERN(FWRETRACT, true, false); +bool DGUSScreenHandler::HasRGBSettings = TERN(HAS_COLOR_LEDS, true, false); + +static_assert(GRID_MAX_POINTS_X == GRID_MAX_POINTS_Y, "Assuming bed leveling points is square"); + +constexpr uint16_t SkipMeshPoint = GRID_MAX_POINTS_X > MESH_LEVEL_EDGE_MAX_POINTS ? ((GRID_MAX_POINTS_X - 1) / (GRID_MAX_POINTS_X - MESH_LEVEL_EDGE_MAX_POINTS)) : 1; + +void DGUSScreenHandler::sendinfoscreen(const char* line1, const char* line2, const char* line3, const char* line4, bool l1inflash, bool l2inflash, bool l3inflash, bool l4inflash) { + DGUS_VP_Variable ramcopy; + if (populate_VPVar(VP_MSGSTR1, &ramcopy)) { + ramcopy.memadr = (void*) line1; + l1inflash ? DGUSScreenHandler::DGUSLCD_SendStringToDisplayPGM(ramcopy) : DGUSScreenHandler::DGUSLCD_SendStringToDisplay(ramcopy); + } + if (populate_VPVar(VP_MSGSTR2, &ramcopy)) { + ramcopy.memadr = (void*) line2; + l2inflash ? DGUSScreenHandler::DGUSLCD_SendStringToDisplayPGM(ramcopy) : DGUSScreenHandler::DGUSLCD_SendStringToDisplay(ramcopy); + } + if (populate_VPVar(VP_MSGSTR3, &ramcopy)) { + ramcopy.memadr = (void*) line3; + l3inflash ? DGUSScreenHandler::DGUSLCD_SendStringToDisplayPGM(ramcopy) : DGUSScreenHandler::DGUSLCD_SendStringToDisplay(ramcopy); + } + //if (populate_VPVar(VP_MSGSTR4, &ramcopy)) { + // ramcopy.memadr = (void*) line4; + // l4inflash ? DGUSScreenHandler::DGUSLCD_SendStringToDisplayPGM(ramcopy) : DGUSScreenHandler::DGUSLCD_SendStringToDisplay(ramcopy); + //} +} + + +void DGUSScreenHandler::Init() { + dgusdisplay.InitDisplay(); +} + +void DGUSScreenHandler::RequestSaveSettings() { + SaveSettingsRequested = true; +} + +void DGUSScreenHandler::DefaultSettings() { + Settings.settings_size = sizeof(creality_dwin_settings_t); + Settings.settings_version = dwin_settings_version; + + Settings.led_state = false; + + Settings.display_standby = true; + Settings.display_sound = true; + + Settings.standby_screen_brightness = 10; + Settings.screen_brightness = 100; + Settings.standby_time_seconds = 60; + + #if ENABLED(LED_COLOR_PRESETS) + Settings.LastLEDColor = LEDLights::defaultLEDColor; + #endif +} + +void DGUSScreenHandler::LoadSettings(const char* buff) { + static_assert( + ExtUI::eeprom_data_size >= sizeof(creality_dwin_settings_t), + "Insufficient space in EEPROM for UI parameters" + ); + + creality_dwin_settings_t eepromSettings; + memcpy(&eepromSettings, buff, sizeof(creality_dwin_settings_t)); + + // If size is not the same, discard settings + if (eepromSettings.settings_size != sizeof(creality_dwin_settings_t)) { + SERIAL_ECHOLNPGM("Discarding DWIN LCD setting from EEPROM - size incorrect"); + + ScreenHandler.DefaultSettings(); + return; + } + + if (eepromSettings.settings_version != dwin_settings_version) { + SERIAL_ECHOLNPGM("Discarding DWIN LCD setting from EEPROM - settings version incorrect"); + + ScreenHandler.DefaultSettings(); + return; + } + + // Copy into final location + SERIAL_ECHOLNPGM("Loading DWIN LCD setting from EEPROM"); + memcpy(&Settings, &eepromSettings, sizeof(creality_dwin_settings_t)); + + // Apply settings + caselight.on = Settings.led_state; + caselight.update(Settings.led_state); + + #if HAS_COLOR_LEDS_PREFERENCES + leds.set_color(Settings.LastLEDColor); + #endif + + ScreenHandler.SetTouchScreenConfiguration(); +} + +void DGUSScreenHandler::StoreSettings(char* buff) { + static_assert( + ExtUI::eeprom_data_size >= sizeof(creality_dwin_settings_t), + "Insufficient space in EEPROM for UI parameters" + ); + + // Update settings from Marlin state, if necessary + Settings.led_state = caselight.on; + + #if HAS_COLOR_LEDS_PREFERENCES + Settings.LastLEDColor = leds.color; + #endif + + // Write to buffer + SERIAL_ECHOLNPGM("Saving DWIN LCD setting from EEPROM"); + memcpy(buff, &Settings, sizeof(creality_dwin_settings_t)); +} + +void DGUSScreenHandler::SetTouchScreenConfiguration() { + LIMIT(Settings.screen_brightness, 10, 100); // Prevent a possible all-dark screen + LIMIT(Settings.standby_time_seconds, 10, 655); // Prevent a possible all-dark screen for standby, yet also don't go higher than the DWIN limitation + + dgusdisplay.SetTouchScreenConfiguration(Settings.display_standby, Settings.display_sound, Settings.standby_screen_brightness, Settings.screen_brightness, Settings.standby_time_seconds); +} + +void DGUSScreenHandler::KillScreenCalled() { + // If killed, always fully wake up + dgusdisplay.SetTouchScreenConfiguration(false, true, 100, 100, 100 /*Doesn't really matter*/); + + // Hey! Something is going on! + Buzzer(1000 /*ignored*/, 880); +} + +void DGUSScreenHandler::OnPowerlossResume() { + GotoScreen(DGUSLCD_SCREEN_POWER_LOSS); + + // Send print filename + dgusdisplay.WriteVariable(VP_SD_Print_Filename, PrintJobRecovery::info.sd_filename, VP_SD_FileName_LEN, true); +} + +void DGUSScreenHandler::HandleUserConfirmationPopUp(uint16_t VP, const char* line1, const char* line2, const char* line3, const char* line4, bool l1, bool l2, bool l3, bool l4) { + if (current_screen == DGUSLCD_SCREEN_CONFIRM) { + // Already showing a pop up, so we need to cancel that first. + PopToOldScreen(); + } + + ConfirmVP = VP; + sendinfoscreen(line1, line2, line3, line4, l1, l2, l3, l4); + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_CONFIRM); +} + +void DGUSScreenHandler::HandleDevelopmentTestButton(DGUS_VP_Variable &var, void *val_ptr) { + // Handle the button press only after 3 taps, so that a regular user won't tap it by accident + static uint8_t tap_count = 0; + + if (++tap_count <= 3) return; + + // Get button value + uint16_t button_value = swap16(*static_cast(val_ptr)); + + // Act on it + switch (button_value) { + case VP_DEVELOPMENT_HELPER_BUTTON_ACTION_FIRMWARE_UPDATE: + ExtUI::injectCommands_P(PSTR("M997")); + break; + + case VP_DEVELOPMENT_HELPER_BUTTON_ACTION_TO_MAIN_MENU: + setstatusmessagePGM(PSTR("Dev action: main menu")); + GotoScreen(DGUSLCD_SCREEN_MAIN, false); + break; + + case VP_DEVELOPMENT_HELPER_BUTTON_ACTION_RESET_DISPLAY: + setstatusmessagePGM(PSTR("Dev action: reset DGUS")); + dgusdisplay.ResetDisplay(); + break; + + default: + setstatusmessagePGM(PSTR("Dev action: unknown")); + break; + } +} + +void setStatusMessage(const char *msg, bool forceScrolling) { + const bool needs_scrolling = forceScrolling || strlen(msg) > M117_STATIC_DISPLAY_LEN; + + DGUS_VP_Variable ramcopy; + + // Update static message to either NULL or the value + if (populate_VPVar(VP_M117_STATIC, &ramcopy)) { + ramcopy.memadr = (void*) (needs_scrolling ? NUL_STR : msg); + DGUSScreenHandler::DGUSLCD_SendStringToDisplay(ramcopy); + } + + // Update scrolling message to either NULL or the value + if (populate_VPVar(VP_M117, &ramcopy)) { + ramcopy.memadr = (void*) (needs_scrolling ? msg : NUL_STR); + DGUSScreenHandler::DGUSLCD_SendScrollingStringToDisplay(ramcopy); + } +} + +void DGUSScreenHandler::setstatusmessage(const char *msg) { + setStatusMessage(msg, false); +} + +void DGUSScreenHandler::setstatusmessagePGM(PGM_P const msg) { + const bool needs_scrolling = strlen_P(msg) > M117_STATIC_DISPLAY_LEN; + + DGUS_VP_Variable ramcopy; + + // Update static message to either NULL or the value + if (populate_VPVar(VP_M117_STATIC, &ramcopy)) { + ramcopy.memadr = (void*) (needs_scrolling ? nullptr : msg); + DGUSLCD_SendStringToDisplayPGM(ramcopy); + } + + // Update scrolling message to either NULL or the value + if (populate_VPVar(VP_M117, &ramcopy)) { + ramcopy.memadr = (void*) (needs_scrolling ? msg : nullptr); + DGUSLCD_SendScrollingStringToDisplayPGM(ramcopy); + } +} + +// Send an 8 bit or 16 bit value to the display. +void DGUSScreenHandler::DGUSLCD_SendWordValueToDisplay(DGUS_VP_Variable &var) { + if (var.memadr) { + //DEBUG_ECHOPAIR(" DGUS_LCD_SendWordValueToDisplay ", var.VP); + //DEBUG_ECHOLNPAIR(" data ", *(uint16_t *)var.memadr); + if (var.size > 1) + dgusdisplay.WriteVariable(var.VP, *(int16_t*)var.memadr); + else + dgusdisplay.WriteVariable(var.VP, *(int8_t*)var.memadr); + } +} + +// Send an uint8_t between 0 and 255 to the display, but scale to a percentage (0..100) +void DGUSScreenHandler::DGUSLCD_SendPercentageToDisplay(DGUS_VP_Variable &var) { + if (var.memadr) { + //DEBUG_ECHOPAIR(" DGUS_LCD_SendWordValueToDisplay ", var.VP); + //DEBUG_ECHOLNPAIR(" data ", *(uint16_t *)var.memadr); + uint16_t tmp = *(uint8_t *) var.memadr +1 ; // +1 -> avoid rounding issues for the display. + tmp = map(tmp, 0, 255, 0, 100); + dgusdisplay.WriteVariable(var.VP, tmp); + } +} + +// Send the current print progress to the display. +void DGUSScreenHandler::DGUSLCD_SendPrintProgressToDisplay(DGUS_VP_Variable &var) { + uint16_t tmp = ExtUI::getProgress_percent(); + dgusdisplay.WriteVariable(var.VP, tmp); +} + +// Send the current print time to the display. +// It is using a hex display for that: It expects BSD coded data in the format xxyyzz +void DGUSScreenHandler::DGUSLCD_SendPrintTimeToDisplay(DGUS_VP_Variable &var) { + // Clear if changed and we shouldn't display + static bool last_shouldDisplay = true; + bool shouldDisplay = ui.remaining_time == 0; + if (last_shouldDisplay != shouldDisplay) { + if (!shouldDisplay) { + dgusdisplay.WriteVariable(VP_PrintTime, nullptr, var.size, true); + } + } + + last_shouldDisplay = shouldDisplay; + if (!shouldDisplay) return; + + // Write if changed + duration_t elapsed = print_job_timer.duration(); + + static uint32_t last_elapsed; + if (elapsed == last_elapsed) { + return; + } + + char buf[32]; + elapsed.toString(buf); + dgusdisplay.WriteVariable(VP_PrintTime, buf, var.size, true); + + last_elapsed = elapsed.second(); +} + +void DGUSScreenHandler::DGUSLCD_SendPrintTimeWithRemainingToDisplay(DGUS_VP_Variable &var) { + // Clear if changed and we shouldn't display + static bool last_shouldDisplay = true; + bool shouldDisplay = ui.remaining_time != 0; + if (last_shouldDisplay != shouldDisplay) { + if (!shouldDisplay) { + dgusdisplay.WriteVariable(VP_PrintTimeWithRemainingVisible, nullptr, var.size, true); + } + } + + last_shouldDisplay = shouldDisplay; + if (!shouldDisplay) return; + + // Write if changed + duration_t elapsed = print_job_timer.duration(); + + static uint32_t last_elapsed; + if (elapsed == last_elapsed) { + return; + } + + char buf[32]; + elapsed.toString(buf); + dgusdisplay.WriteVariable(VP_PrintTimeWithRemainingVisible, buf, var.size, true); + + last_elapsed = elapsed.second(); +} + +// Send the current print time to the display. +// It is using a hex display for that: It expects BSD coded data in the format xxyyzz +void DGUSScreenHandler::DGUSLCD_SendPrintTimeRemainingToDisplay(DGUS_VP_Variable &var) { +#if ENABLED(SHOW_REMAINING_TIME) + static uint32_t lastRemainingTime = -1; + uint32_t remaining_time = ui.remaining_time; + if (lastRemainingTime == remaining_time) { + return; + } + + bool has_remaining_time = remaining_time != 0; + + // Update display of SPs (toggle between large and small print timer) + if (has_remaining_time) { + dgusdisplay.WriteVariable(VP_HideRemainingTime_Ico, ICON_REMAINING_VISIBLE); + } else { + dgusdisplay.WriteVariable(VP_HideRemainingTime_Ico, ICON_REMAINING_HIDDEN); + } + + if (!has_remaining_time) { + // Clear remaining time + dgusdisplay.WriteVariable(VP_PrintTimeRemaining, nullptr, var.size, true); + lastRemainingTime = remaining_time; + return; + } + + // Send a progress update to the display if anything is different. + // This allows custom M117 commands to override the displayed string if desired. + + // Remaining time is seconds. When Marlin accepts a M73 R[minutes] command, it multiplies + // the R value by 60 to make a number of seconds. But... Marlin can also predict time + // if the M73 R command has not been used. So we should be good either way. + duration_t remaining(remaining_time); + constexpr size_t buffer_size = 21; + + // Write the duration + char buffer[buffer_size] = {0}; + remaining.toString(buffer); + + dgusdisplay.WriteVariable(VP_PrintTimeRemaining, buffer, var.size, true); + + lastRemainingTime = remaining_time; +#endif +} + +void DGUSScreenHandler::DGUSLCD_SendAboutFirmwareWebsite(DGUS_VP_Variable &var) { + const char* websiteUrl = PSTR(WEBSITE_URL); + + dgusdisplay.WriteVariablePGM(var.VP, websiteUrl, var.size, true); +} + +void DGUSScreenHandler::DGUSLCD_SendAboutFirmwareVersion(DGUS_VP_Variable &var) { + const char* fwVersion = PSTR(SHORT_BUILD_VERSION); + + dgusdisplay.WriteVariablePGM(var.VP, fwVersion, var.size, true); +} + +void DGUSScreenHandler::DGUSLCD_SendAboutPrintSize(DGUS_VP_Variable &var) { + char PRINTSIZE[VP_PRINTER_BEDSIZE_LEN] = {0}; + sprintf(PRINTSIZE,"%dx%dx%d", X_BED_SIZE, Y_BED_SIZE, Z_MAX_POS); + + dgusdisplay.WriteVariablePGM(var.VP, &PRINTSIZE, sizeof(PRINTSIZE), true); +} + + +// Send an uint8_t between 0 and 100 to a variable scale to 0..255 +void DGUSScreenHandler::DGUSLCD_PercentageToUint8(DGUS_VP_Variable &var, void *val_ptr) { + if (var.memadr) { + uint16_t value = swap16(*(uint16_t*)val_ptr); + *(uint8_t*)var.memadr = map(constrain(value, 0, 100), 0, 100, 0, 255); + } +} + +// Sends a (RAM located) string to the DGUS Display +// (Note: The DGUS Display does not clear after the \0, you have to +// overwrite the remainings with spaces.// var.size has the display buffer size! +void DGUSScreenHandler::DGUSLCD_SendStringToDisplay(DGUS_VP_Variable &var) { + char *tmp = (char*) var.memadr; + dgusdisplay.WriteVariable(var.VP, tmp, var.size, true, DWIN_DEFAULT_FILLER_CHAR); +} + +// Sends a (RAM located) string to the DGUS Display +// (Note: The DGUS Display does not clear after the \0, you have to +// overwrite the remainings with spaces.// var.size has the display buffer size! +void DGUSScreenHandler::DGUSLCD_SendScrollingStringToDisplay(DGUS_VP_Variable &var) { + char *tmp = (char*) var.memadr; + dgusdisplay.WriteVariable(var.VP, tmp, var.size, true, DWIN_SCROLLER_FILLER_CHAR); +} + +// Sends a (flash located) string to the DGUS Display +// (Note: The DGUS Display does not clear after the \0, you have to +// overwrite the remainings with spaces.// var.size has the display buffer size! +void DGUSScreenHandler::DGUSLCD_SendStringToDisplayPGM(DGUS_VP_Variable &var) { + char *tmp = (char*) var.memadr; + dgusdisplay.WriteVariablePGM(var.VP, tmp, var.size, true, DWIN_DEFAULT_FILLER_CHAR); +} + + +// Sends a (flash located) string to the DGUS Display +// (Note: The DGUS Display does not clear after the \0, you have to +// overwrite the remainings with spaces.// var.size has the display buffer size! +void DGUSScreenHandler::DGUSLCD_SendScrollingStringToDisplayPGM(DGUS_VP_Variable &var) { + char *tmp = (char*) var.memadr; + dgusdisplay.WriteVariablePGM(var.VP, tmp, var.size, true, DWIN_SCROLLER_FILLER_CHAR); +} + +#if HAS_PID_HEATING + void DGUSScreenHandler::DGUSLCD_SendTemperaturePID(DGUS_VP_Variable &var) { + float value = *(float *)var.memadr; + float valuesend = 0; + switch (var.VP) { + default: return; + #if HOTENDS >= 1 + case VP_E0_PID_P: valuesend = value; break; + case VP_E0_PID_I: valuesend = unscalePID_i(value); break; + case VP_E0_PID_D: valuesend = unscalePID_d(value); break; + #endif + #if HAS_HEATED_BED + case VP_BED_PID_P: valuesend = value; break; + case VP_BED_PID_I: valuesend = unscalePID_i(value); break; + case VP_BED_PID_D: valuesend = unscalePID_d(value); break; + #endif + } + + valuesend *= cpow(10, 1); + union { int16_t i; char lb[2]; } endian; + + char tmp[2]; + endian.i = valuesend; + tmp[0] = endian.lb[1]; + tmp[1] = endian.lb[0]; + dgusdisplay.WriteVariable(var.VP, tmp, 2); + } +#endif + +// Send fan status value to the display. +#if HAS_FAN + void DGUSScreenHandler::DGUSLCD_SendFanStatusToDisplay(DGUS_VP_Variable &var) { + if (var.memadr) { + DEBUG_ECHOPAIR(" DGUSLCD_SendFanStatusToDisplay ", var.VP); + DEBUG_ECHOLNPAIR(" data ", *(uint8_t *)var.memadr); + uint16_t data_to_send = ICON_TOGGLE_OFF; + if (*(uint8_t *) var.memadr) data_to_send = ICON_TOGGLE_ON; + dgusdisplay.WriteVariable(var.VP, data_to_send); + } + } + + void DGUSScreenHandler::DGUSLCD_SendFanSpeedToDisplay(DGUS_VP_Variable &var) { + if (var.memadr) { + int16_t data_to_send = static_cast(round(ExtUI::getTargetFan_percent(ExtUI::fan_t::FAN0))); + dgusdisplay.WriteVariable(var.VP, data_to_send); + } + } +#endif + +// Send heater status value to the display. +void DGUSScreenHandler::DGUSLCD_SendHeaterStatusToDisplay(DGUS_VP_Variable &var) { + if (var.memadr) { + DEBUG_ECHOPAIR(" DGUSLCD_SendHeaterStatusToDisplay ", var.VP); + DEBUG_ECHOLNPAIR(" data ", *(int16_t *)var.memadr); + uint16_t data_to_send = 0; + if (*(int16_t *) var.memadr) data_to_send = 1; + dgusdisplay.WriteVariable(var.VP, data_to_send); + } +} + +#if ENABLED(DGUS_UI_WAITING) + void DGUSScreenHandler::DGUSLCD_SendWaitingStatusToDisplay(DGUS_VP_Variable &var) { + // In FYSETC UI design there are 10 statuses to loop + static uint16_t period = 0; + static uint16_t index = 0; + //DEBUG_ECHOPAIR(" DGUSLCD_SendWaitingStatusToDisplay ", var.VP); + //DEBUG_ECHOLNPAIR(" data ", swap16(index)); + if (period++ > DGUS_UI_WAITING_STATUS_PERIOD) { + dgusdisplay.WriteVariable(var.VP, index); + //DEBUG_ECHOLNPAIR(" data ", swap16(index)); + if (++index >= DGUS_UI_WAITING_STATUS) index = 0; + period = 0; + } + } +#endif + +#if ENABLED(SDSUPPORT) + + void DGUSScreenHandler::ScreenChangeHookIfSD(DGUS_VP_Variable &var, void *val_ptr) { + // default action executed when there is a SD card, but not printing + if (ExtUI::isMediaInserted() && !ExtUI::isPrintingFromMedia()) { + ScreenChangeHook(var, val_ptr); + GotoScreen(current_screen); + return; + } + + // if we are printing, we jump to two screens after the requested one. + // This should host e.g a print pause / print abort / print resume dialog. + // This concept allows to recycle this hook for other file + if (ExtUI::isPrintingFromMedia() && !card.flag.abort_sd_printing) { + GotoScreen(DGUSLCD_SCREEN_SDPRINTMANIPULATION); + return; + } + + // Don't let the user in the dark why there is no reaction. + if (!ExtUI::isMediaInserted()) { + setstatusmessagePGM(GET_TEXT(MSG_NO_MEDIA)); + return; + } + if (card.flag.abort_sd_printing) { + setstatusmessagePGM(GET_TEXT(MSG_MEDIA_ABORTING)); + return; + } + } + + void DGUSScreenHandler::DGUSLCD_SD_ScrollFilelist(DGUS_VP_Variable& var, void *val_ptr) { + auto old_top = top_file; + const int16_t scroll = (int16_t)swap16(*(uint16_t*)val_ptr); + if (scroll) { + top_file += scroll; + DEBUG_ECHOPAIR("new topfile calculated:", top_file); + if (top_file < 0) { + top_file = 0; + DEBUG_ECHOLNPGM("Top of filelist reached"); + } + else { + int16_t max_top = filelist.count() - DGUS_SD_FILESPERSCREEN; + NOLESS(max_top, 0); + NOMORE(top_file, max_top); + } + DEBUG_ECHOPAIR("new topfile adjusted:", top_file); + } + else { + if (!filelist.isAtRootDir()) { + filelist.upDir(); + top_file = 0; + ForceCompleteUpdate(); + } else { + // Navigate back to home + GotoScreen(DGUSLCD_SCREEN_MAIN); + } + } + + if (old_top != top_file) ForceCompleteUpdate(); + } + + void DGUSScreenHandler::DGUSLCD_SD_FileSelected(DGUS_VP_Variable &var, void *val_ptr) { + uint16_t touched_nr = (int16_t)swap16(*(uint16_t*)val_ptr) + top_file; + + DEBUG_ECHOLNPAIR("Selected file: ", touched_nr); + + if (touched_nr > filelist.count()) return; + if (!filelist.seek(touched_nr)) return; + if (filelist.isDir()) { + filelist.changeDir(filelist.shortFilename()); + top_file = 0; + ForceCompleteUpdate(); + return; + } + + // Send print filename + dgusdisplay.WriteVariable(VP_SD_Print_Filename, filelist.filename(), VP_SD_FileName_LEN, true); + + // Setup Confirmation screen + file_to_print = touched_nr; + HandleUserConfirmationPopUp(VP_SD_FileSelectConfirm, PSTR("Print file"), filelist.filename(), PSTR("from SD Card?"), nullptr, true, false, true, true); + } + + void DGUSScreenHandler::SetPrintingFromHost() { + const char* printFromHostString = PSTR("Printing from host"); + dgusdisplay.WriteVariablePGM(VP_SD_Print_Filename, printFromHostString, VP_SD_FileName_LEN, true); + } + + void DGUSScreenHandler::DGUSLCD_SD_StartPrint(DGUS_VP_Variable &var, void *val_ptr) { + if (!filelist.seek(file_to_print)) return; + ExtUI::printFile(filelist.shortFilename()); + ScreenHandler.GotoScreen( + DGUSLCD_SCREEN_SDPRINTMANIPULATION + ); + } + + void DGUSScreenHandler::DGUSLCD_SD_SendFilename(DGUS_VP_Variable& var) { + uint16_t target_line = (var.VP - VP_SD_FileName0) / VP_SD_FileName_LEN; + if (target_line > DGUS_SD_FILESPERSCREEN) return; + char tmpfilename[VP_SD_FileName_LEN + 1] = ""; + var.memadr = (void*)tmpfilename; + if (filelist.seek(top_file + target_line)) + snprintf_P(tmpfilename, VP_SD_FileName_LEN, PSTR("%s%c"), filelist.filename(), filelist.isDir() ? '/' : 0); + DGUSLCD_SendStringToDisplay(var); + } + + void DGUSScreenHandler::SDCardInserted() { + top_file = 0; + filelist.refresh(); + auto cs = ScreenHandler.getCurrentScreen(); + if (cs == DGUSLCD_SCREEN_MAIN || cs == DGUSLCD_SCREEN_SETUP) + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_SDFILELIST); + } + + void DGUSScreenHandler::SDCardRemoved() { + if (!IS_SD_PRINTING()) { + return; + } + + if (current_screen == DGUSLCD_SCREEN_SDFILELIST + || (current_screen == DGUSLCD_SCREEN_CONFIRM && (ConfirmVP == VP_SD_AbortPrintConfirmed || ConfirmVP == VP_SD_FileSelectConfirm)) + || current_screen == DGUSLCD_SCREEN_SDPRINTMANIPULATION + ) ScreenHandler.GotoScreen(DGUSLCD_SCREEN_MAIN, false); + } + + void DGUSScreenHandler::SDCardError() { + DGUSScreenHandler::SDCardRemoved(); + ScreenHandler.sendinfoscreen(PSTR("NOTICE"), nullptr, PSTR("SD card error"), nullptr, true, true, true, true); + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_POPUP); + } + +#endif // SDSUPPORT + +void DGUSScreenHandler::FilamentRunout() { + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_FILAMENTRUNOUT1); +} + +void DGUSScreenHandler::OnFactoryReset() { + ScreenHandler.DefaultSettings(); + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_MAIN); +} + +#if HAS_BUZZER || ENABLED(SPEAKER) +void DGUSScreenHandler::Buzzer(const uint16_t frequency, const uint16_t duration) { + // Frequency is fixed - duration is not but in 8 ms steps + const uint8_t durationUnits = static_cast(duration / 8); + + DEBUG_ECHOLNPAIR("Invoking buzzer with units: ", durationUnits); + const unsigned char buzzerCommand[] = { 0x00, durationUnits, 0x40 /*Volume*/, 0x02 }; + + // WAE_Music_Play_Set + dgusdisplay.WriteVariable(0xA0, buzzerCommand, sizeof(buzzerCommand)); +} +#endif + +bool DGUSScreenHandler::HandlePendingUserConfirmation() { + if (!ExtUI::isWaitingOnUser()) { + return false; + } + + // Switch to the resume screen + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_PRINT_RUNNING, false); + + // We might be re-entrant here + ExtUI::setUserConfirmed(); + + return true; +} + +void DGUSScreenHandler::SetSynchronousOperationStart() { + HasSynchronousOperation = true; + ForceCompleteUpdate(); +} + +void DGUSScreenHandler::SetSynchronousOperationFinish() { + HasSynchronousOperation = false; +} + +void DGUSScreenHandler::SendBusyState(DGUS_VP_Variable &var) { + dgusdisplay.WriteVariable(VP_BACK_BUTTON_STATE, HasSynchronousOperation ? ICON_BACK_BUTTON_DISABLED : ICON_BACK_BUTTON_ENABLED); + dgusdisplay.WriteVariable(VP_BUSY_ANIM_STATE, HasSynchronousOperation ? ICON_THROBBER_ANIM_ON : ICON_THROBBER_ANIM_OFF); +} + +void DGUSScreenHandler::OnHomingStart() { + ScreenHandler.SetSynchronousOperationStart(); + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_AUTOHOME); +} + +void DGUSScreenHandler::OnHomingComplete() { + ScreenHandler.SetSynchronousOperationFinish(); + ScreenHandler.PopToOldScreen(); +} + +void DGUSScreenHandler::OnPrintFinished() { + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_PRINT_FINISH, false); +} + +void DGUSScreenHandler::ScreenConfirmedOK(DGUS_VP_Variable &var, void *val_ptr) { + DGUS_VP_Variable ramcopy; + if (!populate_VPVar(ConfirmVP, &ramcopy)) return; + if (ramcopy.set_by_display_handler) ramcopy.set_by_display_handler(ramcopy, val_ptr); +} + +void DGUSScreenHandler::HandleZoffsetChange(DGUS_VP_Variable &var, void *val_ptr) { + HandleLiveAdjustZ(var, val_ptr); +} + +void DGUSScreenHandler::OnMeshLevelingStart() { + GotoScreen(DGUSLCD_SCREEN_LEVELING); + dgusdisplay.WriteVariable(VP_MESH_SCREEN_MESSAGE_ICON, static_cast(MESH_SCREEN_MESSAGE_ICON_LEVELING)); + + ResetMeshValues(); + SetSynchronousOperationStart(); + + MeshLevelIndex = 0; + MeshLevelIconIndex = 0; +} + +void DGUSScreenHandler::OnMeshLevelingUpdate(const int8_t x, const int8_t y, const float z) { + SERIAL_ECHOPAIR("X: ", x); + SERIAL_ECHOPAIR("; Y: ", y); + SERIAL_ECHOPAIR("; Index ", MeshLevelIndex); + SERIAL_ECHOLNPAIR("; Icon ", MeshLevelIconIndex); + + UpdateMeshValue(x, y, z); + + if (MeshLevelIndex < 0) { + // We're not leveling + return; + } + + MeshLevelIndex++; + MeshLevelIconIndex++; + + // Update icon + dgusdisplay.WriteVariable(VP_MESH_LEVEL_STATUS, static_cast(MeshLevelIconIndex + DGUS_GRID_VISUALIZATION_START_ID)); + + if (MeshLevelIndex == GRID_MAX_POINTS) { + // Done + MeshLevelIndex = -1; + + RequestSaveSettings(); + + if (GetPreviousScreen() == DGUSLCD_SCREEN_ZOFFSET_LEVEL) { + // If the user is in the leveling workflow (not printing), get that hotend out of the way + char gcodeBuffer[50] = {0}; + sprintf_P(gcodeBuffer, PSTR("G0 F3500 X%d\nG0 Y%d\nG0 Z%d\nM84"), (X_BED_SIZE / 2), (Y_BED_SIZE / 2), 35); + queue.inject(gcodeBuffer); + + // Change text at the top + ScreenHandler.SetViewMeshLevelState(); + } else { + // When leveling from anywhere but the Z-offset/level screen, automatically pop back to the previous screen + PopToOldScreen(); + } + + SetSynchronousOperationFinish(); + } else { + // We've already updated the icon, so nothing left + } +} + +void DGUSScreenHandler::SetViewMeshLevelState() { + dgusdisplay.WriteVariable(VP_MESH_SCREEN_MESSAGE_ICON, static_cast(MESH_SCREEN_MESSAGE_ICON_VIEWING)); +} + +void DGUSScreenHandler::InitMeshValues() { + if (ExtUI::getMeshValid()) { + for (uint8_t x = 0; x < GRID_MAX_POINTS_X; x++) { + for (uint8_t y = 0; y < GRID_MAX_POINTS_Y; y++) { + float z = ExtUI::getMeshPoint({ x, y }); + UpdateMeshValue(x, y, z); + } + + safe_delay(100); + } + + dgusdisplay.WriteVariable(VP_MESH_LEVEL_STATUS, static_cast(DGUS_GRID_VISUALIZATION_START_ID + GRID_MAX_POINTS)); + } else { + ResetMeshValues(); + } +} + +void DGUSScreenHandler::ResetMeshValues() { + for (uint8_t x = 0; x < GRID_MAX_POINTS_X; x++) { + for (uint8_t y = 0; y < GRID_MAX_POINTS_Y; y++) { + UpdateMeshValue(x, y, 0); + } + + safe_delay(100); + } + + dgusdisplay.WriteVariable(VP_MESH_LEVEL_STATUS, static_cast(DGUS_GRID_VISUALIZATION_START_ID)); +} + +uint16_t CreateRgb(double h, double s, double v) { + struct { + double h; // angle in degrees + double s; // a fraction between 0 and 1 + double v; // a fraction between 0 and 1 + } in = { h, s, v}; + + double hh, p, q, t, ff; + long i; + struct { + double r; // a fraction between 0 and 1 + double g; // a fraction between 0 and 1 + double b; // a fraction between 0 and 1 + } out; + + if(in.s <= 0.0) { // < is bogus, just shuts up warnings + out.r = in.v; + out.g = in.v; + out.b = in.v; + return 0; + } + + hh = in.h; + if(hh >= 360.0) hh = 0.0; + hh /= 60.0; + i = (long)hh; + ff = hh - i; + p = in.v * (1.0 - in.s); + q = in.v * (1.0 - (in.s * ff)); + t = in.v * (1.0 - (in.s * (1.0 - ff))); + + switch(i) { + case 0: + out.r = in.v; + out.g = t; + out.b = p; + break; + case 1: + out.r = q; + out.g = in.v; + out.b = p; + break; + case 2: + out.r = p; + out.g = in.v; + out.b = t; + break; + + case 3: + out.r = p; + out.g = q; + out.b = in.v; + break; + case 4: + out.r = t; + out.g = p; + out.b = in.v; + break; + case 5: + default: + out.r = in.v; + out.g = p; + out.b = q; + break; + } + + return (((static_cast(out.r * 255) & 0xf8)<<8) + ((static_cast(out.g * 255) & 0xfc)<<3) + (static_cast(out.b * 255)>>3)); +} + + +void DGUSScreenHandler::UpdateMeshValue(const int8_t x, const int8_t y, const float z) { + SERIAL_ECHOPAIR("X", x); + SERIAL_ECHOPAIR(" Y", y); + SERIAL_ECHO(" Z"); + SERIAL_ECHO_F(z, 4); + + // Determine the screen X and Y value + if (x % SkipMeshPoint != 0 || y % SkipMeshPoint != 0) { + // Skip this point + SERIAL_ECHOLN(""); + return; + } + + const uint8_t scrX = x / SkipMeshPoint; + const uint8_t scrY = y / SkipMeshPoint; + + // Each Y is a full edge of X values + const uint16_t vpAddr = VP_MESH_LEVEL_X0_Y0 + (scrY * MESH_LEVEL_VP_SIZE) + (scrX * MESH_LEVEL_VP_EDGE_SIZE); + + // ... DWIN is inconsistently truncating floats. Examples: 0.1811 becomes 0.181, 0.1810 becomes 0.180. But 0.1800 is not 0.179 + // so we need to calculate a good number here that will not overflow + float displayZ = z; + + { + constexpr float correctionFactor = 0.0001; + + if (round(z * cpow(10,3)) == round((z + correctionFactor) * cpow(10,3))) { + // If we don't accidently overshoot to the next number, trick the display by upping the number 0.0001 💩 + displayZ += correctionFactor; + + SERIAL_ECHO(" displayZ: "); + SERIAL_ECHO_F(z, 4); + } + } + + SERIAL_ECHOLN(""); + + dgusdisplay.WriteVariable(vpAddr, displayZ); + + // Set color + const uint16_t spAddr = SP_MESH_LEVEL_X0_Y0 + (scrY * MESH_LEVEL_SP_SIZE) + (scrX * MESH_LEVEL_SP_EDGE_SIZE); + + uint16_t color = MESH_COLOR_NOT_MEASURED; + + // ... Only calculate if set + if (abs(z) > MESH_UNSET_EPSILON) { + // Determine color scale + float clampedZ = max(min(z, 0.5f),-0.5f) * -1; + float h = (clampedZ + 0.5f) * 240; + + // Convert to RGB + color = CreateRgb(h, 1, 0.75); + } + + dgusdisplay.SetVariableDisplayColor(spAddr, color); +} + +void DGUSScreenHandler::HandleMeshPoint(DGUS_VP_Variable &var, void *val_ptr) { + // Determine the X and Y for this mesh point + // We can do this because we assume MESH_INPUT_SUPPORTED_X_SIZE and MESH_INPUT_SUPPORTED_Y_SIZE with MESH_INPUT_DATA_SIZE. + // So each VP is MESH_INPUT_DATA_SIZE apart + + if (HasSynchronousOperation) { + setstatusmessagePGM(PSTR("Wait for leveling to complete")); + return; + } + + const uint16_t probe_point = var.VP - VP_MESH_INPUT_X0_Y0; + constexpr uint16_t col_size = MESH_INPUT_SUPPORTED_Y_SIZE * MESH_INPUT_DATA_SIZE; + + const uint8_t x = probe_point / col_size; // Will be 0 to 3 inclusive + const uint8_t y = (probe_point - (x * col_size)) / MESH_INPUT_DATA_SIZE; + + int16_t rawZ = *(int16_t*)val_ptr; + float z = swap16(rawZ) * 0.001; + + SERIAL_ECHOPAIR("Overriding mesh value. X:", x); + SERIAL_ECHOPAIR(" Y:", y); + SERIAL_ECHO(" Z:"); + SERIAL_ECHO_F(z, 4); + SERIAL_ECHOPAIR(" [raw: ", rawZ); + SERIAL_ECHOPAIR("] [point ", probe_point, "] "); + SERIAL_ECHOPAIR(" [VP: ", var.VP); + SERIAL_ECHOLN("]"); + + UpdateMeshValue(x, y, z); + ExtUI::setMeshPoint({ x, y }, z); + + RequestSaveSettings(); +} + +#if HAS_COLOR_LEDS +void DGUSScreenHandler::HandleLED(DGUS_VP_Variable &var, void *val_ptr) { + // The display returns a 16-bit integer + uint16_t newValue = swap16(*(uint16_t*)val_ptr); + + NOLESS(newValue, 0); + NOMORE(newValue, 255); + + (*(uint8_t*)var.memadr) = static_cast(newValue); + leds.set_color(leds.color); + + SERIAL_ECHOLNPAIR("HandleLED ", newValue); + RequestSaveSettings(); + + skipVP = var.VP; // don't overwrite value the next update time as the display might autoincrement in parallel +} + +void DGUSScreenHandler::SendLEDToDisplay(DGUS_VP_Variable &var) { + DGUS_VP_Variable rcpy; + if (!populate_VPVar(var.VP, &rcpy)) { + return; + } + + // The display wants a 16-bit integer + uint16_t val = *(uint8_t*)var.memadr; + rcpy.memadr = &val; + + DGUSLCD_SendWordValueToDisplay(rcpy); +} +#endif + +const uint16_t* DGUSLCD_FindScreenVPMapList(uint8_t screen) { + const uint16_t *ret; + const struct VPMapping *map = VPMap; + while ((ret = (uint16_t*) pgm_read_ptr(&(map->VPList)))) { + if (pgm_read_byte(&(map->screen)) == screen) return ret; + map++; + } + return nullptr; +} + +const DGUS_VP_Variable* DGUSLCD_FindVPVar(const uint16_t vp) { + const DGUS_VP_Variable *ret = ListOfVP; + do { + const uint16_t vpcheck = pgm_read_word(&(ret->VP)); + if (vpcheck == 0) break; + if (vpcheck == vp) return ret; + ++ret; + } while (1); + + DEBUG_ECHOLNPAIR("FindVPVar NOT FOUND ", vp); + return nullptr; +} + +void DGUSScreenHandler::ScreenChangeHookIfIdle(DGUS_VP_Variable &var, void *val_ptr) { + if (!ExtUI::isPrinting()) { + ScreenChangeHook(var, val_ptr); + GotoScreen(current_screen); + } +} + +void DGUSScreenHandler::ScreenChangeHook(DGUS_VP_Variable &var, void *val_ptr) { + uint8_t *tmp = (uint8_t*)val_ptr; + + // The keycode in target is coded as , so 0x0100A means + // from screen 1 (main) to 10 (temperature). DGUSLCD_SCREEN_POPUP is special, + // meaning "return to previous screen" + DGUSLCD_Screens target = (DGUSLCD_Screens)tmp[1]; + + DEBUG_ECHOLNPAIR("Current screen:", current_screen); + DEBUG_ECHOLNPAIR("Cancel target:", target); + + if (ExtUI::isWaitingOnUser() && current_screen == DGUSLCD_SCREEN_POPUP) { + DEBUG_ECHOLN("Executing confirmation action"); + ExtUI::setUserConfirmed(); + PopToOldScreen(); + return; + } + + if (target == DGUSLCD_SCREEN_POPUP || target == DGUSLCD_SCREEN_CONFIRM || target == 0 || target == 255 /*Buggy DWIN screen sometimes just returns 255*/) { + PopToOldScreen(); + return; + } + + UpdateNewScreen(target); + + #ifdef DEBUG_DGUSLCD + if (!DGUSLCD_FindScreenVPMapList(target)) DEBUG_ECHOLNPAIR("WARNING: No screen Mapping found for ", target); + #endif +} + +void DGUSScreenHandler::HandleAllHeatersOff(DGUS_VP_Variable &var, void *val_ptr) { + ExtUI::coolDown(); + ScreenHandler.ForceCompleteUpdate(); // hint to send all data. +} + +void DGUSScreenHandler::HandleTemperatureChanged(DGUS_VP_Variable &var, void *val_ptr) { + celsius_t newvalue = swap16(*(uint16_t*)val_ptr); + celsius_t acceptedvalue; + + switch (var.VP) { + default: return; + #if HOTENDS >= 1 + case VP_T_E0_Set: + NOMORE(newvalue, thermalManager.hotend_max_target(0)); + thermalManager.setTargetHotend(newvalue, 0); + acceptedvalue = thermalManager.degTargetHotend(0); + break; + #endif + #if HAS_HEATED_BED + case VP_T_Bed_Set: + NOMORE(newvalue, BED_MAXTEMP); + thermalManager.setTargetBed(newvalue); + acceptedvalue = thermalManager.degTargetBed(); + break; + #endif + } + + // reply to display the new value to update the view if the new value was rejected by the Thermal Manager. + if (newvalue != acceptedvalue && var.send_to_display_handler) var.send_to_display_handler(var); + skipVP = var.VP; // don't overwrite value the next update time as the display might autoincrement in parallel +} + +void DGUSScreenHandler::HandleFanSpeedChanged(DGUS_VP_Variable &var, void *val_ptr) { + uint16_t newValue = swap16(*(uint16_t*)val_ptr); + + SERIAL_ECHOLNPAIR("Fan speed changed: ", newValue); + ExtUI::setTargetFan_percent(newValue, ExtUI::fan_t::FAN0); + + ScreenHandler.skipVP = var.VP; // don't overwrite value the next update time as the display might autoincrement in parallel +} + +void DGUSScreenHandler::HandleFlowRateChanged(DGUS_VP_Variable &var, void *val_ptr) { + #if EXTRUDERS + uint16_t newValue = swap16(*(uint16_t*)val_ptr); + + SERIAL_ECHOLNPAIR("Flow rate changed: ", newValue); + ExtUI::setFlow_percent(newValue, ExtUI::E0); + + ScreenHandler.skipVP = var.VP; // don't overwrite value the next update time as the display might autoincrement in parallel + #else + UNUSED(var); UNUSED(val_ptr); + #endif +} + +void DGUSScreenHandler::HandleManualExtrude(DGUS_VP_Variable &var, void *val_ptr) { + DEBUG_ECHOLNPGM("HandleManualExtrude"); + + int16_t movevalue = swap16(*(uint16_t*)val_ptr); + float target = movevalue * 0.01f; + ExtUI::extruder_t target_extruder; + + switch (var.VP) { + #if HOTENDS >= 1 + case VP_MOVE_E0: target_extruder = ExtUI::extruder_t::E0; break; + #endif + #if HOTENDS >= 2 + case VP_MOVE_E1: target_extruder = ExtUI::extruder_t::E1; break; + #endif + default: return; + } + + target += ExtUI::getAxisPosition_mm(target_extruder); + ExtUI::setAxisPosition_mm(target, target_extruder); + skipVP = var.VP; +} + +void DGUSScreenHandler::HandleMotorLockUnlock(DGUS_VP_Variable &var, void *val_ptr) { + DEBUG_ECHOLNPGM("HandleMotorLockUnlock"); + + char buf[4]; + const int16_t lock = swap16(*(uint16_t*)val_ptr); + strcpy_P(buf, lock ? PSTR("M18") : PSTR("M17")); + + //DEBUG_ECHOPAIR(" ", buf); + queue.enqueue_one_now(buf); +} + +#if ENABLED(POWER_LOSS_RECOVERY) + + void DGUSScreenHandler::TogglePowerLossRecovery(DGUS_VP_Variable &var, void *val_ptr) { + PrintJobRecovery::enable(!PrintJobRecovery::enabled); + } + + void DGUSScreenHandler::HandlePowerLossRecovery(DGUS_VP_Variable &var, void *val_ptr) { + uint16_t value = swap16(*(uint16_t*)val_ptr); + if (value) { + queue.inject_P(PSTR("M1000")); + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_SDPRINTMANIPULATION, false); + } + else { + recovery.cancel(); + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_MAIN, false); + } + } + +#endif + + + +void DGUSScreenHandler::HandleScreenVersion(DGUS_VP_Variable &var, void *val_ptr) { + DEBUG_ECHOLNPGM("HandleScreenVersion"); + + uint16_t actualScreenVersion = swap16(*(uint16_t*)val_ptr); + + SERIAL_ECHOLNPAIR("DWIN version received: ", actualScreenVersion); + SERIAL_ECHOLNPAIR("We expected DWIN version: ", EXPECTED_UI_VERSION_MAJOR); + + if (actualScreenVersion == EXPECTED_UI_VERSION_MAJOR) { + SERIAL_ECHOLN("Screen version check passed."); + return; + } + + // Dump error to serial + SERIAL_ECHOLN("WARNING: Your screen is not flashed correctly."); + + SERIAL_ECHOPAIR("We received version ", actualScreenVersion); + SERIAL_ECHOLN("from the display"); + + SERIAL_ECHOLNPAIR("This firmware needs screen version ", actualScreenVersion); + SERIAL_ECHOLN("Please follow the release notes for flashing instructions."); + + // Will cause flashing in the loop() + HasScreenVersionMismatch = true; + + // Show on display if user has M117 message + if (actualScreenVersion >= 6) { + // We have a scrolling message so we can do something more complicated + char buffer[VP_M117_LEN] = {0}; + sprintf_P(buffer, "Please flash your TFT screen: version mismatch - build %d found but expected %d", actualScreenVersion, EXPECTED_UI_VERSION_MAJOR); + setStatusMessage(buffer, true); + } else { + char buffer[VP_M117_LEN] = {0}; + sprintf_P(buffer, "Flash TFT please v%d<>v%d", actualScreenVersion, EXPECTED_UI_VERSION_MAJOR); + setstatusmessage(buffer); + } + + // Audio buzzer + Buzzer(500, 500); + for (int times=0;times= 1 + case VP_E0_STEP_PER_MM: extruder = ExtUI::extruder_t::E0; break; + #endif + #if HOTENDS >= 2 + case VP_E1_STEP_PER_MM: extruder = ExtUI::extruder_t::E1; break; + #endif + } + DEBUG_ECHOLNPAIR_F("value:", value); + ExtUI::setAxisSteps_per_mm(value,extruder); + DEBUG_ECHOLNPAIR_F("value_set:", ExtUI::getAxisSteps_per_mm(extruder)); + ScreenHandler.skipVP = var.VP; // don't overwrite value the next update time as the display might autoincrement in parallel + return; +} + +#if HAS_PID_HEATING + void DGUSScreenHandler::HandleTemperaturePIDChanged(DGUS_VP_Variable &var, void *val_ptr) { + uint16_t rawvalue = swap16(*(uint16_t*)val_ptr); + DEBUG_ECHOLNPAIR("V1:", rawvalue); + float value = (float)rawvalue / 10; + DEBUG_ECHOLNPAIR("V2:", value); + float newvalue = 0; + + switch (var.VP) { + default: return; + #if HOTENDS >= 1 + case VP_E0_PID_P: newvalue = value; break; + case VP_E0_PID_I: newvalue = scalePID_i(value); break; + case VP_E0_PID_D: newvalue = scalePID_d(value); break; + #endif + #if HOTENDS >= 2 + case VP_E1_PID_P: newvalue = value; break; + case VP_E1_PID_I: newvalue = scalePID_i(value); break; + case VP_E1_PID_D: newvalue = scalePID_d(value); break; + #endif + #if HAS_HEATED_BED + case VP_BED_PID_P: newvalue = value; break; + case VP_BED_PID_I: newvalue = scalePID_i(value); break; + case VP_BED_PID_D: newvalue = scalePID_d(value); break; + #endif + } + + DEBUG_ECHOLNPAIR_F("V3:", newvalue); + *(float *)var.memadr = newvalue; + ScreenHandler.skipVP = var.VP; // don't overwrite value the next update time as the display might autoincrement in parallel + } + + void DGUSScreenHandler::HandlePIDAutotune(DGUS_VP_Variable &var, void *val_ptr) { + DEBUG_ECHOLNPGM("HandlePIDAutotune"); + + char buf[32] = {0}; + + switch (var.VP) { + default: break; + #if ENABLED(PIDTEMP) + #if HOTENDS >= 1 + case VP_PID_AUTOTUNE_E0: // Autotune Extruder 0 + sprintf(buf, "M303 E%d C5 S210 U1", ExtUI::extruder_t::E0); + break; + #endif + #if HOTENDS >= 2 + case VP_PID_AUTOTUNE_E1: + sprintf(buf, "M303 E%d C5 S210 U1", ExtUI::extruder_t::E1); + break; + #endif + #endif + #if ENABLED(PIDTEMPBED) + case VP_PID_AUTOTUNE_BED: + sprintf(buf, "M303 E-1 C5 S70 U1"); + break; + #endif + } + + if (buf[0]) queue.enqueue_one_now(buf); + + #if ENABLED(DGUS_UI_WAITING) + sendinfoscreen(PSTR("PID is autotuning"), PSTR("please wait"), NUL_STR, NUL_STR, true, true, true, true); + GotoScreen(DGUSLCD_SCREEN_WAITING); + #endif + } +#endif + +void DGUSScreenHandler::HandleFadeHeight(DGUS_VP_Variable &var, void *val_ptr) { + DGUSLCD_SetFloatAsIntFromDisplay<1>(var, val_ptr); + + RequestSaveSettings(); + return; +} + +void DGUSScreenHandler::HandlePositionChange(DGUS_VP_Variable &var, void *val_ptr) { + DEBUG_ECHOLNPGM("HandlePositionChange"); + + unsigned int speed = homing_feedrate_mm_m.x; + float target_position = ((float)swap16(*(uint16_t*)val_ptr)) / 10.0; + + switch (var.VP) { + default: return; + + case VP_X_POSITION: + if (!ExtUI::canMove(ExtUI::axis_t::X)) return; + current_position.x = min(target_position, static_cast(X_MAX_POS)); + break; + + case VP_Y_POSITION: + if (!ExtUI::canMove(ExtUI::axis_t::Y)) return; + current_position.y = min(target_position, static_cast(Y_MAX_POS)); + break; + + case VP_Z_POSITION: + if (!ExtUI::canMove(ExtUI::axis_t::Z)) return; + speed = homing_feedrate_mm_m.z; + current_position.z = min(target_position, static_cast(Z_MAX_POS)); + break; + } + + line_to_current_position(MMM_TO_MMS(speed)); + + ScreenHandler.ForceCompleteUpdate(); + DEBUG_ECHOLNPGM("poschg done."); +} + +void DGUSScreenHandler::HandleLiveAdjustZ(DGUS_VP_Variable &var, void *val_ptr) { + DEBUG_ECHOLNPGM("HandleLiveAdjustZ"); + + float absoluteAmount = float(swap16(*(int16_t*)val_ptr)) / 100.0f; + float existingAmount = ExtUI::getZOffset_mm(); + float difference = (absoluteAmount - existingAmount) < 0 ? -0.01 : 0.01; + + int16_t steps = ExtUI::mmToWholeSteps(difference, ExtUI::axis_t::Z); + + ExtUI::smartAdjustAxis_steps(steps, ExtUI::axis_t::Z, true); + + RequestSaveSettings(); + + ScreenHandler.ForceCompleteUpdate(); + ScreenHandler.skipVP = var.VP; // don't overwrite value the next update time as the display might autoincrement in parallel + return; +} + +void DGUSScreenHandler::HandleHeaterControl(DGUS_VP_Variable &var, void *val_ptr) { + DEBUG_ECHOLNPGM("HandleHeaterControl"); + + uint8_t preheat_temp = 0; + switch (var.VP) { + #if HOTENDS >= 1 + case VP_E0_CONTROL: + #endif + #if HOTENDS >= 2 + case VP_E1_CONTROL: + #endif + #if HOTENDS >= 3 + case VP_E2_CONTROL: + #endif + preheat_temp = PREHEAT_1_TEMP_HOTEND; + break; + + case VP_BED_CONTROL: + preheat_temp = PREHEAT_1_TEMP_BED; + break; + } + + *(int16_t*)var.memadr = *(int16_t*)var.memadr > 0 ? 0 : preheat_temp; +} + +void DGUSScreenHandler::HandleLEDToggle() { + bool newState = !caselight.on; + + caselight.on = newState; + caselight.update(newState); + + RequestSaveSettings(); + ForceCompleteUpdate(); +} + +void DGUSScreenHandler::HandleToggleTouchScreenMute(DGUS_VP_Variable &var, void *val_ptr) { + Settings.display_sound = !Settings.display_sound; + ScreenHandler.SetTouchScreenConfiguration(); + + RequestSaveSettings(); + ForceCompleteUpdate(); + + ScreenHandler.skipVP = var.VP; // don't overwrite value the next update time as the display might autoincrement in parallel +} + +#if HAS_PROBE_SETTINGS +void DGUSScreenHandler::HandleToggleProbeHeaters(DGUS_VP_Variable &var, void *val_ptr) { + probe.settings.turn_heaters_off = !probe.settings.turn_heaters_off; + + RequestSaveSettings(); +} + +void DGUSScreenHandler::HandleToggleProbeTemperatureStabilization(DGUS_VP_Variable &var, void *val_ptr) { + probe.settings.stabilize_temperatures_after_probing = !probe.settings.stabilize_temperatures_after_probing; + + RequestSaveSettings(); +} + +void DGUSScreenHandler::HandleToggleProbePreheatTemp(DGUS_VP_Variable &var, void *val_ptr) { + ScreenHandler.DGUSLCD_SetValueDirectly(var, val_ptr); + + RequestSaveSettings(); +} +#endif + +void DGUSScreenHandler::HandleTouchScreenBrightnessSetting(DGUS_VP_Variable &var, void *val_ptr) { + uint16_t newvalue = swap16(*(uint16_t*)val_ptr); + + SERIAL_ECHOLNPAIR("HandleTouchScreenBrightnessSetting: ", newvalue); + Settings.screen_brightness = newvalue; + ScreenHandler.SetTouchScreenConfiguration(); + + RequestSaveSettings(); + ForceCompleteUpdate(); +} + +void DGUSScreenHandler::HandleTouchScreenStandbyBrightnessSetting(DGUS_VP_Variable &var, void *val_ptr) { + uint16_t newvalue = swap16(*(uint16_t*)val_ptr); + + SERIAL_ECHOLNPAIR("HandleTouchScreenStandbyBrightnessSetting: ", newvalue); + Settings.standby_screen_brightness = newvalue; + ScreenHandler.SetTouchScreenConfiguration(); + + RequestSaveSettings(); + ForceCompleteUpdate(); +} + +void DGUSScreenHandler::HandleTouchScreenStandbyTimeSetting(DGUS_VP_Variable &var, void *val_ptr) { + uint16_t newvalue = swap16(*(uint16_t*)val_ptr); + + SERIAL_ECHOLNPAIR("HandleTouchScreenStandbyTimeSetting: ", newvalue); + Settings.standby_time_seconds = newvalue; + ScreenHandler.SetTouchScreenConfiguration(); + + RequestSaveSettings(); + ForceCompleteUpdate(); +} + +void DGUSScreenHandler::HandleToggleTouchScreenStandbySetting(DGUS_VP_Variable &var, void *val_ptr) { + SERIAL_ECHOLNPAIR("HandleToggleTouchScreenStandbySetting"); + + Settings.display_standby = !Settings.display_standby; + ScreenHandler.SetTouchScreenConfiguration(); + + RequestSaveSettings(); + ForceCompleteUpdate(); +} + +void DGUSScreenHandler::HandleFanToggle() { + thermalManager.fan_speed[0] = (thermalManager.fan_speed[0] > 0) ? 0 : 255; + + ForceCompleteUpdate(); +} + +void DGUSScreenHandler::UpdateNewScreen(DGUSLCD_Screens newscreen, bool save_current_screen) { + SERIAL_ECHOLNPAIR("SetNewScreen: ", newscreen); + + if (save_current_screen && current_screen != DGUSLCD_SCREEN_POPUP && current_screen != DGUSLCD_SCREEN_CONFIRM) { + SERIAL_ECHOLNPAIR("SetNewScreen (saving): ", newscreen); + memmove(&past_screens[1], &past_screens[0], sizeof(past_screens) - 1); + past_screens[0] = current_screen; + } + + current_screen = newscreen; + skipVP = 0; + ForceCompleteUpdate(); +} + +void DGUSScreenHandler::PopToOldScreen() { + DEBUG_ECHOLNPAIR("PopToOldScreen s=", past_screens[0]); + + if(past_screens[0] != 0) { + GotoScreen(past_screens[0], false); + memmove(&past_screens[0], &past_screens[1], sizeof(past_screens) - 1); + past_screens[sizeof(past_screens) - 1] = DGUSLCD_SCREEN_MAIN; + } else { + if(ExtUI::isPrinting()) { + GotoScreen(DGUSLCD_SCREEN_PRINT_RUNNING, false); + } else { + GotoScreen(DGUSLCD_SCREEN_MAIN, false); + } + } +} + +void DGUSScreenHandler::OnBackButton(DGUS_VP_Variable &var, void *val_ptr) { + // If we're busy: ignore + if (HasSynchronousOperation) return; + + // Pop back + uint16_t button_value = uInt16Value(val_ptr); + + PopToOldScreen(); + + // Handle optional save from back button + if (button_value == GENERIC_BACK_BUTTON_NEED_SAVE) { + RequestSaveSettings(); + } +} + +void DGUSScreenHandler::UpdateScreenVPData() { + if (!dgusdisplay.isInitialized()) { + return; + } + + //DEBUG_ECHOPAIR(" UpdateScreenVPData Screen: ", current_screen); + + const uint16_t *VPList = DGUSLCD_FindScreenVPMapList(current_screen); + if (!VPList) { + DEBUG_ECHOLNPAIR(" NO SCREEN FOR: ", current_screen); + ScreenComplete = true; + return; // nothing to do, likely a bug or boring screen. + } + + // Round-robin updating of all VPs. + VPList += update_ptr; + + bool sent_one = false; + do { + uint16_t VP = pgm_read_word(VPList); + DEBUG_ECHOPAIR(" VP: ", VP); + if (!VP) { + update_ptr = 0; + DEBUG_ECHOLNPGM(" UpdateScreenVPData done"); + ScreenComplete = true; + return; // Screen completed. + } + + if (VP == skipVP) { skipVP = 0; continue; } + + DGUS_VP_Variable rcpy; + if (populate_VPVar(VP, &rcpy)) { + uint8_t expected_tx = 6 + rcpy.size; // expected overhead is 6 bytes + payload. + // Send the VP to the display, but try to avoid overrunning the Tx Buffer. + // But send at least one VP, to avoid getting stalled. + if (rcpy.send_to_display_handler && (!sent_one || expected_tx <= dgusdisplay.GetFreeTxBuffer())) { + DEBUG_ECHOPAIR(" calling handler for ", rcpy.VP); + sent_one = true; + rcpy.send_to_display_handler(rcpy); + } + else { + auto x = dgusdisplay.GetFreeTxBuffer(); + DEBUG_ECHOLNPAIR(" tx almost full: ", x); + UNUSED(x); + //DEBUG_ECHOPAIR(" update_ptr ", update_ptr); + ScreenComplete = false; + return; // please call again! + } + } + + } while (++update_ptr, ++VPList, true); +} + +void DGUSScreenHandler::GotoScreen(DGUSLCD_Screens screen, bool save_current_screen) { + if (current_screen == screen) { + // Ignore this request + return; + } + + DEBUG_ECHOLNPAIR("Issuing command to go to screen: ", screen); + dgusdisplay.RequestScreen(screen); + UpdateNewScreen(screen, save_current_screen); +} + +bool DGUSScreenHandler::loop() { + dgusdisplay.loop(); + + HandleScreenVersionMismatchLEDFlash(); + + const millis_t ms = millis(); + static millis_t next_event_ms = 0; + + if (ELAPSED(ms, next_event_ms) && SaveSettingsRequested) { + // Only save settings so many times in a second - otherwise the EEPROM chip gets overloaded and the watchdog reboots the CPU + settings.save(); + SaveSettingsRequested = false; + } + + if (!IsScreenComplete() || ELAPSED(ms, next_event_ms)) { + next_event_ms = ms + DGUS_UPDATE_INTERVAL_MS; + + UpdateScreenVPData(); + } + + if (dgusdisplay.isInitialized()) { + static bool booted = false; + + if (!booted) { + progmem_str message = GET_TEXT_F(WELCOME_MSG); + char buff[strlen_P((const char * const)message)+1]; + strcpy_P(buff, (const char * const) message); + ExtUI::onStatusChanged((const char *)buff); + + int16_t percentage = static_cast(((float) ms / (float)BOOTSCREEN_TIMEOUT) * 100); + if (percentage > 100) percentage = 100; + + dgusdisplay.WriteVariable(VP_STARTPROGRESSBAR, percentage); + } + + if (!booted && TERN0(POWER_LOSS_RECOVERY, recovery.valid())) { + booted = true; + DEBUG_ECHOLN("Power loss recovery..."); + } + + if (!booted && ELAPSED(ms, BOOTSCREEN_TIMEOUT)) { + booted = true; + + #if HAS_COLOR_LEDS && !HAS_COLOR_LEDS_PREFERENCES + leds.set_default(); + #endif + + // Ensure to pick up the settings + SetTouchScreenConfiguration(); + + // Set initial leveling status + InitMeshValues(); + + // No disabled back button + ScreenHandler.SetSynchronousOperationFinish(); + + // Ask for the screen version - HandleScreenVersion will act + dgusdisplay.ReadVariable(VP_UI_VERSION_MAJOR); + + // Main menu + GotoScreen(DGUSLCD_SCREEN_MAIN); + } + } + + return IsScreenComplete(); +} + +#endif // HAS_DGUS_LCD diff --git a/Marlin/src/lcd/extui/dgus_creality/DGUSScreenHandler.h b/Marlin/src/lcd/extui/dgus_creality/DGUSScreenHandler.h new file mode 100644 index 0000000000..7b8562b88d --- /dev/null +++ b/Marlin/src/lcd/extui/dgus_creality/DGUSScreenHandler.h @@ -0,0 +1,458 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +#pragma once + +#include "DGUSDisplay.h" +#include "DGUSVPVariable.h" + +#include "../../../inc/MarlinConfig.h" + +#if HAS_COLOR_LEDS + #include "../../../feature/leds/leds.h" + + #if ENABLED(PRINTER_EVENT_LEDS) + #define HAS_COLOR_LEDS_PREFERENCES 1 + #endif +#endif + +enum DGUSLCD_Screens : uint8_t; + +struct creality_dwin_settings_t { + size_t settings_size; + uint8_t settings_version; + + bool led_state; + bool display_standby; + bool display_sound; + int16_t standby_screen_brightness; + int16_t screen_brightness; + int16_t standby_time_seconds; + + #if HAS_COLOR_LEDS_PREFERENCES + LEDColor LastLEDColor; + #endif +}; + +class DGUSScreenHandler { +public: + DGUSScreenHandler() = default; + + static bool loop(); + + static void Init(); + static void DefaultSettings(); + static void LoadSettings(const char* buff); + static void StoreSettings(char* buff); + static void SetTouchScreenConfiguration(); + static void KillScreenCalled(); + + static void OnPowerlossResume(); + + static void RequestSaveSettings(); + + /// Send all 4 strings that are displayed on the infoscreen, confirmation screen and kill screen + /// The bools specifing whether the strings are in RAM or FLASH. + static void sendinfoscreen(const char* line1, const char* line2, const char* line3, const char* line4, bool l1inflash, bool l2inflash, bool l3inflash, bool liinflash); + + static void HandleUserConfirmationPopUp(uint16_t ConfirmVP, const char* line1, const char* line2, const char* line3, const char* line4, bool l1inflash, bool l2inflash, bool l3inflash, bool liinflash); + + static void HandleDevelopmentTestButton(DGUS_VP_Variable &var, void *val_ptr); + + /// "M117" Message -- msg is a RAM ptr. + static void setstatusmessage(const char* msg); + /// The same for messages from Flash + static void setstatusmessagePGM(PGM_P const msg); + + // Callback for VP "Display wants to change screen on idle printer" + static void ScreenChangeHookIfIdle(DGUS_VP_Variable &var, void *val_ptr); + // Callback for VP "Screen has been changed" + static void ScreenChangeHook(DGUS_VP_Variable &var, void *val_ptr); + // Callback for VP "All Heaters Off" + static void HandleAllHeatersOff(DGUS_VP_Variable &var, void *val_ptr); + // Hook for "Change this temperature" + static void HandleTemperatureChanged(DGUS_VP_Variable &var, void *val_ptr); + static void HandleFanSpeedChanged(DGUS_VP_Variable &var, void *val_ptr); + // Hook for "Change Flowrate" + static void HandleFlowRateChanged(DGUS_VP_Variable &var, void *val_ptr); + // Hook for manual extrude. + static void HandleManualExtrude(DGUS_VP_Variable &var, void *val_ptr); + // Hook for motor lock and unlook + static void HandleMotorLockUnlock(DGUS_VP_Variable &var, void *val_ptr); + #if ENABLED(POWER_LOSS_RECOVERY) + static void TogglePowerLossRecovery(DGUS_VP_Variable &var, void *val_ptr); + + // Hook for power loss recovery. + static void HandlePowerLossRecovery(DGUS_VP_Variable &var, void *val_ptr); + #endif + + // Version sanity check + static void HandleScreenVersion(DGUS_VP_Variable &var, void *val_ptr); + + // Hook for settings + static void HandleStepPerMMChanged(DGUS_VP_Variable &var, void *val_ptr); + static void HandleStepPerMMExtruderChanged(DGUS_VP_Variable &var, void *val_ptr); + static void HandleFadeHeight(DGUS_VP_Variable &var, void *val_ptr); + + // Hook for move to position + static void HandlePositionChange(DGUS_VP_Variable &var, void *val_ptr); + + static void HandleToggleTouchScreenMute(DGUS_VP_Variable &var, void *val_ptr); + static void HandleToggleTouchScreenStandbySetting(DGUS_VP_Variable &var, void *val_ptr); + static void HandleTouchScreenBrightnessSetting(DGUS_VP_Variable &var, void *val_ptr); + static void HandleTouchScreenStandbyBrightnessSetting(DGUS_VP_Variable &var, void *val_ptr); + static void HandleTouchScreenStandbyTimeSetting(DGUS_VP_Variable &var, void *val_ptr); + + #if HAS_PROBE_SETTINGS + static void HandleToggleProbeHeaters(DGUS_VP_Variable &var, void *val_ptr); + static void HandleToggleProbeTemperatureStabilization(DGUS_VP_Variable &var, void *val_ptr); + static void HandleToggleProbePreheatTemp(DGUS_VP_Variable &var, void *val_ptr); + #endif + + #if HAS_PID_HEATING + // Hook for "Change this temperature PID para" + static void HandleTemperaturePIDChanged(DGUS_VP_Variable &var, void *val_ptr); + // Hook for PID autotune + static void HandlePIDAutotune(DGUS_VP_Variable &var, void *val_ptr); + #endif + #if HAS_BED_PROBE + // Hook for "Change probe offset z" + static void HandleZoffsetChange(DGUS_VP_Variable &var, void *val_ptr); + + static void OnMeshLevelingStart(); + + static void OnMeshLevelingUpdate(const int8_t x, const int8_t y, const float z); + + static void InitMeshValues(); + + static void ResetMeshValues(); + + static void UpdateMeshValue(const int8_t x, const int8_t y, const float z); + + static void HandleMeshPoint(DGUS_VP_Variable &var, void *val_ptr); + #endif + + // LED + #if HAS_COLOR_LEDS + static void HandleLED(DGUS_VP_Variable &var, void *val_ptr); + static void SendLEDToDisplay(DGUS_VP_Variable &var); + #endif + + // Hook for live z adjust action + static void HandleLiveAdjustZ(DGUS_VP_Variable &var, void *val_ptr); + + // Hook for heater control + static void HandleHeaterControl(DGUS_VP_Variable &var, void *val_ptr); + #if ENABLED(DGUS_PREHEAT_UI) + // Hook for preheat + static void HandlePreheat(DGUS_VP_Variable &var, void *val_ptr); + #endif + #if ENABLED(DGUS_FILAMENT_LOADUNLOAD) + // Hook for filament load and unload filament option + static void HandleFilamentOption(DGUS_VP_Variable &var, void *val_ptr); + // Hook for filament load and unload + static void HandleFilamentLoadUnload(DGUS_VP_Variable &var); + #endif + + #if ENABLED(SDSUPPORT) + // Callback for VP "Display wants to change screen when there is a SD card" + static void ScreenChangeHookIfSD(DGUS_VP_Variable &var, void *val_ptr); + /// Scroll buttons on the file listing screen. + static void DGUSLCD_SD_ScrollFilelist(DGUS_VP_Variable &var, void *val_ptr); + /// File touched. + static void DGUSLCD_SD_FileSelected(DGUS_VP_Variable &var, void *val_ptr); + /// start print after confirmation received. + static void DGUSLCD_SD_StartPrint(DGUS_VP_Variable &var, void *val_ptr); + /// Send a single filename to the display. + static void DGUSLCD_SD_SendFilename(DGUS_VP_Variable &var); + /// Marlin informed us that a new SD has been inserted. + static void SDCardInserted(); + /// Marlin informed us that the SD Card has been removed(). + static void SDCardRemoved(); + /// Marlin informed us about a bad SD Card. + static void SDCardError(); + + static void SetPrintingFromHost(); + #endif + + static void HandleLEDToggle(); + + static void HandleFanToggle(); + + static void FilamentRunout(); + + static void OnFactoryReset(); + +#if HAS_BUZZER || ENABLED(SPEAKER) + static void Buzzer(const uint16_t frequency, const uint16_t duration); +#endif + + static void OnHomingStart(); + static void OnHomingComplete(); + static void OnPrintFinished(); + + // OK Button the Confirm screen. + static void ScreenConfirmedOK(DGUS_VP_Variable &var, void *val_ptr); + + // Update data after went to new screen (by display or by GotoScreen) + // remember: store the last-displayed screen, so it can get returned to. + // (e.g for pop up messages) + static void UpdateNewScreen(DGUSLCD_Screens newscreen, bool save_current_screen=true); + + // Recall the remembered screen. + static void PopToOldScreen(); + static void OnBackButton(DGUS_VP_Variable &var, void *val_ptr); + + // Make the display show the screen and update all VPs in it. + static void GotoScreen(DGUSLCD_Screens screen, bool save_current_screen = true); + + static void UpdateScreenVPData(); + + // Helpers to convert and transfer data to the display. + static void DGUSLCD_SendWordValueToDisplay(DGUS_VP_Variable &var); + static void DGUSLCD_SendStringToDisplay(DGUS_VP_Variable &var); + static void DGUSLCD_SendScrollingStringToDisplay(DGUS_VP_Variable &var); + static void DGUSLCD_SendStringToDisplayPGM(DGUS_VP_Variable &var); + static void DGUSLCD_SendScrollingStringToDisplayPGM(DGUS_VP_Variable &var); + static void DGUSLCD_SendTemperaturePID(DGUS_VP_Variable &var); + static void DGUSLCD_SendPercentageToDisplay(DGUS_VP_Variable &var); + static void DGUSLCD_SendPrintProgressToDisplay(DGUS_VP_Variable &var); + static void DGUSLCD_SendPrintTimeToDisplay(DGUS_VP_Variable &var); + static void DGUSLCD_SendPrintTimeWithRemainingToDisplay(DGUS_VP_Variable &var); + static void DGUSLCD_SendPrintTimeRemainingToDisplay(DGUS_VP_Variable &var); + #if ENABLED(PRINTCOUNTER) + static void DGUSLCD_SendPrintAccTimeToDisplay(DGUS_VP_Variable &var); + static void DGUSLCD_SendPrintsTotalToDisplay(DGUS_VP_Variable &var); + #endif + #if HAS_FAN + static void DGUSLCD_SendFanStatusToDisplay(DGUS_VP_Variable &var); + static void DGUSLCD_SendFanSpeedToDisplay(DGUS_VP_Variable &var); + #endif + static void DGUSLCD_SendHeaterStatusToDisplay(DGUS_VP_Variable &var); + #if ENABLED(DGUS_UI_WAITING) + static void DGUSLCD_SendWaitingStatusToDisplay(DGUS_VP_Variable &var); + #endif + + static void DGUSLCD_SendAboutFirmwareWebsite(DGUS_VP_Variable &var); + static void DGUSLCD_SendAboutFirmwareVersion(DGUS_VP_Variable &var); + static void DGUSLCD_SendAboutPrintSize(DGUS_VP_Variable &var); + + /// Send a value from 0..100 to a variable with a range from 0..255 + static void DGUSLCD_PercentageToUint8(DGUS_VP_Variable &var, void *val_ptr); + + template + static void DGUSLCD_SetValueDirectly(DGUS_VP_Variable &var, void *val_ptr) { + if (!var.memadr) return; + union { unsigned char tmp[sizeof(T)]; T t; } x; + unsigned char *ptr = (unsigned char*)val_ptr; + LOOP_L_N(i, sizeof(T)) x.tmp[i] = ptr[sizeof(T) - i - 1]; + *(T*)var.memadr = x.t; + } + + template + static void DGUSLCD_NavigateToPage(DGUS_VP_Variable &var, void *val_ptr) { + GotoScreen(TPage); + } + + template + static void DGUSLCD_NavigateToPage(DGUS_VP_Variable &var, void *val_ptr) { + GotoScreen(TPage); + Handler::Init(); + } + + /// Send a float value to the display. + /// Display will get a 4-byte integer scaled to the number of digits: + /// Tell the display the number of digits and it cheats by displaying a dot between... + template + static void DGUSLCD_SendFloatAsLongValueToDisplay(DGUS_VP_Variable &var) { + if (var.memadr) { + double d = static_cast(*(float *)var.memadr); + d *= cpow(10, decimals); + + // Round - truncated values look like skipped numbers + static_assert(sizeof(long) == 4, "Assuming long is 4 bytes"); + long roundedValue = static_cast(round(d)); + dgusdisplay.WriteVariable(var.VP, roundedValue); + } + } + + // Receive a float from the display - Display will send a 2-byte integer scaled to the number of digits + template + static void DGUSLCD_SetFloatAsIntFromDisplay(DGUS_VP_Variable &var, void *val_ptr) { + if (var.memadr) { + uint16_t value_raw = swap16(*(uint16_t*)val_ptr); + + float value = static_cast(static_cast(value_raw) /cpow(10, decimals)); + *(float *)var.memadr = value; + } + } + + // Receive a float from the display - Display will send a 4-byte integer scaled to the number of digits + template + static void DGUSLCD_SetFloatAsLongFromDisplay(DGUS_VP_Variable &var, void *val_ptr) { + if (var.memadr) { + uint32_t value_raw = uInt32Value(*(uint32_t*)val_ptr); + + float value = static_cast(static_cast(value_raw) /cpow(10, decimals)); + *(float *)var.memadr = value; + } + } + + static void DGUSLCD_SendULongToDisplay(DGUS_VP_Variable &var) { + if (var.memadr) { + // Round - truncated values look like skipped numbers + long roundedValue = *(uint32_t *) var.memadr; + dgusdisplay.WriteVariable(var.VP, roundedValue); + } + } + + static void DGUSLCD_ReceiveULongFromDisplay(DGUS_VP_Variable &var, void* val_ptr) { + if (var.memadr) { + // Round - truncated values look like skipped numbers + uint32_t incomingValue = *(uint32_t *) val_ptr; + + *(uint32_t*)var.memadr = uInt32Value(incomingValue); + } + } + + // Toggle a boolean at the specified memory address + static void DGUSLCD_ToggleBoolean(DGUS_VP_Variable &var, void *val_ptr) { + if (var.memadr) { + SERIAL_ECHOLNPAIR("Toggle boolean - ", var.VP); + + bool* val = (bool *)var.memadr; + *val = !*val; + } + } + + // Send an icon to the display, depending on whether it is true or false + template + static void DGUSLCD_SendIconValue(DGUS_VP_Variable &var) { + if (var.memadr) { + bool value = *(bool *)var.memadr; + uint16_t valueToSend = value ? value_if_true : value_if_false; + dgusdisplay.WriteVariable(var.VP, valueToSend); + } + } + + /// Send a float value to the display. + /// Display will get a 2-byte integer scaled to the number of digits: + /// Tell the display the number of digits and it cheats by displaying a dot between... + template + static void DGUSLCD_SendFloatAsIntValueToDisplay(DGUS_VP_Variable &var) { + if (var.memadr) { + double d = static_cast(*(float *)var.memadr); + d *= cpow(10, decimals); + + // Round - truncated values look like skipped numbers + int16_t roundedValue = static_cast(round(d)); + dgusdisplay.WriteVariable(var.VP, roundedValue); + } + } + + template + static void SendAxisTrustValue(DGUS_VP_Variable &var) { + bool trust = axis_is_trusted(Axis); + + uint16_t color = trust ? 0xFFFF /*White*/ : 0XF800 /*Red*/; + dgusdisplay.SetVariableDisplayColor(var.VP, color); + } + + /// Force an update of all VP on the current screen. + static inline void ForceCompleteUpdate() { update_ptr = 0; ScreenComplete = false; } + /// Has all VPs sent to the screen + static inline bool IsScreenComplete() { return ScreenComplete; } + + static inline DGUSLCD_Screens getCurrentScreen() { return current_screen; } + + static bool HandlePendingUserConfirmation(); + + static void SetSynchronousOperationStart(); + static void SetSynchronousOperationFinish(); + static bool HasCurrentSynchronousOperation() { return HasSynchronousOperation; } + static void SendBusyState(DGUS_VP_Variable &var); + + static void SetViewMeshLevelState(); + + static bool fwretract_available; + +private: + static void HandleScreenVersionMismatchLEDFlash(); + + static DGUSLCD_Screens current_screen; ///< currently on screen + static constexpr uint8_t NUM_PAST_SCREENS = 4; + static DGUSLCD_Screens past_screens[NUM_PAST_SCREENS]; ///< LIFO with past screens for the "back" button. + + static uint8_t update_ptr; ///< Last sent entry in the VPList for the actual screen. + static uint16_t skipVP; ///< When updating the screen data, skip this one, because the user is interacting with it. + static bool ScreenComplete; ///< All VPs sent to screen? + + static uint16_t ConfirmVP; ///< context for confirm screen (VP that will be emulated-sent on "OK"). + + static uint8_t MeshLevelIndex; + static uint8_t MeshLevelIconIndex; + static bool SaveSettingsRequested; + static bool HasScreenVersionMismatch; + static bool HasSynchronousOperation; + + #if ENABLED(SDSUPPORT) + static int16_t top_file; ///< file on top of file chooser + static int16_t file_to_print; ///< touched file to be confirmed + #endif + +private: + FORCE_INLINE static DGUSLCD_Screens GetPreviousScreen() { + return past_screens[0]; + } + +public: // Needed for VP auto-upload + static bool HasRGBSettings; + static creality_dwin_settings_t Settings; +}; + +extern DGUSScreenHandler ScreenHandler; + +struct DGUSSynchronousOperation { + private: + bool is_running; + + public: + DGUSSynchronousOperation() : is_running(false) {} + + // Don't allow this to be created on the stack + void* operator new (std::size_t size) = delete; + + void start() { + is_running = true; + ScreenHandler.SetSynchronousOperationStart(); + } + + void done() { + is_running = false; + ScreenHandler.SetSynchronousOperationFinish(); + } + + ~DGUSSynchronousOperation() { + if (is_running) { + ScreenHandler.SetSynchronousOperationFinish(); + } + } +}; diff --git a/Marlin/src/lcd/extui/dgus_creality/DGUSVPVariable.h b/Marlin/src/lcd/extui/dgus_creality/DGUSVPVariable.h new file mode 100644 index 0000000000..c0ea0a2877 --- /dev/null +++ b/Marlin/src/lcd/extui/dgus_creality/DGUSVPVariable.h @@ -0,0 +1,67 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +#pragma once + +#include + +/** + * DGUSVPVariable.h + * + * Created on: Feb 9, 2019 + * Author: tobi + */ + +struct DGUS_VP_Variable { + uint16_t VP; + void* memadr; // If nullptr, the value cannot be uploaded to the display. + uint8_t size; + + // Callback that will be called if the display modified the value. + // nullptr makes it readonly for the display. + void (*set_by_display_handler)(DGUS_VP_Variable &var, void *val_ptr); + void (*send_to_display_handler)(DGUS_VP_Variable &var); + + template + DGUS_VP_Variable& operator =(T &o) { + *(T*)memadr = o; // warning this is not typesafe. + // TODO: Call out the display or mark as dirty for the next update. + return *this; + } +}; + +// endianness swap +FORCE_INLINE uint16_t swap16(const uint16_t value) { return (value & 0xffU) << 8U | (value >> 8U); } + +FORCE_INLINE int16_t swap16(const int16_t value) { + union { int16_t l; char lb[2]; } endian; + + endian.l = value; + char tmp = endian.lb[1]; + endian.lb[1] = endian.lb[0]; + endian.lb[0] = tmp; + + return endian.l; +} + +FORCE_INLINE uint32_t uInt32Value(const uint32_t value) { return ((value>>24)&0xff) | ((value<<8)&0xff0000) | ((value>>8)&0xff00) | ((value<<24)&0xff000000); } + +FORCE_INLINE uint16_t uInt16Value(void *val_ptr) { return swap16(*static_cast(val_ptr)); } \ No newline at end of file diff --git a/Marlin/src/lcd/extui/dgus_creality/creality_touch/AxisSettingsHandler.cpp b/Marlin/src/lcd/extui/dgus_creality/creality_touch/AxisSettingsHandler.cpp new file mode 100644 index 0000000000..c4d1cbfd28 --- /dev/null +++ b/Marlin/src/lcd/extui/dgus_creality/creality_touch/AxisSettingsHandler.cpp @@ -0,0 +1,230 @@ + +#include "../../../../inc/MarlinConfigPre.h" + +#if ENABLED(DGUS_LCD_UI_CREALITY_TOUCH) + +#include "../DGUSDisplayDef.h" +#include "../DGUSDisplay.h" +#include "../DGUSScreenHandler.h" + +#include "EstepsHandler.h" +#include "AxisSettingsHandler.h" + +#include "../../ui_api.h" +#include "../../../marlinui.h" + +#include "../../../../module/temperature.h" +#include "../../../../module/settings.h" +#include "../../../../module/planner.h" +#include "../../../../gcode/gcode.h" + +#if HAS_TRINAMIC_CONFIG +#include "../../../../feature/tmc_util.h" +#include "../../../../module/stepper/indirection.h" +#endif + +AxisEnum AxisSettingsHandler::current_axis; +uint16_t AxisSettingsHandler::axis_settings_title_icon = ICON_AXIS_SETTINGS_TITLE_X; + +float AxisSettingsHandler::axis_steps_mm; +uint16_t AxisSettingsHandler::max_acceleration_mm_per_s2; +float AxisSettingsHandler::jerk; +feedRate_t AxisSettingsHandler::max_feedrate; + +#if HAS_TRINAMIC_CONFIG +bool AxisSettingsHandler::has_tmc_settings = true; +#else +bool AxisSettingsHandler::has_tmc_settings = false; +#endif + +uint16_t AxisSettingsHandler::tmc_current; +bool AxisSettingsHandler::stealthchop; +uint32_t AxisSettingsHandler::hybrid_threshold; + +void AxisSettingsHandler::HandleNavigation(DGUS_VP_Variable &var, void *val_ptr) { + switch (uInt16Value(val_ptr)) { + case AXIS_SETTINGS_NAV_BUTTON_VAL_X: + current_axis = X_AXIS; + axis_settings_title_icon = ICON_AXIS_SETTINGS_TITLE_X; + break; + + case AXIS_SETTINGS_NAV_BUTTON_VAL_Y: + current_axis = Y_AXIS; + axis_settings_title_icon = ICON_AXIS_SETTINGS_TITLE_Y; + break; + + case AXIS_SETTINGS_NAV_BUTTON_VAL_Z: + current_axis = Z_AXIS; + axis_settings_title_icon = ICON_AXIS_SETTINGS_TITLE_Z; + break; + + case AXIS_SETTINGS_NAV_BUTTON_VAL_E: + current_axis = E_AXIS; + axis_settings_title_icon = ICON_AXIS_SETTINGS_TITLE_E; + break; + } + + // Load settings for axis + axis_steps_mm = planner.settings.axis_steps_per_mm[current_axis]; + max_acceleration_mm_per_s2 = static_cast(planner.settings.max_acceleration_mm_per_s2[current_axis]); + IF_ENABLED(CLASSIC_JERK, jerk = planner.max_jerk[current_axis]); + max_feedrate = planner.settings.max_feedrate_mm_s[current_axis]; + + #if HAS_TRINAMIC_CONFIG + switch (current_axis){ + #if AXIS_IS_TMC(X) + case X_AXIS: + tmc_current = stepperX.getMilliamps(); + + #if AXIS_HAS_STEALTHCHOP(X) + stealthchop = stepperX.get_stored_stealthChop(); + + #if ENABLED(HYBRID_THRESHOLD) + hybrid_threshold = static_cast(stepperX.get_pwm_thrs()); + #endif + #endif + break; + #endif + + #if AXIS_IS_TMC(Y) + case Y_AXIS: + tmc_current = stepperY.getMilliamps(); + + #if AXIS_HAS_STEALTHCHOP(Y) + stealthchop = stepperY.get_stored_stealthChop(); + + #if ENABLED(HYBRID_THRESHOLD) + hybrid_threshold = static_cast(stepperY.get_pwm_thrs()); + #endif + #endif + break; + #endif + + #if AXIS_IS_TMC(Z) + case Z_AXIS: + tmc_current = stepperZ.getMilliamps(); + + #if AXIS_HAS_STEALTHCHOP(Z) + stealthchop = stepperZ.get_stored_stealthChop(); + + #if ENABLED(HYBRID_THRESHOLD) + hybrid_threshold = static_cast(stepperZ.get_pwm_thrs()); + #endif + #endif + break; + #endif + + #if AXIS_IS_TMC(E0) + case E_AXIS: + tmc_current = stepperE0.getMilliamps(); + + #if AXIS_HAS_STEALTHCHOP(E0) + stealthchop = stepperE0.get_stored_stealthChop(); + + #if ENABLED(HYBRID_THRESHOLD) + hybrid_threshold = static_cast(stepperE0.get_pwm_thrs()); + #endif + #endif + break; + #endif + + default: + // Too bad + break; + } + #endif + + // Nav + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_AXIS_SETTINGS_AXIS); +} + +void AxisSettingsHandler::HandleBackNavigation(DGUS_VP_Variable &var, void *val_ptr) { + // Save settings for axis + planner.settings.axis_steps_per_mm[current_axis] = axis_steps_mm; + planner.settings.max_acceleration_mm_per_s2[current_axis] = max_acceleration_mm_per_s2; + IF_ENABLED(CLASSIC_JERK, planner.max_jerk[current_axis] = jerk); + planner.settings.max_feedrate_mm_s[current_axis] = max_feedrate; + + // If we're handling the E-axis, the back button might end on that screen. Show that we didn't forget the settings. + if (current_axis == E_AXIS) { + EstepsHandler::set_esteps = axis_steps_mm; + EstepsHandler::calculated_esteps = axis_steps_mm; + } + + #if HAS_TRINAMIC_CONFIG + switch (current_axis){ + #if AXIS_IS_TMC(X) + case X_AXIS: + stepperX.rms_current(tmc_current); + + #if AXIS_HAS_STEALTHCHOP(X) + stepperX.set_stealthChop(stealthchop); + + #if ENABLED(HYBRID_THRESHOLD) + stepperX.set_pwm_thrs(hybrid_threshold); + #endif + #endif + break; + #endif + + #if AXIS_IS_TMC(Y) + case Y_AXIS: + stepperY.rms_current(tmc_current); + + #if AXIS_HAS_STEALTHCHOP(Y) + stepperY.set_stealthChop(stealthchop); + + #if ENABLED(HYBRID_THRESHOLD) + stepperY.set_pwm_thrs(hybrid_threshold); + #endif + #endif + break; + #endif + + #if AXIS_IS_TMC(Z) + case Z_AXIS: + stepperZ.rms_current(tmc_current); + + #if AXIS_HAS_STEALTHCHOP(Z) + stepperZ.set_stealthChop(stealthchop); + + #if ENABLED(HYBRID_THRESHOLD) + stepperZ.set_pwm_thrs(hybrid_threshold); + #endif + #endif + break; + #endif + + #if AXIS_IS_TMC(E0) + case E_AXIS: + stepperE0.rms_current(tmc_current); + + #if AXIS_HAS_STEALTHCHOP(E0) + stepperE0.set_stealthChop(stealthchop); + + #if ENABLED(HYBRID_THRESHOLD) + stepperE0.set_pwm_thrs(hybrid_threshold); + #endif + #endif + break; + #endif + + default: + // Too bad + break; + } + #endif + + // Save and pop + ScreenHandler.PopToOldScreen(); + + settings.save(); +} + +void AxisSettingsHandler::HandleTMCNavigation(DGUS_VP_Variable &var, void *val_ptr) { + #if HAS_TRINAMIC_CONFIG + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_AXIS_SETTINGS_TMC); + #endif +} + +#endif \ No newline at end of file diff --git a/Marlin/src/lcd/extui/dgus_creality/creality_touch/AxisSettingsHandler.h b/Marlin/src/lcd/extui/dgus_creality/creality_touch/AxisSettingsHandler.h new file mode 100644 index 0000000000..02fce604ec --- /dev/null +++ b/Marlin/src/lcd/extui/dgus_creality/creality_touch/AxisSettingsHandler.h @@ -0,0 +1,26 @@ +#include + +class AxisSettingsHandler { + private: + static AxisEnum current_axis; + + public: + static uint16_t axis_settings_title_icon; + + static float axis_steps_mm; + static uint16_t max_acceleration_mm_per_s2; + + static float jerk; + static feedRate_t max_feedrate; + + static bool has_tmc_settings; + static uint16_t tmc_current; + static bool stealthchop; + static uint32_t hybrid_threshold; + + public: + static void HandleNavigation(DGUS_VP_Variable &var, void *val_ptr); + static void HandleBackNavigation(DGUS_VP_Variable &var, void *val_ptr); + static void HandleTMCNavigation(DGUS_VP_Variable &var, void *val_ptr); +}; + diff --git a/Marlin/src/lcd/extui/dgus_creality/creality_touch/DGUSDisplayDef.cpp b/Marlin/src/lcd/extui/dgus_creality/creality_touch/DGUSDisplayDef.cpp new file mode 100644 index 0000000000..24a01743ba --- /dev/null +++ b/Marlin/src/lcd/extui/dgus_creality/creality_touch/DGUSDisplayDef.cpp @@ -0,0 +1,782 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/* DGUS implementation written by Sebastiaan Dammann in 2020 for Marlin */ + +#include "../../../../inc/MarlinConfigPre.h" + +#if ENABLED(DGUS_LCD_UI_CREALITY_TOUCH) + +#include "../DGUSDisplayDef.h" +#include "../DGUSDisplay.h" +#include "../DGUSScreenHandler.h" +#include "AxisSettingsHandler.h" +#include "EstepsHandler.h" +#include "FilamentLoadUnloadHandler.h" +#include "PIDHandler.h" +#include "MeshValidationHandler.h" + +#include "../../../../module/temperature.h" +#include "../../../../module/motion.h" +#include "../../../../module/planner.h" + +#include "../../../../feature/caselight.h" + +#if ENABLED(FWRETRACT) + #include "../../../../feature/fwretract.h" +#endif + +#if HAS_COLOR_LEDS + #include "../../../../feature/leds/leds.h" +#endif + +#if ENABLED(POWER_LOSS_RECOVERY) + #include "../../../../feature/powerloss.h" +#endif + +#if ENABLED(FILAMENT_RUNOUT_SENSOR) +#include "../../../../feature/runout.h" +#endif + +#include "../../ui_api.h" +#include "../../../marlinui.h" + +#include "PageHandlers.h" + +#if ENABLED(DGUS_UI_MOVE_DIS_OPTION) + uint16_t distanceToMove = 10; +#endif +using namespace ExtUI; + + +const char MarlinVersion[] PROGMEM = SHORT_BUILD_VERSION; + +// ----- Common var lists +#if HOTENDS >= 1 +#define VPList_HeatHotend VP_T_E0_Is, VP_T_E0_Set, +#else +#define VPList_HeatHotend +#endif + +#if HAS_HEATED_BED +#define VPList_HeatBed VP_T_Bed_Is, VP_T_Bed_Set, +#else +#define VPList_HeatBed +#endif + +#define VPList_Common VP_BACK_BUTTON_STATE +#define VPList_CommonWithStatus VPList_HeatHotend VPList_HeatBed VP_Z_OFFSET, VP_Feedrate_Percentage, VP_BACK_BUTTON_STATE +#define VPList_CommonWithHeatOnly VPList_HeatHotend VPList_HeatBed VP_BACK_BUTTON_STATE + +// ----- Which variables to auto-update on which screens +const uint16_t VPList_None[] PROGMEM = { + VPList_Common, + + 0x0000 +}; + +const uint16_t VPList_DialogStop[] PROGMEM = { + VPList_Common, + + 0x0000 +}; + +const uint16_t VPList_Main[] PROGMEM = { + VPList_CommonWithStatus, + + 0x0000 +}; + +const uint16_t VPList_SDFileList[] PROGMEM = { + VPList_CommonWithStatus, + + VP_SD_FileName0, + VP_SD_FileName1, + VP_SD_FileName2, + VP_SD_FileName3, + VP_SD_FileName4, + VP_SD_FileName5, + + 0x0000 +}; + +const uint16_t VPList_Control[] PROGMEM = { + VPList_CommonWithStatus, + + VP_LED_TOGGLE, + VP_MUTE_ICON, + + 0x0000 +}; + +const uint16_t VPList_Feed[] PROGMEM = { + VPList_CommonWithStatus, + + VP_FILCHANGE_NOZZLE_TEMP, + VP_FILCHANGE_LENGTH, + + 0x0000 +}; + +const uint16_t VPList_Temp[] PROGMEM = { + VPList_CommonWithStatus, + + VP_FAN_TOGGLE, + + 0x0000 +}; + + +const uint16_t VPList_PreheatPLASettings[] PROGMEM = { + VPList_CommonWithStatus, + + VP_PREHEAT_PLA_HOTEND_TEMP, + VP_PREHEAT_PLA_BED_TEMP, + + 0x0000 +}; + +const uint16_t VPList_PreheatABSSettings[] PROGMEM = { + VPList_CommonWithStatus, + + VP_PREHEAT_ABS_HOTEND_TEMP, + VP_PREHEAT_ABS_BED_TEMP, + + 0x0000 +}; + + +const uint16_t VPList_PrintPausingError[] PROGMEM = { + VPList_CommonWithStatus, + + VP_X_POSITION, + VP_Y_POSITION, + VP_Z_POSITION_PRECISION, + VP_Z_OFFSET, + VP_Fan0_Percentage, + VP_Feedrate_Percentage, + + VP_PrintProgress_Percentage, + VP_PrintTimeProgressBar, + VP_PrintTime, + VP_PrintTimeWithRemainingVisible, + VP_PrintTimeRemaining, + VP_LINEAR_ADVANCE_FACTOR, + + 0x0000 +}; + +const uint16_t VPList_PrintScreen[] PROGMEM = { + VPList_CommonWithStatus, + + VP_X_POSITION, VP_Y_POSITION, VP_Z_POSITION, VP_Z_POSITION_PRECISION, + SP_X_POSITION, SP_Y_POSITION, SP_Z_POSITION, + + VP_Flowrate_E0, + VP_Fan0_Percentage, + + VP_PrintProgress_Percentage, + VP_PrintTimeProgressBar, + VP_PrintTime, + VP_PrintTimeWithRemainingVisible, + VP_PrintTimeRemaining, + VP_LINEAR_ADVANCE_FACTOR, + + VP_FWRETRACT_INDICATOR_ICON, + + 0x0000 +}; + +const uint16_t VPList_Leveling[] PROGMEM = { + VPList_CommonWithStatus, + + VP_MESH_LEVEL_TEMP, + + 0x0000 +}; + +const uint16_t VPList_ZOffsetLevel[] PROGMEM = { + VPList_CommonWithStatus, + + 0x0000 +}; + +const uint16_t VPList_TuneScreen[] PROGMEM = { + VPList_CommonWithStatus, + + VP_PrintTime, + + VP_Flowrate_E0, + + VP_LED_TOGGLE, + VP_FAN_TOGGLE, + VP_Fan0_Percentage, + + 0x0000 +}; + + +const uint16_t VPList_TuneExtraScreen[] PROGMEM = { + VPList_CommonWithStatus, + + VP_LINEAR_ADVANCE_FACTOR, + VP_RGB_NAV_BUTTON_ICON, + + 0x0000 +}; + + +const uint16_t VPList_Prepare[] PROGMEM = { + VPList_CommonWithStatus, + + VP_PrintTime, + + 0x0000 +}; + +const uint16_t VPList_Info[] PROGMEM = { + VPList_CommonWithStatus, + + VP_PrintTime, + + VP_PRINTER_BEDSIZE, + VP_MARLIN_WEBSITE, + VP_MARLIN_VERSION, + + 0x0000 +}; + +const uint16_t VPList_EstepsCalibration[] PROGMEM = { + VPList_CommonWithHeatOnly, + + VP_ESTEPS_CURRENT, + VP_ESTEPS_CALIBRATION_TEMP, + VP_ESTEPS_CALIBRATION_LENGTH, + VP_ESTEPS_CALIBRATION_LEFTOVER_LENGTH, + VP_ESTEPS_CALIBRATION_MARK_LENGTH, + VP_ESTEPS_CALCULATED_ESTEPS, + + 0x0000 +}; + +const uint16_t VPList_PidTune[] PROGMEM = { + VPList_CommonWithHeatOnly, + + VP_PIDTUNE_TARGET_TEMP, + VP_PIDTUNE_FAN_TOGGLE_ICON, + VP_PIDTUNE_CYCLES, + + 0x0000 +}; + +const uint16_t VPList_FWRetractTune[] PROGMEM = { + VPList_CommonWithStatus, + + VP_FWRETRACT_RETRACT_LENGTH, + VP_FWRETRACT_RETRACT_FEEDRATE, + VP_FWRETRACT_RETRACT_ZHOP, + VP_FWRETRACT_RESTART_LENGTH, + VP_FWRETRACT_RESTART_FEEDRATE, + + VP_FWRETRACT_TOGGLE_BUTTON_ICON, + + 0x0000 +}; + +const uint16_t VPList_LevelingSettings[] PROGMEM = { + VPList_CommonWithStatus, + + VP_TOGGLE_PROBING_HEATERS_OFF_ONOFF_ICON, + VP_TOGGLE_PROBE_PREHEAT_HOTEND_TEMP, + VP_TOGGLE_PROBE_PREHEAT_BED_TEMP, + VP_TOGGLE_POST_PROBING_TEMPERATURE_STABILIZATION_ICON, + VP_LEVELING_FADE_HEIGHT, + + 0x0000 +}; + +const uint16_t VPList_AxisSettingsNav[] PROGMEM = { + VPList_CommonWithStatus, + + 0x0000 +}; + +const uint16_t VPList_AxisSettingsAxis[] PROGMEM = { + VPList_CommonWithHeatOnly, + + VP_AXIS_SETTINGS_TITLE_ICON, + + VP_AXIS_SETTINGS_AXIS_STEPSMM, + VP_AXIS_SETTINGS_AXIS_MAX_ACCEL, + VP_AXIS_SETTINGS_AXIS_JERK, + VP_AXIS_SETTINGS_AXIS_FEEDRATE, + + VP_AXIS_TMC_NAV_ICON, + + 0x0000 +}; + +const uint16_t VPList_AxisSettingsTMC[] PROGMEM = { + VPList_CommonWithHeatOnly, + + VP_AXIS_SETTINGS_AXIS_TMCCURRENT, + VP_AXIS_SETTINGS_AXIS_TMCSTEALTHCHOP_ICON, + VP_AXIS_SETTINGS_AXIS_TMCHYBRIDTHRESHOLD, + + 0x0000 +}; + +const uint16_t VPList_AdvMovementSettings[] PROGMEM = { + VPList_CommonWithHeatOnly, + + VP_MOV_MINIMUM_SEGMENT_TIME, + VP_MOV_MINIMUM_FEEDRATE, + VP_MOV_NORMAL_ACCELERATION, + VP_MOV_RETRACT_ACCELERATION, + + VP_MOV_MINIMUM_TRAVEL_FEEDRATE, + VP_MOV_MINIMUM_TRAVEL_ACCELERATION, + + 0x0000 +}; + +const uint16_t VPList_MiscSettings[] PROGMEM = { + VPList_CommonWithHeatOnly, + + VP_FILAMENTRUNOUT_SENSOR_TOGGLE_ICON, + + VP_PLR_TOGGLE_ICON, + + VP_STANDBY_BACKLIGHT_ICON, + VP_SCREEN_BACKLIGHT_STANDBY, + VP_SCREEN_BACKLIGHT, + VP_SCREEN_STANDBY_TIME, + VP_RGB_NAV_BUTTON_ICON, + + 0x0000 +}; + +const uint16_t VPList_MeshValidation[] PROGMEM = { + VPList_CommonWithHeatOnly, + + VP_MESHPATTERN_NOZZLE_TEMP, + VP_MESHPATTERN_BED_TEMP, + + VP_MESHPATTERN_BUTTON_ICON, + + 0x0000 +}; + +const uint16_t VPList_Calibrate[] PROGMEM = { + VPList_CommonWithStatus, + + 0x0000 +}; + +const uint16_t VPList_RGB[] PROGMEM = { + VPList_CommonWithHeatOnly, + + VP_RGB_CONTROL_R, + VP_RGB_CONTROL_G, + VP_RGB_CONTROL_B, + VP_RGB_CONTROL_W, + VP_RGB_CONTROL_I, + + 0x0000 +}; + + +// -- Mapping from screen to variable list +const struct VPMapping VPMap[] PROGMEM = { + { DGUSLCD_SCREEN_BOOT, VPList_None }, + { DGUSLCD_SCREEN_MAIN, VPList_Main }, + + { DGUSLCD_SCREEN_SDFILELIST, VPList_SDFileList }, + + { DGUSLCD_SCREEN_FILAMENTRUNOUT1, VPList_PrintPausingError }, + { DGUSLCD_SCREEN_FILAMENTRUNOUT2, VPList_PrintPausingError }, + + { DGUSLCD_SCREEN_PRINT_FINISH, VPList_PrintScreen }, + { DGUSLCD_SCREEN_PRINT_RUNNING, VPList_PrintScreen }, + { DGUSLCD_SCREEN_PRINT_PAUSED, VPList_PrintScreen }, + + { DGUSLCD_SCREEN_TUNING, VPList_TuneScreen }, + { DGUSLCD_SCREEN_TUNING_EXTRA, VPList_TuneExtraScreen }, + { DGUSLCD_SCREEN_PREPARE, VPList_Prepare }, + + { DGUSLCD_SCREEN_INFO, VPList_Info }, + + { DGUSLCD_SCREEN_MOVE1MM, VPList_PrintScreen }, + { DGUSLCD_SCREEN_MOVE10MM, VPList_PrintScreen }, + { DGUSLCD_SCREEN_MOVE01MM, VPList_PrintScreen }, + + { DGUSLCD_SCREEN_FEED, VPList_Feed }, + { DGUSLCD_SCREEN_SETUP, VPList_Control }, + + { DGUSLCD_SCREEN_TEMP, VPList_Temp }, + { DGUSLCD_SCREEN_TEMP_PLA, VPList_PreheatPLASettings }, + { DGUSLCD_SCREEN_TEMP_ABS, VPList_PreheatABSSettings }, + + { DGUSLCD_SCREEN_INFO, VPList_PrintScreen }, + { DGUSLCD_SCREEN_ZOFFSET_LEVEL, VPList_ZOffsetLevel }, + { DGUSLCD_SCREEN_LEVELING, VPList_Leveling }, + + { DGUSLCD_SCREEN_POWER_LOSS, VPList_None }, + { DGUSLCD_SCREEN_THERMAL_RUNAWAY, VPList_None }, + { DGUSLCD_SCREEN_HEATING_FAILED, VPList_None }, + { DGUSLCD_SCREEN_THERMISTOR_ERROR, VPList_None }, + + { DGUSLCD_SCREEN_AUTOHOME, VPList_PrintScreen }, + + { DGUSLCD_SCREEN_DIALOG_PAUSE, VPList_None }, + { DGUSLCD_SCREEN_DIALOG_STOP, VPList_DialogStop }, + + { DGUSLCD_SCREEN_CONFIRM, VPList_None }, + { DGUSLCD_SCREEN_POPUP, VPList_None }, + + { DGUSLCD_SCREEN_ESTEPS_CALIBRATION, VPList_EstepsCalibration }, + { DGUSLCD_SCREEN_PIDTUNE_CALIBRATION, VPList_PidTune }, + + { DGUSLCD_SCREEN_TUNEFWRETRACT, VPList_FWRetractTune }, + + { DGUSLCD_SCREEN_ESTEPS_CALIBRATION_RESULTS, VPList_EstepsCalibration }, + { DGUSLCD_SCREEN_LEVELING_SETTINGS, VPList_LevelingSettings }, + + { DGUSLCD_SCREEN_AXIS_SETTINGS_NAV, VPList_AxisSettingsNav }, + { DGUSLCD_SCREEN_AXIS_SETTINGS_AXIS , VPList_AxisSettingsAxis }, + { DGUSLCD_SCREEN_AXIS_SETTINGS_TMC, VPList_AxisSettingsTMC }, + { DGUSLCD_SCREEN_ADV_MOV_SETTINGS, VPList_AdvMovementSettings }, + + { DGUSLCD_SCREEN_MISC_SETTINGS, VPList_MiscSettings }, + { DGUSLCD_SCREEN_MESH_VALIDATION, VPList_MeshValidation }, + + { DGUSLCD_SCREEN_CALIBRATE, VPList_Calibrate }, + { DGUSLCD_SCREEN_RGB, VPList_RGB}, + + { 0 , nullptr } // List is terminated with an nullptr as table entry. +}; + +// Helper to define a DGUS_VP_Variable for common use cases. +#define VPHELPER(VPADR, VPADRVAR, RXFPTR, TXFPTR ) { .VP=VPADR, .memadr=VPADRVAR, .size=sizeof(VPADRVAR), \ + .set_by_display_handler = RXFPTR, .send_to_display_handler = TXFPTR } + +// Helper to define a DGUS_VP_Variable when the sizeo of the var cannot be determined automaticalyl (eg. a string) +#define VPHELPER_STR(VPADR, VPADRVAR, STRLEN, RXFPTR, TXFPTR ) { .VP=VPADR, .memadr=VPADRVAR, .size=STRLEN, \ + .set_by_display_handler = RXFPTR, .send_to_display_handler = TXFPTR } + +const struct DGUS_VP_Variable ListOfVP[] PROGMEM = { + // Back button state + VPHELPER(VP_BACK_BUTTON_STATE, nullptr, nullptr, ScreenHandler.SendBusyState), + + // Screen version + VPHELPER(VP_UI_VERSION_MAJOR, nullptr, ScreenHandler.HandleScreenVersion, nullptr), + + #if HOTENDS >= 1 + VPHELPER(VP_Flowrate_E0, &planner.flow_percentage[ExtUI::extruder_t::E0], ScreenHandler.HandleFlowRateChanged, &ScreenHandler.DGUSLCD_SendWordValueToDisplay), + VPHELPER(VP_T_E0_Is, &thermalManager.temp_hotend[0].celsius, nullptr, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<0>), + VPHELPER(VP_T_E0_Set, &thermalManager.temp_hotend[0].target, ScreenHandler.HandleTemperatureChanged, &ScreenHandler.DGUSLCD_SendWordValueToDisplay), + #endif + + #if HAS_HEATED_BED + VPHELPER(VP_T_Bed_Is, &thermalManager.temp_bed.celsius, nullptr, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<0>), + VPHELPER(VP_T_Bed_Set, &thermalManager.temp_bed.target, ScreenHandler.HandleTemperatureChanged, &ScreenHandler.DGUSLCD_SendWordValueToDisplay), + #endif + + VPHELPER(VP_MESH_LEVEL_TEMP, &thermalManager.temp_hotend[0].target, nullptr, &ScreenHandler.DGUSLCD_SendWordValueToDisplay), + + // Feedrate + VPHELPER(VP_Feedrate_Percentage, &feedrate_percentage, ScreenHandler.DGUSLCD_SetValueDirectly, &ScreenHandler.DGUSLCD_SendWordValueToDisplay), + + VPHELPER(VP_PrintProgress_Percentage, nullptr, nullptr, ScreenHandler.DGUSLCD_SendPrintProgressToDisplay), + VPHELPER(VP_PrintTimeProgressBar, nullptr, nullptr, ScreenHandler.DGUSLCD_SendPrintProgressToDisplay), + + // Calibration + // ... e-steps + VPHELPER(VP_ESTEPS_CURRENT, &EstepsHandler::set_esteps, ScreenHandler.DGUSLCD_SetFloatAsIntFromDisplay<1>, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<1>), + VPHELPER(VP_ESTEPS_CALIBRATION_TEMP, &EstepsHandler::calibration_temperature, ScreenHandler.DGUSLCD_SetValueDirectly, ScreenHandler.DGUSLCD_SendWordValueToDisplay), + VPHELPER(VP_ESTEPS_CALIBRATION_LENGTH, &EstepsHandler::filament_to_extrude, ScreenHandler.DGUSLCD_SetFloatAsIntFromDisplay<1>, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<1>), + VPHELPER(VP_ESTEPS_CALIBRATION_MARK_LENGTH, &EstepsHandler::mark_filament_mm, ScreenHandler.DGUSLCD_SetFloatAsIntFromDisplay<1>, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<1>), + VPHELPER(VP_ESTEPS_CALIBRATION_LEFTOVER_LENGTH, &EstepsHandler::remaining_filament, EstepsHandler::HandleRemainingFilament, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<1>), + VPHELPER(VP_ESTEPS_CALCULATED_ESTEPS, &EstepsHandler::calculated_esteps, ScreenHandler.DGUSLCD_SetFloatAsIntFromDisplay<1>, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<1>), + + VPHELPER(VP_ESTEPS_CALIBRATESTART_BUTTON, nullptr, EstepsHandler::HandleStartButton, nullptr), + VPHELPER(VP_ESTEPS_APPLY_BUTTON, nullptr, EstepsHandler::HandleApplyButton, nullptr), + VPHELPER(VP_ESTEPS_BACK_BUTTON, nullptr, EstepsHandler::HandleBackButton, nullptr), + + VPHELPER(VP_ESTEP_NAV_BUTTON, nullptr, (ScreenHandler.DGUSLCD_NavigateToPage), nullptr), + + // ... PID + VPHELPER(VP_PIDTUNE_TARGET_TEMP, &PIDHandler::calibration_temperature, ScreenHandler.DGUSLCD_SetValueDirectly, ScreenHandler.DGUSLCD_SendWordValueToDisplay), + VPHELPER(VP_PIDTUNE_CYCLES, &PIDHandler::cycles, ScreenHandler.DGUSLCD_SetValueDirectly, ScreenHandler.DGUSLCD_SendWordValueToDisplay), + VPHELPER(VP_PIDTUNE_FAN_TOGGLE, &PIDHandler::fan_on, ScreenHandler.DGUSLCD_ToggleBoolean, nullptr), + VPHELPER(VP_PIDTUNE_FAN_TOGGLE_ICON, &PIDHandler::fan_on, nullptr, (ScreenHandler.DGUSLCD_SendIconValue)), + + VPHELPER(VP_PIDTUNE_START_BUTTON, nullptr, PIDHandler::HandleStartButton, nullptr), + + VPHELPER(VP_PIDTUNE_NAV_BUTTON, nullptr, (ScreenHandler.DGUSLCD_NavigateToPage), nullptr), + + VPHELPER(VP_GENERIC_BACK_BUTTON, nullptr, ScreenHandler.OnBackButton, nullptr), + + // ... Mesh validation + VPHELPER(VP_MESHPATTERN_NOZZLE_TEMP, &MeshValidationHandler::nozzle_temperature, MeshValidationHandler::HandleTemperature, ScreenHandler.DGUSLCD_SendWordValueToDisplay), + VPHELPER(VP_MESHPATTERN_BED_TEMP, &MeshValidationHandler::bed_temperature, MeshValidationHandler::HandleTemperature, ScreenHandler.DGUSLCD_SendWordValueToDisplay), + + VPHELPER(VP_MESHPATTERN_BUTTON_ICON, &MeshValidationHandler::is_running, nullptr, (ScreenHandler.DGUSLCD_SendIconValue)), + + VPHELPER(VP_MESHPATTERN_START_BUTTON, nullptr, MeshValidationHandler::HandleStartOrCancelButton, nullptr), + VPHELPER(VP_MESHPATTERN_NAV_BUTTON, nullptr, (ScreenHandler.DGUSLCD_NavigateToPage), nullptr), + + + // Axis settings + VPHELPER(VP_AXIS_SETTINGS_NAV_BUTTON, nullptr, AxisSettingsHandler::HandleNavigation, nullptr), + VPHELPER(VP_AXIS_SETTINGS_TITLE_ICON, &AxisSettingsHandler::axis_settings_title_icon, nullptr, ScreenHandler.DGUSLCD_SendWordValueToDisplay), + + VPHELPER(VP_AXIS_SETTINGS_AXIS_STEPSMM, &AxisSettingsHandler::axis_steps_mm, ScreenHandler.DGUSLCD_SetFloatAsIntFromDisplay<1>, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<1>), + VPHELPER(VP_AXIS_SETTINGS_AXIS_MAX_ACCEL, &AxisSettingsHandler::max_acceleration_mm_per_s2, ScreenHandler.DGUSLCD_SetValueDirectly, ScreenHandler.DGUSLCD_SendWordValueToDisplay), + + VPHELPER(VP_AXIS_SETTINGS_AXIS_JERK, &AxisSettingsHandler::jerk, ScreenHandler.DGUSLCD_SetFloatAsIntFromDisplay<1>, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<1>), + VPHELPER(VP_AXIS_SETTINGS_AXIS_FEEDRATE, &AxisSettingsHandler::max_feedrate, ScreenHandler.DGUSLCD_SetFloatAsIntFromDisplay<1>, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<1>), + + VPHELPER(VP_AXIS_SETTINGS_AXIS_TMCCURRENT, &AxisSettingsHandler::tmc_current, ScreenHandler.DGUSLCD_SetValueDirectly, ScreenHandler.DGUSLCD_SendWordValueToDisplay), + VPHELPER(VP_AXIS_SETTINGS_AXIS_TMCSTEALTHCHOP_BUTTON, &AxisSettingsHandler::stealthchop, ScreenHandler.DGUSLCD_ToggleBoolean, nullptr), + VPHELPER(VP_AXIS_SETTINGS_AXIS_TMCSTEALTHCHOP_ICON, &AxisSettingsHandler::stealthchop, nullptr, (ScreenHandler.DGUSLCD_SendIconValue)), + VPHELPER(VP_AXIS_SETTINGS_AXIS_TMCHYBRIDTHRESHOLD, &AxisSettingsHandler::hybrid_threshold, ScreenHandler.DGUSLCD_ReceiveULongFromDisplay, ScreenHandler.DGUSLCD_SendULongToDisplay), + + VPHELPER(VP_AXIS_TMC_NAV_ICON, &AxisSettingsHandler::has_tmc_settings, nullptr, (ScreenHandler.DGUSLCD_SendIconValue)), + + VPHELPER(VP_AXIS_SETTINGS_NAV_BACKBUTTON, nullptr, AxisSettingsHandler::HandleBackNavigation, nullptr), + + VPHELPER(VP_AXIS_TUNING_NAV_BUTTON, nullptr, (ScreenHandler.DGUSLCD_NavigateToPage), nullptr), + VPHELPER(VP_AXIS_TMC_NAV_BUTTON, nullptr, (ScreenHandler.DGUSLCD_NavigateToPage), nullptr), + + // Advanced movement settings + VPHELPER(VP_MOV_NAV_BUTTON, nullptr, (ScreenHandler.DGUSLCD_NavigateToPage), nullptr), + + VPHELPER(VP_MOV_MINIMUM_SEGMENT_TIME, &planner.settings.min_segment_time_us, ScreenHandler.DGUSLCD_SetValueDirectly, ScreenHandler.DGUSLCD_SendWordValueToDisplay), + + VPHELPER(VP_MOV_MINIMUM_FEEDRATE, &planner.settings.min_feedrate_mm_s, ScreenHandler.DGUSLCD_SetFloatAsIntFromDisplay<1>, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<1>), + VPHELPER(VP_MOV_NORMAL_ACCELERATION, &planner.settings.acceleration, ScreenHandler.DGUSLCD_SetFloatAsIntFromDisplay<1>, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<1>), + VPHELPER(VP_MOV_RETRACT_ACCELERATION, &planner.settings.retract_acceleration, ScreenHandler.DGUSLCD_SetFloatAsLongFromDisplay<1>, ScreenHandler.DGUSLCD_SendFloatAsLongValueToDisplay<1>), + + VPHELPER(VP_MOV_MINIMUM_TRAVEL_FEEDRATE, &planner.settings.min_travel_feedrate_mm_s, ScreenHandler.DGUSLCD_SetFloatAsIntFromDisplay<1>, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<1>), + VPHELPER(VP_MOV_MINIMUM_TRAVEL_ACCELERATION, &planner.settings.travel_acceleration, ScreenHandler.DGUSLCD_SetFloatAsIntFromDisplay<1>, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<1>), + + // Misc settings + VPHELPER(VP_MISCSETTINGS_NAV_BUTTON, nullptr, (ScreenHandler.DGUSLCD_NavigateToPage), nullptr), + +#if ENABLED(FILAMENT_RUNOUT_SENSOR) + VPHELPER(VP_FILAMENTRUNOUT_SENSOR_TOGGLE_BUTTON, &runout.enabled, ScreenHandler.DGUSLCD_ToggleBoolean, nullptr), + VPHELPER(VP_FILAMENTRUNOUT_SENSOR_TOGGLE_ICON, &runout.enabled, nullptr, (ScreenHandler.DGUSLCD_SendIconValue)), +#endif + +#if ENABLED(POWER_LOSS_RECOVERY) + VPHELPER(VP_PLR_TOGGLE_BUTTON, nullptr, ScreenHandler.TogglePowerLossRecovery, nullptr), + VPHELPER(VP_PLR_TOGGLE_ICON, &PrintJobRecovery::enabled, nullptr, (ScreenHandler.DGUSLCD_SendIconValue)), +#endif + + // Preheat settings + #ifdef PREHEAT_1_LABEL + VPHELPER(VP_PREHEAT_PLA_HOTEND_TEMP, &ui.material_preset[0].hotend_temp, ScreenHandler.DGUSLCD_SetValueDirectly, &ScreenHandler.DGUSLCD_SendWordValueToDisplay), + VPHELPER(VP_PREHEAT_PLA_BED_TEMP, &ui.material_preset[0].bed_temp, ScreenHandler.DGUSLCD_SetValueDirectly, &ScreenHandler.DGUSLCD_SendWordValueToDisplay), + #endif + + #ifdef PREHEAT_2_LABEL + VPHELPER(VP_PREHEAT_ABS_HOTEND_TEMP, &ui.material_preset[1].hotend_temp, ScreenHandler.DGUSLCD_SetValueDirectly, &ScreenHandler.DGUSLCD_SendWordValueToDisplay), + VPHELPER(VP_PREHEAT_ABS_BED_TEMP, &ui.material_preset[1].bed_temp, ScreenHandler.DGUSLCD_SetValueDirectly, &ScreenHandler.DGUSLCD_SendWordValueToDisplay), + #endif + + // About info + VPHELPER_STR(VP_MARLIN_WEBSITE, nullptr, VP_MARLIN_WEBSITE_LEN, nullptr, ScreenHandler.DGUSLCD_SendAboutFirmwareWebsite), + VPHELPER_STR(VP_MARLIN_VERSION, nullptr, VP_MARLIN_VERSION_LEN, nullptr, ScreenHandler.DGUSLCD_SendAboutFirmwareVersion), + VPHELPER_STR(VP_PRINTER_BEDSIZE, nullptr, VP_PRINTER_BEDSIZE_LEN, nullptr, ScreenHandler.DGUSLCD_SendAboutPrintSize), + + // Position Data + VPHELPER(VP_X_POSITION, ¤t_position.x, ScreenHandler.HandlePositionChange, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<1>), + VPHELPER(VP_Y_POSITION, ¤t_position.y, ScreenHandler.HandlePositionChange, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<1>), + VPHELPER(VP_Z_POSITION, ¤t_position.z, ScreenHandler.HandlePositionChange, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<1>), + VPHELPER(VP_Z_POSITION_PRECISION, ¤t_position.z, nullptr, ScreenHandler.DGUSLCD_SendFloatAsLongValueToDisplay<2>), + + VPHELPER(SP_X_POSITION, nullptr, nullptr, ScreenHandler.SendAxisTrustValue), + VPHELPER(SP_Y_POSITION, nullptr, nullptr, ScreenHandler.SendAxisTrustValue), + VPHELPER(SP_Z_POSITION, nullptr, nullptr, ScreenHandler.SendAxisTrustValue), + + VPHELPER(VP_Z_OFFSET, &probe.offset.z, ScreenHandler.HandleZoffsetChange, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<2>), + + VPHELPER(VP_FAN_TOGGLE, &thermalManager.fan_speed[0], nullptr, ScreenHandler.DGUSLCD_SendFanStatusToDisplay), + VPHELPER(VP_Fan0_Percentage, &thermalManager.fan_speed[0], ScreenHandler.HandleFanSpeedChanged, ScreenHandler.DGUSLCD_SendFanSpeedToDisplay), + + #if ENABLED(POWER_LOSS_RECOVERY) + VPHELPER(VP_POWER_LOSS_RECOVERY, nullptr, &ScreenHandler.HandlePowerLossRecovery, nullptr), + #endif + + VPHELPER_STR(VP_PrintTime, nullptr, VP_PrintTime_LEN, nullptr, ScreenHandler.DGUSLCD_SendPrintTimeToDisplay), + VPHELPER_STR(VP_PrintTimeWithRemainingVisible, nullptr, VP_PrintTime_LEN, nullptr, ScreenHandler.DGUSLCD_SendPrintTimeWithRemainingToDisplay), + VPHELPER_STR(VP_PrintTimeRemaining, nullptr, VP_PrintTimeRemaining_LEN, nullptr, ScreenHandler.DGUSLCD_SendPrintTimeRemainingToDisplay), + VPHELPER(VP_SCREENCHANGE, nullptr, ScreenHandler.ScreenChangeHook, nullptr), + VPHELPER(VP_CONFIRMED, nullptr, ScreenHandler.ScreenConfirmedOK, nullptr), + + #if HAS_PROBE_SETTINGS + VPHELPER(VP_TOGGLE_PROBING_HEATERS_OFF_ONOFF_BUTTON, nullptr, ScreenHandler.HandleToggleProbeHeaters, nullptr), + VPHELPER(VP_TOGGLE_PROBING_HEATERS_OFF_ONOFF_ICON, &probe.settings.turn_heaters_off, nullptr, (ScreenHandler.DGUSLCD_SendIconValue)), + + VPHELPER(VP_TOGGLE_POST_PROBING_TEMPERATURE_STABILIZATION_BUTTON, nullptr, ScreenHandler.HandleToggleProbeTemperatureStabilization, nullptr), + VPHELPER(VP_TOGGLE_POST_PROBING_TEMPERATURE_STABILIZATION_ICON, &probe.settings.stabilize_temperatures_after_probing, nullptr, (ScreenHandler.DGUSLCD_SendIconValue)), + + VPHELPER(VP_TOGGLE_PROBE_PREHEAT_HOTEND_TEMP, &probe.settings.preheat_hotend_temp, ScreenHandler.HandleToggleProbePreheatTemp, ScreenHandler.DGUSLCD_SendWordValueToDisplay), + VPHELPER(VP_TOGGLE_PROBE_PREHEAT_BED_TEMP, &probe.settings.preheat_bed_temp, ScreenHandler.HandleToggleProbePreheatTemp, ScreenHandler.DGUSLCD_SendWordValueToDisplay), + #endif + + VPHELPER(VP_LEVELING_FADE_HEIGHT, &planner.z_fade_height, ScreenHandler.HandleFadeHeight, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<1>), + + VPHELPER(VP_LEVELING_NAV_BUTTON, nullptr, (ScreenHandler.DGUSLCD_NavigateToPage), nullptr), + VPHELPER(VP_LEVELING_EDIT_NAV_BUTTON, nullptr, (ScreenHandler.DGUSLCD_NavigateToPage), nullptr), + VPHELPER(VP_TOGGLE_PROBE_SETTINGS_NAV_BUTTON, nullptr, (ScreenHandler.DGUSLCD_NavigateToPage), nullptr), + + + // Creality has the same button ID mapped all over the place, so let the generic handler figure it out + VPHELPER(VP_BUTTON_MAINENTERKEY, nullptr, DGUSCrealityDisplay_HandleReturnKeyEvent, nullptr), + VPHELPER(VP_BUTTON_ADJUSTENTERKEY, nullptr, DGUSCrealityDisplay_HandleReturnKeyEvent, nullptr), + VPHELPER(VP_BUTTON_PREPAREENTERKEY, nullptr, DGUSCrealityDisplay_HandleReturnKeyEvent, nullptr), + VPHELPER(VP_BUTTON_RESUMEPRINTKEY, nullptr, DGUSCrealityDisplay_HandleReturnKeyEvent, nullptr), + VPHELPER(VP_BUTTON_SELECTFILEKEY, nullptr, DGUSCrealityDisplay_HandleReturnKeyEvent, nullptr), + VPHELPER(VP_BUTTON_STARTPRINTKEY, nullptr, DGUSCrealityDisplay_HandleReturnKeyEvent, nullptr), + VPHELPER(VP_BUTTON_STOPPRINTKEY, nullptr, DGUSCrealityDisplay_HandleReturnKeyEvent, nullptr), + VPHELPER(VP_BUTTON_PAUSEPRINTKEY, nullptr, DGUSCrealityDisplay_HandleReturnKeyEvent, nullptr), + VPHELPER(VP_BUTTON_COOLDOWN, nullptr, DGUSCrealityDisplay_HandleReturnKeyEvent, nullptr), + VPHELPER(VP_BUTTON_TEMPCONTROL, nullptr, DGUSCrealityDisplay_HandleReturnKeyEvent, nullptr), + VPHELPER(VP_BUTTON_BEDLEVELKEY, nullptr, DGUSCrealityDisplay_HandleReturnKeyEvent, nullptr), + VPHELPER(VP_BUTTON_HEATLOADSTARTKEY, nullptr, DGUSCrealityDisplay_HandleReturnKeyEvent, nullptr), + VPHELPER(VP_BUTTON_MOVEKEY, nullptr, DGUSCrealityDisplay_HandleReturnKeyEvent, nullptr), + + // File listing + VPHELPER(VP_SD_ScrollEvent, nullptr, ScreenHandler.DGUSLCD_SD_ScrollFilelist, nullptr), + VPHELPER(VP_SD_FileSelected, nullptr, ScreenHandler.DGUSLCD_SD_FileSelected, nullptr), + VPHELPER(VP_SD_FileSelectConfirm, nullptr, ScreenHandler.DGUSLCD_SD_StartPrint, nullptr), + VPHELPER_STR(VP_SD_FileName0, nullptr, VP_SD_FileName_LEN, nullptr, ScreenHandler.DGUSLCD_SD_SendFilename), + VPHELPER_STR(VP_SD_FileName1, nullptr, VP_SD_FileName_LEN, nullptr, ScreenHandler.DGUSLCD_SD_SendFilename), + VPHELPER_STR(VP_SD_FileName2, nullptr, VP_SD_FileName_LEN, nullptr, ScreenHandler.DGUSLCD_SD_SendFilename), + VPHELPER_STR(VP_SD_FileName3, nullptr, VP_SD_FileName_LEN, nullptr, ScreenHandler.DGUSLCD_SD_SendFilename), + VPHELPER_STR(VP_SD_FileName4, nullptr, VP_SD_FileName_LEN, nullptr, ScreenHandler.DGUSLCD_SD_SendFilename), + VPHELPER_STR(VP_SD_FileName5, nullptr, VP_SD_FileName_LEN, nullptr, ScreenHandler.DGUSLCD_SD_SendFilename), + + // Firmware retract + VPHELPER(VP_FWRETRACT_NAV_BUTTON, nullptr, ScreenHandler.DGUSLCD_NavigateToPage, nullptr), + + VPHELPER(VP_FWRETRACT_RETRACT_LENGTH, &fwretract.settings.retract_length, ScreenHandler.DGUSLCD_SetFloatAsIntFromDisplay<1>, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<1>), + VPHELPER(VP_FWRETRACT_RETRACT_FEEDRATE, &fwretract.settings.retract_feedrate_mm_s, ScreenHandler.DGUSLCD_SetFloatAsIntFromDisplay<1>, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<1>), + VPHELPER(VP_FWRETRACT_RETRACT_ZHOP, &fwretract.settings.retract_zraise, ScreenHandler.DGUSLCD_SetFloatAsIntFromDisplay<1>, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<1>), + + VPHELPER(VP_FWRETRACT_RESTART_LENGTH, &fwretract.settings.retract_recover_extra, ScreenHandler.DGUSLCD_SetFloatAsIntFromDisplay<1>, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<1>), + VPHELPER(VP_FWRETRACT_RESTART_FEEDRATE, &fwretract.settings.retract_recover_feedrate_mm_s, ScreenHandler.DGUSLCD_SetFloatAsIntFromDisplay<1>, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<1>), + + VPHELPER(VP_FWRETRACT_INDICATOR_ICON, &fwretract.autoretract_enabled, nullptr, (ScreenHandler.DGUSLCD_SendIconValue)), + VPHELPER(VP_FWRETRACT_TOGGLE_BUTTON_ICON, &fwretract.autoretract_enabled, nullptr, (ScreenHandler.DGUSLCD_SendIconValue)), + VPHELPER(VP_FWRETRACT_TOGGLE_BUTTON, &fwretract.autoretract_enabled, ScreenHandler.DGUSLCD_ToggleBoolean, nullptr), + + // Other tuning +#if ENABLED(LIN_ADVANCE) + VPHELPER(VP_LINEAR_ADVANCE_FACTOR, &planner.extruder_advance_K[0], ScreenHandler.DGUSLCD_SetFloatAsIntFromDisplay<2>, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<2>), +#endif + + VPHELPER(VP_OTHER_TUNE_NAV_BUTTON, nullptr, (ScreenHandler.DGUSLCD_NavigateToPage), nullptr), + + + // Additional buttons + VPHELPER(VP_MUTE_TOGGLE, nullptr, ScreenHandler.HandleToggleTouchScreenMute, nullptr), + VPHELPER(VP_STANDBY_BACKLIGHT_TOGGLE, nullptr, ScreenHandler.HandleToggleTouchScreenStandbySetting, nullptr), + + // Additional settings + VPHELPER(VP_SCREEN_BACKLIGHT_STANDBY, &ScreenHandler.Settings.standby_screen_brightness, ScreenHandler.HandleTouchScreenStandbyBrightnessSetting, ScreenHandler.DGUSLCD_SendWordValueToDisplay), + VPHELPER(VP_SCREEN_BACKLIGHT, &ScreenHandler.Settings.screen_brightness, ScreenHandler.HandleTouchScreenBrightnessSetting, ScreenHandler.DGUSLCD_SendWordValueToDisplay), + VPHELPER(VP_SCREEN_STANDBY_TIME, &ScreenHandler.Settings.standby_time_seconds, ScreenHandler.HandleTouchScreenStandbyTimeSetting, ScreenHandler.DGUSLCD_SendWordValueToDisplay), + + // RGB + VPHELPER(VP_RGB_NAV_BUTTON_ICON, &ScreenHandler.HasRGBSettings, nullptr, (ScreenHandler.DGUSLCD_SendIconValue)), + + #if HAS_COLOR_LEDS + VPHELPER(VP_RGB_NAV_BUTTON, nullptr, ScreenHandler.DGUSLCD_NavigateToPage, nullptr), + + VPHELPER(VP_RGB_CONTROL_R, &leds.color.r, ScreenHandler.HandleLED, ScreenHandler.SendLEDToDisplay), + VPHELPER(VP_RGB_CONTROL_G, &leds.color.g, ScreenHandler.HandleLED, ScreenHandler.SendLEDToDisplay), + VPHELPER(VP_RGB_CONTROL_B, &leds.color.b, ScreenHandler.HandleLED, ScreenHandler.SendLEDToDisplay), + + #if EITHER(RGBW_LED, NEOPIXEL_LED) + VPHELPER(VP_RGB_CONTROL_W, &leds.color.w, ScreenHandler.HandleLED, ScreenHandler.SendLEDToDisplay), + + #if ENABLED(NEOPIXEL_LED) + VPHELPER(VP_RGB_CONTROL_I, &leds.color.i, ScreenHandler.HandleLED, ScreenHandler.SendLEDToDisplay), + #endif + #endif + #endif + + // Filament load/unload + VPHELPER(VP_FILCHANGE_NAV_BUTTON, nullptr, (ScreenHandler.DGUSLCD_NavigateToPage), nullptr), + + VPHELPER(VP_FILCHANGE_LENGTH, &FilamentLoadUnloadHandler::length, ScreenHandler.DGUSLCD_SetFloatAsIntFromDisplay<1>, ScreenHandler.DGUSLCD_SendFloatAsIntValueToDisplay<1>), + VPHELPER(VP_FILCHANGE_NOZZLE_TEMP, &FilamentLoadUnloadHandler::nozzle_temperature, FilamentLoadUnloadHandler::HandleTemperature, ScreenHandler.DGUSLCD_SendWordValueToDisplay), + VPHELPER(VP_FILCHANGE_ACTION_BUTTON, nullptr, FilamentLoadUnloadHandler::HandleLoadUnloadButton, nullptr), + + // Icons + VPHELPER(VP_LED_TOGGLE, &caselight.on, nullptr, (ScreenHandler.DGUSLCD_SendIconValue)), + VPHELPER(VP_STANDBY_BACKLIGHT_ICON, &ScreenHandler.Settings.display_standby, nullptr, (ScreenHandler.DGUSLCD_SendIconValue)), + VPHELPER(VP_MUTE_ICON, &ScreenHandler.Settings.display_sound, nullptr, (ScreenHandler.DGUSLCD_SendIconValue)), + + // Development test button + VPHELPER(VP_DEVELOPMENT_HELPER_BUTTON, nullptr, ScreenHandler.HandleDevelopmentTestButton, nullptr), + + // Mesh override input +#if MESH_INPUT_SUPPORTED_SIZE == GRID_MAX_POINTS + //#define _VPHELPER_GP(N) VPHELPER((VP_MESH_INPUT_X0_Y0 + ( ##N## * MESH_INPUT_DATA_SIZE)), nullptr, ScreenHandler.HandleMeshPoint, nullptr), + //REPEAT(MESH_INPUT_SUPPORTED_SIZE, _VPHELPER_GP) + + VPHELPER((VP_MESH_INPUT_X0_Y0 + ( 0 * MESH_INPUT_DATA_SIZE)), nullptr, ScreenHandler.HandleMeshPoint, nullptr), + VPHELPER((VP_MESH_INPUT_X0_Y0 + ( 1 * MESH_INPUT_DATA_SIZE)), nullptr, ScreenHandler.HandleMeshPoint, nullptr), + VPHELPER((VP_MESH_INPUT_X0_Y0 + ( 2 * MESH_INPUT_DATA_SIZE)), nullptr, ScreenHandler.HandleMeshPoint, nullptr), + VPHELPER((VP_MESH_INPUT_X0_Y0 + ( 3 * MESH_INPUT_DATA_SIZE)), nullptr, ScreenHandler.HandleMeshPoint, nullptr), + VPHELPER((VP_MESH_INPUT_X0_Y0 + ( 4 * MESH_INPUT_DATA_SIZE)), nullptr, ScreenHandler.HandleMeshPoint, nullptr), + VPHELPER((VP_MESH_INPUT_X0_Y0 + ( 5 * MESH_INPUT_DATA_SIZE)), nullptr, ScreenHandler.HandleMeshPoint, nullptr), + VPHELPER((VP_MESH_INPUT_X0_Y0 + ( 6 * MESH_INPUT_DATA_SIZE)), nullptr, ScreenHandler.HandleMeshPoint, nullptr), + VPHELPER((VP_MESH_INPUT_X0_Y0 + ( 7 * MESH_INPUT_DATA_SIZE)), nullptr, ScreenHandler.HandleMeshPoint, nullptr), + VPHELPER((VP_MESH_INPUT_X0_Y0 + ( 8 * MESH_INPUT_DATA_SIZE)), nullptr, ScreenHandler.HandleMeshPoint, nullptr), + VPHELPER((VP_MESH_INPUT_X0_Y0 + ( 9 * MESH_INPUT_DATA_SIZE)), nullptr, ScreenHandler.HandleMeshPoint, nullptr), + VPHELPER((VP_MESH_INPUT_X0_Y0 + ( 10 * MESH_INPUT_DATA_SIZE)), nullptr, ScreenHandler.HandleMeshPoint, nullptr), + VPHELPER((VP_MESH_INPUT_X0_Y0 + ( 11 * MESH_INPUT_DATA_SIZE)), nullptr, ScreenHandler.HandleMeshPoint, nullptr), + VPHELPER((VP_MESH_INPUT_X0_Y0 + ( 12 * MESH_INPUT_DATA_SIZE)), nullptr, ScreenHandler.HandleMeshPoint, nullptr), + VPHELPER((VP_MESH_INPUT_X0_Y0 + ( 13 * MESH_INPUT_DATA_SIZE)), nullptr, ScreenHandler.HandleMeshPoint, nullptr), + VPHELPER((VP_MESH_INPUT_X0_Y0 + ( 14 * MESH_INPUT_DATA_SIZE)), nullptr, ScreenHandler.HandleMeshPoint, nullptr), + VPHELPER((VP_MESH_INPUT_X0_Y0 + ( 15 * MESH_INPUT_DATA_SIZE)), nullptr, ScreenHandler.HandleMeshPoint, nullptr), +#endif + + // M117 LCD String (We don't need the string in memory but "just" push it to the display on demand, hence the nullptr + { .VP = VP_M117, .memadr = nullptr, .size = VP_M117_LEN, .set_by_display_handler = nullptr, .send_to_display_handler =&ScreenHandler.DGUSLCD_SendStringToDisplay }, + { .VP = VP_M117_STATIC, .memadr = nullptr, .size = VP_M117_STATIC_LEN, .set_by_display_handler = nullptr, .send_to_display_handler =&ScreenHandler.DGUSLCD_SendStringToDisplay }, + + // Messages for the User, shared by the popup and the kill screen. They cant be autouploaded as we do not buffer content. + { .VP = VP_MSGSTR1, .memadr = nullptr, .size = VP_MSGSTR1_LEN, .set_by_display_handler = nullptr, .send_to_display_handler = &ScreenHandler.DGUSLCD_SendStringToDisplayPGM }, + { .VP = VP_MSGSTR2, .memadr = nullptr, .size = VP_MSGSTR2_LEN, .set_by_display_handler = nullptr, .send_to_display_handler = &ScreenHandler.DGUSLCD_SendStringToDisplayPGM }, + { .VP = VP_MSGSTR3, .memadr = nullptr, .size = VP_MSGSTR3_LEN, .set_by_display_handler = nullptr, .send_to_display_handler = &ScreenHandler.DGUSLCD_SendStringToDisplayPGM }, + //{ .VP = VP_MSGSTR4, .memadr = nullptr, .size = VP_MSGSTR4_LEN, .set_by_display_handler = nullptr, .send_to_display_handler = &ScreenHandler.DGUSLCD_SendStringToDisplayPGM }, + + VPHELPER(0, 0, 0, 0) // must be last entry. +}; + +#endif // DGUS_LCD_UI_ORIGIN diff --git a/Marlin/src/lcd/extui/dgus_creality/creality_touch/DGUSDisplayDef.h b/Marlin/src/lcd/extui/dgus_creality/creality_touch/DGUSDisplayDef.h new file mode 100644 index 0000000000..c18f2d1b32 --- /dev/null +++ b/Marlin/src/lcd/extui/dgus_creality/creality_touch/DGUSDisplayDef.h @@ -0,0 +1,562 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +#pragma once + +enum DGUSLCD_Screens : uint8_t { + DGUSLCD_SCREEN_BOOT = 0, + + DGUSLCD_SCREEN_MAIN = 28, + + DGUSLCD_SCREEN_CONFIRM = 66, + DGUSLCD_SCREEN_SDPRINTMANIPULATION = 37 , + DGUSLCD_SCREEN_SDPRINTTUNE = 41, + DGUSLCD_SCREEN_SDFILELIST = 67, + + DGUSLCD_SCREEN_FILAMENTRUNOUT1 = 34, // DWINTouchPage::ERR_FILAMENTRUNOUT_HOTEND_COLD + DGUSLCD_SCREEN_FILAMENTRUNOUT2 = 35, // DWINTouchPage::ERR_FILAMENTRUNOUT_FILAMENT_LOADED + + DGUSLCD_SCREEN_PRINT_FINISH = 36, // DWINTouchPage::PRINT_FINISHED + DGUSLCD_SCREEN_PRINT_RUNNING = 37, // DWINTouchPage::PRINT_PROGRESS_RUNNING + DGUSLCD_SCREEN_PRINT_PAUSED = 39, // DWINTouchPage::PRINT_PROGRESS_PAUSED + + DGUSLCD_SCREEN_DIALOG_PAUSE = 38, // DWINTouchPage::DIALOG_PAUSE_PRINTING + DGUSLCD_SCREEN_DIALOG_STOP = 40, // DWINTouchPage::DIALOG_STOP_PRINTING + + DGUSLCD_SCREEN_TUNING = 41, // DWINTouchPage::MENU_TUNING + DGUSLCD_SCREEN_TUNING_EXTRA = 79, // (this is a new page) + DGUSLCD_SCREEN_PREPARE = 42, // DWINTouchPage::MENU_PREPARE + + DGUSLCD_SCREEN_MOVE10MM = 43, // DWINTouchPage::MOVE_1MM + DGUSLCD_SCREEN_MOVE1MM = 44, // DWINTouchPage::MOVE_10MM + DGUSLCD_SCREEN_MOVE01MM = 45, // DWINTouchPage::MOVE_01MM + + DGUSLCD_SCREEN_FEED = 46, // DWINTouchPage::FEED + DGUSLCD_SCREEN_SETUP = 47, // DWINTouchPage::MENU_CONTROL + + DGUSLCD_SCREEN_TEMP = 48, // DWINTouchPage::MENU_TEMP + DGUSLCD_SCREEN_TEMP_PLA = 49, // DWINTouchPage::MENU_PLA_TEMP + DGUSLCD_SCREEN_TEMP_ABS = 50, // DWINTouchPage::MENU_ABS_TEMP + + DGUSLCD_SCREEN_INFO = 51, // DWINTouchPage::MENU_ABOUT + + DGUSLCD_SCREEN_ZOFFSET_LEVEL = 52, // DWINTouchPage::MENU_ZOFFSET_LEVELING + DGUSLCD_SCREEN_LEVELING = 53, // DWINTouchPage::LEVELING + + DGUSLCD_SCREEN_POWER_LOSS = 54, // DWINTouchPage::DIALOG_POWER_FAILURE + DGUSLCD_SCREEN_THERMAL_RUNAWAY = 57, // DWINTouchPage::ERR_THERMAL_RUNAWAY + DGUSLCD_SCREEN_HEATING_FAILED = 58, // DWINTouchPage::ERR_HEATING_FAILED + DGUSLCD_SCREEN_THERMISTOR_ERROR = 59, // DWINTouchPage::ERR_THERMISTOR + + DGUSLCD_SCREEN_AUTOHOME = 61, // DWINTouchPage::AUTOHOME_IN_PROGRESS + + DGUSLCD_SCREEN_POPUP = 63, // NEW - does not exist in original display + DGUSLCD_SCREEN_KILL = 64, // NEW - does not exist in original display + + DGUSLCD_SCREEN_PIDTUNE_CALIBRATION = 68, + DGUSLCD_SCREEN_ESTEPS_CALIBRATION = 69, + + DGUSLCD_SCREEN_TUNEFWRETRACT = 70, + + DGUSLCD_SCREEN_ESTEPS_CALIBRATION_RESULTS = 71, + DGUSLCD_SCREEN_LEVELING_SETTINGS = 72, + + DGUSLCD_SCREEN_AXIS_SETTINGS_NAV = 73, + DGUSLCD_SCREEN_AXIS_SETTINGS_AXIS = 74, + DGUSLCD_SCREEN_AXIS_SETTINGS_TMC = 75, + + DGUSLCD_SCREEN_ADV_MOV_SETTINGS = 76, + DGUSLCD_SCREEN_MISC_SETTINGS = 77, + DGUSLCD_SCREEN_MESH_VALIDATION = 78, + + DGUSLCD_SCREEN_CALIBRATE = 80, + DGUSLCD_SCREEN_RGB = 81 +}; + +// Version checks +constexpr uint16_t VP_UI_VERSION_MAJOR = 0xFFFA; +constexpr uint16_t EXPECTED_UI_VERSION_MAJOR = 61; +constexpr uint16_t VERSION_MISMATCH_BUZZ_AMOUNT = 5; +constexpr uint16_t VERSION_MISMATCH_LED_FLASH_DELAY = 1000; + +#define VP_STARTPROGRESSBAR 0x1000 + +// // Storage space for the Killscreen messages. Reused for the popup. +constexpr uint16_t VP_MSGSTR1 = 0x2010; +constexpr uint8_t VP_MSGSTR1_LEN = 0x20; // might be more place for it... +constexpr uint16_t VP_MSGSTR2 = 0x2030; +constexpr uint8_t VP_MSGSTR2_LEN = 0x40; +constexpr uint16_t VP_MSGSTR3 = 0x2070; +constexpr uint8_t VP_MSGSTR3_LEN = 0x40; +constexpr uint16_t VP_MSGSTR4 = 0x2080; +constexpr uint8_t VP_MSGSTR4_LEN = 0x20; + +// // Screenchange request for screens that only make sense when printer is idle. +// // e.g movement is only allowed if printer is not printing. +// // Marlin must confirm by setting the screen manually. +// constexpr uint16_t VP_SCREENCHANGE_ASK = 0x2000; +constexpr uint16_t VP_SCREENCHANGE = 0x219f; // Key-Return button to new menu pressed. Data contains target screen in low byte and info in high byte. +// constexpr uint16_t VP_TEMP_ALL_OFF = 0x2002; // Turn all heaters off. Value arbitrary ;)= +// constexpr uint16_t VP_SCREENCHANGE_WHENSD = 0x2003; // "Print" Button touched -- go only there if there is an SD Card. + +constexpr uint16_t VP_CONFIRMED = 0x219E; // OK on confirm screen. + +// // Buttons on the SD-Card File listing. +// constexpr uint16_t VP_SD_ScrollEvent = 0x2020; // Data: 0 for "up a directory", numbers are the amount to scroll, e.g -1 one up, 1 one down +// constexpr uint16_t VP_SD_FileSelected = 0x2022; // Number of file field selected. +constexpr uint16_t VP_SD_FileSelectConfirm = 0x2024; // (This is a virtual VP and emulated by the Confirm Screen when a file has been confirmed) + +// constexpr uint16_t VP_SD_ResumePauseAbort = 0x2026; // Resume(Data=0), Pause(Data=1), Abort(Data=2) SD Card prints +constexpr uint16_t VP_SD_AbortPrintConfirmed = 0x2028; // Abort print confirmation (virtual, will be injected by the confirm dialog) +// constexpr uint16_t VP_SD_Print_Setting = 0x2040; +// constexpr uint16_t VP_SD_Print_LiveAdjustZ = 0x2050; // Data: 0 down, 1 up + +// // Controls for movement (we can't use the incremental / decremental feature of the display at this feature works only with 16 bit values +// // (which would limit us to 655.35mm, which is likely not a problem for common setups, but i don't want to rule out hangprinters support) +// // A word about the coding: The VP will be per axis and the return code will be an signed 16 bit value in 0.01 mm resolution, telling us +// // the relative travel amount t he user wants to do. So eg. if the display sends us VP=2100 with value 100, the user wants us to move X by +1 mm. +constexpr uint16_t VP_MOVE_X = 0x2100; +constexpr uint16_t VP_MOVE_Y = 0x2102; +constexpr uint16_t VP_MOVE_Z = 0x2104; +constexpr uint16_t VP_MOVE_E0 = 0x2110; +// constexpr uint16_t VP_MOVE_E1 = 0x2112; +// //constexpr uint16_t VP_MOVE_E2 = 0x2114; +// //constexpr uint16_t VP_MOVE_E3 = 0x2116; +// //constexpr uint16_t VP_MOVE_E4 = 0x2118; +// //constexpr uint16_t VP_MOVE_E5 = 0x211A; +constexpr uint16_t VP_HOME_ALL = 0x2120; +// constexpr uint16_t VP_MOTOR_LOCK_UNLOK = 0x2130; + +// // Power loss recovery +// constexpr uint16_t VP_POWER_LOSS_RECOVERY = 0x2180; + +// // Fan Control Buttons , switch between "off" and "on" +// constexpr uint16_t VP_FAN0_CONTROL = 0x2200; +// constexpr uint16_t VP_FAN1_CONTROL = 0x2202; +// //constexpr uint16_t VP_FAN2_CONTROL = 0x2204; +// //constexpr uint16_t VP_FAN3_CONTROL = 0x2206; + +// // Heater Control Buttons , triged between "cool down" and "heat PLA" state +constexpr uint16_t VP_E0_CONTROL = 0x2210; +// constexpr uint16_t VP_E1_CONTROL = 0x2212; +// //constexpr uint16_t VP_E2_CONTROL = 0x2214; +// //constexpr uint16_t VP_E3_CONTROL = 0x2216; +// //constexpr uint16_t VP_E4_CONTROL = 0x2218; +// //constexpr uint16_t VP_E5_CONTROL = 0x221A; +constexpr uint16_t VP_BED_CONTROL = 0x221C; + +// // Preheat +// constexpr uint16_t VP_E0_BED_PREHEAT = 0x2220; +// constexpr uint16_t VP_E1_BED_CONTROL = 0x2222; +// //constexpr uint16_t VP_E2_BED_CONTROL = 0x2224; +// //constexpr uint16_t VP_E3_BED_CONTROL = 0x2226; +// //constexpr uint16_t VP_E4_BED_CONTROL = 0x2228; +// //constexpr uint16_t VP_E5_BED_CONTROL = 0x222A; + +// // Filament load and unload +// constexpr uint16_t VP_E0_FILAMENT_LOAD_UNLOAD = 0x2300; +// constexpr uint16_t VP_E1_FILAMENT_LOAD_UNLOAD = 0x2302; + +// // Settings store , reset +// constexpr uint16_t VP_SETTINGS = 0x2400; + +// // PID autotune +constexpr uint16_t VP_PID_AUTOTUNE_E0 = 0x2410; +// //constexpr uint16_t VP_PID_AUTOTUNE_E1 = 0x2412; +// //constexpr uint16_t VP_PID_AUTOTUNE_E2 = 0x2414; +// //constexpr uint16_t VP_PID_AUTOTUNE_E3 = 0x2416; +// //constexpr uint16_t VP_PID_AUTOTUNE_E4 = 0x2418; +// //constexpr uint16_t VP_PID_AUTOTUNE_E5 = 0x241A; +constexpr uint16_t VP_PID_AUTOTUNE_BED = 0x2420; + +// // Firmware version on the boot screen. +constexpr uint16_t VP_PRINTER_BEDSIZE = 0x1074; +constexpr uint16_t VP_PRINTER_BEDSIZE_LEN = 12; +constexpr uint16_t VP_MARLIN_VERSION = 0x2222; +constexpr uint8_t VP_MARLIN_VERSION_LEN = 32; + +constexpr uint16_t VP_MARLIN_WEBSITE = 0x2242; +constexpr uint8_t VP_MARLIN_WEBSITE_LEN = 32; + +constexpr uint16_t VP_STANDBY_BACKLIGHT_ICON = 0x2280; +constexpr uint16_t VP_STANDBY_BACKLIGHT_TOGGLE = 0x2282; + +constexpr uint16_t VP_MUTE_ICON = 0x2284; +constexpr uint16_t VP_MUTE_TOGGLE = 0x2286; + +constexpr uint16_t VP_SCREEN_BACKLIGHT_STANDBY = 0x228D; +constexpr uint16_t VP_SCREEN_BACKLIGHT = 0x2384; + +constexpr uint16_t VP_SCREEN_STANDBY_TIME = 0x2386; + +// Material preheat settings +constexpr uint16_t VP_PREHEAT_PLA_HOTEND_TEMP = 0x1102; +constexpr uint16_t VP_PREHEAT_PLA_BED_TEMP = 0x1104; + +constexpr uint16_t VP_PREHEAT_ABS_HOTEND_TEMP = 0x1108; +constexpr uint16_t VP_PREHEAT_ABS_BED_TEMP = 0x110a; + +// Place for status messages. + +// ... We have memory space for scrolling messages +constexpr uint16_t VP_M117 = 0x3000 + (3 * 1); // Text Variable Pointer. First three VP must be reserved [a VP is two bytes red.]. Text is saved after the 3rd VP and ended with 0x00 or 0x0F. +constexpr uint8_t VP_M117_LEN = 100; + +// ... And memory space for static (short) messages. Note this VPAddr is also the VP of the 5 beta and alpha 4 touch screens. +constexpr uint8_t M117_STATIC_DISPLAY_LEN = 28; // Fits "TFT flashed incorrectly v0" exactly +constexpr uint16_t VP_M117_STATIC = 0x21B3; +constexpr uint8_t VP_M117_STATIC_LEN = 70; + +// // Temperatures. +constexpr uint16_t VP_T_E0_Is = 0x1036; // 4 Byte Integer - HEAD_CURRENT_TEMP_VP +constexpr uint16_t VP_T_E0_Set = 0x1034; // 2 Byte Integer - HEAD_SET_TEMP_VP + +constexpr uint16_t VP_T_Bed_Is = 0x103c; // 4 Byte Integer - BED_SET_TEMP_VP +constexpr uint16_t VP_T_Bed_Set = 0x103A; // 2 Byte Integer - BED_CURRENT_TEMP_VP + +constexpr uint16_t VP_Flowrate_E0 = 0x228A; // 2 Byte Integer + +constexpr uint16_t VP_Fan0_Percentage = 0x228F; // 2 Byte Integer (0..100) + +constexpr uint16_t VP_Feedrate_Percentage = 0x1006; // 2 Byte Integer (0..100) - PRINT_SPEED_RATE_VP +constexpr uint16_t VP_PrintProgress_Percentage = 0x1016; // 2 Byte Integer (0..100) + +constexpr uint16_t VP_PrintTimeProgressBar = 0x100E; + +constexpr uint16_t VP_PrintTime = 0x21a0; +constexpr uint16_t VP_PrintTimeWithRemainingVisible = 0x2335; +constexpr uint16_t VP_PrintTime_LEN = 19; + +constexpr uint16_t VP_PrintTimeRemaining = 0x231f; +constexpr uint16_t VP_PrintTimeRemaining_LEN = 21; + +constexpr uint16_t VP_HideRemainingTime_Ico = 0x2380; +constexpr uint16_t ICON_REMAINING_VISIBLE = 26; +constexpr uint16_t ICON_REMAINING_HIDDEN = 27; + +constexpr uint16_t VP_Z_OFFSET = 0x1026; + +// // SDCard File Listing +constexpr uint16_t VP_SD_ScrollEvent = 0x20D4; // Data: 0 for "up a directory", numbers are the amount to scroll, e.g -1 one up, 1 one down +constexpr uint16_t VP_SD_FileSelected = 0x2200; // Number of file field selected. +constexpr uint16_t VP_SD_FileName_LEN = 21; // LEN is shared for all entries. +constexpr uint16_t VP_SD_FileName_CNT = 5; // LEN is shared for all entries. +constexpr uint16_t DGUS_SD_FILESPERSCREEN = VP_SD_FileName_CNT; // FIXME move that info to the display and read it from there. +constexpr uint16_t VP_SD_FileName0 = 0x20D5; +constexpr uint16_t VP_SD_FileName1 = VP_SD_FileName0 + VP_SD_FileName_LEN; +constexpr uint16_t VP_SD_FileName2 = VP_SD_FileName1 + VP_SD_FileName_LEN; +constexpr uint16_t VP_SD_FileName3 = VP_SD_FileName2 + VP_SD_FileName_LEN; +constexpr uint16_t VP_SD_FileName4 = VP_SD_FileName3 + VP_SD_FileName_LEN; +constexpr uint16_t VP_SD_FileName5 = VP_SD_FileName4 + VP_SD_FileName_LEN; + +constexpr uint16_t VP_SD_Print_ProbeOffsetZ = 0x32A0; // +constexpr uint16_t VP_SD_Print_Filename = 0x2000; // + +constexpr uint16_t VP_ICON_OVERLAY_CLEAR = 10; +constexpr uint16_t VP_ICON_OVERLAY_SELECTED = 6; + +// // Step per mm +constexpr uint16_t VP_X_STEP_PER_MM = 0x3600; +constexpr uint16_t VP_Y_STEP_PER_MM = 0x3604; +constexpr uint16_t VP_Z_STEP_PER_MM = 0x3608; +constexpr uint16_t VP_E0_STEP_PER_MM = 0x3610; +// // PIDs +constexpr uint16_t VP_E0_PID_P = 0x3700; +constexpr uint16_t VP_E0_PID_I = 0x3702; +constexpr uint16_t VP_E0_PID_D = 0x3704; +constexpr uint16_t VP_BED_PID_P = 0x3710; +constexpr uint16_t VP_BED_PID_I = 0x3712; +constexpr uint16_t VP_BED_PID_D = 0x3714; + +// Power loss recovery +constexpr uint16_t VP_POWER_LOSS_RECOVERY = 0x105F; + +// Buttons defined by Creality - Don't worry if you're confused by the naming, so am I +constexpr uint16_t VP_BUTTON_MAINENTERKEY = 0x1002; +constexpr uint16_t VP_BUTTON_ADJUSTENTERKEY = 0x1004; +constexpr uint16_t VP_BUTTON_PAUSEPRINTKEY = 0x100A; +constexpr uint16_t VP_BUTTON_TEMPCONTROL = 0x1030; +constexpr uint16_t VP_BUTTON_COOLDOWN = 0x1032; +constexpr uint16_t VP_BUTTON_PREPAREENTERKEY = 0x103E; + +constexpr uint16_t VP_BUTTON_SELECTFILEKEY = 0x20D3; +constexpr uint16_t VP_BUTTON_STARTPRINTKEY = 0x20D2; +constexpr uint16_t VP_BUTTON_STOPPRINTKEY = 0x1008; +constexpr uint16_t VP_BUTTON_RESUMEPRINTKEY = 0x100C; +constexpr uint16_t VP_BUTTON_BEDLEVELKEY = 0x1044; + +constexpr uint16_t VP_BUTTON_HEATLOADSTARTKEY = 0x1056; + +// Additional stuff defined by Creality +constexpr uint16_t VP_FAN_TOGGLE = 0x101E; +constexpr uint16_t VP_LED_TOGGLE = 0x101F; + +// Axis settings +constexpr uint16_t VP_AXIS_SETTINGS_NAV_BUTTON = 0x22D9; +constexpr uint16_t AXIS_SETTINGS_NAV_BUTTON_VAL_X = 1; +constexpr uint16_t AXIS_SETTINGS_NAV_BUTTON_VAL_Y = 2; +constexpr uint16_t AXIS_SETTINGS_NAV_BUTTON_VAL_Z = 3; +constexpr uint16_t AXIS_SETTINGS_NAV_BUTTON_VAL_E = 4; + +constexpr uint16_t VP_AXIS_SETTINGS_TITLE_ICON = 0x22DB; +constexpr uint16_t ICON_AXIS_SETTINGS_TITLE_X = 20; +constexpr uint16_t ICON_AXIS_SETTINGS_TITLE_Y = 21; +constexpr uint16_t ICON_AXIS_SETTINGS_TITLE_Z = 22; +constexpr uint16_t ICON_AXIS_SETTINGS_TITLE_E = 23; + +constexpr uint16_t VP_AXIS_SETTINGS_NAV_BACKBUTTON = 0x22DD; + +constexpr uint16_t VP_AXIS_SETTINGS_AXIS_STEPSMM = 0x22DF; // 2-byte +constexpr uint16_t VP_AXIS_SETTINGS_AXIS_MAX_ACCEL = 0x22E1; // 4-byte (!) +constexpr uint16_t VP_AXIS_SETTINGS_AXIS_JERK = 0x22E5; // 2-byte +constexpr uint16_t VP_AXIS_SETTINGS_AXIS_FEEDRATE = 0x22E7; // 2-byte + +constexpr uint16_t VP_AXIS_SETTINGS_AXIS_TMCCURRENT = 0x22E9; // 2-byte + +constexpr uint16_t VP_AXIS_SETTINGS_AXIS_TMCSTEALTHCHOP_BUTTON = 0x22EB; // 2-byte +constexpr uint16_t VP_AXIS_SETTINGS_AXIS_TMCSTEALTHCHOP_ICON = 0x22ED; // 2-byte +constexpr uint16_t VP_AXIS_SETTINGS_AXIS_TMCHYBRIDTHRESHOLD = 0x22EF; // 4-byte (!) + +constexpr uint16_t VP_AXIS_TUNING_NAV_BUTTON = 0x22F5; +constexpr uint16_t VP_AXIS_TMC_NAV_BUTTON = 0x22F7; + +constexpr uint16_t VP_AXIS_TMC_NAV_ICON = 0x22F3; // 2-byte +constexpr uint16_t AXIS_TMC_NAV_ICON_SHOWING = 10; +constexpr uint16_t AXIS_TMC_NAV_ICON_HIDING = 11; + +// ... Advanced movement settings +constexpr uint16_t VP_MOV_NAV_BUTTON = 0x2305; + +constexpr uint16_t VP_MOV_MINIMUM_SEGMENT_TIME = 0x22F9; // uint 2-byte +constexpr uint16_t VP_MOV_MINIMUM_FEEDRATE = 0x22FB; // float 2-byte +constexpr uint16_t VP_MOV_NORMAL_ACCELERATION = 0x22FD; // float 2-byte + +constexpr uint16_t VP_MOV_MINIMUM_TRAVEL_FEEDRATE = 0x2301; // float 2-byte +constexpr uint16_t VP_MOV_MINIMUM_TRAVEL_ACCELERATION = 0x2303; // float 2-byte + +constexpr uint16_t VP_MOV_RETRACT_ACCELERATION = 0x2307; // float 4-byte + +// Misc settings +constexpr uint16_t VP_MISCSETTINGS_NAV_BUTTON = 0x2311; + +constexpr uint16_t VP_FILAMENTRUNOUT_SENSOR_TOGGLE_BUTTON = 0x2309; +constexpr uint16_t VP_FILAMENTRUNOUT_SENSOR_TOGGLE_ICON = 0x230b; + +constexpr uint16_t VP_PLR_TOGGLE_BUTTON = 0x230d; +constexpr uint16_t VP_PLR_TOGGLE_ICON = 0x230F; + +// Mesh leveling +constexpr uint16_t VP_LEVELING_NAV_BUTTON = 0x238a; +constexpr uint16_t VP_LEVELING_EDIT_NAV_BUTTON = 0x23A8; + +constexpr uint16_t VP_MESH_SCREEN_MESSAGE_ICON = 0x22cb; +constexpr uint16_t MESH_SCREEN_MESSAGE_ICON_LEVELING = 5; +constexpr uint16_t MESH_SCREEN_MESSAGE_ICON_VIEWING = 6; + +constexpr uint16_t VP_MESH_LEVEL_TEMP = 0x108A; +constexpr uint16_t VP_MESH_LEVEL_STATUS = 0x108D; + +constexpr uint8_t DGUS_GRID_VISUALIZATION_START_ID = GRID_MAX_POINTS > (4*4) ? 30 : 1; + +static_assert( + (GRID_MAX_POINTS == 16 && DGUS_GRID_VISUALIZATION_START_ID == 1)|| // CR-6 SE + (GRID_MAX_POINTS == 49 && DGUS_GRID_VISUALIZATION_START_ID == 30) || // CR-6 MAX + (GRID_MAX_POINTS != 16 && GRID_MAX_POINTS != 49), // Custom Leveling + "Incorrect offset selected for leveling config" +); +static_assert(GRID_MAX_POINTS_X == GRID_MAX_POINTS_Y, "Assuming bed leveling points is square"); + + +constexpr uint16_t VP_MESH_LEVEL_X0_Y0 = 0x1350; +constexpr uint16_t SP_MESH_LEVEL_X0_Y0 = 0x5000; + +constexpr uint16_t MESH_LEVEL_EDGE_MAX_POINTS = 4; + +constexpr uint16_t MESH_LEVEL_VP_SIZE = 0x4; // 4-byte native float +constexpr uint16_t MESH_LEVEL_SP_SIZE = 0x10; // 0x10 distance + +constexpr uint16_t MESH_LEVEL_VP_EDGE_SIZE = MESH_LEVEL_VP_SIZE * MESH_LEVEL_EDGE_MAX_POINTS; +constexpr uint16_t MESH_LEVEL_SP_EDGE_SIZE = MESH_LEVEL_SP_SIZE * MESH_LEVEL_EDGE_MAX_POINTS; + +constexpr uint16_t MESH_LEVEL_MAX_POINTS = MESH_LEVEL_EDGE_MAX_POINTS * MESH_LEVEL_EDGE_MAX_POINTS; + +// Mesh inputs +constexpr uint16_t VP_MESH_INPUT_X0_Y0 = 0x2360; + +#define MESH_INPUT_SUPPORTED_X_SIZE 4 +#define MESH_INPUT_SUPPORTED_Y_SIZE 4 +#define MESH_INPUT_SUPPORTED_SIZE 16 +#define MESH_INPUT_DATA_SIZE 2 // 2 byte integer + +// Color table: https://stackoverflow.com/q/13720937/646215 +// Color picker: https://ee-programming-notepad.blogspot.com/2016/10/16-bit-color-generator-picker.html +// Colors below were picked on bed leveling visualizer defaults + +constexpr uint16_t MESH_COLOR_BELOW_ZERO = 0x899B; // Blue +constexpr uint16_t MESH_COLOR_NEAR_ZERO = 0x1C80; // Green +constexpr uint16_t MESH_COLOR_ABOVE_ZERO = 0xB800; // Purple +constexpr uint16_t MESH_COLOR_NOT_MEASURED = 0xFFFF; // White (becomes invisible on white background) + +constexpr float MESH_NEAR_ZERO = 0.020; +constexpr float MESH_UNSET_EPSILON = 0.001; + +// Mesh validation pattern +constexpr uint16_t VP_MESHPATTERN_NOZZLE_TEMP = 0x2313; +constexpr uint16_t VP_MESHPATTERN_BED_TEMP = 0x2315; + +constexpr uint16_t VP_MESHPATTERN_START_BUTTON = 0x2317; +constexpr uint16_t VP_MESHPATTERN_BUTTON_ICON = 0x2319; + +constexpr uint16_t VP_MESHPATTERN_NAV_BUTTON = 0x231B; + +constexpr uint16_t MESHPATTERN_BUTTON_START = 24; // This icon does not exist, and will therefore not show +constexpr uint16_t MESHPATTERN_BUTTON_CANCEL = 25; + + +// Movement screen +constexpr uint16_t VP_X_POSITION = 0x1048; +constexpr uint16_t SP_X_POSITION = 0x4000; +constexpr uint16_t VP_Y_POSITION = 0x104A; +constexpr uint16_t SP_Y_POSITION = 0x4030; +constexpr uint16_t VP_Z_POSITION = 0x104C; +constexpr uint16_t VP_Z_POSITION_PRECISION = 0x108F; +constexpr uint16_t SP_Z_POSITION = 0x4060; +constexpr uint16_t VP_BUTTON_MOVEKEY = 0x1046; + +// Buttons +constexpr uint16_t VP_ESTEP_NAV_BUTTON = 0x2291; +constexpr uint16_t VP_PIDTUNE_NAV_BUTTON = 0x2293; +constexpr uint16_t VP_GENERIC_BACK_BUTTON = 0x2295; // Generic button for popping back to the old display + +constexpr uint16_t GENERIC_BACK_BUTTON_NEED_SAVE = 0x1; + +// PID tuning +constexpr uint16_t VP_PIDTUNE_TARGET_TEMP = 0x2297; +constexpr uint16_t VP_PIDTUNE_CYCLES = 0x2299; +constexpr uint16_t VP_PIDTUNE_FAN_TOGGLE = 0x238C; +constexpr uint16_t VP_PIDTUNE_FAN_TOGGLE_ICON = 0x238E; +constexpr uint16_t VP_PIDTUNE_START_BUTTON = 0x229B; + +// FWRetract +constexpr uint16_t VP_FWRETRACT_NAV_BUTTON = 0x22AD; + +constexpr uint16_t VP_FWRETRACT_RETRACT_LENGTH = 0x22B1; +constexpr uint16_t VP_FWRETRACT_RETRACT_FEEDRATE = 0x22B3; +constexpr uint16_t VP_FWRETRACT_RETRACT_ZHOP = 0x22B5; + +constexpr uint16_t VP_FWRETRACT_RESTART_LENGTH = 0x22B7; +constexpr uint16_t VP_FWRETRACT_RESTART_FEEDRATE = 0x22B9; + +constexpr uint16_t VP_FWRETRACT_TOGGLE_BUTTON = 0x22BB; +constexpr uint16_t VP_FWRETRACT_TOGGLE_BUTTON_ICON = 0x22BD; +constexpr uint16_t VP_FWRETRACT_INDICATOR_ICON = 0x22BF; + +// Other tuning +constexpr uint16_t VP_LINEAR_ADVANCE_FACTOR = 0x22AF; + +constexpr uint16_t VP_OTHER_TUNE_NAV_BUTTON = 0x2382; + +// Leveling settings +constexpr uint16_t VP_TOGGLE_PROBING_HEATERS_OFF_ONOFF_BUTTON = 0x22C1; +constexpr uint16_t VP_TOGGLE_PROBING_HEATERS_OFF_ONOFF_ICON = 0x22C3; + +constexpr uint16_t VP_TOGGLE_PROBE_PREHEAT_HOTEND_TEMP = 0x22C5; +constexpr uint16_t VP_TOGGLE_PROBE_PREHEAT_BED_TEMP = 0x22C7; + +constexpr uint16_t VP_TOGGLE_PROBE_SETTINGS_NAV_BUTTON = 0x22C9; + +constexpr uint16_t VP_TOGGLE_POST_PROBING_TEMPERATURE_STABILIZATION_ICON = 0x22CD; +constexpr uint16_t VP_TOGGLE_POST_PROBING_TEMPERATURE_STABILIZATION_BUTTON = 0x22CF; + +constexpr uint16_t VP_LEVELING_FADE_HEIGHT = 0x231D; + +// E-steps calibration +constexpr uint16_t VP_ESTEPS_CURRENT = 0x229d; +constexpr uint16_t VP_ESTEPS_CALIBRATION_TEMP = 0x229f; +constexpr uint16_t VP_ESTEPS_CALIBRATION_LENGTH = 0x22a1; +constexpr uint16_t VP_ESTEPS_CALIBRATION_MARK_LENGTH = 0x22ab; +constexpr uint16_t VP_ESTEPS_CALIBRATION_LEFTOVER_LENGTH = 0x22a3; +constexpr uint16_t VP_ESTEPS_CALCULATED_ESTEPS = 0x22a5; +constexpr uint16_t VP_ESTEPS_CALIBRATESTART_BUTTON = 0x22a7; +constexpr uint16_t VP_ESTEPS_APPLY_BUTTON = 0x22a9; +constexpr uint16_t VP_ESTEPS_BACK_BUTTON = 0x22D7; + +// RGB +constexpr uint16_t VP_RGB_NAV_BUTTON = 0x2390; + +constexpr uint16_t VP_RGB_CONTROL_R = 0x2392; +constexpr uint16_t VP_RGB_CONTROL_G = 0x2394; +constexpr uint16_t VP_RGB_CONTROL_B = 0x2396; +constexpr uint16_t VP_RGB_CONTROL_W = 0x2398; +constexpr uint16_t VP_RGB_CONTROL_I = 0x239A; // brightness + +constexpr uint16_t VP_RGB_NAV_BUTTON_ICON = 0x239E; +constexpr uint16_t ICON_RGB_SETTINGS_AVAILABLE = 28; +constexpr uint16_t ICON_RGB_SETTINGS_UNAVAILABLE = 29; + +// Filament load/unload +constexpr uint16_t VP_FILCHANGE_NAV_BUTTON = 0x23a6; + +constexpr uint16_t VP_FILCHANGE_NOZZLE_TEMP = 0x23a0; +constexpr uint16_t VP_FILCHANGE_LENGTH = 0x23a2; + +constexpr uint16_t VP_FILCHANGE_ACTION_BUTTON = 0x23a4; +constexpr uint16_t FILCHANGE_ACTION_UNLOAD_BUTTON = 1; +constexpr uint16_t FILCHANGE_ACTION_LOAD_BUTTON = 2; + +// Icons +constexpr uint16_t ICON_TOGGLE_ON = 1; +constexpr uint16_t ICON_TOGGLE_OFF = 2; + +constexpr uint16_t ICON_BACK_BUTTON_ENABLED = 7; +constexpr uint16_t ICON_BACK_BUTTON_DISABLED = 8; + +constexpr uint16_t ICON_THROBBER_ANIM_OFF = 0; +constexpr uint16_t ICON_THROBBER_ANIM_ON = 1; + +// Allow to visually disable/enable the back button +constexpr uint16_t VP_BACK_BUTTON_STATE = 0x22D1; + +// Throbber animation +constexpr uint16_t VP_BUSY_ANIM_STATE = 0x22D3; + +// Toggles +constexpr uint16_t ICON_FAN_TOGGLE_ON = 1; +constexpr uint16_t ICON_FAN_TOGGLE_OFF = 2; +constexpr uint16_t ICON_LED_TOGGLE_ON = 3; +constexpr uint16_t ICON_LED_TOGGLE_OFF = 4; +constexpr uint16_t ICON_SOUND_TOGGLE_ON = 5; +constexpr uint16_t ICON_SOUND_TOGGLE_OFF = 6; +constexpr uint16_t ICON_FWRETRACT_AUTO_TOGGLE_ON = 9; +constexpr uint16_t ICON_FWRETRACT_AUTO_TOGGLE_OFF = 10; +constexpr uint16_t ICON_ACCURACY_TOGGLE_ON = 11; +constexpr uint16_t ICON_ACCURACY_TOGGLE_OFF = 12; +constexpr uint16_t ICON_POST_PROBE_TEMP_STABILIZATION_TOGGLE_ON = 13; +constexpr uint16_t ICON_POST_PROBE_TEMP_STABILIZATION_TOGGLE_OFF = 14; + +constexpr uint16_t ICON_FWRETRACT_AUTO_DISENGAGED = 4; // This icon deliberately does not exist +constexpr uint16_t ICON_FWRETRACT_AUTO_ENGAGED = 3; + +// Development helper +constexpr uint16_t VP_DEVELOPMENT_HELPER_BUTTON = 0x22D5; +constexpr uint16_t VP_DEVELOPMENT_HELPER_BUTTON_ACTION_FIRMWARE_UPDATE = 2; +constexpr uint16_t VP_DEVELOPMENT_HELPER_BUTTON_ACTION_TO_MAIN_MENU = 4; +constexpr uint16_t VP_DEVELOPMENT_HELPER_BUTTON_ACTION_RESET_DISPLAY = 8; \ No newline at end of file diff --git a/Marlin/src/lcd/extui/dgus_creality/creality_touch/EstepsHandler.cpp b/Marlin/src/lcd/extui/dgus_creality/creality_touch/EstepsHandler.cpp new file mode 100644 index 0000000000..f5ce2628e2 --- /dev/null +++ b/Marlin/src/lcd/extui/dgus_creality/creality_touch/EstepsHandler.cpp @@ -0,0 +1,188 @@ + +#include "../../../../inc/MarlinConfigPre.h" + +#if ENABLED(DGUS_LCD_UI_CREALITY_TOUCH) + +#include "../DGUSDisplayDef.h" +#include "../DGUSDisplay.h" +#include "../DGUSScreenHandler.h" + +#include "EstepsHandler.h" + +#include "../../ui_api.h" +#include "../../../marlinui.h" + +#include "../../../../module/temperature.h" +#include "../../../../module/settings.h" +#include "../../../../module/planner.h" +#include "../../../../gcode/gcode.h" + +// Storage init +float EstepsHandler::set_esteps = 0; +float EstepsHandler::calculated_esteps = 0; +float EstepsHandler::remaining_filament = 0; +float EstepsHandler::mark_filament_mm = 0; +float EstepsHandler::filament_to_extrude = 0; +celsius_t EstepsHandler::calibration_temperature = 0; + +void EstepsHandler::Init() { + // Use steps + set_esteps = ExtUI::getAxisSteps_per_mm(ExtUI::E0); + calculated_esteps = 0; + + // Reset + filament_to_extrude = 100; + mark_filament_mm = 120; + remaining_filament = 0; + + // Use configured PLA temps + 10 degrees + calibration_temperature = ui.material_preset[0].hotend_temp + 10; + + // Welcome message + SetStatusMessage(PSTR("Ready")); +} + + +void EstepsHandler::HandleStartButton(DGUS_VP_Variable &var, void *val_ptr) { + //static_assert(ADVANCED_PAUSE_PURGE_LENGTH == 0, "Assuming PURGE_LENGTH is 0 so we can use M701"); + + // Validate + if (calibration_temperature < EXTRUDE_MINTEMP) { + SetStatusMessage(PSTR("Invalid temperature set")); + return; + } + + if (filament_to_extrude < 10) { + SetStatusMessage(PSTR("Invalid extrusion length set")); + return; + } + + if (mark_filament_mm < filament_to_extrude) { + SetStatusMessage(PSTR("Invalid mark length set")); + return; + } + + // Synchronous operation - disable back button + DGUSSynchronousOperation syncOperation; + syncOperation.start(); + + // Prepare + bool zAxisWasRelative = GcodeSuite::axis_is_relative(Z_AXIS); + bool eAxisWasRelative = GcodeSuite::axis_is_relative(E_AXIS); + #if ENABLED(LIN_ADVANCE) + float kFactor = planner.extruder_advance_K[0]; + #endif + + GcodeSuite::set_e_relative(); + GcodeSuite::set_relative_mode(true); + #if ENABLED(LIN_ADVANCE) + planner.extruder_advance_K[0] = 0; + #endif + + ExtUI::injectCommands_P("G0 Z5 F150"); + queue.advance(); + + // Heat up if necessary + if (abs(ExtUI::getActualTemp_celsius(ExtUI::E0) - calibration_temperature) > 2) { + thermalManager.setTargetHotend(calibration_temperature, ExtUI::H0); + + SetStatusMessage(PSTR("Heating up...")); + thermalManager.wait_for_hotend(ExtUI::H0, false); + } + + planner.synchronize(); + + // Set-up command + SetStatusMessage(PSTR("Extruding...")); + + char cmd[64]; + sprintf_P(cmd, PSTR("G1 E%f F50"), filament_to_extrude); + + ExtUI::injectCommands(cmd); + queue.advance(); + planner.synchronize(); + + // Restore position + ExtUI::injectCommands_P("G0 Z-5 F150"); + queue.advance(); + planner.synchronize(); + + // Restore defaults + if (!zAxisWasRelative) GcodeSuite::set_relative_mode(false); + if (!eAxisWasRelative) GcodeSuite::set_e_absolute(); + #if ENABLED(LIN_ADVANCE) + planner.extruder_advance_K[0] = kFactor; + #endif + + // Done + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_ESTEPS_CALIBRATION_RESULTS, false); + ScreenHandler.Buzzer(0, 250); + syncOperation.done(); + SetStatusMessage(PSTR("Measure remaining filament")); +} + +void EstepsHandler::HandleApplyButton(DGUS_VP_Variable &var, void *val_ptr) { + if (abs(calculated_esteps) < 1) { + // User intented to set e-steps directly + ExtUI::setAxisSteps_per_mm(set_esteps, ExtUI::E0); + } else { + ExtUI::setAxisSteps_per_mm(calculated_esteps, ExtUI::E0); + } + + SaveSettingsAndReturn(true); +} + +void EstepsHandler::HandleBackButton(DGUS_VP_Variable &var, void *val_ptr) { + // User intented to set e-steps directly + ExtUI::setAxisSteps_per_mm(set_esteps, ExtUI::E0); + + SaveSettingsAndReturn(false); +} + +void EstepsHandler::SaveSettingsAndReturn(bool fullConfirm) { + // Save & reset + settings.save(); + + if (fullConfirm) ScreenHandler.Buzzer(0, 250); + + ScreenHandler.PopToOldScreen(); + if (fullConfirm) ScreenHandler.GotoScreen(DGUSLCD_SCREEN_MAIN, false); + + SetStatusMessage(PSTR("New e-steps value saved")); +} + +void EstepsHandler::HandleRemainingFilament(DGUS_VP_Variable &var, void *val_ptr) { + ScreenHandler.DGUSLCD_SetFloatAsIntFromDisplay<1>(var, val_ptr); + + // Calculate + constexpr float precision = 0.01; + float actualExtrusion = mark_filament_mm - remaining_filament; + if (actualExtrusion < (-precision)) { + SetStatusMessage(PSTR("Mark filament further")); + return; + } + + if (actualExtrusion < precision) { + SetStatusMessage(PSTR("E-steps are correct")); + calculated_esteps = set_esteps; + return; + } + + float current_steps = ExtUI::getAxisSteps_per_mm(ExtUI::E0); + SERIAL_ECHOLNPAIR("Current steps: ", current_steps); + SERIAL_ECHOLNPAIR("Actual extrusion: ", actualExtrusion); + + float new_steps = (current_steps * filament_to_extrude) / actualExtrusion; + SERIAL_ECHOLNPAIR("New steps: ", new_steps); + + calculated_esteps = new_steps; + + // Status update + SetStatusMessage(PSTR("Calculated new e-steps")); +} + +void EstepsHandler::SetStatusMessage(PGM_P statusMessage) { + ScreenHandler.setstatusmessagePGM(statusMessage); +} + +#endif \ No newline at end of file diff --git a/Marlin/src/lcd/extui/dgus_creality/creality_touch/EstepsHandler.h b/Marlin/src/lcd/extui/dgus_creality/creality_touch/EstepsHandler.h new file mode 100644 index 0000000000..3e8daf806a --- /dev/null +++ b/Marlin/src/lcd/extui/dgus_creality/creality_touch/EstepsHandler.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +class EstepsHandler { + public: + static void Init(); + + static void HandleStartButton(DGUS_VP_Variable &var, void *val_ptr); + static void HandleApplyButton(DGUS_VP_Variable &var, void *val_ptr); + static void HandleBackButton(DGUS_VP_Variable &var, void *val_ptr); + + static void HandleRemainingFilament(DGUS_VP_Variable &var, void *val_ptr); + + public: + static float set_esteps; + static float calculated_esteps; + + static float mark_filament_mm; + static float remaining_filament; + + static float filament_to_extrude; + static celsius_t calibration_temperature; + + private: + static void SaveSettingsAndReturn(bool fullConfirm); + static void SetStatusMessage(PGM_P statusMessage); +}; + diff --git a/Marlin/src/lcd/extui/dgus_creality/creality_touch/FilamentLoadUnloadHandler.cpp b/Marlin/src/lcd/extui/dgus_creality/creality_touch/FilamentLoadUnloadHandler.cpp new file mode 100644 index 0000000000..765428a37a --- /dev/null +++ b/Marlin/src/lcd/extui/dgus_creality/creality_touch/FilamentLoadUnloadHandler.cpp @@ -0,0 +1,106 @@ +#include "../../../../inc/MarlinConfigPre.h" + +#if ENABLED(DGUS_LCD_UI_CREALITY_TOUCH) + +#include "../DGUSDisplayDef.h" +#include "../DGUSDisplay.h" +#include "../DGUSScreenHandler.h" + +#include "FilamentLoadUnloadHandler.h" + +#include "../../ui_api.h" +#include "../../../marlinui.h" + +#include "../../../../module/temperature.h" +#include "../../../../module/settings.h" +#include "../../../../module/planner.h" +#include "../../../../gcode/gcode.h" + +celsius_t FilamentLoadUnloadHandler::nozzle_temperature = 0; +float FilamentLoadUnloadHandler::length = 0; + +void FilamentLoadUnloadHandler::Init() { + nozzle_temperature = ui.material_preset[0].hotend_temp; + length = 150; + + if (ExtUI::isPrinting()) { + nozzle_temperature = ExtUI::getTargetTemp_celsius(ExtUI::extruder_t::E0); + } +} + +void FilamentLoadUnloadHandler::HandleTemperature(DGUS_VP_Variable &var, void *val_ptr) { + ScreenHandler.DGUSLCD_SetValueDirectly(var, val_ptr); + + ValidateTemperatures(); +} + +void FilamentLoadUnloadHandler::HandleLoadUnloadButton(DGUS_VP_Variable &var, void *val_ptr) { + // Common for load/unload -> determine minimum temperature + if (length < 0.1) { + SetStatusMessage("Invalid feed length"); + return; + } + + if (ExtUI::isPrinting() && !ExtUI::isPrintingPaused()) { + SetStatusMessage(PSTR("Please pause print first")); + return; + } + + DGUSSynchronousOperation syncOperation; + uint16_t button_value = uInt16Value(val_ptr); + switch (button_value) { + case FILCHANGE_ACTION_LOAD_BUTTON: + syncOperation.start(); + + ChangeFilamentWithTemperature(PSTR("M701 L%f P0")); + + syncOperation.done(); + break; + + case FILCHANGE_ACTION_UNLOAD_BUTTON: + syncOperation.start(); + + ChangeFilamentWithTemperature(PSTR("M702 U%f")); + + syncOperation.done(); + break; + } + +} + +void FilamentLoadUnloadHandler::ValidateTemperatures() { + LIMIT(nozzle_temperature, EXTRUDE_MINTEMP, HEATER_0_MAXTEMP - HOTEND_OVERSHOOT); +} + +void FilamentLoadUnloadHandler::ChangeFilamentWithTemperature(PGM_P command) { + // Heat if necessary + if (ExtUI::getActualTemp_celsius(ExtUI::E0) < nozzle_temperature && abs(ExtUI::getActualTemp_celsius(ExtUI::E0) - nozzle_temperature) > THERMAL_PROTECTION_HYSTERESIS) { + SetStatusMessage(PSTR("Heating up...")); + + uint16_t target_celsius = nozzle_temperature; + NOMORE(target_celsius, thermalManager.hotend_max_target(0)); + + thermalManager.setTargetHotend(target_celsius, ExtUI::H0); + thermalManager.wait_for_hotend(ExtUI::H0, false); + } + + // Inject load filament command + SetStatusMessage(PSTR("Filament load/unload...")); + + char cmd[64]; + sprintf_P(cmd, command, length); + + // Handle commands + SERIAL_ECHOPAIR("Injecting command: ", cmd); + GcodeSuite::process_subcommands_now(cmd); + SERIAL_ECHOPGM_P("- done"); + + if (ScreenHandler.Settings.display_sound) ScreenHandler.Buzzer(500, 100); + SetStatusMessage(PSTR("Filament load/unload complete")); +} + +void FilamentLoadUnloadHandler::SetStatusMessage(PGM_P statusMessage) { + ScreenHandler.setstatusmessagePGM(statusMessage); +} + +#endif \ No newline at end of file diff --git a/Marlin/src/lcd/extui/dgus_creality/creality_touch/FilamentLoadUnloadHandler.h b/Marlin/src/lcd/extui/dgus_creality/creality_touch/FilamentLoadUnloadHandler.h new file mode 100644 index 0000000000..531439a965 --- /dev/null +++ b/Marlin/src/lcd/extui/dgus_creality/creality_touch/FilamentLoadUnloadHandler.h @@ -0,0 +1,22 @@ + +#pragma once + +#include + +class FilamentLoadUnloadHandler { + public: + static void Init(); + + static void HandleTemperature(DGUS_VP_Variable &var, void *val_ptr); + static void HandleLoadUnloadButton(DGUS_VP_Variable &var, void *val_ptr); + + public: + static celsius_t nozzle_temperature; + static float length; + + private: + static void ValidateTemperatures(); + static void ChangeFilamentWithTemperature(PGM_P command); + static void SetStatusMessage(PGM_P statusMessage); +}; + diff --git a/Marlin/src/lcd/extui/dgus_creality/creality_touch/MeshValidationHandler.cpp b/Marlin/src/lcd/extui/dgus_creality/creality_touch/MeshValidationHandler.cpp new file mode 100644 index 0000000000..427a9c0041 --- /dev/null +++ b/Marlin/src/lcd/extui/dgus_creality/creality_touch/MeshValidationHandler.cpp @@ -0,0 +1,150 @@ + +#include "../../../../inc/MarlinConfigPre.h" + +#if ENABLED(DGUS_LCD_UI_CREALITY_TOUCH) + +#include "../DGUSDisplayDef.h" +#include "../DGUSDisplay.h" +#include "../DGUSScreenHandler.h" + +#include "MeshValidationHandler.h" + +#include "../../ui_api.h" +#include "../../../marlinui.h" + +#include "../../../../module/temperature.h" +#include "../../../../module/settings.h" +#include "../../../../module/planner.h" +#include "../../../../gcode/gcode.h" + +celsius_t MeshValidationHandler::nozzle_temperature; +celsius_t MeshValidationHandler::bed_temperature; +bool MeshValidationHandler::is_running; +bool MeshValidationHandler::started_from_screen; +bool MeshValidationHandler::is_cancelling; +feedRate_t MeshValidationHandler::prev_feedrate; + +void MeshValidationHandler::Init() { + // Set to PLA pre-heat temps by default + nozzle_temperature = ui.material_preset[0].hotend_temp; + bed_temperature = ui.material_preset[0].bed_temp; + + ValidateTemperatures(); +} + +void MeshValidationHandler::HandleTemperature(DGUS_VP_Variable &var, void *val_ptr) { + ScreenHandler.DGUSLCD_SetValueDirectly(var, val_ptr); + + ValidateTemperatures(); +} + +void MeshValidationHandler::HandleStartOrCancelButton(DGUS_VP_Variable &var, void *val_ptr) { + if (!is_running) { + Start(); + } else { + Cancel(); + } +} + +void MeshValidationHandler::Start() { + if (is_running) return; + + // Validate + if (!ExtUI::getMeshValid()) { + SetStatusMessage("Please level bed first"); + return; + } + + // Block + ScreenHandler.SetSynchronousOperationStart(); + is_running = true; + started_from_screen = true; + + // Home if necessary - do this synchronously + if (!all_axes_trusted()) { + queue.inject_P("G28 U0"); + queue.advance(); + } + + // Home X and Y so we droop at the side of the bed. + // G26 with temperature and set for full bed, full pattern, retract 4mm, prime 5mm + char gcodeBuffer[128] = {0}; + sprintf_P(gcodeBuffer, PSTR("G90\nG0 X0\nG26 B%d H%d R Q4 P2 X0 Y0"), bed_temperature, nozzle_temperature); + queue.inject(gcodeBuffer); + queue.advance(); + + // Set feedrate + prev_feedrate = ExtUI::getFeedrate_mm_s(); + ExtUI::setFeedrate_mm_s(MESH_VALIDATION_PATTERN_FEEDRATE); + + SetStatusMessage("Starting..."); +} + +void MeshValidationHandler::Cancel() { + if (is_cancelling) return; + + is_cancelling = true; + ExtUI::ui_setUICancelOperation(true); + + SetStatusMessage("Cancelling..."); +} + +void MeshValidationHandler::OnMeshValidationStart() { + // Note: can also be called when manually invoking G26 + if (ScreenHandler.getCurrentScreen() != DGUSLCD_SCREEN_MESH_VALIDATION) { + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_MESH_VALIDATION); + } + + // Set state + ScreenHandler.SetSynchronousOperationStart(); + is_running = true; +} + +void MeshValidationHandler::OnMeshValidationFinish() { + // If invoked externally, pop back + if (!started_from_screen) { + ScreenHandler.PopToOldScreen(); + } + + if (started_from_screen) { + ExtUI::setFeedrate_mm_s(prev_feedrate); + + char gcodeBuffer[128] = {0}; + if (!is_cancelling) { + // Present + // - Set absolute mode + // - Present bed, high Z + // - Disable stepper + strcpy_P(gcodeBuffer, PSTR("M84")); + + SetStatusMessage("Mesh validation pattern printed"); + } else { + // Park and disable steppers + strcpy_P(gcodeBuffer, PSTR("G0 X5 F2000\nG27\nM84")); + + SetStatusMessage("Canceled mesh validation pattern"); + } + + // Enqueue + gcode.process_subcommands_now(gcodeBuffer); + } + + // Reset state + is_running = false; + started_from_screen = false; + is_cancelling = false; + + ScreenHandler.SetSynchronousOperationFinish(); + ExtUI::ui_setUICancelOperation(false); +} + +void MeshValidationHandler::ValidateTemperatures() { + LIMIT(nozzle_temperature, EXTRUDE_MINTEMP, HEATER_0_MAXTEMP - HOTEND_OVERSHOOT); + LIMIT(bed_temperature, 40 /*Hardcoded minimum for G26, apparently*/, BED_MAXTEMP - BED_OVERSHOOT); +} + +void MeshValidationHandler::SetStatusMessage(PGM_P statusMessage) { + ScreenHandler.setstatusmessagePGM(statusMessage); +} + +#endif \ No newline at end of file diff --git a/Marlin/src/lcd/extui/dgus_creality/creality_touch/MeshValidationHandler.h b/Marlin/src/lcd/extui/dgus_creality/creality_touch/MeshValidationHandler.h new file mode 100644 index 0000000000..6fb8f10bbf --- /dev/null +++ b/Marlin/src/lcd/extui/dgus_creality/creality_touch/MeshValidationHandler.h @@ -0,0 +1,32 @@ +#include + +class MeshValidationHandler { + public: + static void Init(); + + static void HandleTemperature(DGUS_VP_Variable &var, void *val_ptr); + + static void HandleStartOrCancelButton(DGUS_VP_Variable &var, void *val_ptr); + + static void OnMeshValidationStart(); + static void OnMeshValidationFinish(); + + public: + static celsius_t nozzle_temperature; + static celsius_t bed_temperature; + + static feedRate_t prev_feedrate; + + static bool is_cancelling; + static bool is_running; + static bool started_from_screen; + + private: + static void Start(); + static void Cancel(); + + static void ValidateTemperatures(); + static void SetStatusMessage(PGM_P statusMessage); +}; + +constexpr feedRate_t MESH_VALIDATION_PATTERN_FEEDRATE = G26_XY_FEEDRATE; diff --git a/Marlin/src/lcd/extui/dgus_creality/creality_touch/PIDHandler.cpp b/Marlin/src/lcd/extui/dgus_creality/creality_touch/PIDHandler.cpp new file mode 100644 index 0000000000..c06e5870bd --- /dev/null +++ b/Marlin/src/lcd/extui/dgus_creality/creality_touch/PIDHandler.cpp @@ -0,0 +1,91 @@ + +#include "../../../../inc/MarlinConfigPre.h" + +#if ENABLED(DGUS_LCD_UI_CREALITY_TOUCH) + +#include "../DGUSDisplayDef.h" +#include "../DGUSDisplay.h" +#include "../DGUSScreenHandler.h" + +#include "PIDHandler.h" + +#include "../../ui_api.h" +#include "../../../marlinui.h" + +#include "../../../../module/temperature.h" +#include "../../../../module/settings.h" +#include "../../../../module/planner.h" +#include "../../../../gcode/gcode.h" + +// Storage init +uint16_t PIDHandler::cycles = 0; +celsius_t PIDHandler::calibration_temperature = 0; +bool PIDHandler::fan_on = false; +PGM_P PIDHandler::result_message = nullptr; + +void PIDHandler::Init() { + // Reset + cycles = 3; + + fan_on = ExtUI::getTargetFan_percent(ExtUI::fan_t::FAN0) > 10; + + // Use configured PLA temps + 10 degrees + calibration_temperature = ui.material_preset[0].hotend_temp + 15; + + // Welcome message + SetStatusMessage(PSTR("Ready")); +} + + +void PIDHandler::HandleStartButton(DGUS_VP_Variable &var, void *val_ptr) { + //static_assert(ADVANCED_PAUSE_PURGE_LENGTH == 0, "Assuming PURGE_LENGTH is 0 so we can use M701"); + + // Validate + if (calibration_temperature < EXTRUDE_MINTEMP) { + SetStatusMessage(PSTR("Invalid temperature set")); + return; + } + + if (calibration_temperature > HEATER_0_MAXTEMP) { + SetStatusMessage(PSTR("Invalid temperature set")); + return; + } + + if (cycles < 1) { + SetStatusMessage(PSTR("Invalid number of cycles")); + return; + } + + // Synchronous operation - disable back button + DGUSSynchronousOperation syncOperation; + syncOperation.start(); + + // Fan + const auto prev_fan_percentage = ExtUI::getActualFan_percent(ExtUI::fan_t::FAN0); + const uint8_t fan_speed = fan_on ? 255 : 0; + + // Set-up command + SetStatusMessage(PSTR("PID tuning. Please wait...")); + + char cmd[64]; // Add a G4 to allow the fan speed to take effect + sprintf_P(cmd, PSTR("M106 S%d\nG4 S2\nM303 S%d C%d U1"), fan_speed, calibration_temperature, cycles); + SERIAL_ECHOLNPAIR("Executing: ", cmd); + + ExtUI::injectCommands(cmd); + while (queue.has_commands_queued()) queue.advance(); + + // Done + ExtUI::setTargetFan_percent(prev_fan_percentage, ExtUI::fan_t::FAN0); + + ScreenHandler.Buzzer(0, 250); + settings.save(); + syncOperation.done(); + + SetStatusMessage(result_message); +} + +void PIDHandler::SetStatusMessage(PGM_P statusMessage) { + ScreenHandler.setstatusmessagePGM(statusMessage); +} + +#endif \ No newline at end of file diff --git a/Marlin/src/lcd/extui/dgus_creality/creality_touch/PIDHandler.h b/Marlin/src/lcd/extui/dgus_creality/creality_touch/PIDHandler.h new file mode 100644 index 0000000000..810fab4338 --- /dev/null +++ b/Marlin/src/lcd/extui/dgus_creality/creality_touch/PIDHandler.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +class PIDHandler { + public: + static void Init(); + + static void HandleStartButton(DGUS_VP_Variable &var, void *val_ptr); + + public: + static uint16_t cycles; + static celsius_t calibration_temperature; + static bool fan_on; + static PGM_P result_message; + + private: + + static void SetStatusMessage(PGM_P statusMessage); +}; + diff --git a/Marlin/src/lcd/extui/dgus_creality/creality_touch/PageHandlers.cpp b/Marlin/src/lcd/extui/dgus_creality/creality_touch/PageHandlers.cpp new file mode 100644 index 0000000000..af95866b5a --- /dev/null +++ b/Marlin/src/lcd/extui/dgus_creality/creality_touch/PageHandlers.cpp @@ -0,0 +1,443 @@ +#include "../../../../inc/MarlinConfigPre.h" + +#if ENABLED(DGUS_LCD_UI_CREALITY_TOUCH) + +#include "../DGUSDisplayDef.h" +#include "../DGUSDisplay.h" +#include "../DGUSScreenHandler.h" + +#include "../../../../module/temperature.h" +#include "../../../../module/motion.h" +#include "../../../../module/planner.h" +#include "../../../../feature/pause.h" +#include "../../../../gcode/gcode.h" + +#if ENABLED(FILAMENT_RUNOUT_SENSOR) +#include "../../../../feature/runout.h" +#endif + +#include "../../../../module/settings.h" + +#include "../../ui_api.h" +#include "../../../marlinui.h" + +#include "PageHandlers.h" + + +// Definitions of page handlers + +void MainMenuHandler(DGUS_VP_Variable &var, unsigned short buttonValue) { + switch (var.VP) { + case VP_BUTTON_MAINENTERKEY: + switch (buttonValue) { + case 1: + // Try to mount an unmounted card (BTT SKR board has especially some trouble sometimes) + card.mount(); + ScreenHandler.SDCardInserted(); + break; + + case 2: + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_PREPARE); + break; + + case 3: + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_SETUP); + break; + + case 4: + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_CALIBRATE); + break; + } + break; + } +} + +void SetupMenuHandler(DGUS_VP_Variable &var, unsigned short buttonValue) { + switch (var.VP) { + case VP_BUTTON_PREPAREENTERKEY: + switch(buttonValue) { + case 5: // About + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_INFO); + break; + + case 7: // Reset to factory settings + settings.reset(); + settings.save(); + + ExtUI::injectCommands_P(PSTR("M300")); + + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_MAIN, false); + ScreenHandler.setstatusmessagePGM(PSTR("Restored default settings. Please turn your printer off and then on to complete the reset")); + break; + } + break; + + case VP_BUTTON_TEMPCONTROL: + if (buttonValue == 2) ScreenHandler.GotoScreen(DGUSLCD_SCREEN_TEMP); + break; + + case VP_BUTTON_ADJUSTENTERKEY: + ScreenHandler.HandleLEDToggle(); + break; + } +} + +void LevelingModeHandler(DGUS_VP_Variable &var, unsigned short buttonValue) { + switch (var.VP) { + case VP_BUTTON_BEDLEVELKEY: + switch (buttonValue) { + case 1: + queue.enqueue_one_P("G28 U0"); + queue.enqueue_one_P("G0 Z0"); + break; + + case 2: + // Increase Z-offset + ExtUI::smartAdjustAxis_steps(ExtUI::mmToWholeSteps(0.01, ExtUI::axis_t::Z), ExtUI::axis_t::Z, true);; + ScreenHandler.ForceCompleteUpdate(); + ScreenHandler.RequestSaveSettings(); + break; + + case 3: + // Decrease Z-offset + ExtUI::smartAdjustAxis_steps(ExtUI::mmToWholeSteps(-0.01, ExtUI::axis_t::Z), ExtUI::axis_t::Z, true);; + ScreenHandler.ForceCompleteUpdate(); + ScreenHandler.RequestSaveSettings(); + break; + } + + break; + + case VP_BUTTON_PREPAREENTERKEY: + if (buttonValue == 9) { + #if DISABLED(HOTEND_IDLE_TIMEOUT) + thermalManager.disable_all_heaters(); + #endif + + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_MAIN, false); + } + + if (buttonValue == 1) { + // TODO: set state for "view leveling mesh" + ScreenHandler.SetViewMeshLevelState(); + ScreenHandler.InitMeshValues(); + + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_LEVELING); + } + break; + + case VP_BUTTON_MAINENTERKEY: + // Go to leveling screen + ExtUI::injectCommands_P("G28 U0\nG29 U0"); + + ScreenHandler.ResetMeshValues(); + + dgusdisplay.WriteVariable(VP_MESH_SCREEN_MESSAGE_ICON, static_cast(MESH_SCREEN_MESSAGE_ICON_LEVELING)); + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_LEVELING); + break; + } +} + +void LevelingHandler(DGUS_VP_Variable &var, unsigned short buttonValue) { + switch (var.VP) { + case VP_BUTTON_BEDLEVELKEY: + const bool busy = TERN0(HOST_KEEPALIVE_FEATURE, ((ExtUI::getMachineState() == GcodeSuite::MarlinBusyState::IN_PROCESS) || (ExtUI::getMachineState() == GcodeSuite::MarlinBusyState::IN_HANDLER))); + if (!busy) { + ScreenHandler.PopToOldScreen(); + } else { + ScreenHandler.setstatusmessagePGM("Wait for leveling completion..."); + } + break; + } +} + +void TempMenuHandler(DGUS_VP_Variable &var, unsigned short buttonValue) { + switch (var.VP) { + case VP_BUTTON_ADJUSTENTERKEY: + switch (buttonValue) { + case 3: + ScreenHandler.HandleFanToggle(); + break; + } + + break; + + case VP_BUTTON_TEMPCONTROL: + switch (buttonValue){ + case 3: + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_TEMP_PLA); + break; + + case 4: + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_TEMP_ABS); + break; + } + break; + } +} + +void PrepareMenuHandler(DGUS_VP_Variable &var, unsigned short buttonValue) { + switch (var.VP) { + case VP_BUTTON_PREPAREENTERKEY: + switch (buttonValue){ + case 3: + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_MOVE10MM); + break; + + case 6: + // Disable steppers + ScreenHandler.HandleMotorLockUnlock(var, &buttonValue); + break; + } + break; + + case VP_BUTTON_HEATLOADSTARTKEY: + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_FEED); + break; + + case VP_BUTTON_COOLDOWN: + ScreenHandler.HandleAllHeatersOff(var, &buttonValue); + break; + + case VP_BUTTON_TEMPCONTROL: + switch (buttonValue) { + case 5: + thermalManager.setTargetHotend(ui.material_preset[0].hotend_temp, 0); + thermalManager.setTargetBed(ui.material_preset[0].bed_temp); + + break; + + case 6: + thermalManager.setTargetHotend(ui.material_preset[1].hotend_temp, 0); + thermalManager.setTargetBed(ui.material_preset[1].bed_temp); + break; + } + break; + } + + ScreenHandler.ForceCompleteUpdate(); +} + +void TuneMenuHandler(DGUS_VP_Variable &var, unsigned short buttonValue) { + switch (var.VP) { + case VP_BUTTON_ADJUSTENTERKEY: + switch (buttonValue) { + case 2: + ScreenHandler.GotoScreen(ExtUI::isPrintingPaused() ? DGUSLCD_SCREEN_PRINT_PAUSED : DGUSLCD_SCREEN_PRINT_RUNNING, false); + break; + + case 3: + ScreenHandler.HandleFanToggle(); + break; + + case 4: + ScreenHandler.HandleLEDToggle(); + break; + } + } +} + +void PrintRunningMenuHandler(DGUS_VP_Variable &var, unsigned short buttonValue) { + switch (var.VP) { + case VP_BUTTON_ADJUSTENTERKEY: + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_TUNING); + break; + + case VP_BUTTON_PAUSEPRINTKEY: + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_DIALOG_PAUSE); + break; + + case VP_BUTTON_STOPPRINTKEY: + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_DIALOG_STOP); + break; + } +} + +void PrintPausedMenuHandler(DGUS_VP_Variable &var, unsigned short buttonValue) { + switch (var.VP) { + case VP_BUTTON_RESUMEPRINTKEY: +#if ENABLED(FILAMENT_RUNOUT_SENSOR) + runout.reset(); +#endif + + if (!ScreenHandler.HandlePendingUserConfirmation()) { + ExtUI::resumePrint(); + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_PRINT_RUNNING); + } + break; + + case VP_BUTTON_ADJUSTENTERKEY: + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_TUNING); + break; + + case VP_BUTTON_STOPPRINTKEY: + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_DIALOG_STOP); + break; + } +} + +void PrintPauseDialogHandler(DGUS_VP_Variable &var, unsigned short buttonValue) { + switch (var.VP){ + case VP_BUTTON_PAUSEPRINTKEY: + switch (buttonValue) { + case 2: + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_PRINT_RUNNING); + ScreenHandler.setstatusmessagePGM(PSTR("Pausing print - please wait...")); + ExtUI::pausePrint(); + break; + + case 3: + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_PRINT_RUNNING); + break; + } + break; + } +} + +void PrintFinishMenuHandler(DGUS_VP_Variable &var, unsigned short buttonValue) { + switch (var.VP){ + case VP_BUTTON_MAINENTERKEY: + switch (buttonValue) { + case 5: + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_MAIN); + break; + } + break; + } +} + +void FilamentRunoutHandler(DGUS_VP_Variable &var, unsigned short buttonValue) { + switch (var.VP){ + case VP_BUTTON_RESUMEPRINTKEY: + ExtUI::resumePrint(); + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_PRINT_RUNNING); + break; + + case VP_BUTTON_STOPPRINTKEY: + ExtUI::stopPrint(); + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_MAIN); + break; + } +} + +void StopConfirmScreenHandler(DGUS_VP_Variable &var, unsigned short buttonValue) { + switch (var.VP){ + case VP_BUTTON_STOPPRINTKEY: + switch (buttonValue) { + case 2: + ExtUI::stopPrint(); + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_MAIN); + break; + + case 3: + ScreenHandler.GotoScreen(ExtUI::isPrintingPaused() ? DGUSLCD_SCREEN_PRINT_PAUSED : DGUSLCD_SCREEN_PRINT_RUNNING); + break; + } + break; + } +} + +void PreheatSettingsScreenHandler(DGUS_VP_Variable &var, unsigned short buttonValue) { + switch (var.VP){ + case VP_BUTTON_PREPAREENTERKEY: + // Save button, save settings and go back + ScreenHandler.RequestSaveSettings(); + ScreenHandler.PopToOldScreen(); + break; + + case VP_BUTTON_COOLDOWN: // You can't make this up + // Back button, discard settings + settings.load(); + ScreenHandler.PopToOldScreen(); + break; + } +} + +void MoveHandler(DGUS_VP_Variable &var, unsigned short buttonValue) { + if (var.VP == VP_BUTTON_MOVEKEY) { + switch (buttonValue) { + case 1: + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_MOVE10MM, false); + break; + case 2: + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_MOVE1MM, false); + break; + case 3: + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_MOVE01MM, false); + break; + case 4: + // Temporary copy probe settings so we home without preheating, then restore setings afterward + //This machine should never home Z cold... + //auto prev_probe_settings = probe.settings; + + //probe.settings.preheat_bed_temp = 0; + //probe.settings.preheat_hotend_temp = 0; + //probe.settings.stabilize_temperatures_after_probing = false; + + ExtUI::injectCommands_P("G28"); + while (queue.has_commands_queued()) queue.advance(); + + // ... Restore settings + //probe.settings = prev_probe_settings; + break; + } + } +} + +// Register the page handlers +#define PAGE_HANDLER(SCRID, HDLRPTR) { .ScreenID=SCRID, .Handler=HDLRPTR }, +const struct PageHandler PageHandlers[] PROGMEM = { + PAGE_HANDLER(DGUSLCD_Screens::DGUSLCD_SCREEN_MAIN, MainMenuHandler) + + PAGE_HANDLER(DGUSLCD_Screens::DGUSLCD_SCREEN_SETUP, SetupMenuHandler) + + PAGE_HANDLER(DGUSLCD_Screens::DGUSLCD_SCREEN_ZOFFSET_LEVEL, LevelingModeHandler) + PAGE_HANDLER(DGUSLCD_Screens::DGUSLCD_SCREEN_LEVELING, LevelingHandler) + + PAGE_HANDLER(DGUSLCD_Screens::DGUSLCD_SCREEN_TEMP, TempMenuHandler) + PAGE_HANDLER(DGUSLCD_Screens::DGUSLCD_SCREEN_TEMP_PLA, PreheatSettingsScreenHandler) + PAGE_HANDLER(DGUSLCD_Screens::DGUSLCD_SCREEN_TEMP_ABS, PreheatSettingsScreenHandler) + + PAGE_HANDLER(DGUSLCD_Screens::DGUSLCD_SCREEN_TUNING, TuneMenuHandler) + PAGE_HANDLER(DGUSLCD_Screens::DGUSLCD_SCREEN_MOVE01MM, MoveHandler) + PAGE_HANDLER(DGUSLCD_Screens::DGUSLCD_SCREEN_MOVE1MM, MoveHandler) + PAGE_HANDLER(DGUSLCD_Screens::DGUSLCD_SCREEN_MOVE10MM, MoveHandler) + + PAGE_HANDLER(DGUSLCD_Screens::DGUSLCD_SCREEN_FILAMENTRUNOUT1, FilamentRunoutHandler) + PAGE_HANDLER(DGUSLCD_Screens::DGUSLCD_SCREEN_FILAMENTRUNOUT2, FilamentRunoutHandler) + + PAGE_HANDLER(DGUSLCD_Screens::DGUSLCD_SCREEN_DIALOG_STOP, StopConfirmScreenHandler) + + PAGE_HANDLER(DGUSLCD_Screens::DGUSLCD_SCREEN_PRINT_RUNNING, PrintRunningMenuHandler) + PAGE_HANDLER(DGUSLCD_Screens::DGUSLCD_SCREEN_PRINT_PAUSED, PrintPausedMenuHandler) + PAGE_HANDLER(DGUSLCD_Screens::DGUSLCD_SCREEN_PRINT_FINISH, PrintFinishMenuHandler) + + PAGE_HANDLER(DGUSLCD_Screens::DGUSLCD_SCREEN_DIALOG_PAUSE, PrintPauseDialogHandler) + + PAGE_HANDLER(DGUSLCD_Screens::DGUSLCD_SCREEN_PREPARE, PrepareMenuHandler) + + // Terminating + PAGE_HANDLER(static_cast(0) ,0) +}; + +void DGUSCrealityDisplay_HandleReturnKeyEvent(DGUS_VP_Variable &var, void *val_ptr) { + const struct PageHandler *map = PageHandlers; + const uint16_t *ret; + const DGUSLCD_Screens current_screen = DGUSScreenHandler::getCurrentScreen(); + + while ((ret = (uint16_t*) pgm_read_ptr(&(map->Handler)))) { + if ((map->ScreenID) == current_screen) { + uint16_t button_value = uInt16Value(val_ptr); + + SERIAL_ECHOPAIR("Invoking handler for screen ", current_screen); + SERIAL_ECHOLNPAIR("with VP=", var.VP, " value=", button_value); + + map->Handler(var, button_value); + return; + } + + map++; + } +} + +#endif diff --git a/Marlin/src/lcd/extui/dgus_creality/creality_touch/PageHandlers.h b/Marlin/src/lcd/extui/dgus_creality/creality_touch/PageHandlers.h new file mode 100644 index 0000000000..b0ac14f48a --- /dev/null +++ b/Marlin/src/lcd/extui/dgus_creality/creality_touch/PageHandlers.h @@ -0,0 +1,11 @@ +#pragma once + +// Mapping of handlers per page. This construction is necessary because the CR-6 touch screen re-uses the same button IDs all over the place. +typedef void (*DGUS_CREALITY_SCREEN_BUTTON_HANDLER)(DGUS_VP_Variable &var, unsigned short buttonValue); + +struct PageHandler { + DGUSLCD_Screens ScreenID; + DGUS_CREALITY_SCREEN_BUTTON_HANDLER Handler; +}; + +void DGUSCrealityDisplay_HandleReturnKeyEvent(DGUS_VP_Variable &var, void *val_ptr); \ No newline at end of file diff --git a/Marlin/src/lcd/extui/dgus_creality_lcd.cpp b/Marlin/src/lcd/extui/dgus_creality_lcd.cpp new file mode 100644 index 0000000000..4706cebbb5 --- /dev/null +++ b/Marlin/src/lcd/extui/dgus_creality_lcd.cpp @@ -0,0 +1,255 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * dgus_creality_lcd.cpp + * + * DGUS implementation written by coldtobi in 2019 for Marlin + */ + +#include "../../inc/MarlinConfigPre.h" + +#if ENABLED(DGUS_LCD_UI_CREALITY_TOUCH) + +#include "ui_api.h" +#include "../marlinui.h" +#include "./dgus_creality/DGUSDisplay.h" +#include "./dgus_creality/DGUSDisplayDef.h" +#include "./dgus_creality/DGUSScreenHandler.h" +#include "./dgus_creality/creality_touch/PIDHandler.h" +#include "./dgus_creality/creality_touch/MeshValidationHandler.h" + +#if ENABLED(POWER_LOSS_RECOVERY) + #include "../../feature/powerloss.h" +#endif + +extern const char NUL_STR[]; + +namespace ExtUI { + + void onStartup() { + ScreenHandler.Init(); + ScreenHandler.UpdateScreenVPData(); + } + + void onIdle() { ScreenHandler.loop(); } + + void onPrinterKilled(PGM_P const error, PGM_P const component) { + ScreenHandler.sendinfoscreen(GET_TEXT(MSG_HALTED), error, GET_TEXT(MSG_PLEASE_RESET), GET_TEXT(MSG_PLEASE_RESET), true, true, true, true); + + if (strcmp_P(error, GET_TEXT(MSG_ERR_MAXTEMP)) == 0 || strcmp_P(error, GET_TEXT(MSG_THERMAL_RUNAWAY)) == 0) { + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_THERMAL_RUNAWAY); + } else if (strcmp_P(error, GET_TEXT(MSG_HEATING_FAILED_LCD)) == 0) { + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_HEATING_FAILED); + }else if (strcmp_P(error, GET_TEXT(MSG_ERR_MINTEMP)) == 0) { + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_THERMISTOR_ERROR); + } else { + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_KILL); + } + + ScreenHandler.KillScreenCalled(); + while (!ScreenHandler.loop()); // Wait while anything is left to be sent +} + + void onMediaInserted() { TERN_(SDSUPPORT, ScreenHandler.SDCardInserted()); } + void onMediaError() { TERN_(SDSUPPORT, ScreenHandler.SDCardError()); } + void onMediaRemoved() { TERN_(SDSUPPORT, ScreenHandler.SDCardRemoved()); } + + void onPlayTone(const uint16_t frequency, const uint16_t duration) { + if (ScreenHandler.getCurrentScreen() == DGUSLCD_SCREEN_FEED) { + // We're in the feed (load filament) workflow - no beep - there is no confirmation + return; + } + + ScreenHandler.Buzzer(frequency, duration); + } + +bool hasPrintTimer = false; + + void onPrintTimerStarted() { + hasPrintTimer = true; + + if (!IS_SD_FILE_OPEN() && !(PrintJobRecovery::valid() && PrintJobRecovery::exists())) { + ScreenHandler.SetPrintingFromHost(); + } + +#if ENABLED(LCD_SET_PROGRESS_MANUALLY) + ui.progress_reset(); +#endif + + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_PRINT_RUNNING); + } + + void onPrintTimerPaused() { + // Handle M28 Pause SD print - But only if we're not waiting on a user + if (ExtUI::isPrintingFromMediaPaused() && ScreenHandler.getCurrentScreen() == DGUSLCD_SCREEN_PRINT_RUNNING && !ExtUI::isWaitingOnUser()) { + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_PRINT_PAUSED); + } + } + + void onPrintTimerStopped() { + hasPrintTimer = false; + + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_PRINT_FINISH); + } + + void onFilamentRunout(const extruder_t extruder) { + // Only navigate to filament runout screen when we don't use M600 for changing the filament - otherwise it gets confusing for the user + if (strcmp_P(FILAMENT_RUNOUT_SCRIPT, PSTR("M600")) != 0) { + ScreenHandler.FilamentRunout(); + } + } + + void onUserConfirmed() { + DEBUG_ECHOLN("User confirmation invoked"); + + ExtUI::setUserConfirmed(); + } + + void onUserConfirmRequired(const char * const msg) { + if (msg) { + DEBUG_ECHOLNPAIR("User confirmation requested: ", msg); + + if (ScreenHandler.getCurrentScreen() == DGUSLCD_SCREEN_FEED) { + // We're in the feed (load filament) workflow - immediately assume confirmed + onUserConfirmed(); + return; + } + + ScreenHandler.setstatusmessagePGM(msg); + ScreenHandler.sendinfoscreen(PSTR("Confirmation required"), msg, NUL_STR, PSTR("Ok"), true, true, false, true); + + if (ExtUI::isPrinting()) { + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_PRINT_PAUSED); + } else { + ScreenHandler.GotoScreen(DGUSLCD_SCREEN_POPUP); + } + } + else if (ScreenHandler.getCurrentScreen() == DGUSLCD_SCREEN_POPUP) { + DEBUG_ECHOLNPAIR("User confirmation canceled"); + + ScreenHandler.setstatusmessagePGM(nullptr); + ScreenHandler.PopToOldScreen(); + } + } + + void onStatusChanged(const char * const msg) { ScreenHandler.setstatusmessage(msg); } + + void onFactoryReset() { + ScreenHandler.OnFactoryReset(); + } + + void onHomingStart() { + ScreenHandler.OnHomingStart(); + } + + void onHomingComplete() { + ScreenHandler.OnHomingComplete(); + } + + void onPrintFinished() { + ScreenHandler.OnPrintFinished(); + } + + void onStoreSettings(char *buff) { + ScreenHandler.StoreSettings(buff); + } + + void onLoadSettings(const char *buff) { + ScreenHandler.LoadSettings(buff); + } + + void onPostprocessSettings() { + // Called after loading or resetting stored settings + } + + void onConfigurationStoreWritten(bool success) { + // Called after the entire EEPROM has been written, + // whether successful or not. + } + + void onConfigurationStoreRead(bool success) { + // Called after the entire EEPROM has been read, + // whether successful or not. + } + + #if HAS_MESH + void onMeshLevelingStart() { + ScreenHandler.OnMeshLevelingStart(); + } + + void onMeshUpdate(const int8_t xpos, const int8_t ypos, const float zval) { + ScreenHandler.OnMeshLevelingUpdate(xpos, ypos, zval); + } + + void onMeshUpdate(const int8_t xpos, const int8_t ypos, const ExtUI::probe_state_t state) { + ScreenHandler.OnMeshLevelingUpdate(xpos, ypos, 0); + } + #endif + + #if ENABLED(POWER_LOSS_RECOVERY) + void onPowerLossResume() { + // Called on resume from power-loss + ScreenHandler.OnPowerlossResume(); + } + #endif + + + #if HAS_PID_HEATING + void onPidTuning(const result_t rst) { + // Called for temperature PID tuning result + switch (rst) { + case PID_BAD_EXTRUDER_NUM: + PIDHandler::result_message = GET_TEXT(MSG_PID_BAD_EXTRUDER_NUM); + ScreenHandler.setstatusmessagePGM(PIDHandler::result_message); + break; + case PID_TEMP_TOO_HIGH: + PIDHandler::result_message = GET_TEXT(MSG_PID_TEMP_TOO_HIGH); + ScreenHandler.setstatusmessagePGM(PIDHandler::result_message); + break; + case PID_TUNING_TIMEOUT: + PIDHandler::result_message = GET_TEXT(MSG_PID_TIMEOUT); + ScreenHandler.setstatusmessagePGM(PIDHandler::result_message); + break; + case PID_DONE: + PIDHandler::result_message = GET_TEXT(MSG_PID_AUTOTUNE_DONE); + ScreenHandler.setstatusmessagePGM(PIDHandler::result_message); + break; + } + } + #endif + + void onSteppersDisabled() { + } + + void onSteppersEnabled() { + } + + void onMeshValidationStarting() { + MeshValidationHandler::OnMeshValidationStart(); + } + + void onMeshValidationFinished() { + MeshValidationHandler::OnMeshValidationFinish(); + } +} +#endif // HAS_DGUS_LCD diff --git a/Marlin/src/pins/ramps/pins_RAMPS_CREALITY.h b/Marlin/src/pins/ramps/pins_RAMPS_CREALITY.h index 8f5ef2933d..05de208ca1 100644 --- a/Marlin/src/pins/ramps/pins_RAMPS_CREALITY.h +++ b/Marlin/src/pins/ramps/pins_RAMPS_CREALITY.h @@ -44,7 +44,7 @@ #endif #ifndef PS_ON_PIN - #define PS_ON_PIN 40 // Used by CR2020 Industrial series + #define PS_ON_PIN 40 // Used by CR2020 Industrial series #endif #if ENABLED(CASE_LIGHT_ENABLE) && !defined(CASE_LIGHT_PIN) diff --git a/Marlin/src/pins/stm32f1/pins_BTT_SKR_CR6.h b/Marlin/src/pins/stm32f1/pins_BTT_SKR_CR6.h index e76c77e706..50434a889e 100644 --- a/Marlin/src/pins/stm32f1/pins_BTT_SKR_CR6.h +++ b/Marlin/src/pins/stm32f1/pins_BTT_SKR_CR6.h @@ -177,5 +177,13 @@ #define LED_CONTROL_PIN PA13 #ifndef NEOPIXEL_PIN - #define NEOPIXEL_PIN PA8 +#define NEOPIXEL_PIN PA8 +#endif + +#define CASE_LIGHT_PIN LED_CONTROL_PIN + +#define SUICIDE_PIN PC13 + +#ifndef SUICIDE_PIN_INVERTING + #define SUICIDE_PIN_INVERTING false #endif