123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536 |
- /**
- * 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
|