Move ExtUI displays to sub-folder (#19070)
This commit is contained in:
536
Marlin/src/lcd/extui/anycubic_chiron_lcd.cpp
Normal file
536
Marlin/src/lcd/extui/anycubic_chiron_lcd.cpp
Normal file
@@ -0,0 +1,536 @@
|
||||
/**
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* anycubic_chiron_lcd.cpp
|
||||
*
|
||||
* Anycubic Chiron TFT support for Marlin
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfigPre.h"
|
||||
|
||||
#if ENABLED(ANYCUBIC_LCD_CHIRON)
|
||||
|
||||
#include "ui_api.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) {
|
||||
ANYCUBIC_LCD_SERIAL.write('\r');
|
||||
ANYCUBIC_LCD_SERIAL.write('\n');
|
||||
}
|
||||
|
||||
static void send(const char *str) {
|
||||
ANYCUBIC_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++))
|
||||
ANYCUBIC_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);
|
||||
ANYCUBIC_LCD_SERIAL.print(value);
|
||||
}
|
||||
|
||||
static void sendValue_P(PGM_P prefix, float value) {
|
||||
send_P(prefix);
|
||||
ANYCUBIC_LCD_SERIAL.print(value);
|
||||
}
|
||||
|
||||
static void sendValueLine_P(PGM_P prefix, int value) {
|
||||
send_P(prefix);
|
||||
ANYCUBIC_LCD_SERIAL.print(value);
|
||||
sendNewLine();
|
||||
}
|
||||
|
||||
static void sendValueLine_P(PGM_P prefix, float value) {
|
||||
send_P(prefix);
|
||||
ANYCUBIC_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)
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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 (ANYCUBIC_LCD_SERIAL.available()) {
|
||||
const char c = ANYCUBIC_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() {
|
||||
ANYCUBIC_LCD_SERIAL.begin(115200);
|
||||
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 onPlayTone(const uint16_t frequency, const uint16_t duration) {
|
||||
tone(BEEPER_PIN, frequency, duration);
|
||||
}
|
||||
|
||||
void onPrintTimerStarted() { }
|
||||
|
||||
void onPrintTimerPaused() { }
|
||||
|
||||
void onPrintTimerStopped() { }
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
void onUserConfirmRequired(const char * const msg) { }
|
||||
|
||||
void onStatusChanged(const char * const msg) { }
|
||||
|
||||
void onFactoryReset() { }
|
||||
|
||||
void onStoreSettings(char *buff) { }
|
||||
|
||||
void onLoadSettings(const char *buff) { }
|
||||
|
||||
void onConfigurationStoreWritten(bool success) { }
|
||||
|
||||
void onConfigurationStoreRead(bool success) { }
|
||||
|
||||
void onMeshUpdate(const int8_t xpos, const int8_t ypos, const float zval) { }
|
||||
|
||||
#if ENABLED(POWER_LOSS_RECOVERY)
|
||||
void onPowerLossResume() { }
|
||||
#endif
|
||||
|
||||
#if HAS_PID_HEATING
|
||||
void onPidTuning(const result_t rst) { }
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // ANYCUBIC_LCD_CHIRON
|
Reference in New Issue
Block a user