My Marlin configs for Fabrikator Mini and CTC i3 Pro B
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

anycubic_chiron_lcd.cpp 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. /**
  2. * Marlin 3D Printer Firmware
  3. * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
  4. *
  5. * Based on Sprinter and grbl.
  6. * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
  7. *
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. *
  21. */
  22. /**
  23. * anycubic_chiron_lcd.cpp
  24. *
  25. * Anycubic Chiron TFT support for Marlin
  26. */
  27. #include "../../inc/MarlinConfigPre.h"
  28. #if ENABLED(ANYCUBIC_LCD_CHIRON)
  29. #include "ui_api.h"
  30. #if ENABLED(AUTO_BED_LEVELING_BILINEAR)
  31. #if GRID_MAX_POINTS_X != 5 || GRID_MAX_POINTS_Y != 5
  32. #error ANYCUBIC CHIRON LCD requires a 5x5 bed leveling grid (GRID_MAX_POINTS_X and GRID_MAX_POINTS_Y)
  33. #endif
  34. #else
  35. #error ANYCUBIC CHIRON LCD requires AUTO_BED_LEVELING_BILINEAR enabled
  36. #endif
  37. #if DISABLED(FILAMENT_RUNOUT_SENSOR)
  38. #error ANYCUBIC CHIRON LCD requires FILAMENT_RUNOUT_SENSOR enabled
  39. #endif
  40. #if ENABLED(POWER_LOSS_RECOVERY)
  41. #error ANYCUBIC CHIRON LCD does not currently support POWER_LOSS_RECOVERY
  42. #endif
  43. static bool is_auto_leveling = false;
  44. static bool is_printing_from_sd = false;
  45. static bool is_out_of_filament = false;
  46. static void sendNewLine(void) {
  47. LCD_SERIAL.write('\r');
  48. LCD_SERIAL.write('\n');
  49. }
  50. static void send(const char *str) {
  51. LCD_SERIAL.print(str);
  52. }
  53. static void sendLine(const char *str) {
  54. send(str);
  55. sendNewLine();
  56. }
  57. static void send_P(PGM_P str) {
  58. while (const char c = pgm_read_byte(str++))
  59. LCD_SERIAL.write(c);
  60. }
  61. static void sendLine_P(PGM_P str) {
  62. send_P(str);
  63. sendNewLine();
  64. }
  65. static void sendValue_P(PGM_P prefix, int value) {
  66. send_P(prefix);
  67. LCD_SERIAL.print(value);
  68. }
  69. static void sendValue_P(PGM_P prefix, float value) {
  70. send_P(prefix);
  71. LCD_SERIAL.print(value);
  72. }
  73. static void sendValueLine_P(PGM_P prefix, int value) {
  74. send_P(prefix);
  75. LCD_SERIAL.print(value);
  76. sendNewLine();
  77. }
  78. static void sendValueLine_P(PGM_P prefix, float value) {
  79. send_P(prefix);
  80. LCD_SERIAL.print(value);
  81. sendNewLine();
  82. }
  83. static int parseIntArgument(const char *buffer, char letterId) {
  84. char *p = strchr(buffer, letterId);
  85. if (!p)
  86. return -1;
  87. return atoi(p+1);
  88. }
  89. static float parseFloatArgument(const char *buffer, char letterId) {
  90. char *p = strchr(buffer, letterId);
  91. if (!p)
  92. return NAN;
  93. return strtof(p+1, nullptr);
  94. }
  95. static int mmToHundredths(float x) {
  96. // Round
  97. if (x >= 0)
  98. x += 0.005f;
  99. else
  100. x -= 0.005f;
  101. return (int)(x * 100.0f);
  102. }
  103. static float hundredthsToMm(int x) {
  104. return x / 100.0f;
  105. }
  106. #define SEND_PGM(str) send_P(PSTR(str))
  107. #define SENDLINE_PGM(str) sendLine_P(PSTR(str))
  108. #define SENDVALUE_PGM(prefix, value) sendValue_P(PSTR(prefix), value)
  109. #define SENDVALUELINE_PGM(prefix, value) sendValueLine_P(PSTR(prefix), value)
  110. namespace ExtUI {
  111. static void moveAxis(float delta, feedRate_t feedrate, axis_t axis) {
  112. float pos = getAxisPosition_mm(axis);
  113. pos += delta;
  114. setAxisPosition_mm(pos, axis, feedrate);
  115. }
  116. static void handleCmd(const char *rx) {
  117. static FileList fileList;
  118. static char selectedFileShortName[8+1+3+1];
  119. if (rx[0] != 'A') {
  120. SERIAL_ECHOPGM("Unexpected RX: ");
  121. SERIAL_ECHOLN(rx);
  122. return;
  123. }
  124. const int cmd = atoi(&rx[1]);
  125. // Uncomment for debugging RX
  126. //if (cmd > 7 && cmd != 20) {
  127. // SERIAL_ECHOPGM("RX: ");
  128. // SERIAL_ECHOLN(rx);
  129. //}
  130. switch (cmd) {
  131. case 0: // Get Hotend Actual Temperature
  132. SENDVALUELINE_PGM("A0V ", (int)getActualTemp_celsius(E0));
  133. break;
  134. case 1: // Get Hotend Target Temperature
  135. SENDVALUELINE_PGM("A1V ", (int)getTargetTemp_celsius(E0));
  136. break;
  137. case 2: // Get Bed Actual Temperature
  138. SENDVALUELINE_PGM("A2V ", (int)getActualTemp_celsius(BED));
  139. break;
  140. case 3: // Get Bed Target Temperature
  141. SENDVALUELINE_PGM("A3V ", (int)getTargetTemp_celsius(BED));
  142. break;
  143. case 4: // Get Fan Speed
  144. SENDVALUELINE_PGM("A4V ", (int)getTargetFan_percent(FAN0));
  145. break;
  146. case 5: // Get Current Coordinates
  147. SENDVALUE_PGM("A5V X: ", getAxisPosition_mm(X));
  148. SENDVALUE_PGM(" Y: ", getAxisPosition_mm(Y));
  149. SENDVALUE_PGM(" Z: ", getAxisPosition_mm(Z));
  150. sendNewLine();
  151. break;
  152. case 6: // Get SD Card Print Status
  153. if (isPrintingFromMedia())
  154. SENDVALUELINE_PGM("A6V ", (int)getProgress_percent());
  155. else
  156. SENDLINE_PGM("A6V ---");
  157. break;
  158. case 7: // Get Printing Time
  159. if (isPrinting()) {
  160. const int totalMinutes = getProgress_seconds_elapsed() / 60;
  161. SENDVALUE_PGM("A7V ", (int)(totalMinutes/60));
  162. SENDVALUE_PGM(" H ", (int)(totalMinutes%60));
  163. SENDLINE_PGM(" M");
  164. } else {
  165. SENDLINE_PGM("A7V 999:999");
  166. }
  167. break;
  168. case 8: // Get SD Card File List
  169. if (isMediaInserted()) {
  170. const int startIndex = parseIntArgument(rx, 'S');
  171. SENDLINE_PGM("FN ");
  172. for (int i = 0, fileIndex = 0, numFiles = 0; i < (int)fileList.count() && numFiles < 4; i++) {
  173. fileList.seek(i);
  174. if (!fileList.isDir()) {
  175. if (fileIndex >= startIndex) {
  176. sendLine(fileList.shortFilename());
  177. sendLine(fileList.longFilename());
  178. numFiles++;
  179. }
  180. fileIndex++;
  181. }
  182. }
  183. SENDLINE_PGM("END");
  184. } else {
  185. SENDLINE_PGM("J02");
  186. }
  187. break;
  188. case 9: // Pause SD Card Print
  189. if (isPrintingFromMedia()) {
  190. pausePrint();
  191. is_printing_from_sd = false;
  192. SENDLINE_PGM("J05");
  193. } else {
  194. SENDLINE_PGM("J16"); // Print stopped
  195. }
  196. break;
  197. case 10: // Resume SD Card Print
  198. if (is_out_of_filament) {
  199. is_out_of_filament = false;
  200. // Filament change did eject the old filament automatically,
  201. // now continue and load the new one
  202. setUserConfirmed();
  203. SENDLINE_PGM("J04"); // Printing from SD card
  204. } else if (isPrintingFromMediaPaused()) {
  205. resumePrint();
  206. SENDLINE_PGM("J04"); // Printing from SD card
  207. }
  208. break;
  209. case 11: // Stop SD Card Print
  210. if (isPrintingFromMedia()) {
  211. stopPrint();
  212. is_printing_from_sd = false;
  213. SENDLINE_PGM("J16"); // Print stopped
  214. }
  215. break;
  216. //case 12: // Kill
  217. // break;
  218. case 13: // Select File
  219. if (!isPrinting()) {
  220. // Store selected file name
  221. char *p = strchr(rx, ' ');
  222. if (p != nullptr && strlen(p+1) < sizeof(selectedFileShortName)) {
  223. strcpy(selectedFileShortName, p+1);
  224. SENDLINE_PGM("J20"); // Open succeeded
  225. }
  226. else
  227. SENDLINE_PGM("J21"); // Open failed
  228. }
  229. break;
  230. case 14: // Start Print
  231. if (!isPrinting() && strcmp(selectedFileShortName, "") != 0) {
  232. printFile(selectedFileShortName);
  233. is_printing_from_sd = true;
  234. SENDLINE_PGM("J04"); // Printing from SD card
  235. }
  236. break;
  237. case 15: // Resume from power outage
  238. // This is not supported, just report print as completed
  239. SENDLINE_PGM("J16"); // Print stopped
  240. break;
  241. case 16: // Set Hotend Target Temperature
  242. {
  243. int temp = parseIntArgument(rx, 'S');
  244. if (temp >= 0)
  245. setTargetTemp_celsius(temp, E0);
  246. }
  247. break;
  248. case 17: // Set Bed Target Temperature
  249. {
  250. int temp = parseIntArgument(rx, 'S');
  251. if (temp >= 0)
  252. setTargetTemp_celsius(temp, BED);
  253. }
  254. break;
  255. case 18: // Set Fan Speed
  256. {
  257. int temp = parseIntArgument(rx, 'S');
  258. if (temp >= 0)
  259. setTargetFan_percent(temp, FAN0);
  260. }
  261. break;
  262. case 19: // Disable Motors
  263. injectCommands_P(PSTR("M84"));
  264. break;
  265. case 20: // Get/Set Printing Speed
  266. {
  267. int newPerc = parseIntArgument(rx, 'S');
  268. if (newPerc >= 0)
  269. setFeedrate_percent(newPerc);
  270. else
  271. SENDVALUELINE_PGM("A20V ", (int)getFeedrate_percent());
  272. }
  273. break;
  274. case 21: // Home axes
  275. if (!isPrinting()) {
  276. const bool hasX = strchr(rx, 'X') != nullptr,
  277. hasY = strchr(rx, 'Y') != nullptr,
  278. hasZ = strchr(rx, 'Z') != nullptr,
  279. hasC = strchr(rx, 'C') != nullptr;
  280. if (hasX || hasY || hasZ) {
  281. if (hasX) injectCommands_P(PSTR("G28 X"));
  282. if (hasY) injectCommands_P(PSTR("G28 Y"));
  283. if (hasZ) injectCommands_P(PSTR("G28 Z"));
  284. } else if (hasC) {
  285. injectCommands_P(PSTR("G28"));
  286. }
  287. }
  288. break;
  289. case 22: // Move axes
  290. if (!isPrinting()) {
  291. const int feedrate = parseIntArgument(rx, 'F') / 60;
  292. float delta;
  293. if (!isnan(delta = parseFloatArgument(rx, 'X')))
  294. moveAxis(delta, feedrate, X);
  295. else if (!isnan(delta = parseFloatArgument(rx, 'Y')))
  296. moveAxis(delta, feedrate, Y);
  297. else if (!isnan(delta = parseFloatArgument(rx, 'Z')))
  298. moveAxis(delta, feedrate, Z);
  299. }
  300. break;
  301. case 23: // Preheat PLA
  302. setTargetTemp_celsius(PREHEAT_1_TEMP_HOTEND, E0);
  303. setTargetTemp_celsius(PREHEAT_1_TEMP_BED, BED);
  304. SENDLINE_PGM("OK");
  305. break;
  306. case 24: // Preheat ABS
  307. setTargetTemp_celsius(PREHEAT_2_TEMP_HOTEND, E0);
  308. setTargetTemp_celsius(PREHEAT_2_TEMP_BED, BED);
  309. SENDLINE_PGM("OK");
  310. break;
  311. case 25: // Cool down
  312. setTargetTemp_celsius(0, E0);
  313. setTargetTemp_celsius(0, BED);
  314. SENDLINE_PGM("J12");
  315. break;
  316. case 26: // Refresh SD Card
  317. fileList.refresh();
  318. break;
  319. //case 27: // Adjust Servo Angles
  320. // break;
  321. //case 28: // Filament Test
  322. // break;
  323. case 29: // Get Bed Autolevel Grid
  324. {
  325. int x = parseIntArgument(rx, 'X'),
  326. y = parseIntArgument(rx, 'Y');
  327. if (x != -1 && y != -1) {
  328. xy_uint8_t coord;
  329. coord.set(x, y);
  330. const int value = mmToHundredths(getMeshPoint(coord));
  331. SENDVALUELINE_PGM("A29V ", value);
  332. }
  333. }
  334. break;
  335. case 30: // Autolevel
  336. if (strchr(rx, 'S')) { // Autoleveling started by clicking "PROBE" and then "OK"
  337. // Note:
  338. // We check for completion by monitoring the command queue.
  339. // Since it will become empty *while* processing the last injected command,
  340. // we enqueue an extra 10ms delay so we can the determine when all the others
  341. // have completed.
  342. if (isMachineHomed())
  343. injectCommands_P(PSTR("G29\nG4 P10"));
  344. else
  345. injectCommands_P(PSTR("G28\nG29\nG4 P10"));
  346. is_auto_leveling = true;
  347. } else { // Entering Autoleveling screen
  348. if (isPrinting())
  349. SENDLINE_PGM("J24"); // Disallow autoleveling
  350. else
  351. SENDLINE_PGM("J26"); // Allow autoleveling
  352. }
  353. break;
  354. case 31: // Set Bed Autolevel Z offset
  355. if (strchr(rx, 'G')) { // Get
  356. SENDVALUELINE_PGM("A31V ", getZOffset_mm());
  357. } else if (strchr(rx, 'S')) { // Set
  358. float delta = parseFloatArgument(rx, 'S');
  359. delta = constrain(delta, -1.0, 1.0);
  360. setZOffset_mm(getZOffset_mm() + delta);
  361. SENDVALUELINE_PGM("A31V ", getZOffset_mm());
  362. } else if (strchr(rx, 'D')) { // Save
  363. injectCommands_P(PSTR("M500"));
  364. }
  365. break;
  366. //case 32: // ?
  367. // break;
  368. case 33: // Get Version Info
  369. SENDLINE_PGM("J33 " SHORT_BUILD_VERSION);
  370. break;
  371. case 34: // Set Bed Autolevel Grid
  372. {
  373. int x = parseIntArgument(rx, 'X'),
  374. y = parseIntArgument(rx, 'Y'),
  375. v = parseIntArgument(rx, 'V');
  376. if (x != -1 && y != -1 && v != -1) { // Set new value
  377. float value = hundredthsToMm(v);
  378. value = constrain(value, -10, 10);
  379. xy_uint8_t coord;
  380. coord.set(x, y);
  381. setMeshPoint(coord, value);
  382. } else if (strchr(rx, 'S')) { // Save (apply new values)
  383. injectCommands_P(PSTR("M500"));
  384. } else if (strchr(rx, 'C')) { // Cancel (discard new values)
  385. injectCommands_P(PSTR("M501"));
  386. }
  387. }
  388. break;
  389. }
  390. }
  391. #define RX_LEN_MAX 63
  392. static void parseSerialRx() {
  393. static char rxBuffer[RX_LEN_MAX+1];
  394. static uint8_t rxLen = 0;
  395. while (LCD_SERIAL.available()) {
  396. const char c = LCD_SERIAL.read();
  397. switch (c) {
  398. case '\r': case '\n':
  399. if (rxLen > 0 && rxLen <= RX_LEN_MAX) {
  400. rxBuffer[rxLen] = '\0'; // Terminate string
  401. handleCmd(rxBuffer);
  402. }
  403. rxLen = 0;
  404. break;
  405. default:
  406. if (rxLen < RX_LEN_MAX)
  407. rxBuffer[rxLen++] = c;
  408. else {
  409. rxLen = 0xFF; // Overrun
  410. SERIAL_ECHOPGM("Warning: dropping long received line");
  411. }
  412. break;
  413. }
  414. }
  415. }
  416. static void detectPrintFromSdCompletion() {
  417. // Note: printFile() queues some commands that actually start the print, so isPrintingFromMedia()
  418. // initially returns false
  419. if (is_printing_from_sd && !commandsInQueue() && !isPrintingFromMedia()) {
  420. is_printing_from_sd = false;
  421. SENDLINE_PGM("J14"); // Print done
  422. }
  423. }
  424. static void detectAutolevelingCompletion() {
  425. if (is_auto_leveling && !commandsInQueue()) {
  426. is_auto_leveling = false;
  427. injectCommands_P(PSTR("M500"));
  428. SENDLINE_PGM("J25"); // Autoleveling done
  429. }
  430. }
  431. void onStartup() {
  432. #ifndef LCD_BAUDRATE
  433. #define LCD_BAUDRATE 115200
  434. #endif
  435. LCD_SERIAL.begin(LCD_BAUDRATE);
  436. sendNewLine();
  437. SENDLINE_PGM("J17"); // Reset
  438. delay_ms(10);
  439. SENDLINE_PGM("J12"); // Ready
  440. }
  441. void onIdle() {
  442. parseSerialRx();
  443. detectAutolevelingCompletion();
  444. detectPrintFromSdCompletion();
  445. }
  446. void onPrinterKilled(PGM_P const error, PGM_P const component) { }
  447. void onMediaInserted() {
  448. SENDLINE_PGM("J00"); // SD Inserted
  449. }
  450. void onMediaError() { }
  451. void onMediaRemoved() {
  452. SENDLINE_PGM("J01"); // SD Removed
  453. }
  454. void onPlayTone(const uint16_t frequency, const uint16_t duration) {
  455. tone(BEEPER_PIN, frequency, duration);
  456. }
  457. void onPrintTimerStarted() { }
  458. void onPrintTimerPaused() { }
  459. void onPrintTimerStopped() { }
  460. void onFilamentRunout(const extruder_t extruder) {
  461. is_out_of_filament = true;
  462. SENDLINE_PGM("J23"); // Filament runout
  463. SENDLINE_PGM("J18"); // Print paused
  464. // Note: printer will unload filament automatically
  465. }
  466. void onUserConfirmRequired(const char * const msg) { }
  467. void onStatusChanged(const char * const msg) { }
  468. void onFactoryReset() { }
  469. void onStoreSettings(char *buff) { }
  470. void onLoadSettings(const char *buff) { }
  471. void onConfigurationStoreWritten(bool success) { }
  472. void onConfigurationStoreRead(bool success) { }
  473. void onMeshUpdate(const int8_t xpos, const int8_t ypos, const float zval) { }
  474. #if ENABLED(POWER_LOSS_RECOVERY)
  475. void onPowerLossResume() { }
  476. #endif
  477. #if HAS_PID_HEATING
  478. void onPidTuning(const result_t rst) { }
  479. #endif
  480. }
  481. #endif // ANYCUBIC_LCD_CHIRON