diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h
index a7b6245c2..b6ea64c2a 100644
--- a/Marlin/Configuration_adv.h
+++ b/Marlin/Configuration_adv.h
@@ -1166,6 +1166,7 @@
#if ENABLED(POWER_LOSS_RECOVERY)
#define PLR_ENABLED_DEFAULT false // Power Loss Recovery enabled by default. (Set with 'M413 Sn' & M500)
//#define BACKUP_POWER_SUPPLY // Backup power / UPS to move the steppers on power loss
+ //#define POWER_LOSS_RECOVER_ZHOME // Z homing is needed for proper recovery. 99.9% of the time this should be disabled!
//#define POWER_LOSS_ZRAISE 2 // (mm) Z axis raise on resume (on power loss with UPS)
//#define POWER_LOSS_PIN 44 // Pin to detect power loss. Set to -1 to disable default pin on boards without module.
//#define POWER_LOSS_STATE HIGH // State of pin indicating power loss
diff --git a/Marlin/src/HAL/AVR/pinsDebug.h b/Marlin/src/HAL/AVR/pinsDebug.h
index 2cf740a55..dac6b1b15 100644
--- a/Marlin/src/HAL/AVR/pinsDebug.h
+++ b/Marlin/src/HAL/AVR/pinsDebug.h
@@ -26,7 +26,7 @@
#define NUMBER_PINS_TOTAL NUM_DIGITAL_PINS
-#if MB(BQ_ZUM_MEGA_3D, MIGHTYBOARD_REVE, MINIRAMBO, SCOOVO_X9H)
+#if MB(BQ_ZUM_MEGA_3D, MIGHTYBOARD_REVE, MINIRAMBO, SCOOVO_X9H, TRIGORILLA_14)
#define AVR_ATmega2560_FAMILY_PLUS_70 1
#endif
diff --git a/Marlin/src/HAL/AVR/pinsDebug_plus_70.h b/Marlin/src/HAL/AVR/pinsDebug_plus_70.h
index 46c03088d..db3fdf1f7 100644
--- a/Marlin/src/HAL/AVR/pinsDebug_plus_70.h
+++ b/Marlin/src/HAL/AVR/pinsDebug_plus_70.h
@@ -22,15 +22,12 @@
* Structures for 2560 family boards that use more than 70 pins
*/
-#undef NUM_DIGITAL_PINS
-#if MB(BQ_ZUM_MEGA_3D)
+#if MB(BQ_ZUM_MEGA_3D, MINIRAMBO, SCOOVO_X9H, TRIGORILLA_14)
+ #undef NUM_DIGITAL_PINS
#define NUM_DIGITAL_PINS 85
#elif MB(MIGHTYBOARD_REVE)
+ #undef NUM_DIGITAL_PINS
#define NUM_DIGITAL_PINS 80
-#elif MB(MINIRAMBO)
- #define NUM_DIGITAL_PINS 85
-#elif MB(SCOOVO_X9H)
- #define NUM_DIGITAL_PINS 85
#endif
#define PA 1
diff --git a/Marlin/src/feature/powerloss.cpp b/Marlin/src/feature/powerloss.cpp
index faf6202db..e4bc605bb 100644
--- a/Marlin/src/feature/powerloss.cpp
+++ b/Marlin/src/feature/powerloss.cpp
@@ -367,7 +367,9 @@ void PrintJobRecovery::resume() {
gcode.process_subcommands_now_P(PSTR(
"G28R0" // No raise during G28
- TERN_(IS_CARTESIAN, "XY") // Don't home Z on Cartesian
+ #if IS_CARTESIAN && DISABLED(POWER_LOSS_RECOVER_ZHOME)
+ "XY" // Don't home Z on Cartesian unless overridden
+ #endif
));
#endif
@@ -375,6 +377,12 @@ void PrintJobRecovery::resume() {
// Pretend that all axes are homed
set_all_homed();
+ #if ENABLED(POWER_LOSS_RECOVER_ZHOME)
+ // Z has been homed so restore Z to ZsavedPos + POWER_LOSS_ZRAISE
+ sprintf_P(cmd, PSTR("G1 F500 Z%s"), dtostrf(info.current_position.z + POWER_LOSS_ZRAISE, 1, 3, str_1));
+ gcode.process_subcommands_now(cmd);
+ #endif
+
// Recover volumetric extrusion state
#if DISABLED(NO_VOLUMETRICS)
#if HAS_MULTI_EXTRUDER
@@ -481,7 +489,7 @@ void PrintJobRecovery::resume() {
// Move back to the saved Z
dtostrf(info.current_position.z, 1, 3, str_1);
- #if Z_HOME_DIR > 0
+ #if Z_HOME_DIR > 0 || ENABLED(POWER_LOSS_RECOVER_ZHOME)
sprintf_P(cmd, PSTR("G1 Z%s F200"), str_1);
#else
gcode.process_subcommands_now_P(PSTR("G1 Z0 F200"));
diff --git a/Marlin/src/lcd/extui/anycubic_chiron_lcd.cpp b/Marlin/src/lcd/extui/anycubic_chiron_lcd.cpp
index 4a8095b6e..a7f9a7a0c 100644
--- a/Marlin/src/lcd/extui/anycubic_chiron_lcd.cpp
+++ b/Marlin/src/lcd/extui/anycubic_chiron_lcd.cpp
@@ -21,7 +21,7 @@
*/
/**
- * anycubic_chiron_lcd.cpp
+ * lcd/extui/anycubic_chiron_lcd.cpp
*
* Anycubic Chiron TFT support for Marlin
*/
@@ -31,508 +31,90 @@
#if ENABLED(ANYCUBIC_LCD_CHIRON)
#include "ui_api.h"
+#include "lib/anycubic_chiron/chiron_tft.h"
-#if ENABLED(AUTO_BED_LEVELING_BILINEAR)
- #if GRID_MAX_POINTS_X != 5 || GRID_MAX_POINTS_Y != 5
- #error ANYCUBIC CHIRON LCD requires a 5x5 bed leveling grid (GRID_MAX_POINTS_X and GRID_MAX_POINTS_Y)
- #endif
-#else
- #error ANYCUBIC CHIRON LCD requires AUTO_BED_LEVELING_BILINEAR enabled
-#endif
-
-#if DISABLED(FILAMENT_RUNOUT_SENSOR)
- #error ANYCUBIC CHIRON LCD requires FILAMENT_RUNOUT_SENSOR enabled
-#endif
-
-#if ENABLED(POWER_LOSS_RECOVERY)
- #error ANYCUBIC CHIRON LCD does not currently support POWER_LOSS_RECOVERY
-#endif
-
-static bool is_auto_leveling = false;
-static bool is_printing_from_sd = false;
-static bool is_out_of_filament = false;
-
-static void sendNewLine(void) {
- LCD_SERIAL.write('\r');
- LCD_SERIAL.write('\n');
-}
-
-static void send(const char *str) {
- LCD_SERIAL.print(str);
-}
-
-static void sendLine(const char *str) {
- send(str);
- sendNewLine();
-}
-
-static void send_P(PGM_P str) {
- while (const char c = pgm_read_byte(str++))
- LCD_SERIAL.write(c);
-}
-
-static void sendLine_P(PGM_P str) {
- send_P(str);
- sendNewLine();
-}
-
-static void sendValue_P(PGM_P prefix, int value) {
- send_P(prefix);
- LCD_SERIAL.print(value);
-}
-
-static void sendValue_P(PGM_P prefix, float value) {
- send_P(prefix);
- LCD_SERIAL.print(value);
-}
-
-static void sendValueLine_P(PGM_P prefix, int value) {
- send_P(prefix);
- LCD_SERIAL.print(value);
- sendNewLine();
-}
-
-static void sendValueLine_P(PGM_P prefix, float value) {
- send_P(prefix);
- LCD_SERIAL.print(value);
- sendNewLine();
-}
-
-static int parseIntArgument(const char *buffer, char letterId) {
- char *p = strchr(buffer, letterId);
- if (!p)
- return -1;
- return atoi(p+1);
-}
-
-static float parseFloatArgument(const char *buffer, char letterId) {
- char *p = strchr(buffer, letterId);
- if (!p)
- return NAN;
- return strtof(p+1, nullptr);
-}
-
-static int mmToHundredths(float x) {
- // Round
- if (x >= 0)
- x += 0.005f;
- else
- x -= 0.005f;
- return (int)(x * 100.0f);
-}
-
-static float hundredthsToMm(int x) {
- return x / 100.0f;
-}
-
-#define SEND_PGM(str) send_P(PSTR(str))
-#define SENDLINE_PGM(str) sendLine_P(PSTR(str))
-#define SENDVALUE_PGM(prefix, value) sendValue_P(PSTR(prefix), value)
-#define SENDVALUELINE_PGM(prefix, value) sendValueLine_P(PSTR(prefix), value)
+using namespace Anycubic;
namespace ExtUI {
- static void moveAxis(float delta, feedRate_t feedrate, axis_t axis) {
- float pos = getAxisPosition_mm(axis);
- pos += delta;
- setAxisPosition_mm(pos, axis, feedrate);
+ void onStartup() { Chiron.Startup(); }
+
+ void onIdle() { Chiron.IdleLoop(); }
+
+ void onPrinterKilled(PGM_P const error, PGM_P const component) {
+ Chiron.PrinterKilled(error,component);
}
- static void handleCmd(const char *rx) {
- static FileList fileList;
- static char selectedFileShortName[8+1+3+1];
-
- if (rx[0] != 'A') {
- SERIAL_ECHOPGM("Unexpected RX: ");
- SERIAL_ECHOLN(rx);
-
- return;
- }
-
- const int cmd = atoi(&rx[1]);
-
- // Uncomment for debugging RX
- //if (cmd > 7 && cmd != 20) {
- // SERIAL_ECHOPGM("RX: ");
- // SERIAL_ECHOLN(rx);
- //}
-
- switch (cmd) {
- case 0: // Get Hotend Actual Temperature
- SENDVALUELINE_PGM("A0V ", (int)getActualTemp_celsius(E0));
- break;
- case 1: // Get Hotend Target Temperature
- SENDVALUELINE_PGM("A1V ", (int)getTargetTemp_celsius(E0));
- break;
- case 2: // Get Bed Actual Temperature
- SENDVALUELINE_PGM("A2V ", (int)getActualTemp_celsius(BED));
- break;
- case 3: // Get Bed Target Temperature
- SENDVALUELINE_PGM("A3V ", (int)getTargetTemp_celsius(BED));
- break;
- case 4: // Get Fan Speed
- SENDVALUELINE_PGM("A4V ", (int)getTargetFan_percent(FAN0));
- break;
- case 5: // Get Current Coordinates
- SENDVALUE_PGM("A5V X: ", getAxisPosition_mm(X));
- SENDVALUE_PGM(" Y: ", getAxisPosition_mm(Y));
- SENDVALUE_PGM(" Z: ", getAxisPosition_mm(Z));
- sendNewLine();
- break;
- case 6: // Get SD Card Print Status
- if (isPrintingFromMedia())
- SENDVALUELINE_PGM("A6V ", (int)getProgress_percent());
- else
- SENDLINE_PGM("A6V ---");
- break;
- case 7: // Get Printing Time
- if (isPrinting()) {
- const int totalMinutes = getProgress_seconds_elapsed() / 60;
- SENDVALUE_PGM("A7V ", (int)(totalMinutes/60));
- SENDVALUE_PGM(" H ", (int)(totalMinutes%60));
- SENDLINE_PGM(" M");
- } else {
- SENDLINE_PGM("A7V 999:999");
- }
- break;
- case 8: // Get SD Card File List
- if (isMediaInserted()) {
- const int startIndex = parseIntArgument(rx, 'S');
- SENDLINE_PGM("FN ");
- for (int i = 0, fileIndex = 0, numFiles = 0; i < (int)fileList.count() && numFiles < 4; i++) {
- fileList.seek(i);
- if (!fileList.isDir()) {
- if (fileIndex >= startIndex) {
- sendLine(fileList.shortFilename());
- sendLine(fileList.longFilename());
- numFiles++;
- }
- fileIndex++;
- }
- }
- SENDLINE_PGM("END");
- } else {
- SENDLINE_PGM("J02");
- }
- break;
- case 9: // Pause SD Card Print
- if (isPrintingFromMedia()) {
- pausePrint();
- is_printing_from_sd = false;
- SENDLINE_PGM("J05");
- } else {
- SENDLINE_PGM("J16"); // Print stopped
- }
- break;
- case 10: // Resume SD Card Print
- if (is_out_of_filament) {
- is_out_of_filament = false;
- // Filament change did eject the old filament automatically,
- // now continue and load the new one
- setUserConfirmed();
- SENDLINE_PGM("J04"); // Printing from SD card
- } else if (isPrintingFromMediaPaused()) {
- resumePrint();
- SENDLINE_PGM("J04"); // Printing from SD card
- }
- break;
- case 11: // Stop SD Card Print
- if (isPrintingFromMedia()) {
- stopPrint();
- is_printing_from_sd = false;
- SENDLINE_PGM("J16"); // Print stopped
- }
- break;
- //case 12: // Kill
- // break;
- case 13: // Select File
- if (!isPrinting()) {
- // Store selected file name
- char *p = strchr(rx, ' ');
- if (p != nullptr && strlen(p+1) < sizeof(selectedFileShortName)) {
- strcpy(selectedFileShortName, p+1);
- SENDLINE_PGM("J20"); // Open succeeded
- }
- else
- SENDLINE_PGM("J21"); // Open failed
- }
- break;
- case 14: // Start Print
- if (!isPrinting() && strcmp(selectedFileShortName, "") != 0) {
- printFile(selectedFileShortName);
- is_printing_from_sd = true;
- SENDLINE_PGM("J04"); // Printing from SD card
- }
- break;
- case 15: // Resume from power outage
- // This is not supported, just report print as completed
- SENDLINE_PGM("J16"); // Print stopped
- break;
- case 16: // Set Hotend Target Temperature
- {
- int temp = parseIntArgument(rx, 'S');
- if (temp >= 0)
- setTargetTemp_celsius(temp, E0);
- }
- break;
- case 17: // Set Bed Target Temperature
- {
- int temp = parseIntArgument(rx, 'S');
- if (temp >= 0)
- setTargetTemp_celsius(temp, BED);
- }
- break;
- case 18: // Set Fan Speed
- {
- int temp = parseIntArgument(rx, 'S');
- if (temp >= 0)
- setTargetFan_percent(temp, FAN0);
- }
- break;
- case 19: // Disable Motors
- injectCommands_P(PSTR("M84"));
- break;
- case 20: // Get/Set Printing Speed
- {
- int newPerc = parseIntArgument(rx, 'S');
- if (newPerc >= 0)
- setFeedrate_percent(newPerc);
- else
- SENDVALUELINE_PGM("A20V ", (int)getFeedrate_percent());
- }
- break;
- case 21: // Home axes
- if (!isPrinting()) {
- const bool hasX = strchr(rx, 'X') != nullptr,
- hasY = strchr(rx, 'Y') != nullptr,
- hasZ = strchr(rx, 'Z') != nullptr,
- hasC = strchr(rx, 'C') != nullptr;
- if (hasX || hasY || hasZ) {
- if (hasX) injectCommands_P(PSTR("G28 X"));
- if (hasY) injectCommands_P(PSTR("G28 Y"));
- if (hasZ) injectCommands_P(PSTR("G28 Z"));
- } else if (hasC) {
- injectCommands_P(PSTR("G28"));
- }
- }
- break;
- case 22: // Move axes
- if (!isPrinting()) {
- const int feedrate = parseIntArgument(rx, 'F') / 60;
- float delta;
- if (!isnan(delta = parseFloatArgument(rx, 'X')))
- moveAxis(delta, feedrate, X);
- else if (!isnan(delta = parseFloatArgument(rx, 'Y')))
- moveAxis(delta, feedrate, Y);
- else if (!isnan(delta = parseFloatArgument(rx, 'Z')))
- moveAxis(delta, feedrate, Z);
- }
- break;
- case 23: // Preheat PLA
- setTargetTemp_celsius(PREHEAT_1_TEMP_HOTEND, E0);
- setTargetTemp_celsius(PREHEAT_1_TEMP_BED, BED);
- SENDLINE_PGM("OK");
- break;
- case 24: // Preheat ABS
- setTargetTemp_celsius(PREHEAT_2_TEMP_HOTEND, E0);
- setTargetTemp_celsius(PREHEAT_2_TEMP_BED, BED);
- SENDLINE_PGM("OK");
- break;
- case 25: // Cool down
- setTargetTemp_celsius(0, E0);
- setTargetTemp_celsius(0, BED);
- SENDLINE_PGM("J12");
- break;
- case 26: // Refresh SD Card
- fileList.refresh();
- break;
- //case 27: // Adjust Servo Angles
- // break;
- //case 28: // Filament Test
- // break;
- case 29: // Get Bed Autolevel Grid
- {
- int x = parseIntArgument(rx, 'X'),
- y = parseIntArgument(rx, 'Y');
- if (x != -1 && y != -1) {
- xy_uint8_t coord;
- coord.set(x, y);
- const int value = mmToHundredths(getMeshPoint(coord));
- SENDVALUELINE_PGM("A29V ", value);
- }
- }
- break;
- case 30: // Autolevel
- if (strchr(rx, 'S')) { // Autoleveling started by clicking "PROBE" and then "OK"
- // Note:
- // We check for completion by monitoring the command queue.
- // Since it will become empty *while* processing the last injected command,
- // we enqueue an extra 10ms delay so we can the determine when all the others
- // have completed.
- if (isMachineHomed())
- injectCommands_P(PSTR("G29\nG4 P10"));
- else
- injectCommands_P(PSTR("G28\nG29\nG4 P10"));
- is_auto_leveling = true;
- } else { // Entering Autoleveling screen
- if (isPrinting())
- SENDLINE_PGM("J24"); // Disallow autoleveling
- else
- SENDLINE_PGM("J26"); // Allow autoleveling
- }
- break;
- case 31: // Set Bed Autolevel Z offset
- if (strchr(rx, 'G')) { // Get
- SENDVALUELINE_PGM("A31V ", getZOffset_mm());
- } else if (strchr(rx, 'S')) { // Set
- float delta = parseFloatArgument(rx, 'S');
- delta = constrain(delta, -1.0, 1.0);
- setZOffset_mm(getZOffset_mm() + delta);
-
- SENDVALUELINE_PGM("A31V ", getZOffset_mm());
- } else if (strchr(rx, 'D')) { // Save
- injectCommands_P(PSTR("M500"));
- }
- break;
- //case 32: // ?
- // break;
- case 33: // Get Version Info
- SENDLINE_PGM("J33 " SHORT_BUILD_VERSION);
- break;
- case 34: // Set Bed Autolevel Grid
- {
- int x = parseIntArgument(rx, 'X'),
- y = parseIntArgument(rx, 'Y'),
- v = parseIntArgument(rx, 'V');
- if (x != -1 && y != -1 && v != -1) { // Set new value
- float value = hundredthsToMm(v);
- value = constrain(value, -10, 10);
-
- xy_uint8_t coord;
- coord.set(x, y);
- setMeshPoint(coord, value);
- } else if (strchr(rx, 'S')) { // Save (apply new values)
- injectCommands_P(PSTR("M500"));
- } else if (strchr(rx, 'C')) { // Cancel (discard new values)
- injectCommands_P(PSTR("M501"));
- }
- }
- break;
- }
- }
-
- #define RX_LEN_MAX 63
- static void parseSerialRx() {
- static char rxBuffer[RX_LEN_MAX+1];
- static uint8_t rxLen = 0;
-
- while (LCD_SERIAL.available()) {
- const char c = LCD_SERIAL.read();
- switch (c) {
- case '\r': case '\n':
- if (rxLen > 0 && rxLen <= RX_LEN_MAX) {
- rxBuffer[rxLen] = '\0'; // Terminate string
- handleCmd(rxBuffer);
- }
- rxLen = 0;
- break;
- default:
- if (rxLen < RX_LEN_MAX)
- rxBuffer[rxLen++] = c;
- else {
- rxLen = 0xFF; // Overrun
- SERIAL_ECHOPGM("Warning: dropping long received line");
- }
- break;
- }
- }
- }
-
- static void detectPrintFromSdCompletion() {
- // Note: printFile() queues some commands that actually start the print, so isPrintingFromMedia()
- // initially returns false
- if (is_printing_from_sd && !commandsInQueue() && !isPrintingFromMedia()) {
- is_printing_from_sd = false;
- SENDLINE_PGM("J14"); // Print done
- }
- }
-
- static void detectAutolevelingCompletion() {
- if (is_auto_leveling && !commandsInQueue()) {
- is_auto_leveling = false;
- injectCommands_P(PSTR("M500"));
- SENDLINE_PGM("J25"); // Autoleveling done
- }
- }
-
- void onStartup() {
- #ifndef LCD_BAUDRATE
- #define LCD_BAUDRATE 115200
- #endif
- LCD_SERIAL.begin(LCD_BAUDRATE);
- sendNewLine();
- SENDLINE_PGM("J17"); // Reset
- delay_ms(10);
- SENDLINE_PGM("J12"); // Ready
- }
-
- void onIdle() {
- parseSerialRx();
- detectAutolevelingCompletion();
- detectPrintFromSdCompletion();
- }
-
- void onPrinterKilled(PGM_P const error, PGM_P const component) { }
-
- void onMediaInserted() {
- SENDLINE_PGM("J00"); // SD Inserted
- }
-
- void onMediaError() { }
-
- void onMediaRemoved() {
- SENDLINE_PGM("J01"); // SD Removed
- }
+ void onMediaInserted() { Chiron.MediaEvent(AC_media_inserted); }
+ void onMediaError() { Chiron.MediaEvent(AC_media_error); }
+ void onMediaRemoved() { Chiron.MediaEvent(AC_media_removed); }
void onPlayTone(const uint16_t frequency, const uint16_t duration) {
- tone(BEEPER_PIN, frequency, duration);
+ #if ENABLED(SPEAKER)
+ ::tone(BEEPER_PIN, frequency, duration);
+ #endif
}
- void onPrintTimerStarted() { }
+ void onPrintTimerStarted() { Chiron.TimerEvent(AC_timer_started); }
+ void onPrintTimerPaused() { Chiron.TimerEvent(AC_timer_paused); }
+ void onPrintTimerStopped() { Chiron.TimerEvent(AC_timer_stopped); }
+ void onFilamentRunout(const extruder_t) { Chiron.FilamentRunout(); }
+ void onUserConfirmRequired(const char * const msg) { Chiron.ConfirmationRequest(msg); }
+ void onStatusChanged(const char * const msg) { Chiron.StatusChange(msg); }
- void onPrintTimerPaused() { }
+ void onFactoryReset() {}
- void onPrintTimerStopped() { }
+ void onStoreSettings(char *buff) {
+ // Called when saving to EEPROM (i.e. M500). If the ExtUI needs
+ // permanent data to be stored, it can write up to eeprom_data_size bytes
+ // into buff.
- void onFilamentRunout(const extruder_t extruder) {
- is_out_of_filament = true;
- SENDLINE_PGM("J23"); // Filament runout
- SENDLINE_PGM("J18"); // Print paused
- // Note: printer will unload filament automatically
+ // Example:
+ // static_assert(sizeof(myDataStruct) <= ExtUI::eeprom_data_size);
+ // memcpy(buff, &myDataStruct, sizeof(myDataStruct));
}
- void onUserConfirmRequired(const char * const msg) { }
+ void onLoadSettings(const char *buff) {
+ // Called while loading settings from EEPROM. If the ExtUI
+ // needs to retrieve data, it should copy up to eeprom_data_size bytes
+ // from buff
- void onStatusChanged(const char * const msg) { }
+ // Example:
+ // static_assert(sizeof(myDataStruct) <= ExtUI::eeprom_data_size);
+ // memcpy(&myDataStruct, buff, sizeof(myDataStruct));
+ }
- void onFactoryReset() { }
+ void onConfigurationStoreWritten(bool success) {
+ // Called after the entire EEPROM has been written,
+ // whether successful or not.
+ }
- void onStoreSettings(char *buff) { }
+ void onConfigurationStoreRead(bool success) {
+ // Called after the entire EEPROM has been read,
+ // whether successful or not.
+ }
- void onLoadSettings(const char *buff) { }
+ #if HAS_MESH
+ void onMeshUpdate(const int8_t xpos, const int8_t ypos, const float zval) {
+ // Called when any mesh points are updated
+ //SERIAL_ECHOLNPAIR("onMeshUpdate() x:", xpos, " y:", ypos, " z:", zval);
+ }
- void onConfigurationStoreWritten(bool success) { }
-
- void onConfigurationStoreRead(bool success) { }
-
- void onMeshUpdate(const int8_t xpos, const int8_t ypos, const float zval) { }
+ void onMeshUpdate(const int8_t xpos, const int8_t ypos, const ExtUI::probe_state_t state) {
+ // Called to indicate a special condition
+ //SERIAL_ECHOLNPAIR("onMeshUpdate() x:", xpos, " y:", ypos, " state:", state);
+ }
+ #endif
#if ENABLED(POWER_LOSS_RECOVERY)
- void onPowerLossResume() { }
+ // Called on resume from power-loss
+ void onPowerLossResume() { Chiron.PowerLossRecovery(); }
#endif
#if HAS_PID_HEATING
- void onPidTuning(const result_t rst) { }
+ void onPidTuning(const result_t rst) {
+ // Called for temperature PID tuning result
+ }
#endif
}
diff --git a/Marlin/src/lcd/extui/lib/anycubic_chiron/FileNavigator.cpp b/Marlin/src/lcd/extui/lib/anycubic_chiron/FileNavigator.cpp
new file mode 100644
index 000000000..fb4c84abb
--- /dev/null
+++ b/Marlin/src/lcd/extui/lib/anycubic_chiron/FileNavigator.cpp
@@ -0,0 +1,162 @@
+/**
+ * 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 .
+ *
+ */
+
+/**
+ * lcd/extui/lib/FileNavigator.cpp
+ *
+ * Extensible_UI implementation for Anycubic Chiron
+ * Written By Nick Wells, 2020 [https://github.com/SwiftNick]
+ * (not affiliated with Anycubic, Ltd.)
+ */
+
+/***************************************************************************
+ * The AC panel wants files in block of 4 and can only display a flat list *
+ * This library allows full folder traversal. *
+ ***************************************************************************/
+
+#include "../../../../inc/MarlinConfigPre.h"
+
+#if ENABLED(ANYCUBIC_LCD_CHIRON)
+
+#include "FileNavigator.h"
+#include "chiron_tft.h"
+
+using namespace ExtUI;
+
+namespace Anycubic {
+
+ FileList FileNavigator::filelist; // Instance of the Marlin file API
+ char FileNavigator::currentfoldername[MAX_PATH_LEN]; // Current folder path
+ uint16_t FileNavigator::lastindex;
+ uint8_t FileNavigator::folderdepth;
+ uint16_t FileNavigator::currentindex; // override the panel request
+
+ FileNavigator::FileNavigator() { reset(); }
+
+ void FileNavigator::reset() {
+ currentfoldername[0] = '\0';
+ folderdepth = 0;
+ currentindex = 0;
+ lastindex = 0;
+ // Start at root folder
+ while (!filelist.isAtRootDir()) filelist.upDir();
+ refresh();
+ }
+
+ void FileNavigator::refresh() { filelist.refresh(); }
+
+ void FileNavigator::getFiles(uint16_t index) {
+ uint8_t files = 4;
+ if (index == 0) currentindex = 0;
+
+ // Each time we change folder we reset the file index to 0 and keep track
+ // of the current position as the TFT panel isnt aware of folders trees.
+ if (index > 0) {
+ --currentindex; // go back a file to take account off the .. we added to the root.
+ if (index > lastindex)
+ currentindex += files;
+ else
+ currentindex = currentindex < 4 ? 0 : currentindex - files;
+ }
+ lastindex = index;
+
+ #if ACDEBUG(AC_FILE)
+ SERIAL_ECHOLNPAIR("index=", index, " currentindex=", currentindex);
+ #endif
+
+ if (currentindex == 0 && folderdepth > 0) { // Add a link to go up a folder
+ TFTSer.println("<<");
+ TFTSer.println("..");
+ files--;
+ }
+
+ for (uint16_t seek = currentindex; seek < currentindex + files; seek++) {
+ if (filelist.seek(seek)) {
+ sendFile();
+ #if ACDEBUG(AC_FILE)
+ SERIAL_ECHOLNPAIR("-", seek, " '", filelist.longFilename(), "' '", currentfoldername, "", filelist.shortFilename(), "'\n");
+ #endif
+ }
+ }
+ }
+
+ void FileNavigator::sendFile() {
+ // send the file and folder info to the panel
+ // this info will be returned when the file is selected
+ // Permitted special characters in file name -_*#~
+ // Panel can display 22 characters per line
+ if (filelist.isDir()) {
+ //TFTSer.print(currentfoldername);
+ TFTSer.println(filelist.shortFilename());
+ TFTSer.print(filelist.shortFilename());
+ TFTSer.println("/");
+ }
+ else {
+ // Logical Name
+ TFTSer.print("/");
+ if (folderdepth > 0) TFTSer.print(currentfoldername);
+
+ TFTSer.println(filelist.shortFilename());
+
+ // Display Name
+ TFTSer.println(filelist.longFilename());
+ }
+ }
+ void FileNavigator::changeDIR(char *folder) {
+ #if ACDEBUG(AC_FILE)
+ SERIAL_ECHOLNPAIR("currentfolder: ", currentfoldername, " New: ", folder);
+ #endif
+ if (folderdepth >= MAX_FOLDER_DEPTH) return; // limit the folder depth
+ strcat(currentfoldername, folder);
+ strcat(currentfoldername, "/");
+ filelist.changeDir(folder);
+ refresh();
+ folderdepth++;
+ currentindex = 0;
+ }
+
+ void FileNavigator::upDIR() {
+ filelist.upDir();
+ refresh();
+ folderdepth--;
+ currentindex = 0;
+ // Remove the last child folder from the stored path
+ if (folderdepth == 0) {
+ currentfoldername[0] = '\0';
+ reset();
+ }
+ else {
+ char *pos = nullptr;
+ for (uint8_t f = 0; f < folderdepth; f++)
+ pos = strchr(currentfoldername, '/');
+
+ *(pos + 1) = '\0';
+ }
+ #if ACDEBUG(AC_FILE)
+ SERIAL_ECHOLNPAIR("depth: ", folderdepth, " currentfoldername: ", currentfoldername);
+ #endif
+ }
+
+ char* FileNavigator::getCurrentFolderName() { return currentfoldername; }
+}
+
+#endif // ANYCUBIC_LCD_CHIRON
diff --git a/Marlin/src/lcd/extui/lib/anycubic_chiron/FileNavigator.h b/Marlin/src/lcd/extui/lib/anycubic_chiron/FileNavigator.h
new file mode 100644
index 000000000..8e03614a4
--- /dev/null
+++ b/Marlin/src/lcd/extui/lib/anycubic_chiron/FileNavigator.h
@@ -0,0 +1,56 @@
+/**
+ * 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
+
+/**
+ * lcd/extui/lib/FileNavigator.h
+ *
+ * Extensible_UI implementation for Anycubic Chiron
+ * Written By Nick Wells, 2020 [https://github.com/SwiftNick]
+ * (not affiliated with Anycubic, Ltd.)
+ */
+
+#include "chiron_tft_defs.h"
+#include "../../ui_api.h"
+
+using namespace ExtUI;
+
+namespace Anycubic {
+ class FileNavigator {
+ public:
+ FileNavigator();
+ void reset();
+ void getFiles(uint16_t);
+ void upDIR();
+ void changeDIR(char *);
+ void sendFile();
+ void refresh();
+ char * getCurrentFolderName();
+ private:
+ static FileList filelist;
+ static char currentfoldername[MAX_PATH_LEN];
+ static uint16_t lastindex;
+ static uint8_t folderdepth;
+ static uint16_t currentindex;
+ };
+ extern FileNavigator filenavigator;
+}
diff --git a/Marlin/src/lcd/extui/lib/anycubic_chiron/Tunes.cpp b/Marlin/src/lcd/extui/lib/anycubic_chiron/Tunes.cpp
new file mode 100644
index 000000000..f09c4db3f
--- /dev/null
+++ b/Marlin/src/lcd/extui/lib/anycubic_chiron/Tunes.cpp
@@ -0,0 +1,62 @@
+/**
+ * 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 .
+ *
+ */
+
+/**
+ * lcd/extui/lib/Tunes.cpp
+ *
+ * Extensible_UI implementation for Anycubic Chiron
+ * Written By Nick Wells, 2020 [https://github.com/SwiftNick]
+ * (not affiliated with Anycubic, Ltd.)
+ */
+
+/***********************************************************************
+ * A Utility to play tunes using the buzzer in the printer controller. *
+ * See Tunes.h for note and tune definitions. *
+ ***********************************************************************/
+
+#include "../../../../inc/MarlinConfigPre.h"
+
+#if ENABLED(ANYCUBIC_LCD_CHIRON)
+
+#include "Tunes.h"
+#include "../../ui_api.h"
+
+namespace Anycubic {
+
+ void PlayTune(uint8_t beeperPin, const uint16_t *tune, uint8_t speed=1) {
+ uint8_t pos = 1;
+ uint16_t wholenotelen = tune[0] / speed;
+ do {
+ uint16_t freq = tune[pos];
+ uint16_t notelen = wholenotelen / tune[pos + 1];
+
+ ::tone(beeperPin, freq, notelen);
+ ExtUI::delay_ms(notelen);
+ pos += 2;
+
+ if (pos >= MAX_TUNE_LENGTH) break;
+ } while (tune[pos] != n_END);
+ }
+
+}
+
+#endif // ANYCUBIC_LCD_CHIRON
diff --git a/Marlin/src/lcd/extui/lib/anycubic_chiron/Tunes.h b/Marlin/src/lcd/extui/lib/anycubic_chiron/Tunes.h
new file mode 100644
index 000000000..1bafec43a
--- /dev/null
+++ b/Marlin/src/lcd/extui/lib/anycubic_chiron/Tunes.h
@@ -0,0 +1,224 @@
+/**
+ * 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
+
+/**
+ * lcd/extui/lib/Tunes.h
+ *
+ * Extensible_UI implementation for Anycubic Chiron
+ * Written By Nick Wells, 2020 [https://github.com/SwiftNick]
+ * (not affiliated with Anycubic, Ltd.)
+ */
+
+/**************************************************************************
+ * Notes definition from https://pages.mtu.edu/~suits/NoteFreqCalcs.html *
+ * *
+ * The format of a tune is: *
+ * {,,, ,, ... } *
+ * *
+ * 1) The first value is the length of a whole note in milliseconds *
+ * 2) Then a sequence of pitch and duration pairs *
+ * 3) Finally the END marker so your tunes can be any length up to *
+ * MAX_TUNE_LEN *
+ *************************************************************************/
+
+#include
+
+#define MAX_TUNE_LENGTH 128
+
+// Special notes!
+#define n_P 0 // silence or pause
+#define n_END 10000 // end of tune marker
+
+// Note duration divisors
+#define l_T1 1
+#define l_T2 2
+#define l_T3 3
+#define l_T4 4
+#define l_T8 8
+#define l_T16 16
+
+// Note Frequency
+#define n_C0 16
+#define n_CS0 17
+#define n_D0 18
+#define n_DS0 19
+#define n_E0 21
+#define n_F0 22
+#define n_FS0 23
+#define n_G0 25
+#define n_GS0 26
+#define n_A0 28
+#define n_AS0 29
+#define n_B0 31
+#define n_C1 33
+#define n_CS1 35
+#define n_D1 37
+#define n_DS1 39
+#define n_E1 41
+#define n_F1 44
+#define n_FS1 46
+#define n_G1 49
+#define n_GS1 52
+#define n_A1 55
+#define n_AS1 58
+#define n_B1 62
+#define n_C2 65
+#define n_CS2 69
+#define n_D2 73
+#define n_DS2 78
+#define n_E2 82
+#define n_F2 87
+#define n_FS2 93
+#define n_G2 98
+#define n_GS2 104
+#define n_A2 110
+#define n_AS2 117
+#define n_B2 123
+#define n_C3 131
+#define n_CS3 139
+#define n_D3 147
+#define n_DS3 156
+#define n_E3 165
+#define n_F3 175
+#define n_FS3 185
+#define n_G3 196
+#define n_GS3 208
+#define n_A3 220
+#define n_AS3 233
+#define n_B3 247
+#define n_C4 262
+#define n_CS4 277
+#define n_D4 294
+#define n_DS4 311
+#define n_E4 330
+#define n_F4 349
+#define n_FS4 370
+#define n_G4 392
+#define n_GS4 415
+#define n_A4 440
+#define n_AS4 466
+#define n_B4 494
+#define n_C5 523
+#define n_CS5 554
+#define n_D5 587
+#define n_DS5 622
+#define n_E5 659
+#define n_F5 698
+#define n_FS5 740
+#define n_G5 784
+#define n_GS5 831
+#define n_A5 880
+#define n_AS5 932
+#define n_B5 988
+#define n_C6 1047
+#define n_CS6 1109
+#define n_D6 1175
+#define n_DS6 1245
+#define n_E6 1319
+#define n_F6 1397
+#define n_FS6 1480
+#define n_G6 1568
+#define n_GS6 1661
+#define n_A6 1760
+#define n_AS6 1865
+#define n_B6 1976
+#define n_C7 2093
+#define n_CS7 2217
+#define n_D7 2349
+#define n_DS7 2489
+#define n_E7 2637
+#define n_F7 2794
+#define n_FS7 2960
+#define n_G7 3136
+#define n_GS7 3322
+#define n_A7 3520
+#define n_AS7 3729
+#define n_B7 3951
+#define n_C8 4186
+#define n_CS8 4435
+#define n_D8 4699
+#define n_DS8 4978
+#define n_E8 5274
+#define n_F8 5587
+#define n_FS8 5920
+#define n_G8 6272
+#define n_GS8 6645
+#define n_A8 7040
+#define n_AS8 7459
+#define n_B8 7902
+
+namespace Anycubic {
+
+ void PlayTune(uint8_t beeperPin, const uint16_t *tune, uint8_t speed);
+
+ // Only uncomment the tunes you are using to save memory
+ // This will help you write tunes!
+ // https://www.apronus.com/music/flashpiano.htm
+
+ const uint16_t SOS[] = {
+ 250,
+ n_G6,l_T3, n_P,l_T3, n_G6,l_T3, n_P,l_T3, n_G6,l_T3, n_P,l_T1,
+ n_G6,l_T1, n_P,l_T3, n_G6,l_T1, n_P,l_T3, n_G6,l_T1, n_P,l_T1,
+ n_G6,l_T3, n_P,l_T3, n_G6,l_T3, n_P,l_T3, n_G6,l_T3, n_P,l_T1,
+ n_END
+ };
+
+ const uint16_t BeepBeep[] = {
+ 500,
+ n_C7,l_T8, n_P,l_T16, n_C7,l_T8, n_P,l_T8,
+ n_END
+ };
+
+ const uint16_t BeepBeepBeeep[] = {
+ 1000,
+ n_G7,l_T4, n_P,l_T16, n_G7,l_T4, n_P,l_T8, n_G7,l_T2,
+ n_END
+ };
+
+ const uint16_t Anycubic_PowerOn[] = {
+ 1000,
+ n_F7,l_T8, n_P,l_T8, n_C7,l_T8, n_P,l_T8, n_D7,l_T8, n_P,l_T8,
+ n_E7,l_T8, n_P,l_T8, n_D7,l_T4, n_P,l_T4, n_G7,l_T4, n_P,l_T4,
+ n_A7,l_T2, n_P,l_T1,
+ n_END
+ };
+
+ const uint16_t GB_PowerOn[] = {
+ 500,
+ n_C6,l_T4, n_P,l_T16, n_C7,l_T2, n_P,l_T8,
+ n_END
+ };
+
+ const uint16_t Heater_Timedout[] = {
+ 1000,
+ n_C6,l_T1,
+ n_END
+ };
+
+ const uint16_t FilamentOut[] = {
+ 1000,
+ n_AS7,l_T4, n_P,l_T16, n_FS7,l_T2,
+ n_END
+ };
+
+}
diff --git a/Marlin/src/lcd/extui/lib/anycubic_chiron/chiron_tft.cpp b/Marlin/src/lcd/extui/lib/anycubic_chiron/chiron_tft.cpp
new file mode 100644
index 000000000..5e492573e
--- /dev/null
+++ b/Marlin/src/lcd/extui/lib/anycubic_chiron/chiron_tft.cpp
@@ -0,0 +1,896 @@
+/**
+ * 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 .
+ *
+ */
+
+/**
+ * lcd/extui/lib/chiron_tft.cpp
+ *
+ * Extensible_UI implementation for Anycubic Chiron
+ * Written By Nick Wells, 2020 [https://github.com/SwiftNick]
+ * (not affiliated with Anycubic, Ltd.)
+ */
+
+#include "../../../../inc/MarlinConfigPre.h"
+
+#if ENABLED(ANYCUBIC_LCD_CHIRON)
+
+#include "chiron_tft.h"
+#include "Tunes.h"
+#include "FileNavigator.h"
+
+#include "../../../../gcode/queue.h"
+#include "../../../../sd/cardreader.h"
+#include "../../../../libs/numtostr.h"
+#include "../../../../MarlinCore.h"
+namespace Anycubic {
+
+ printer_state_t ChironTFT::printer_state;
+ paused_state_t ChironTFT::pause_state;
+ heater_state_t ChironTFT::hotend_state;
+ heater_state_t ChironTFT::hotbed_state;
+ xy_uint8_t ChironTFT::selectedmeshpoint;
+ char ChironTFT::selectedfile[MAX_PATH_LEN];
+ char ChironTFT::panel_command[MAX_CMND_LEN];
+ uint8_t ChironTFT::command_len;
+ float ChironTFT::live_Zoffset;
+ file_menu_t ChironTFT::file_menu;
+
+ ChironTFT::ChironTFT(){}
+
+ void ChironTFT::Startup() {
+ selectedfile[0] = '\0';
+ panel_command[0] = '\0';
+ command_len = 0;
+ printer_state = AC_printer_idle;
+ pause_state = AC_paused_idle;
+ hotend_state = AC_heater_off;
+ hotbed_state = AC_heater_off;
+ live_Zoffset = 0.0;
+ file_menu = AC_menu_file;
+
+ // Setup pins for powerloss detection
+ // Two IO pins are connected on the Trigorilla Board
+ // On a power interruption the OUTAGECON_PIN goes low.
+
+ #if ENABLED(POWER_LOSS_RECOVERY)
+ OUT_WRITE(OUTAGECON_PIN, HIGH);
+ #endif
+
+ // Filament runout is handled by Marlin settings in Configuration.h
+ // set FIL_RUNOUT_STATE HIGH // Pin state indicating that filament is NOT present.
+ // enable FIL_RUNOUT_PULLUP
+
+ TFTSer.begin(115200);
+
+ // Signal Board has reset
+ SendtoTFTLN(AC_msg_main_board_has_reset);
+
+ safe_delay(200);
+
+ // Enable levelling and Disable end stops during print
+ // as Z home places nozzle above the bed so we need to allow it past the end stops
+ injectCommands_P(AC_cmnd_enable_levelling); //M211 S0\n"));
+
+ // Startup tunes are defined in Tunes.h
+ //PlayTune(BEEPER_PIN, Anycubic_PowerOn, 1);
+ PlayTune(BEEPER_PIN, GB_PowerOn, 1);
+ #if ACDEBUGLEVEL
+ SERIAL_ECHOLNPAIR("AC Debug Level ", ACDEBUGLEVEL);
+ #endif
+ SendtoTFTLN(AC_msg_ready);
+ }
+
+ void ChironTFT::IdleLoop() {
+ if (ReadTFTCommand()) {
+ ProcessPanelRequest();
+ command_len = 0;
+ }
+ CheckHeaters();
+ }
+
+ void ChironTFT::PrinterKilled(PGM_P error,PGM_P component) {
+ SendtoTFTLN(AC_msg_kill_lcd);
+ #if ACDEBUG(AC_MARLIN)
+ SERIAL_ECHOLNPAIR("PrinterKilled()\nerror: ", error , "\ncomponent: ", component);
+ #endif
+ }
+
+ void ChironTFT::MediaEvent(media_event_t event) {
+ #if ACDEBUG(AC_MARLIN)
+ SERIAL_ECHOLNPAIR("ProcessMediaStatus() ", event);
+ #endif
+ switch (event) {
+ case AC_media_inserted:
+ SendtoTFTLN(AC_msg_sd_card_inserted);
+ break;
+
+ case AC_media_removed:
+ SendtoTFTLN(AC_msg_sd_card_removed);
+ break;
+
+ case AC_media_error:
+ SendtoTFTLN(AC_msg_no_sd_card);
+ break;
+ }
+ }
+
+ void ChironTFT::TimerEvent(timer_event_t event) {
+ #if ACDEBUG(AC_MARLIN)
+ SERIAL_ECHOLNPAIR("TimerEvent() ", event);
+ SERIAL_ECHOLNPAIR("Printer State: ", printer_state);
+ #endif
+
+ switch (event) {
+ case AC_timer_started: {
+ live_Zoffset = 0.0; // reset print offset
+ setSoftEndstopState(false); // disable endstops to print
+ printer_state = AC_printer_printing;
+ SendtoTFTLN(AC_msg_print_from_sd_card);
+ } break;
+
+ case AC_timer_paused: {
+ printer_state = AC_printer_paused;
+ pause_state = AC_paused_idle;
+ SendtoTFTLN(AC_msg_paused);
+ } break;
+
+ case AC_timer_stopped: {
+ if (printer_state != AC_printer_idle) {
+ printer_state = AC_printer_stopping;
+ SendtoTFTLN(AC_msg_print_complete);
+ }
+ setSoftEndstopState(true); // enable endstops
+ } break;
+ }
+ }
+
+ void ChironTFT::FilamentRunout() {
+ #if ACDEBUG(AC_MARLIN)
+ SERIAL_ECHOLNPAIR("FilamentRunout() printer_state ", printer_state);
+ #endif
+ // 1 Signal filament out
+ SendtoTFTLN(isPrintingFromMedia() ? AC_msg_filament_out_alert : AC_msg_filament_out_block);
+ //printer_state = AC_printer_filament_out;
+ PlayTune(BEEPER_PIN, FilamentOut, 1);
+ }
+
+ void ChironTFT::ConfirmationRequest(const char * const msg) {
+ // M108 continue
+ #if ACDEBUG(AC_MARLIN)
+ SERIAL_ECHOLNPAIR("ConfirmationRequest() ", msg, " printer_state:", printer_state);
+ #endif
+ switch (printer_state) {
+ case AC_printer_pausing: {
+ if ( (strcmp_P(msg, MARLIN_msg_print_paused) == 0 ) || (strcmp_P(msg, MARLIN_msg_nozzle_parked) == 0 ) ) {
+ SendtoTFTLN(AC_msg_paused); // enable continue button
+ printer_state = AC_printer_paused;
+ }
+ } break;
+
+ case AC_printer_resuming_from_power_outage:
+ case AC_printer_printing:
+ case AC_printer_paused: {
+ // Heater timout, send acknowledgement
+ if (strcmp_P(msg, MARLIN_msg_heater_timeout) == 0 ) {
+ pause_state = AC_paused_heater_timed_out;
+ SendtoTFTLN(AC_msg_paused); // enable continue button
+ PlayTune(BEEPER_PIN,Heater_Timedout,1);
+ }
+ // Reheat finished, send acknowledgement
+ else if (strcmp_P(msg, MARLIN_msg_reheat_done) == 0 ) {
+ pause_state = AC_paused_idle;
+ SendtoTFTLN(AC_msg_paused); // enable continue button
+ }
+ // Filament Purging, send acknowledgement enter run mode
+ else if (strcmp_P(msg, MARLIN_msg_filament_purging) == 0 ) {
+ pause_state = AC_paused_purging_filament;
+ SendtoTFTLN(AC_msg_paused); // enable continue button
+ }
+ } break;
+ default:
+ break;
+ }
+ }
+
+ void ChironTFT::StatusChange(const char * const msg) {
+ #if ACDEBUG(AC_MARLIN)
+ SERIAL_ECHOLNPAIR("StatusChange() ", msg);
+ SERIAL_ECHOLNPAIR("printer_state:", printer_state);
+ #endif
+ bool msg_matched = false;
+ // The only way to get printer status is to parse messages
+ // Use the state to minimise the work we do here.
+ switch (printer_state) {
+ case AC_printer_probing: {
+ // If probing completes ok save the mesh and park
+ if (strcmp_P(msg, MARLIN_msg_ready) == 0 ) {
+ injectCommands_P(PSTR("M500\nG27"));
+ SendtoTFTLN(AC_msg_probing_complete);
+ printer_state = AC_printer_idle;
+ msg_matched = true;
+ }
+ // If probing fails dont save the mesh raise the probe above the bad point
+ if (strcmp_P(msg, MARLIN_msg_probing_failed) == 0 ) {
+ PlayTune(BEEPER_PIN, BeepBeepBeeep, 1);
+ injectCommands_P(PSTR("G1 Z50 F500"));
+ SendtoTFTLN(AC_msg_probing_complete);
+ printer_state = AC_printer_idle;
+ msg_matched = true;
+ }
+ } break;
+
+ case AC_printer_printing: {
+ if (strcmp_P(msg, MARLIN_msg_reheating) == 0 ) {
+ SendtoTFTLN(AC_msg_paused); // enable continue button
+ msg_matched = true;
+ }
+ } break;
+
+ case AC_printer_pausing: {
+ if (strcmp_P(msg, MARLIN_msg_print_paused) == 0 ) {
+ SendtoTFTLN(AC_msg_paused);
+ printer_state = AC_printer_paused;
+ pause_state = AC_paused_idle;
+ msg_matched = true;
+ }
+ } break;
+
+ case AC_printer_stopping: {
+ if (strcmp_P(msg, MARLIN_msg_print_aborted) == 0 ) {
+ SendtoTFTLN(AC_msg_stop);
+ printer_state = AC_printer_idle;
+ msg_matched = true;
+ }
+ } break;
+ default:
+ break;
+ }
+
+ // If not matched earlier see if this was a heater message
+ if (!msg_matched) {
+ if (strcmp_P(msg, MARLIN_msg_extruder_heating) == 0) {
+ SendtoTFTLN(AC_msg_nozzle_heating);
+ hotend_state = AC_heater_temp_set;
+ }
+ else if (strcmp_P(msg, MARLIN_msg_bed_heating) == 0) {
+ SendtoTFTLN(AC_msg_bed_heating);
+ hotbed_state = AC_heater_temp_set;
+ }
+ }
+ }
+
+ void ChironTFT::PowerLossRecovery() {
+ printer_state = AC_printer_resuming_from_power_outage; // Play tune to notify user we can recover.
+ PlayTune(BEEPER_PIN, SOS, 1);
+ SERIAL_ECHOLNPGM("Resuming from power outage...");
+ SERIAL_ECHOLNPGM("Select SD file then press resume");
+ }
+
+ void ChironTFT::SendtoTFT(PGM_P str) { // A helper to print PROGMEN string to the panel
+ #if ACDEBUG(AC_SOME)
+ serialprintPGM(str);
+ #endif
+ while (const char c = pgm_read_byte(str++)) TFTSer.print(c);
+ }
+
+ void ChironTFT::SendtoTFTLN(PGM_P str = nullptr) {
+ if (str != nullptr) {
+ #if ACDEBUG(AC_SOME)
+ SERIAL_ECHO("> ");
+ #endif
+ SendtoTFT(str);
+ #if ACDEBUG(AC_SOME)
+ SERIAL_EOL();
+ #endif
+ }
+ TFTSer.println("");
+ }
+
+ bool ChironTFT::ReadTFTCommand() {
+ bool command_ready = false;
+ while( (TFTSer.available() > 0) && (command_len < MAX_CMND_LEN) ) {
+ panel_command[command_len] = TFTSer.read();
+ if(panel_command[command_len] == '\n') {
+ command_ready = true;
+ break;
+ }
+ command_len++;
+ }
+
+ if(command_ready) {
+ panel_command[command_len] = 0x00;
+ #if ACDEBUG(AC_ALL)
+ SERIAL_ECHOLNPAIR("< ", panel_command);
+ #endif
+ #if ACDEBUG(AC_SOME)
+ // Ignore status request commands
+ uint8_t req = atoi(&panel_command[1]);
+ if (req > 7 && req != 20) {
+ SERIAL_ECHOLNPAIR("> ", panel_command);
+ SERIAL_ECHOLNPAIR("printer_state:", printer_state);
+ }
+ #endif
+ }
+ return command_ready;
+ }
+
+ int8_t ChironTFT::Findcmndpos(const char * buff, char q) {
+ bool found = false;
+ int8_t pos = 0;
+ do {
+ if (buff[pos] == q) {
+ found = true;
+ break;
+ }
+ pos ++;
+ } while(pos < MAX_CMND_LEN);
+ if (found) return pos;
+ return -1;
+ }
+
+ void ChironTFT::CheckHeaters() {
+ uint8_t faultDuration = 0; float temp = 0;
+
+ // if the hotend temp is abnormal, confirm state before signalling panel
+ temp = getActualTemp_celsius(E0);
+ if ( (temp <= HEATER_0_MINTEMP) || (temp >= HEATER_0_MAXTEMP) ) {
+ do {
+ faultDuration ++;
+ if (faultDuration >= AC_HEATER_FAULT_VALIDATION_TIME) {
+ SendtoTFTLN(AC_msg_nozzle_temp_abnormal);
+ SERIAL_ECHOLNPAIR("Extruder temp abnormal! : ", temp);
+ break;
+ }
+ delay_ms(500);
+ temp = getActualTemp_celsius(E0);
+ } while ((temp <= HEATER_0_MINTEMP) || (temp >= HEATER_0_MAXTEMP) );
+ }
+
+ // if the hotbed temp is abnormal, confirm state before signalling panel
+ faultDuration = 0;
+ temp = getActualTemp_celsius(BED);
+ if ( (temp <= BED_MINTEMP) || (temp >= BED_MAXTEMP) ) {
+ do {
+ faultDuration ++;
+ if (faultDuration >= AC_HEATER_FAULT_VALIDATION_TIME) {
+ SendtoTFTLN(AC_msg_nozzle_temp_abnormal);
+ SERIAL_ECHOLNPAIR_P("Bed temp abnormal! : ", temp);
+ break;
+ }
+ delay_ms(500);
+ temp = getActualTemp_celsius(E0);
+ } while ((temp <= BED_MINTEMP) || (temp >= BED_MAXTEMP) );
+ }
+
+ // Update panel with hotend heater status
+ if (hotend_state != AC_heater_temp_reached) {
+ if ( WITHIN( getActualTemp_celsius(E0) - getTargetTemp_celsius(E0), -1, 1 ) ) {
+ SendtoTFTLN(AC_msg_nozzle_heating_done);
+ hotend_state = AC_heater_temp_reached;
+ }
+ }
+
+ // Update panel with bed heater status
+ if (hotbed_state != AC_heater_temp_reached) {
+ if ( WITHIN( getActualTemp_celsius(BED) - getTargetTemp_celsius(BED), -0.5, 0.5 ) ) {
+ SendtoTFTLN(AC_msg_bed_heating_done);
+ hotbed_state = AC_heater_temp_reached;
+ }
+ }
+ }
+
+ void ChironTFT::SendFileList(int8_t startindex) {
+ // respond to panel request for 4 files starting at index
+ #if ACDEBUG(AC_INFO)
+ SERIAL_ECHOLNPAIR("## SendFileList ## ", startindex);
+ #endif
+ SendtoTFTLN(PSTR("FN "));
+ filenavigator.getFiles(startindex);
+ SendtoTFTLN(PSTR("END"));
+ }
+
+ void ChironTFT::SelectFile() {
+ strncpy(selectedfile,panel_command+4,command_len-4);
+ selectedfile[command_len-5] = '\0';
+ #if ACDEBUG(AC_FILE)
+ SERIAL_ECHOLNPAIR_F(" Selected File: ",selectedfile);
+ #endif
+ switch (selectedfile[0]) {
+ case '/': // Valid file selected
+ SendtoTFTLN(AC_msg_sd_file_open_success);
+ break;
+
+ case '<': // .. (go up folder level)
+ filenavigator.upDIR();
+ SendtoTFTLN(AC_msg_sd_file_open_failed);
+ SendFileList( 0 );
+ break;
+ default: // enter sub folder
+ filenavigator.changeDIR(selectedfile);
+ SendtoTFTLN(AC_msg_sd_file_open_failed);
+ SendFileList( 0 );
+ break;
+ }
+ }
+
+ void ChironTFT::InjectCommandandWait(PGM_P cmd) {
+ //injectCommands_P(cmnd); queue.enqueue_now_P(cmd);
+ //SERIAL_ECHOLN(PSTR("Inject>"));
+ }
+
+ void ChironTFT::ProcessPanelRequest() {
+ // Break these up into logical blocks // as its easier to navigate than one huge switch case!
+ int8_t req = atoi(&panel_command[1]);
+
+ // Information requests A0 - A8 and A33
+ if (req <= 8 || req == 33) PanelInfo(req);
+
+ // Simple Actions A9 - A28
+ else if ( req <= 28) PanelAction(req);
+
+ // Process Initiation
+ else if (req <= 34) PanelProcess(req);
+
+ else SendtoTFTLN();
+ }
+
+ void ChironTFT::PanelInfo(uint8_t req) {
+ // information requests A0-A8 and A33
+ switch (req) {
+ case 0: // A0 Get HOTEND Temp
+ SendtoTFT(PSTR("A0V "));
+ TFTSer.println(getActualTemp_celsius(E0));
+ break;
+
+ case 1: // A1 Get HOTEND Target Temp
+ SendtoTFT(PSTR("A1V "));
+ TFTSer.println(getTargetTemp_celsius(E0));
+ break;
+
+ case 2: // A2 Get BED Temp
+ SendtoTFT(PSTR("A2V "));
+ TFTSer.println(getActualTemp_celsius(BED));
+ break;
+
+ case 3: // A3 Get BED Target Temp
+ SendtoTFT(PSTR("A3V "));
+ TFTSer.println(getTargetTemp_celsius(BED));
+ break;
+
+ case 4: // A4 Get FAN Speed
+ SendtoTFT(PSTR("A4V "));
+ TFTSer.println(getActualFan_percent(FAN0));
+ break;
+
+ case 5: // A5 Get Current Coordinates
+ SendtoTFT(PSTR("A5V X: "));
+ TFTSer.print(getAxisPosition_mm(X));
+ SendtoTFT(PSTR(" Y: "));
+ TFTSer.print(getAxisPosition_mm(Y));
+ SendtoTFT(PSTR(" Z: "));
+ TFTSer.println(getAxisPosition_mm(Z));
+ break;
+
+ case 6: // A6 Get printing progress
+ if (isPrintingFromMedia()) {
+ SendtoTFT(PSTR("A6V "));
+ TFTSer.println(ui8tostr2(getProgress_percent()));
+
+ }
+ else
+ SendtoTFTLN(PSTR("A6V ---"));
+ break;
+
+ case 7: { // A7 Get Printing Time
+ uint32_t time = getProgress_seconds_elapsed() / 60;
+ SendtoTFT(PSTR("A7V "));
+ TFTSer.print(ui8tostr2(time / 60));
+ SendtoTFT(PSTR(" H "));
+ TFTSer.print(ui8tostr2(time % 60));
+ SendtoTFT(PSTR(" M"));
+ #if ACDEBUG(AC_ALL)
+ SERIAL_ECHOLNPAIR("Print time ", ui8tostr2(time / 60), ":", ui8tostr2(time % 60));
+ #endif
+ } break;
+
+ case 8: // A8 Get SD Card list A8 S0
+ if (!isMediaInserted()) safe_delay(500);
+ if (!isMediaInserted()) // Make sure the card is removed
+ SendtoTFTLN(AC_msg_no_sd_card);
+ else if (panel_command[3] == 'S')
+ SendFileList( atoi( &panel_command[4] ) );
+ break;
+
+ case 33: // A33 Get firmware info
+ SendtoTFT(PSTR("J33 "));
+ SendtoTFTLN(PSTR(SHORT_BUILD_VERSION));
+ break;
+ }
+ }
+
+ void ChironTFT::PanelAction(uint8_t req) {
+ switch (req) {
+ case 9: // A9 Pause SD print
+ if (isPrintingFromMedia()) {
+ SendtoTFTLN(AC_msg_pause);
+ pausePrint();
+ printer_state = AC_printer_pausing;
+ }
+ else
+ SendtoTFTLN(AC_msg_stop);
+ break;
+
+ case 10: // A10 Resume SD Print
+ if (pause_state == AC_paused_idle || printer_state == AC_printer_resuming_from_power_outage)
+ resumePrint();
+ else
+ setUserConfirmed();
+ break;
+
+ case 11: // A11 Stop SD print
+ if (isPrintingFromMedia()) {
+ printer_state = AC_printer_stopping;
+ stopPrint();
+ }
+ else {
+ if (printer_state == AC_printer_resuming_from_power_outage)
+ injectCommands_P(PSTR("M1000 C\n")); // Cancel recovery
+ SendtoTFTLN(AC_msg_stop);
+ printer_state = AC_printer_idle;
+ }
+ break;
+
+ case 12: // A12 Kill printer
+ kill(); // from marlincore.h
+ break;
+
+ case 13: // A13 Select file
+ SelectFile();
+ break;
+
+ case 14: { // A14 Start Printing
+ // Allows printer to restart the job if we dont want to recover
+ if (printer_state == AC_printer_resuming_from_power_outage) {
+ injectCommands_P(PSTR("M1000 C\n")); // Cancel recovery
+ printer_state = AC_printer_idle;
+ }
+ #if ACDebugLevel >= 1
+ SERIAL_ECHOLNPAIR_F("Print: ", selectedfile);
+ #endif
+ // the card library needs a path starting // but the File api doesn't...
+ char file[MAX_PATH_LEN];
+ file[0] = '/';
+ strcpy(file + 1, selectedfile);
+ printFile(file);
+ SendtoTFTLN(AC_msg_print_from_sd_card);
+ } break;
+
+ case 15: // A15 Resuming from outage
+ if (printer_state == AC_printer_resuming_from_power_outage)
+ // Need to home here to restore the Z position
+ injectCommands_P(AC_cmnd_power_loss_recovery);
+
+ injectCommands_P(PSTR("M1000\n")); // home and start recovery
+ break;
+
+ case 16: { // A16 Set HotEnd temp A17 S170
+ const float set_Htemp = atof(&panel_command[5]);
+ hotend_state = set_Htemp ? AC_heater_temp_set : AC_heater_off;
+ switch ((char)panel_command[4]) {
+ // Set Temp
+ case 'S': case 'C': setTargetTemp_celsius(set_Htemp, E0);
+ }
+ } break;
+
+ case 17: { // A17 Set bed temp
+ const float set_Btemp = atof(&panel_command[5]);
+ hotbed_state = set_Btemp ? AC_heater_temp_set : AC_heater_off;
+ if (panel_command[4] == 'S')
+ setTargetTemp_celsius(set_Btemp, BED);
+ } break;
+
+ case 18: // A18 Set Fan Speed
+ if (panel_command[4] == 'S')
+ setTargetFan_percent(atof(&panel_command[5]), FAN0);
+ break;
+
+ case 19: // A19 Motors off
+ if (!isPrinting()) {
+ disable_all_steppers(); // from marlincore.h
+ SendtoTFTLN(AC_msg_ready);
+ }
+ break;
+
+ case 20: // A20 Read/write print speed
+ if (panel_command[4] == 'S')
+ setFeedrate_percent(atoi(&panel_command[5]));
+ else {
+ SendtoTFT(PSTR("A20V "));
+ TFTSer.println(getFeedrate_percent());
+ }
+ break;
+
+ case 21: // A21 Home Axis A21 X
+ if (!isPrinting()) {
+ switch ((char)panel_command[4]) {
+ case 'X': injectCommands_P(PSTR("G28 X\n")); break;
+ case 'Y': injectCommands_P(PSTR("G28 Y\n")); break;
+ case 'Z': injectCommands_P(PSTR("G28 Z\n")); break;
+ case 'C': injectCommands_P(PSTR("G28\n")); break;
+ }
+ }
+ break;
+
+ case 22: // A22 Move Axis A22 Y +10F3000
+ // Ignore request if printing
+ if (!isPrinting()) {
+ // setAxisPosition_mm() uses pre defined manual feedrates so ignore the feedrate from the panel
+ setSoftEndstopState(true); // enable endstops
+ float newposition = atof(&panel_command[6]);
+
+ #if ACDEBUG(AC_ACTION)
+ SERIAL_ECHOLNPAIR("Nudge ", panel_command[4], " axis ", newposition);
+ #endif
+
+ switch (panel_command[4]) {
+ case 'X': setAxisPosition_mm(getAxisPosition_mm(X) + newposition, X); break;
+ case 'Y': setAxisPosition_mm(getAxisPosition_mm(Y) + newposition, Y); break;
+ case 'Z': setAxisPosition_mm(getAxisPosition_mm(Z) + newposition, Z); break;
+ case 'E': // The only time we get this command is from the filament load/unload menu
+ // the standard movement is too slow so we will use the load unlod GCode to speed it up a bit
+ if (canMove(E0) && !commandsInQueue())
+ injectCommands_P(newposition > 0 ? AC_cmnd_manual_load_filament : AC_cmnd_manual_unload_filament);
+ break;
+ }
+ }
+ break;
+
+ case 23: // A23 Preheat PLA
+ // Ignore request if printing
+ if (!isPrinting()) {
+ // Temps defined in configuration.h
+ setTargetTemp_celsius(PREHEAT_1_TEMP_BED, BED);
+ setTargetTemp_celsius(PREHEAT_1_TEMP_HOTEND, E0);
+ SendtoTFTLN();
+ hotbed_state = AC_heater_temp_set;
+ hotend_state = AC_heater_temp_set;
+ }
+ break;
+
+ case 24: // A24 Preheat ABS
+ // Ignore request if printing
+ if (!isPrinting()) {
+ setTargetTemp_celsius(PREHEAT_2_TEMP_BED, BED);
+ setTargetTemp_celsius(PREHEAT_2_TEMP_HOTEND, E0);
+ SendtoTFTLN();
+ hotbed_state = AC_heater_temp_set;
+ hotend_state = AC_heater_temp_set;
+ }
+ break;
+
+ case 25: // A25 Cool Down
+ // Ignore request if printing
+ if (!isPrinting()) {
+ setTargetTemp_celsius(0, E0);
+ setTargetTemp_celsius(0, BED);
+ SendtoTFTLN(AC_msg_ready);
+ hotbed_state = AC_heater_off;
+ hotend_state = AC_heater_off;
+ }
+ break;
+
+ case 26: // A26 Refresh SD
+ // M22 M21 maybe needed here to reset sd card
+ filenavigator.reset();
+ break;
+
+ case 27: // A27 Servo Angles adjust
+ break;
+
+ case 28: // A28 Filament set A28 O/C
+ // Ignore request if printing
+ if (isPrinting()) break;
+ SendtoTFTLN();
+ break;
+ }
+ }
+
+ void ChironTFT::PanelProcess(uint8_t req) {
+ switch (req) {
+ case 29: { // A29 Read Mesh Point A29 X1 Y1
+ xy_uint8_t pos;
+ float pos_z;
+ pos.x = atoi(&panel_command[5]);
+ pos.y = atoi(&panel_command[8]);
+ pos_z = getMeshPoint(pos);
+
+ SendtoTFT(PSTR("A29V "));
+ TFTSer.println(pos_z * 100);
+ if (!isPrinting()) {
+ setSoftEndstopState(true); // disable endstops
+ // If the same meshpoint is selected twice in a row, move the head to that ready for adjustment
+ if ((selectedmeshpoint.x == pos.x) && (selectedmeshpoint.y == pos.y)) {
+ if (!isPositionKnown())
+ injectCommands_P(PSTR("G28\n")); // home
+
+ if (isPositionKnown()) {
+ #if ACDEBUG(AC_INFO)
+ SERIAL_ECHOLNPAIR("Moving to mesh point at x: ", pos.x, " y: ", pos.y, " z: ", pos_z);
+ #endif
+ // Go up before moving
+ setAxisPosition_mm(3.0,Z);
+
+ setAxisPosition_mm(17 + (93 * pos.x), X);
+ setAxisPosition_mm(20 + (93 * pos.y), Y);
+ setAxisPosition_mm(0.0, Z);
+ #if ACDEBUG(AC_INFO)
+ SERIAL_ECHOLNPAIR("Current Z: ", getAxisPosition_mm(Z));
+ #endif
+ }
+ }
+ selectedmeshpoint.x = pos.x;
+ selectedmeshpoint.y = pos.y;
+ }
+ } break;
+
+ case 30: { // A30 Auto leveling
+ if (panel_command[3] == 'S') { // Start probing
+ // Ignore request if printing
+ if (isPrinting())
+ SendtoTFTLN(AC_msg_probing_not_allowed); // forbid auto leveling
+ else {
+ injectCommands_P(isMachineHomed() ? PSTR("G29") : PSTR("G28\nG29"));
+ printer_state = AC_printer_probing;
+ SendtoTFTLN(AC_msg_start_probing);
+ }
+ }
+ else SendtoTFTLN(AC_msg_start_probing);
+ } break;
+
+ case 31: { // A31 Adjust all Probe Points
+ switch (panel_command[3]) {
+ case 'C': // Restore and apply original offsets
+ if (!isPrinting()) {
+ injectCommands_P(PSTR("M501\nM420 S1\n"));
+ selectedmeshpoint.x = 99;
+ selectedmeshpoint.y = 99;
+ }
+ break;
+ case 'D': // Save Z Offset tables and restore levelling state
+ if (!isPrinting()) {
+ setAxisPosition_mm(1.0,Z);
+ injectCommands_P(PSTR("M500\n"));
+ selectedmeshpoint.x = 99;
+ selectedmeshpoint.y = 99;
+ }
+ break;
+ case 'G': // Get current offset
+ SendtoTFT(PSTR("A31V "));
+ // When printing use the live z Offset position
+ // we will use babystepping to move the print head
+ if (isPrinting())
+ TFTSer.println(live_Zoffset);
+ else {
+ TFTSer.println(getZOffset_mm());
+ selectedmeshpoint.x = 99;
+ selectedmeshpoint.y = 99;
+ }
+ break;
+ case 'S': { // Set offset (adjusts all points by value)
+ float Zshift = atof(&panel_command[4]);
+ setSoftEndstopState(false); // disable endstops
+ // Allow temporary Z position nudging during print
+ // From the levelling panel use the all points UI to adjust the print pos.
+ if (isPrinting()) {
+ #if ACDEBUG(AC_INFO)
+ SERIAL_ECHOLNPAIR("Change Zoffset from:", live_Zoffset, " to ", live_Zoffset + Zshift);
+ #endif
+ if (isAxisPositionKnown(Z)) {
+ #if ACDEBUG(AC_INFO)
+ const float currZpos = getAxisPosition_mm(Z);
+ SERIAL_ECHOLNPAIR("Nudge Z pos from ", currZpos, " to ", currZpos + constrain(Zshift, -0.05, 0.05));
+ #endif
+ // Use babystepping to adjust the head position
+ int16_t steps = mmToWholeSteps(constrain(Zshift,-0.05,0.05), Z);
+ #if ACDEBUG(AC_INFO)
+ SERIAL_ECHOLNPAIR("Steps to move Z: ", steps);
+ #endif
+ babystepAxis_steps(steps, Z);
+ live_Zoffset += Zshift;
+ }
+ SendtoTFT(PSTR("A31V "));
+ TFTSer.println(live_Zoffset);
+ }
+ else {
+ GRID_LOOP(x, y) {
+ const xy_uint8_t pos { x, y };
+ const float currval = getMeshPoint(pos);
+ setMeshPoint(pos, constrain(currval + Zshift, AC_LOWEST_MESHPOINT_VAL, 2));
+ }
+ const float currZOffset = getZOffset_mm();
+ #if ACDEBUG(AC_INFO)
+ SERIAL_ECHOLNPAIR("Change probe offset from ", currZOffset, " to ", currZOffset + Zshift);
+ #endif
+
+ setZOffset_mm(currZOffset + Zshift);
+ SendtoTFT(PSTR("A31V "));
+ TFTSer.println(getZOffset_mm());
+
+ if (isAxisPositionKnown(Z)) {
+ // Move Z axis
+ const float currZpos = getAxisPosition_mm(Z);
+ #if ACDEBUG(AC_INFO)
+ SERIAL_ECHOLNPAIR("Move Z pos from ", currZpos, " to ", currZpos + constrain(Zshift, -0.05, 0.05));
+ #endif
+ setAxisPosition_mm(currZpos+constrain(Zshift,-0.05,0.05),Z);
+ }
+ }
+ } break;
+ } // end switch
+ } break;
+
+ case 32: { // A32 clean leveling beep flag
+ // Ignore request if printing
+ //if (isPrinting()) break;
+ //injectCommands_P(PSTR("M500\nM420 S1\nG1 Z10 F240\nG1 X0 Y0 F6000"));
+ //TFTSer.println("");
+ } break;
+
+ // A33 firmware info request seet PanelInfo()
+
+ case 34: { // A34 Adjust single mesh point A34 C/S X1 Y1 V123
+ if (panel_command[3] == 'C') { // Restore original offsets
+ injectCommands_P(PSTR("M501\nM420 S1"));
+ selectedmeshpoint.x = 99;
+ selectedmeshpoint.y = 99;
+ //printer_state = AC_printer_idle;
+ }
+ else {
+ xy_uint8_t pos;
+ pos.x = atoi(&panel_command[5]);
+ pos.y = atoi(&panel_command[8]);
+
+ float currmesh = getMeshPoint(pos);
+ float newval = atof(&panel_command[11])/100;
+ #if ACDEBUG(AC_INFO)
+ SERIAL_ECHOLNPAIR("Change mesh point x:", pos.x, " y:", pos.y);
+ SERIAL_ECHOLNPAIR("from ", currmesh, " to ", newval);
+ #endif
+ // Update Meshpoint
+ setMeshPoint(pos,newval);
+ if ( (printer_state == AC_printer_idle) || (printer_state == AC_printer_probing) ) {//!isPrinting()) {
+ // if we are at the current mesh point indicated on the panel Move Z pos +/- 0.05mm ( The panel changes the mesh value by +/- 0.05mm on each button press)
+ if ((selectedmeshpoint.x == pos.x) && (selectedmeshpoint.y == pos.y)) {
+ setSoftEndstopState(false);
+ float currZpos = getAxisPosition_mm(Z);
+ #if ACDEBUG(AC_INFO)
+ SERIAL_ECHOLNPAIR("Move Z pos from ", currZpos, " to ", currZpos + constrain(newval - currmesh, -0.05, 0.05));
+ #endif
+ setAxisPosition_mm(currZpos + constrain(newval - currmesh, -0.05, 0.05), Z);
+ }
+ }
+ }
+ } break;
+ }
+ }
+} // namespace
+
+#endif // ANYCUBIC_LCD_CHIRON
diff --git a/Marlin/src/lcd/extui/lib/anycubic_chiron/chiron_tft.h b/Marlin/src/lcd/extui/lib/anycubic_chiron/chiron_tft.h
new file mode 100644
index 000000000..267f2fe97
--- /dev/null
+++ b/Marlin/src/lcd/extui/lib/anycubic_chiron/chiron_tft.h
@@ -0,0 +1,77 @@
+/**
+ * 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
+
+/**
+ * lcd/extui/lib/chiron_tft.h
+ *
+ * Extensible_UI implementation for Anycubic Chiron
+ * Written By Nick Wells, 2020 [https://github.com/SwiftNick]
+ * (not affiliated with Anycubic, Ltd.)
+ */
+
+#include "chiron_tft_defs.h"
+#include "../../../../inc/MarlinConfigPre.h"
+#include "../../ui_api.h"
+namespace Anycubic {
+
+ class ChironTFT {
+ static printer_state_t printer_state;
+ static paused_state_t pause_state;
+ static heater_state_t hotend_state;
+ static heater_state_t hotbed_state;
+ static xy_uint8_t selectedmeshpoint;
+ static char panel_command[MAX_CMND_LEN];
+ static uint8_t command_len;
+ static char selectedfile[MAX_PATH_LEN];
+ static float live_Zoffset;
+ static file_menu_t file_menu;
+ public:
+ ChironTFT();
+ void Startup();
+ void IdleLoop();
+ void PrinterKilled(PGM_P,PGM_P);
+ void MediaEvent(media_event_t);
+ void TimerEvent(timer_event_t);
+ void FilamentRunout();
+ void ConfirmationRequest(const char * const );
+ void StatusChange(const char * const );
+ void PowerLossRecovery();
+
+ private:
+ void SendtoTFT(PGM_P);
+ void SendtoTFTLN(PGM_P);
+ bool ReadTFTCommand();
+ int8_t Findcmndpos(const char *, char);
+ void CheckHeaters();
+ void SendFileList(int8_t);
+ void SelectFile();
+ void InjectCommandandWait(PGM_P);
+ void ProcessPanelRequest();
+ void PanelInfo(uint8_t);
+ void PanelAction(uint8_t);
+ void PanelProcess(uint8_t);
+ };
+
+ extern ChironTFT Chiron;
+
+}
diff --git a/Marlin/src/lcd/extui/lib/anycubic_chiron/chiron_tft_defs.h b/Marlin/src/lcd/extui/lib/anycubic_chiron/chiron_tft_defs.h
new file mode 100644
index 000000000..937bdfde3
--- /dev/null
+++ b/Marlin/src/lcd/extui/lib/anycubic_chiron/chiron_tft_defs.h
@@ -0,0 +1,151 @@
+/**
+ * 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 .
+ *
+ */
+
+/**
+ * lcd/extui/lib/chiron_defs.h
+ *
+ * Extensible_UI implementation for Anycubic Chiron
+ * Written By Nick Wells, 2020 [https://github.com/SwiftNick]
+ * (not affiliated with Anycubic, Ltd.)
+ */
+
+#pragma once
+#include "../../../../inc/MarlinConfigPre.h"
+//#define ACDEBUGLEVEL 255
+
+#if ACDEBUGLEVEL
+ // Bit-masks for selective debug:
+ enum ACDebugMask : uint8_t {
+ AC_INFO = 1,
+ AC_ACTION = 2,
+ AC_FILE = 4,
+ AC_PANEL = 8,
+ AC_MARLIN = 16,
+ AC_SOME = 32,
+ AC_ALL = 64
+ };
+ #define ACDEBUG(mask) ( ((mask) & ACDEBUGLEVEL) == mask ) // Debug flag macro
+#else
+ #define ACDEBUG(mask) false
+#endif
+
+#define TFTSer LCD_SERIAL // Serial interface for TFT panel now uses marlinserial
+#define MAX_FOLDER_DEPTH 4 // Limit folder depth TFT has a limit for the file path
+#define MAX_CMND_LEN 16 * MAX_FOLDER_DEPTH // Maximum Length for a Panel command
+#define MAX_PATH_LEN 16 * MAX_FOLDER_DEPTH // Maximum number of characters in a SD file path
+
+#define AC_HEATER_FAULT_VALIDATION_TIME 5 // number of 1/2 second loops before signalling a heater fault
+#define AC_LOWEST_MESHPOINT_VAL -7.00 // The lowest value you can set for a single mesh point offset
+
+ // TFT panel commands
+#define AC_msg_sd_card_inserted PSTR("J00")
+#define AC_msg_sd_card_removed PSTR("J01")
+#define AC_msg_no_sd_card PSTR("J02")
+#define AC_msg_usb_connected PSTR("J03")
+#define AC_msg_print_from_sd_card PSTR("J04")
+#define AC_msg_pause PSTR("J05")
+#define AC_msg_nozzle_heating PSTR("J06")
+#define AC_msg_nozzle_heating_done PSTR("J07")
+#define AC_msg_bed_heating PSTR("J08")
+#define AC_msg_bed_heating_done PSTR("J09")
+#define AC_msg_nozzle_temp_abnormal PSTR("J10")
+#define AC_msg_kill_lcd PSTR("J11")
+#define AC_msg_ready PSTR("J12")
+#define AC_msg_low_nozzle_temp PSTR("J13")
+#define AC_msg_print_complete PSTR("J14")
+#define AC_msg_filament_out_alert PSTR("J15")
+#define AC_msg_stop PSTR("J16")
+#define AC_msg_main_board_has_reset PSTR("J17")
+#define AC_msg_paused PSTR("J18")
+#define AC_msg_j19_unknown PSTR("J19")
+#define AC_msg_sd_file_open_success PSTR("J20")
+#define AC_msg_sd_file_open_failed PSTR("J21")
+#define AC_msg_level_monitor_finished PSTR("J22")
+#define AC_msg_filament_out_block PSTR("J23")
+#define AC_msg_probing_not_allowed PSTR("J24")
+#define AC_msg_probing_complete PSTR("J25")
+#define AC_msg_start_probing PSTR("J26")
+#define AC_msg_version PSTR("J27")
+
+#define MARLIN_msg_start_probing PSTR("Probing Point 1/25")
+#define MARLIN_msg_probing_failed PSTR("Probing Failed")
+#define MARLIN_msg_ready PSTR("3D Printer Ready.")
+#define MARLIN_msg_print_paused PSTR("Print Paused")
+#define MARLIN_msg_print_aborted PSTR("Print Aborted")
+#define MARLIN_msg_extruder_heating PSTR("E Heating...")
+#define MARLIN_msg_bed_heating PSTR("Bed Heating...")
+
+#define MARLIN_msg_nozzle_parked PSTR("Nozzle Parked")
+#define MARLIN_msg_heater_timeout PSTR("Heater Timeout")
+#define MARLIN_msg_reheating PSTR("Reheating...")
+#define MARLIN_msg_reheat_done PSTR("Reheat finished.")
+#define MARLIN_msg_filament_purging PSTR("Filament Purging...")
+#define MARLIN_msg_special_pause PSTR("PB")
+#define AC_cmnd_auto_unload_filament PSTR("M701") // Use Marlin unload routine
+#define AC_cmnd_auto_load_filament PSTR("M702 M0 PB") // Use Marlin load routing then pause for user to clean nozzle
+
+#define AC_cmnd_manual_load_filament PSTR("M83\nG1 E50 F700\nM82") // replace the manual panel commands with something a little faster
+#define AC_cmnd_manual_unload_filament PSTR("M83\nG1 E-50 F1200\nM82")
+#define AC_cmnd_enable_levelling PSTR("M420 S1 V1")
+#define AC_cmnd_power_loss_recovery PSTR("G28 X Y R5\nG28 Z") // Lift, home X and Y then home Z when in 'safe' position
+
+namespace Anycubic {
+ enum heater_state_t : uint8_t {
+ AC_heater_off,
+ AC_heater_temp_set,
+ AC_heater_temp_reached
+ };
+
+ enum paused_state_t : uint8_t {
+ AC_paused_heater_timed_out,
+ AC_paused_purging_filament,
+ AC_paused_idle
+ };
+
+ enum printer_state_t : uint8_t {
+ AC_printer_idle,
+ AC_printer_probing,
+ AC_printer_printing,
+ AC_printer_pausing,
+ AC_printer_paused,
+ AC_printer_stopping,
+ AC_printer_resuming_from_power_outage
+ };
+
+ enum timer_event_t : uint8_t {
+ AC_timer_started,
+ AC_timer_paused,
+ AC_timer_stopped
+ };
+
+ enum media_event_t : uint8_t {
+ AC_media_inserted,
+ AC_media_removed,
+ AC_media_error
+ };
+ enum file_menu_t : uint8_t {
+ AC_menu_file,
+ AC_menu_command,
+ AC_menu_change_to_file,
+ AC_menu_change_to_command
+ };
+}
diff --git a/Marlin/src/lcd/extui/lib/anycubic_i3mega/anycubic_i3mega_lcd.cpp b/Marlin/src/lcd/extui/lib/anycubic_i3mega/anycubic_i3mega_lcd.cpp
index c69fb8351..b383cee09 100644
--- a/Marlin/src/lcd/extui/lib/anycubic_i3mega/anycubic_i3mega_lcd.cpp
+++ b/Marlin/src/lcd/extui/lib/anycubic_i3mega/anycubic_i3mega_lcd.cpp
@@ -24,16 +24,17 @@
#if ENABLED(ANYCUBIC_LCD_I3MEGA)
#include "anycubic_i3mega_lcd.h"
-
-#include "../../../../inc/MarlinConfig.h"
#include "../../ui_api.h"
-#include "../../../../MarlinCore.h" // for quickstop_stepper and disable_steppers
+
+#include "../../../../libs/numtostr.h"
#include "../../../../module/motion.h" // for A20 read printing speed feedrate_percentage
+#include "../../../../MarlinCore.h" // for quickstop_stepper and disable_steppers
+#include "../../../../inc/MarlinConfig.h"
// command sending macro's with debugging capability
#define SEND_PGM(x) send_P(PSTR(x))
#define SENDLINE_PGM(x) sendLine_P(PSTR(x))
-#define SEND_PGM_VAL(x,y) (send_P(PSTR(x)), sendLine(itostr3(y)))
+#define SEND_PGM_VAL(x,y) (send_P(PSTR(x)), sendLine(i8tostr3rj(y)))
#define SEND(x) send(x)
#define SENDLINE(x) sendLine(x)
#if ENABLED(ANYCUBIC_LCD_DEBUG)
@@ -44,20 +45,8 @@
#define SENDLINE_DBG_PGM_VAL(x,y,z) sendLine_P(PSTR(x))
#endif
-
AnycubicTFTClass AnycubicTFT;
-char _conv[8];
-
-char *itostr2(const uint8_t &x) {
- // sprintf(conv,"%5.1f",x);
- int xx = x;
- _conv[0] = (xx / 10) % 10 + '0';
- _conv[1] = (xx) % 10 + '0';
- _conv[2] = 0;
- return _conv;
-}
-
static void sendNewLine(void) {
LCD_SERIAL.write('\r');
LCD_SERIAL.write('\n');
@@ -82,34 +71,6 @@ static void sendLine_P(PGM_P str) {
sendNewLine();
}
-#ifndef ULTRA_LCD
- #define DIGIT(n) ('0' + (n))
- #define DIGIMOD(n, f) DIGIT((n) / (f) % 10)
- #define RJDIGIT(n, f) ((n) >= (f) ? DIGIMOD(n, f) : ' ')
- #define MINUSOR(n, alt) (n >= 0 ? (alt) : (n = -n, '-'))
-
- char* itostr3(const int x) {
- int xx = x;
- _conv[4] = MINUSOR(xx, RJDIGIT(xx, 100));
- _conv[5] = RJDIGIT(xx, 10);
- _conv[6] = DIGIMOD(xx, 1);
- return &_conv[4];
- }
-
-// Convert signed float to fixed-length string with 023.45 / -23.45 format
- char *ftostr32(const float &x) {
- long xx = x * 100;
- _conv[1] = MINUSOR(xx, DIGIMOD(xx, 10000));
- _conv[2] = DIGIMOD(xx, 1000);
- _conv[3] = DIGIMOD(xx, 100);
- _conv[4] = '.';
- _conv[5] = DIGIMOD(xx, 10);
- _conv[6] = DIGIMOD(xx, 1);
- return &_conv[1];
- }
-
-#endif
-
AnycubicTFTClass::AnycubicTFTClass() {}
void AnycubicTFTClass::OnSetup() {
@@ -181,7 +142,7 @@ void AnycubicTFTClass::OnKillTFT() {
void AnycubicTFTClass::OnSDCardStateChange(bool isInserted) {
#if ENABLED(ANYCUBIC_LCD_DEBUG)
SERIAL_ECHOPGM("TFT Serial Debug: OnSDCardStateChange event triggered...");
- SERIAL_ECHO(itostr2(isInserted));
+ SERIAL_ECHO(ui8tostr2(isInserted));
SERIAL_EOL();
#endif
DoSDCardStateCheck();
@@ -622,19 +583,15 @@ void AnycubicTFTClass::GetCommandFromTFT() {
case 3: { // A3 GET HOTBED TARGET TEMP
float heatedBedTargetTemp = ExtUI::getTargetTemp_celsius((ExtUI::heater_t) ExtUI::BED);
SEND_PGM_VAL("A3V ", int(heatedBedTargetTemp + 0.5));
- }
- break;
+ } break;
- case 4: // A4 GET FAN SPEED
- {
+ case 4: { // A4 GET FAN SPEED
float fanPercent = ExtUI::getActualFan_percent(ExtUI::FAN0);
fanPercent = constrain(fanPercent, 0, 100);
SEND_PGM_VAL("A4V ", int(fanPercent));
- }
- break;
+ } break;
- case 5: // A5 GET CURRENT COORDINATE
- {
+ case 5: { // A5 GET CURRENT COORDINATE
float xPostition = ExtUI::getAxisPosition_mm(ExtUI::X);
float yPostition = ExtUI::getAxisPosition_mm(ExtUI::Y);
float zPostition = ExtUI::getAxisPosition_mm(ExtUI::Z);
@@ -645,39 +602,34 @@ void AnycubicTFTClass::GetCommandFromTFT() {
SEND_PGM(" Z: ");
LCD_SERIAL.print(zPostition);
SENDLINE_PGM("");
- }
- break;
+ } break;
case 6: // A6 GET SD CARD PRINTING STATUS
#if ENABLED(SDSUPPORT)
if (ExtUI::isPrintingFromMedia()) {
SEND_PGM("A6V ");
- if (ExtUI::isMediaInserted()) {
- SENDLINE(itostr3(int(ExtUI::getProgress_percent())));
- }
- else {
+ if (ExtUI::isMediaInserted())
+ SENDLINE(ui8tostr3rj(ExtUI::getProgress_percent()));
+ else
SENDLINE_DBG_PGM("J02", "TFT Serial Debug: No SD Card mounted to return printing status... J02");
- }
}
- else {
+ else
SENDLINE_PGM("A6V ---");
- }
#endif
break;
case 7: { // A7 GET PRINTING TIME
- uint32_t elapsedSeconds = ExtUI::getProgress_seconds_elapsed();
+ const uint32_t elapsedSeconds = ExtUI::getProgress_seconds_elapsed();
SEND_PGM("A7V ");
if (elapsedSeconds != 0) { // print time
- uint32_t elapsedMinutes = elapsedSeconds / 60;
- SEND(itostr2(elapsedMinutes / 60));
+ const uint32_t elapsedMinutes = elapsedSeconds / 60;
+ SEND(ui8tostr2(elapsedMinutes / 60));
SEND_PGM(" H ");
- SEND(itostr2(elapsedMinutes % 60));
+ SEND(ui8tostr2(elapsedMinutes % 60));
SENDLINE_PGM(" M");
}
- else {
+ else
SENDLINE_PGM(" 999:999");
- }
}
break;
@@ -692,7 +644,6 @@ void AnycubicTFTClass::GetCommandFromTFT() {
#if ENABLED(SDSUPPORT)
if (ExtUI::isPrintingFromMedia())
PausePrint();
-
#endif
break;
@@ -700,14 +651,11 @@ void AnycubicTFTClass::GetCommandFromTFT() {
#if ENABLED(SDSUPPORT)
if (ExtUI::isPrintingFromMediaPaused())
ResumePrint();
-
#endif
break;
case 11: // A11 STOP SD PRINT
- #if ENABLED(SDSUPPORT)
- StopPrint();
- #endif
+ TERN_(SDSUPPORT, StopPrint());
break;
case 12: // A12 kill
@@ -748,7 +696,6 @@ void AnycubicTFTClass::GetCommandFromTFT() {
#if ENABLED(SDSUPPORT)
if (!ExtUI::isPrinting() && strlen(SelectedFile) > 0)
StartPrint();
-
#endif
break;
@@ -771,8 +718,7 @@ void AnycubicTFTClass::GetCommandFromTFT() {
}
break;
- case 17:// A17 set heated bed temp
- {
+ case 17: { // A17 set heated bed temp
unsigned int tempbed;
if (CodeSeen('S')) {
tempbed = constrain(CodeValue(), 0, 100);
@@ -781,19 +727,17 @@ void AnycubicTFTClass::GetCommandFromTFT() {
}
break;
- case 18:// A18 set fan speed
- {
+ case 18: { // A18 set fan speed
float fanPercent;
if (CodeSeen('S')) {
fanPercent = CodeValue();
fanPercent = constrain(fanPercent, 0, 100);
ExtUI::setTargetFan_percent(fanPercent, ExtUI::FAN0);
}
- else {
+ else
fanPercent = 100;
- }
- ExtUI::setTargetFan_percent(fanPercent, ExtUI::FAN0);
+ ExtUI::setTargetFan_percent(fanPercent, ExtUI::FAN0);
SENDLINE_PGM("");
}
break;
@@ -807,13 +751,11 @@ void AnycubicTFTClass::GetCommandFromTFT() {
SENDLINE_PGM("");
break;
- case 20: { // A20 read printing speed
-
+ case 20: // A20 read printing speed
if (CodeSeen('S'))
feedrate_percentage = constrain(CodeValue(), 40, 999);
else
SEND_PGM_VAL("A20V ", feedrate_percentage);
- }
break;
case 21: // A21 all home
diff --git a/Marlin/src/lcd/extui/lib/anycubic_i3mega/anycubic_i3mega_lcd.h b/Marlin/src/lcd/extui/lib/anycubic_i3mega/anycubic_i3mega_lcd.h
index ee011f1df..a4ecf5604 100644
--- a/Marlin/src/lcd/extui/lib/anycubic_i3mega/anycubic_i3mega_lcd.h
+++ b/Marlin/src/lcd/extui/lib/anycubic_i3mega/anycubic_i3mega_lcd.h
@@ -23,12 +23,6 @@
#include "../../../../inc/MarlinConfigPre.h"
#include "../../../../sd/SdFatConfig.h" // for the FILENAME_LENGTH macro
-char *itostr2(const uint8_t &x);
-#ifndef ULTRA_LCD
- char *itostr3(const int);
- char *ftostr32(const float &);
-#endif
-
#define TFTBUFSIZE 4
#define TFT_MAX_CMD_SIZE 96
diff --git a/Marlin/src/libs/numtostr.cpp b/Marlin/src/libs/numtostr.cpp
index 3b36c180e..c3efb2b25 100644
--- a/Marlin/src/libs/numtostr.cpp
+++ b/Marlin/src/libs/numtostr.cpp
@@ -52,6 +52,13 @@ const char* ui8tostr3rj(const uint8_t i) {
return &conv[4];
}
+// Convert uint8_t to string with 12 format
+const char* ui8tostr2(const uint8_t i) {
+ conv[5] = DIGIMOD(i, 10);
+ conv[6] = DIGIMOD(i, 1);
+ return &conv[5];
+}
+
// Convert signed 8bit int to rj string with 123 or -12 format
const char* i8tostr3rj(const int8_t x) {
int xx = x;
diff --git a/Marlin/src/libs/numtostr.h b/Marlin/src/libs/numtostr.h
index e52a7d988..e7c1e67e1 100644
--- a/Marlin/src/libs/numtostr.h
+++ b/Marlin/src/libs/numtostr.h
@@ -26,6 +26,9 @@
// Convert a full-range unsigned 8bit int to a percentage
const char* ui8tostr4pctrj(const uint8_t i);
+// Convert uint8_t to string with 12 format
+const char* ui8tostr2(const uint8_t x);
+
// Convert uint8_t to string with 123 format
const char* ui8tostr3rj(const uint8_t i);