My Marlin configs for Fabrikator Mini and CTC i3 Pro B
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

anycubic_i3mega_lcd.cpp 36KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023
  1. /**
  2. * anycubic_i3mega_lcd.cpp --- Support for Anycubic i3 Mega TFT
  3. * Created by Christian Hopp on 09.12.17.
  4. * Improved by David Ramiro
  5. * Converted to ext_iu by John BouAntoun 21 June 2020
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; either
  10. * version 2.1 of the License, or (at your option) any later version.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this library; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  20. */
  21. #include "../../../../inc/MarlinConfigPre.h"
  22. #if ENABLED(ANYCUBIC_LCD_I3MEGA)
  23. #include "anycubic_i3mega_lcd.h"
  24. #include "../../ui_api.h"
  25. #include "../../../../libs/numtostr.h"
  26. #include "../../../../module/motion.h" // for quickstop_stepper, A20 read printing speed, feedrate_percentage
  27. #include "../../../../MarlinCore.h" // for disable_steppers
  28. #include "../../../../inc/MarlinConfig.h"
  29. // command sending macro's with debugging capability
  30. #define SEND_PGM(x) send_P(PSTR(x))
  31. #define SENDLINE_PGM(x) sendLine_P(PSTR(x))
  32. #define SEND_PGM_VAL(x,y) (send_P(PSTR(x)), sendLine(i16tostr3rj(y)))
  33. #define SEND(x) send(x)
  34. #define SENDLINE(x) sendLine(x)
  35. #if ENABLED(ANYCUBIC_LCD_DEBUG)
  36. #define SENDLINE_DBG_PGM(x,y) (sendLine_P(PSTR(x)), SERIAL_ECHOLNPGM(y))
  37. #define SENDLINE_DBG_PGM_VAL(x,y,z) (sendLine_P(PSTR(x)), SERIAL_ECHOPGM(y), SERIAL_ECHOLN(z))
  38. #else
  39. #define SENDLINE_DBG_PGM(x,y) sendLine_P(PSTR(x))
  40. #define SENDLINE_DBG_PGM_VAL(x,y,z) sendLine_P(PSTR(x))
  41. #endif
  42. AnycubicTFTClass AnycubicTFT;
  43. static void sendNewLine(void) {
  44. LCD_SERIAL.write('\r');
  45. LCD_SERIAL.write('\n');
  46. }
  47. static void send(const char *str) {
  48. LCD_SERIAL.print(str);
  49. }
  50. static void sendLine(const char *str) {
  51. send(str);
  52. sendNewLine();
  53. }
  54. static void send_P(PGM_P str) {
  55. while (const char c = pgm_read_byte(str++))
  56. LCD_SERIAL.write(c);
  57. }
  58. static void sendLine_P(PGM_P str) {
  59. send_P(str);
  60. sendNewLine();
  61. }
  62. AnycubicTFTClass::AnycubicTFTClass() {}
  63. void AnycubicTFTClass::OnSetup() {
  64. #ifndef LCD_BAUDRATE
  65. #define LCD_BAUDRATE 115200
  66. #endif
  67. LCD_SERIAL.begin(LCD_BAUDRATE);
  68. SENDLINE_DBG_PGM("J17", "TFT Serial Debug: Main board reset... J17"); // J17 Main board reset
  69. ExtUI::delay_ms(10);
  70. // initialise the state of the key pins running on the tft
  71. #if ENABLED(SDSUPPORT) && PIN_EXISTS(SD_DETECT)
  72. SET_INPUT_PULLUP(SD_DETECT_PIN);
  73. #endif
  74. #if ENABLED(FILAMENT_RUNOUT_SENSOR)
  75. SET_INPUT_PULLUP(FIL_RUNOUT1_PIN);
  76. #endif
  77. mediaPrintingState = AMPRINTSTATE_NOT_PRINTING;
  78. mediaPauseState = AMPAUSESTATE_NOT_PAUSED;
  79. // DoSDCardStateCheck();
  80. SENDLINE_DBG_PGM("J12", "TFT Serial Debug: Ready... J12"); // J12 Ready
  81. ExtUI::delay_ms(10);
  82. DoFilamentRunoutCheck();
  83. SelectedFile[0] = 0;
  84. #if ENABLED(STARTUP_CHIME)
  85. ExtUI::injectCommands_P(PSTR("M300 P250 S554\nM300 P250 S554\nM300 P250 S740\nM300 P250 S554\nM300 P250 S740\nM300 P250 S554\nM300 P500 S831"));
  86. #endif
  87. #if ENABLED(ANYCUBIC_LCD_DEBUG)
  88. SERIAL_ECHOLNPGM("TFT Serial Debug: Finished startup");
  89. #endif
  90. }
  91. void AnycubicTFTClass::OnCommandScan() {
  92. static millis_t nextStopCheck = 0; // used to slow the stopped print check down to reasonable times
  93. const millis_t ms = millis();
  94. if (ELAPSED(ms, nextStopCheck)) {
  95. nextStopCheck = ms + 1000UL;
  96. if (mediaPrintingState == AMPRINTSTATE_STOP_REQUESTED && IsNozzleHomed()) {
  97. #if ENABLED(ANYCUBIC_LCD_DEBUG)
  98. SERIAL_ECHOLNPGM("TFT Serial Debug: Finished stopping print, releasing motors ...");
  99. #endif
  100. mediaPrintingState = AMPRINTSTATE_NOT_PRINTING;
  101. mediaPauseState = AMPAUSESTATE_NOT_PAUSED;
  102. ExtUI::injectCommands_P(PSTR("M84\nM27")); // disable stepper motors and force report of SD status
  103. ExtUI::delay_ms(200);
  104. // tell printer to release resources of print to indicate it is done
  105. SENDLINE_DBG_PGM("J14", "TFT Serial Debug: SD Print Stopped... J14");
  106. }
  107. }
  108. if (TFTbuflen < (TFTBUFSIZE - 1))
  109. GetCommandFromTFT();
  110. if (TFTbuflen) {
  111. TFTbuflen = (TFTbuflen - 1);
  112. TFTbufindr = (TFTbufindr + 1) % TFTBUFSIZE;
  113. }
  114. }
  115. void AnycubicTFTClass::OnKillTFT() {
  116. SENDLINE_DBG_PGM("J11", "TFT Serial Debug: Kill command... J11");
  117. }
  118. void AnycubicTFTClass::OnSDCardStateChange(bool isInserted) {
  119. #if ENABLED(ANYCUBIC_LCD_DEBUG)
  120. SERIAL_ECHOLNPAIR("TFT Serial Debug: OnSDCardStateChange event triggered...", isInserted);
  121. #endif
  122. DoSDCardStateCheck();
  123. }
  124. void AnycubicTFTClass::OnSDCardError() {
  125. #if ENABLED(ANYCUBIC_LCD_DEBUG)
  126. SERIAL_ECHOLNPGM("TFT Serial Debug: OnSDCardError event triggered...");
  127. #endif
  128. SENDLINE_DBG_PGM("J21", "TFT Serial Debug: On SD Card Error ... J21");
  129. }
  130. void AnycubicTFTClass::OnFilamentRunout() {
  131. #if ENABLED(ANYCUBIC_LCD_DEBUG)
  132. SERIAL_ECHOLNPGM("TFT Serial Debug: FilamentRunout triggered...");
  133. #endif
  134. DoFilamentRunoutCheck();
  135. }
  136. void AnycubicTFTClass::OnUserConfirmRequired(const char * const msg) {
  137. #if ENABLED(ANYCUBIC_LCD_DEBUG)
  138. SERIAL_ECHOLNPAIR("TFT Serial Debug: OnUserConfirmRequired triggered... ", msg);
  139. #endif
  140. #if ENABLED(SDSUPPORT)
  141. /**
  142. * Need to handle the process of following states
  143. * "Nozzle Parked"
  144. * "Load Filament"
  145. * "Filament Purging..."
  146. * "HeaterTimeout"
  147. * "Reheat finished."
  148. *
  149. * NOTE: The only way to handle these states is strcmp_P with the msg unfortunately (very expensive)
  150. */
  151. if (strcmp_P(msg, PSTR("Nozzle Parked")) == 0) {
  152. mediaPrintingState = AMPRINTSTATE_PAUSED;
  153. mediaPauseState = AMPAUSESTATE_PARKED;
  154. // enable continue button
  155. SENDLINE_DBG_PGM("J18", "TFT Serial Debug: UserConfirm SD print paused done... J18");
  156. }
  157. else if (strcmp_P(msg, PSTR("Load Filament")) == 0) {
  158. mediaPrintingState = AMPRINTSTATE_PAUSED;
  159. mediaPauseState = AMPAUSESTATE_FILAMENT_OUT;
  160. // enable continue button
  161. SENDLINE_DBG_PGM("J18", "TFT Serial Debug: UserConfirm Filament is out... J18");
  162. SENDLINE_DBG_PGM("J23", "TFT Serial Debug: UserConfirm Blocking filament prompt... J23");
  163. }
  164. else if (strcmp_P(msg, PSTR("Filament Purging...")) == 0) {
  165. mediaPrintingState = AMPRINTSTATE_PAUSED;
  166. mediaPauseState = AMPAUSESTATE_PARKING;
  167. // TODO: JBA I don't think J05 just disables the continue button, i think it injects a rogue M25. So taking this out
  168. // disable continue button
  169. // SENDLINE_DBG_PGM("J05", "TFT Serial Debug: UserConfirm SD Filament Purging... J05"); // J05 printing pause
  170. // enable continue button
  171. SENDLINE_DBG_PGM("J18", "TFT Serial Debug: UserConfirm Filament is purging... J18");
  172. }
  173. else if (strcmp_P(msg, PSTR("HeaterTimeout")) == 0) {
  174. mediaPrintingState = AMPRINTSTATE_PAUSED;
  175. mediaPauseState = AMPAUSESTATE_HEATER_TIMEOUT;
  176. // enable continue button
  177. SENDLINE_DBG_PGM("J18", "TFT Serial Debug: UserConfirm SD Heater timeout... J18");
  178. }
  179. else if (strcmp_P(msg, PSTR("Reheat finished.")) == 0) {
  180. mediaPrintingState = AMPRINTSTATE_PAUSED;
  181. mediaPauseState = AMPAUSESTATE_REHEAT_FINISHED;
  182. // enable continue button
  183. SENDLINE_DBG_PGM("J18", "TFT Serial Debug: UserConfirm SD Reheat done... J18");
  184. }
  185. #endif
  186. }
  187. float AnycubicTFTClass::CodeValue() {
  188. return (strtod(&TFTcmdbuffer[TFTbufindr][TFTstrchr_pointer - TFTcmdbuffer[TFTbufindr] + 1], nullptr));
  189. }
  190. bool AnycubicTFTClass::CodeSeen(char code) {
  191. TFTstrchr_pointer = strchr(TFTcmdbuffer[TFTbufindr], code);
  192. return !!TFTstrchr_pointer; // Return True if a character was found
  193. }
  194. bool AnycubicTFTClass::IsNozzleHomed() {
  195. const float xPosition = ExtUI::getAxisPosition_mm((ExtUI::axis_t) ExtUI::X);
  196. const float yPosition = ExtUI::getAxisPosition_mm((ExtUI::axis_t) ExtUI::Y);
  197. return WITHIN(xPosition, X_MIN_POS - 0.1, X_MIN_POS + 0.1) &&
  198. WITHIN(yPosition, Y_MIN_POS - 0.1, Y_MIN_POS + 0.1);
  199. }
  200. void AnycubicTFTClass::HandleSpecialMenu() {
  201. /**
  202. * NOTE: that the file selection command actual lowercases the entire selected file/foldername, so charracter comparisons need to be lowercase.
  203. */
  204. if (SelectedDirectory[0] == '<') {
  205. switch (SelectedDirectory[1]) {
  206. case 'e': // "<exit>"
  207. SpecialMenu = false;
  208. return;
  209. break;
  210. #if ENABLED(PROBE_MANUALLY)
  211. case '0':
  212. switch (SelectedDirectory[2]) {
  213. case '1': // "<01ZUp0.1>"
  214. SERIAL_ECHOLNPGM("Special Menu: Z Up 0.1");
  215. ExtUI::injectCommands_P(PSTR("G91\nG1 Z+0.1\nG90"));
  216. break;
  217. case '2': // "<02ZUp0.02>"
  218. SERIAL_ECHOLNPGM("Special Menu: Z Up 0.02");
  219. ExtUI::injectCommands_P(PSTR("G91\nG1 Z+0.02\nG90"));
  220. break;
  221. case '3': // "<03ZDn0.02>"
  222. SERIAL_ECHOLNPGM("Special Menu: Z Down 0.02");
  223. ExtUI::injectCommands_P(PSTR("G91\nG1 Z-0.02\nG90"));
  224. break;
  225. case '4': // "<04ZDn0.1>"
  226. SERIAL_ECHOLNPGM("Special Menu: Z Down 0.1");
  227. ExtUI::injectCommands_P(PSTR("G91\nG1 Z-0.1\nG90"));
  228. break;
  229. case '5': // "<05PrehtBed>"
  230. SERIAL_ECHOLNPGM("Special Menu: Preheat Bed");
  231. ExtUI::injectCommands_P(PSTR("M140 S65"));
  232. break;
  233. case '6': // "<06SMeshLvl>"
  234. SERIAL_ECHOLNPGM("Special Menu: Start Mesh Leveling");
  235. ExtUI::injectCommands_P(PSTR("G29S1"));
  236. break;
  237. case '7': // "<07MeshNPnt>"
  238. SERIAL_ECHOLNPGM("Special Menu: Next Mesh Point");
  239. ExtUI::injectCommands_P(PSTR("G29S2"));
  240. break;
  241. case '8': // "<08HtEndPID>"
  242. SERIAL_ECHOLNPGM("Special Menu: Auto Tune Hotend PID");
  243. // need to dwell for half a second to give the fan a chance to start before the pid tuning starts
  244. ExtUI::injectCommands_P(PSTR("M106 S204\nG4 P500\nM303 E0 S215 C15 U1"));
  245. break;
  246. case '9': // "<09HtBedPID>"
  247. SERIAL_ECHOLNPGM("Special Menu: Auto Tune Hotbed Pid");
  248. ExtUI::injectCommands_P(PSTR("M303 E-1 S65 C6 U1"));
  249. break;
  250. default:
  251. break;
  252. }
  253. break;
  254. case '1':
  255. switch (SelectedDirectory[2]) {
  256. case '0': // "<10FWDeflts>"
  257. SERIAL_ECHOLNPGM("Special Menu: Load FW Defaults");
  258. ExtUI::injectCommands_P(PSTR("M502\nM300 P105 S1661\nM300 P210 S1108"));
  259. break;
  260. case '1': // "<11SvEEPROM>"
  261. SERIAL_ECHOLNPGM("Special Menu: Save EEPROM");
  262. ExtUI::injectCommands_P(PSTR("M500\nM300 P105 S1108\nM300 P210 S1661"));
  263. break;
  264. default:
  265. break;
  266. }
  267. break;
  268. #else // if ENABLED(PROBE_MANUALLY)
  269. case '0':
  270. switch (SelectedDirectory[2]) {
  271. case '1': // "<01PrehtBed>"
  272. SERIAL_ECHOLNPGM("Special Menu: Preheat Bed");
  273. ExtUI::injectCommands_P(PSTR("M140 S65"));
  274. break;
  275. case '2': // "<02ABL>"
  276. SERIAL_ECHOLNPGM("Special Menu: Auto Bed Leveling");
  277. ExtUI::injectCommands_P(PSTR("G29N"));
  278. break;
  279. case '3': // "<03HtendPID>"
  280. SERIAL_ECHOLNPGM("Special Menu: Auto Tune Hotend PID");
  281. // need to dwell for half a second to give the fan a chance to start before the pid tuning starts
  282. ExtUI::injectCommands_P(PSTR("M106 S204\nG4 P500\nM303 E0 S215 C15 U1"));
  283. break;
  284. case '4': // "<04HtbedPID>"
  285. SERIAL_ECHOLNPGM("Special Menu: Auto Tune Hotbed Pid");
  286. ExtUI::injectCommands_P(PSTR("M303 E-1 S65 C6 U1"));
  287. break;
  288. case '5': // "<05FWDeflts>"
  289. SERIAL_ECHOLNPGM("Special Menu: Load FW Defaults");
  290. ExtUI::injectCommands_P(PSTR("M502\nM300 P105 S1661\nM300 P210 S1108"));
  291. break;
  292. case '6': // "<06SvEEPROM>"
  293. SERIAL_ECHOLNPGM("Special Menu: Save EEPROM");
  294. ExtUI::injectCommands_P(PSTR("M500\nM300 P105 S1108\nM300 P210 S1661"));
  295. break;
  296. case '7': // <07SendM108>
  297. SERIAL_ECHOLNPGM("Special Menu: Send User Confirmation");
  298. ExtUI::injectCommands_P(PSTR("M108"));
  299. break;
  300. default:
  301. break;
  302. }
  303. break;
  304. #endif // PROBE_MANUALLY
  305. default:
  306. break;
  307. }
  308. #if ENABLED(ANYCUBIC_LCD_DEBUG)
  309. }
  310. else {
  311. SERIAL_ECHOPGM("TFT Serial Debug: Attempted to HandleSpecialMenu on non-special menu... ");
  312. SERIAL_ECHOLN(SelectedDirectory);
  313. #endif
  314. }
  315. }
  316. void AnycubicTFTClass::RenderCurrentFileList() {
  317. #if ENABLED(SDSUPPORT)
  318. uint16_t selectedNumber = 0;
  319. SelectedDirectory[0] = 0;
  320. SelectedFile[0] = 0;
  321. ExtUI::FileList currentFileList;
  322. SENDLINE_PGM("FN "); // Filelist start
  323. if (!ExtUI::isMediaInserted() && !SpecialMenu) {
  324. SENDLINE_DBG_PGM("J02", "TFT Serial Debug: No SD Card mounted to render Current File List... J02");
  325. SENDLINE_PGM("<Special_Menu>");
  326. SENDLINE_PGM("<Special_Menu>");
  327. }
  328. else {
  329. if (CodeSeen('S'))
  330. selectedNumber = CodeValue();
  331. if (SpecialMenu)
  332. RenderSpecialMenu(selectedNumber);
  333. else if (selectedNumber <= currentFileList.count())
  334. RenderCurrentFolder(selectedNumber);
  335. }
  336. SENDLINE_PGM("END"); // Filelist stop
  337. #endif // SDSUPPORT
  338. }
  339. void AnycubicTFTClass::RenderSpecialMenu(uint16_t selectedNumber) {
  340. switch (selectedNumber) {
  341. #if ENABLED(PROBE_MANUALLY)
  342. case 0: // First Page
  343. SENDLINE_PGM("<01ZUp0.1>");
  344. SENDLINE_PGM("<Z Up 0.1>");
  345. SENDLINE_PGM("<02ZUp0.02>");
  346. SENDLINE_PGM("<Z Up 0.02>");
  347. SENDLINE_PGM("<03ZDn0.02>");
  348. SENDLINE_PGM("<Z Down 0.02>");
  349. SENDLINE_PGM("<04ZDn0.1>");
  350. SENDLINE_PGM("<Z Down 0.1>");
  351. break;
  352. case 4: // Second Page
  353. SENDLINE_PGM("<05PrehtBed>");
  354. SENDLINE_PGM("<Preheat bed>");
  355. SENDLINE_PGM("<06SMeshLvl>");
  356. SENDLINE_PGM("<Start Mesh Leveling>");
  357. SENDLINE_PGM("<07MeshNPnt>");
  358. SENDLINE_PGM("<Next Mesh Point>");
  359. SENDLINE_PGM("<08HtEndPID>");
  360. SENDLINE_PGM("<Auto Tune Hotend PID>");
  361. break;
  362. case 8: // Third Page
  363. SENDLINE_PGM("<09HtBedPID>");
  364. SENDLINE_PGM("<Auto Tune Hotbed PID>");
  365. SENDLINE_PGM("<10FWDeflts>");
  366. SENDLINE_PGM("<Load FW Defaults>");
  367. SENDLINE_PGM("<11SvEEPROM>");
  368. SENDLINE_PGM("<Save EEPROM>");
  369. SENDLINE_PGM("<Exit>");
  370. SENDLINE_PGM("<Exit>");
  371. break;
  372. #else
  373. case 0: // First Page
  374. SENDLINE_PGM("<01PrehtBed>");
  375. SENDLINE_PGM("<Preheat bed>");
  376. SENDLINE_PGM("<02ABL>");
  377. SENDLINE_PGM("<Auto Bed Leveling>");
  378. SENDLINE_PGM("<03HtEndPID>");
  379. SENDLINE_PGM("<Auto Tune Hotend PID>");
  380. SENDLINE_PGM("<04HtBedPID>");
  381. SENDLINE_PGM("<Auto Tune Hotbed PID>");
  382. break;
  383. case 4: // Second Page
  384. SENDLINE_PGM("<05FWDeflts>");
  385. SENDLINE_PGM("<Load FW Defaults>");
  386. SENDLINE_PGM("<06SvEEPROM>");
  387. SENDLINE_PGM("<Save EEPROM>");
  388. SENDLINE_PGM("<07SendM108>");
  389. SENDLINE_PGM("<Send User Confirmation>");
  390. SENDLINE_PGM("<Exit>");
  391. SENDLINE_PGM("<Exit>");
  392. break;
  393. #endif // PROBE_MANUALLY
  394. default:
  395. break;
  396. }
  397. }
  398. void AnycubicTFTClass::RenderCurrentFolder(uint16_t selectedNumber) {
  399. ExtUI::FileList currentFileList;
  400. uint16_t cnt = selectedNumber;
  401. uint16_t max_files;
  402. uint16_t dir_files = currentFileList.count();
  403. if ((dir_files - selectedNumber) < 4)
  404. max_files = dir_files;
  405. else
  406. max_files = selectedNumber + 3;
  407. for (cnt = selectedNumber; cnt <= max_files; cnt++) {
  408. if (cnt == 0) { // Special Entry
  409. if (currentFileList.isAtRootDir()) {
  410. SENDLINE_PGM("<specialmnu>");
  411. SENDLINE_PGM("<Special Menu>");
  412. }
  413. else {
  414. SENDLINE_PGM("/..");
  415. SENDLINE_PGM("/..");
  416. }
  417. }
  418. else {
  419. currentFileList.seek(cnt - 1, false);
  420. #if ENABLED(ANYCUBIC_LCD_DEBUG)
  421. SERIAL_ECHOLN(currentFileList.filename());
  422. #endif
  423. if (currentFileList.isDir()) {
  424. SEND_PGM("/");
  425. SENDLINE(currentFileList.shortFilename());
  426. SEND_PGM("/");
  427. SENDLINE(currentFileList.filename());
  428. }
  429. else {
  430. SENDLINE(currentFileList.shortFilename());
  431. SENDLINE(currentFileList.filename());
  432. }
  433. }
  434. }
  435. }
  436. void AnycubicTFTClass::OnPrintTimerStarted() {
  437. #if ENABLED(SDSUPPORT)
  438. if (mediaPrintingState == AMPRINTSTATE_PRINTING)
  439. SENDLINE_DBG_PGM("J04", "TFT Serial Debug: Starting SD Print... J04"); // J04 Starting Print
  440. #endif
  441. }
  442. void AnycubicTFTClass::OnPrintTimerPaused() {
  443. #if ENABLED(SDSUPPORT)
  444. if (ExtUI::isPrintingFromMedia()) {
  445. mediaPrintingState = AMPRINTSTATE_PAUSED;
  446. mediaPauseState = AMPAUSESTATE_PARKING;
  447. }
  448. #endif
  449. }
  450. void AnycubicTFTClass::OnPrintTimerStopped() {
  451. #if ENABLED(SDSUPPORT)
  452. if (mediaPrintingState == AMPRINTSTATE_PRINTING) {
  453. mediaPrintingState = AMPRINTSTATE_NOT_PRINTING;
  454. mediaPauseState = AMPAUSESTATE_NOT_PAUSED;
  455. SENDLINE_DBG_PGM("J14", "TFT Serial Debug: SD Print Completed... J14");
  456. }
  457. // otherwise it was stopped by the printer so don't send print completed signal to TFT
  458. #endif
  459. }
  460. void AnycubicTFTClass::GetCommandFromTFT() {
  461. char *starpos = nullptr;
  462. while (LCD_SERIAL.available() > 0 && TFTbuflen < TFTBUFSIZE) {
  463. serial3_char = LCD_SERIAL.read();
  464. if (serial3_char == '\n' ||
  465. serial3_char == '\r' ||
  466. serial3_char == ':' ||
  467. serial3_count >= (TFT_MAX_CMD_SIZE - 1)
  468. ) {
  469. if (!serial3_count) return; // if empty line
  470. TFTcmdbuffer[TFTbufindw][serial3_count] = 0; // terminate string
  471. if ((strchr(TFTcmdbuffer[TFTbufindw], 'A') != nullptr)) {
  472. int16_t a_command;
  473. TFTstrchr_pointer = strchr(TFTcmdbuffer[TFTbufindw], 'A');
  474. a_command = ((int)((strtod(&TFTcmdbuffer[TFTbufindw][TFTstrchr_pointer - TFTcmdbuffer[TFTbufindw] + 1], nullptr))));
  475. #if ENABLED(ANYCUBIC_LCD_DEBUG)
  476. if ((a_command > 7) && (a_command != 20)) // No debugging of status polls, please!
  477. SERIAL_ECHOLNPAIR("TFT Serial Command: ", TFTcmdbuffer[TFTbufindw]);
  478. #endif
  479. switch (a_command) {
  480. case 0: { // A0 GET HOTEND TEMP
  481. float hotendActualTemp = ExtUI::getActualTemp_celsius((ExtUI::extruder_t) (ExtUI::extruder_t) ExtUI::E0);
  482. SEND_PGM_VAL("A0V ", int(hotendActualTemp + 0.5));
  483. }
  484. break;
  485. case 1: { // A1 GET HOTEND TARGET TEMP
  486. float hotendTargetTemp = ExtUI::getTargetTemp_celsius((ExtUI::extruder_t) (ExtUI::extruder_t) ExtUI::E0);
  487. SEND_PGM_VAL("A1V ", int(hotendTargetTemp + 0.5));
  488. }
  489. break;
  490. case 2: { // A2 GET HOTBED TEMP
  491. float heatedBedActualTemp = ExtUI::getActualTemp_celsius((ExtUI::heater_t) ExtUI::BED);
  492. SEND_PGM_VAL("A2V ", int(heatedBedActualTemp + 0.5));
  493. }
  494. break;
  495. case 3: { // A3 GET HOTBED TARGET TEMP
  496. float heatedBedTargetTemp = ExtUI::getTargetTemp_celsius((ExtUI::heater_t) ExtUI::BED);
  497. SEND_PGM_VAL("A3V ", int(heatedBedTargetTemp + 0.5));
  498. } break;
  499. case 4: { // A4 GET FAN SPEED
  500. float fanPercent = ExtUI::getActualFan_percent(ExtUI::FAN0);
  501. fanPercent = constrain(fanPercent, 0, 100);
  502. SEND_PGM_VAL("A4V ", int(fanPercent));
  503. } break;
  504. case 5: { // A5 GET CURRENT COORDINATE
  505. const float xPosition = ExtUI::getAxisPosition_mm(ExtUI::X),
  506. yPosition = ExtUI::getAxisPosition_mm(ExtUI::Y),
  507. zPosition = ExtUI::getAxisPosition_mm(ExtUI::Z);
  508. SEND_PGM("A5V X: "); LCD_SERIAL.print(xPosition);
  509. SEND_PGM( " Y: "); LCD_SERIAL.print(yPosition);
  510. SEND_PGM( " Z: "); LCD_SERIAL.print(zPosition);
  511. SENDLINE_PGM("");
  512. } break;
  513. case 6: // A6 GET SD CARD PRINTING STATUS
  514. #if ENABLED(SDSUPPORT)
  515. if (ExtUI::isPrintingFromMedia()) {
  516. SEND_PGM("A6V ");
  517. if (ExtUI::isMediaInserted())
  518. SENDLINE(ui8tostr3rj(ExtUI::getProgress_percent()));
  519. else
  520. SENDLINE_DBG_PGM("J02", "TFT Serial Debug: No SD Card mounted to return printing status... J02");
  521. }
  522. else
  523. SENDLINE_PGM("A6V ---");
  524. #endif
  525. break;
  526. case 7: { // A7 GET PRINTING TIME
  527. const uint32_t elapsedSeconds = ExtUI::getProgress_seconds_elapsed();
  528. SEND_PGM("A7V ");
  529. if (elapsedSeconds != 0) { // print time
  530. const uint32_t elapsedMinutes = elapsedSeconds / 60;
  531. SEND(ui8tostr2(elapsedMinutes / 60));
  532. SEND_PGM(" H ");
  533. SEND(ui8tostr2(elapsedMinutes % 60));
  534. SENDLINE_PGM(" M");
  535. }
  536. else
  537. SENDLINE_PGM(" 999:999");
  538. }
  539. break;
  540. case 8: // A8 GET SD LIST
  541. #if ENABLED(SDSUPPORT)
  542. SelectedFile[0] = 0;
  543. RenderCurrentFileList();
  544. #endif
  545. break;
  546. case 9: // A9 pause sd print
  547. #if ENABLED(SDSUPPORT)
  548. if (ExtUI::isPrintingFromMedia())
  549. PausePrint();
  550. #endif
  551. break;
  552. case 10: // A10 resume sd print
  553. #if ENABLED(SDSUPPORT)
  554. if (ExtUI::isPrintingFromMediaPaused())
  555. ResumePrint();
  556. #endif
  557. break;
  558. case 11: // A11 STOP SD PRINT
  559. TERN_(SDSUPPORT, StopPrint());
  560. break;
  561. case 12: // A12 kill
  562. kill(PSTR(STR_ERR_KILLED));
  563. break;
  564. case 13: // A13 SELECTION FILE
  565. #if ENABLED(SDSUPPORT)
  566. if (ExtUI::isMediaInserted()) {
  567. starpos = (strchr(TFTstrchr_pointer + 4, '*'));
  568. if (TFTstrchr_pointer[4] == '/') {
  569. strcpy(SelectedDirectory, TFTstrchr_pointer + 5);
  570. SelectedFile[0] = 0;
  571. SENDLINE_DBG_PGM("J21", "TFT Serial Debug: Clear file selection... J21 "); // J21 Not File Selected
  572. SENDLINE_PGM("");
  573. }
  574. else if (TFTstrchr_pointer[4] == '<') {
  575. strcpy(SelectedDirectory, TFTstrchr_pointer + 4);
  576. SpecialMenu = true;
  577. SelectedFile[0] = 0;
  578. SENDLINE_DBG_PGM("J21", "TFT Serial Debug: Clear file selection... J21 "); // J21 Not File Selected
  579. SENDLINE_PGM("");
  580. }
  581. else {
  582. SelectedDirectory[0] = 0;
  583. if (starpos) *(starpos - 1) = '\0';
  584. strcpy(SelectedFile, TFTstrchr_pointer + 4);
  585. SENDLINE_DBG_PGM_VAL("J20", "TFT Serial Debug: File Selected... J20 ", SelectedFile); // J20 File Selected
  586. }
  587. }
  588. #endif
  589. break;
  590. case 14: // A14 START PRINTING
  591. #if ENABLED(SDSUPPORT)
  592. if (!ExtUI::isPrinting() && strlen(SelectedFile) > 0)
  593. StartPrint();
  594. #endif
  595. break;
  596. case 15: // A15 RESUMING FROM OUTAGE
  597. // TODO: JBA implement resume form outage
  598. break;
  599. case 16: { // A16 set hotend temp
  600. unsigned int tempvalue;
  601. if (CodeSeen('S')) {
  602. tempvalue = constrain(CodeValue(), 0, 275);
  603. ExtUI::setTargetTemp_celsius(tempvalue, (ExtUI::extruder_t) ExtUI::E0);
  604. }
  605. else if (CodeSeen('C') && !ExtUI::isPrinting()) {
  606. if (ExtUI::getAxisPosition_mm(ExtUI::Z) < 10)
  607. ExtUI::injectCommands_P(PSTR("G1 Z10")); // RASE Z AXIS
  608. tempvalue = constrain(CodeValue(), 0, 275);
  609. ExtUI::setTargetTemp_celsius(tempvalue, (ExtUI::extruder_t) ExtUI::E0);
  610. }
  611. }
  612. break;
  613. case 17: { // A17 set heated bed temp
  614. unsigned int tempbed;
  615. if (CodeSeen('S')) {
  616. tempbed = constrain(CodeValue(), 0, 100);
  617. ExtUI::setTargetTemp_celsius(tempbed, (ExtUI::heater_t)ExtUI::BED);
  618. }
  619. }
  620. break;
  621. case 18: { // A18 set fan speed
  622. float fanPercent;
  623. if (CodeSeen('S')) {
  624. fanPercent = CodeValue();
  625. fanPercent = constrain(fanPercent, 0, 100);
  626. ExtUI::setTargetFan_percent(fanPercent, ExtUI::FAN0);
  627. }
  628. else
  629. fanPercent = 100;
  630. ExtUI::setTargetFan_percent(fanPercent, ExtUI::FAN0);
  631. SENDLINE_PGM("");
  632. }
  633. break;
  634. case 19: // A19 stop stepper drivers - sent on stop extrude command and on turn motors off command
  635. if (!ExtUI::isPrinting()) {
  636. quickstop_stepper();
  637. disable_all_steppers();
  638. }
  639. SENDLINE_PGM("");
  640. break;
  641. case 20: // A20 read printing speed
  642. if (CodeSeen('S'))
  643. feedrate_percentage = constrain(CodeValue(), 40, 999);
  644. else
  645. SEND_PGM_VAL("A20V ", feedrate_percentage);
  646. break;
  647. case 21: // A21 all home
  648. if (!ExtUI::isPrinting() && !ExtUI::isPrintingFromMediaPaused()) {
  649. if (CodeSeen('X') || CodeSeen('Y') || CodeSeen('Z')) {
  650. if (CodeSeen('X'))
  651. ExtUI::injectCommands_P(PSTR("G28X"));
  652. if (CodeSeen('Y'))
  653. ExtUI::injectCommands_P(PSTR("G28Y"));
  654. if (CodeSeen('Z'))
  655. ExtUI::injectCommands_P(PSTR("G28Z"));
  656. }
  657. else if (CodeSeen('C')) {
  658. ExtUI::injectCommands_P(G28_STR);
  659. }
  660. }
  661. break;
  662. case 22: // A22 move X/Y/Z or extrude
  663. if (!ExtUI::isPrinting()) {
  664. float coorvalue;
  665. unsigned int movespeed = 0;
  666. char commandStr[30];
  667. char fullCommandStr[38];
  668. commandStr[0] = 0; // empty string
  669. if (CodeSeen('F')) // Set feedrate
  670. movespeed = CodeValue();
  671. if (CodeSeen('X')) { // Move in X direction
  672. coorvalue = CodeValue();
  673. if ((coorvalue <= 0.2) && coorvalue > 0)
  674. sprintf_P(commandStr, PSTR("G1 X0.1F%i"), movespeed);
  675. else if ((coorvalue <= -0.1) && coorvalue > -1)
  676. sprintf_P(commandStr, PSTR("G1 X-0.1F%i"), movespeed);
  677. else
  678. sprintf_P(commandStr, PSTR("G1 X%iF%i"), int(coorvalue), movespeed);
  679. }
  680. else if (CodeSeen('Y')) { // Move in Y direction
  681. coorvalue = CodeValue();
  682. if ((coorvalue <= 0.2) && coorvalue > 0)
  683. sprintf_P(commandStr, PSTR("G1 Y0.1F%i"), movespeed);
  684. else if ((coorvalue <= -0.1) && coorvalue > -1)
  685. sprintf_P(commandStr, PSTR("G1 Y-0.1F%i"), movespeed);
  686. else
  687. sprintf_P(commandStr, PSTR("G1 Y%iF%i"), int(coorvalue), movespeed);
  688. }
  689. else if (CodeSeen('Z')) { // Move in Z direction
  690. coorvalue = CodeValue();
  691. if ((coorvalue <= 0.2) && coorvalue > 0)
  692. sprintf_P(commandStr, PSTR("G1 Z0.1F%i"), movespeed);
  693. else if ((coorvalue <= -0.1) && coorvalue > -1)
  694. sprintf_P(commandStr, PSTR("G1 Z-0.1F%i"), movespeed);
  695. else
  696. sprintf_P(commandStr, PSTR("G1 Z%iF%i"), int(coorvalue), movespeed);
  697. }
  698. else if (CodeSeen('E')) { // Extrude
  699. coorvalue = CodeValue();
  700. if ((coorvalue <= 0.2) && coorvalue > 0)
  701. sprintf_P(commandStr, PSTR("G1 E0.1F%i"), movespeed);
  702. else if ((coorvalue <= -0.1) && coorvalue > -1)
  703. sprintf_P(commandStr, PSTR("G1 E-0.1F%i"), movespeed);
  704. else
  705. sprintf_P(commandStr, PSTR("G1 E%iF500"), int(coorvalue));
  706. }
  707. if (strlen(commandStr) > 0) {
  708. sprintf_P(fullCommandStr, PSTR("G91\n%s\nG90"), commandStr);
  709. #if ENABLED(ANYCUBIC_LCD_DEBUG)
  710. SERIAL_ECHOPGM("TFT Serial Debug: A22 Move final request with gcode... ");
  711. SERIAL_ECHOLN(fullCommandStr);
  712. #endif
  713. ExtUI::injectCommands(fullCommandStr);
  714. }
  715. }
  716. SENDLINE_PGM("");
  717. break;
  718. case 23: // A23 preheat pla
  719. if (!ExtUI::isPrinting()) {
  720. if (ExtUI::getAxisPosition_mm(ExtUI::Z) < 10)
  721. ExtUI::injectCommands_P(PSTR("G1 Z10")); // RASE Z AXIS
  722. ExtUI::setTargetTemp_celsius(PREHEAT_1_TEMP_BED, (ExtUI::heater_t) ExtUI::BED);
  723. ExtUI::setTargetTemp_celsius(PREHEAT_1_TEMP_HOTEND, (ExtUI::extruder_t) ExtUI::E0);
  724. SENDLINE_PGM("OK");
  725. }
  726. break;
  727. case 24:// A24 preheat abs
  728. if (!ExtUI::isPrinting()) {
  729. if (ExtUI::getAxisPosition_mm(ExtUI::Z) < 10)
  730. ExtUI::injectCommands_P(PSTR("G1 Z10")); // RASE Z AXIS
  731. ExtUI::setTargetTemp_celsius(PREHEAT_2_TEMP_BED, (ExtUI::heater_t) ExtUI::BED);
  732. ExtUI::setTargetTemp_celsius(PREHEAT_2_TEMP_HOTEND, (ExtUI::extruder_t) ExtUI::E0);
  733. SENDLINE_PGM("OK");
  734. }
  735. break;
  736. case 25: // A25 cool down
  737. if (!ExtUI::isPrinting()) {
  738. ExtUI::setTargetTemp_celsius(0, (ExtUI::heater_t) ExtUI::BED);
  739. ExtUI::setTargetTemp_celsius(0, (ExtUI::extruder_t) ExtUI::E0);
  740. SENDLINE_DBG_PGM("J12", "TFT Serial Debug: Cooling down... J12"); // J12 cool down
  741. }
  742. break;
  743. case 26: // A26 refresh SD
  744. #if ENABLED(SDSUPPORT)
  745. if (ExtUI::isMediaInserted()) {
  746. if (strlen(SelectedDirectory) > 0) {
  747. ExtUI::FileList currentFileList;
  748. if ((SelectedDirectory[0] == '.') && (SelectedDirectory[1] == '.')) {
  749. currentFileList.upDir();
  750. }
  751. else {
  752. if (SelectedDirectory[0] == '<')
  753. HandleSpecialMenu();
  754. else
  755. currentFileList.changeDir(SelectedDirectory);
  756. }
  757. }
  758. }
  759. else {
  760. SENDLINE_DBG_PGM("J02", "TFT Serial Debug: No SD Card mounted to refresh SD A26... J02");
  761. }
  762. SelectedDirectory[0] = 0;
  763. #endif
  764. break;
  765. #if ENABLED(SERVO_ENDSTOPS)
  766. case 27: break; // A27 servos angles adjust
  767. #endif
  768. case 28: // A28 filament test
  769. if (CodeSeen('O'))
  770. NOOP;
  771. else if (CodeSeen('C'))
  772. NOOP;
  773. SENDLINE_PGM("");
  774. break;
  775. case 33: // A33 get version info
  776. SEND_PGM("J33 ");
  777. SENDLINE_PGM(DETAILED_BUILD_VERSION);
  778. break;
  779. default:
  780. break;
  781. }
  782. }
  783. TFTbufindw = (TFTbufindw + 1) % TFTBUFSIZE;
  784. TFTbuflen += 1;
  785. serial3_count = 0; // clear buffer
  786. }
  787. else {
  788. TFTcmdbuffer[TFTbufindw][serial3_count++] = serial3_char;
  789. }
  790. }
  791. }
  792. void AnycubicTFTClass::DoSDCardStateCheck() {
  793. #if ENABLED(SDSUPPORT) && PIN_EXISTS(SD_DETECT)
  794. bool isInserted = ExtUI::isMediaInserted();
  795. if (isInserted)
  796. SENDLINE_DBG_PGM("J00", "TFT Serial Debug: SD card state changed... isInserted");
  797. else
  798. SENDLINE_DBG_PGM("J01", "TFT Serial Debug: SD card state changed... !isInserted");
  799. #endif
  800. }
  801. void AnycubicTFTClass::DoFilamentRunoutCheck() {
  802. #if ENABLED(FILAMENT_RUNOUT_SENSOR)
  803. // NOTE: ExtUI::getFilamentRunoutState() only returns the runout state if the job is printing
  804. // we want to actually check the status of the pin here, regardless of printstate
  805. if (READ(FIL_RUNOUT1_PIN)) {
  806. if (mediaPrintingState == AMPRINTSTATE_PRINTING || mediaPrintingState == AMPRINTSTATE_PAUSED || mediaPrintingState == AMPRINTSTATE_PAUSE_REQUESTED) {
  807. // play tone to indicate filament is out
  808. ExtUI::injectCommands_P(PSTR("\nM300 P200 S1567\nM300 P200 S1174\nM300 P200 S1567\nM300 P200 S1174\nM300 P2000 S1567"));
  809. // tell the user that the filament has run out and wait
  810. SENDLINE_DBG_PGM("J23", "TFT Serial Debug: Blocking filament prompt... J23");
  811. }
  812. else {
  813. SENDLINE_DBG_PGM("J15", "TFT Serial Debug: Non blocking filament runout... J15");
  814. }
  815. }
  816. #endif // FILAMENT_RUNOUT_SENSOR
  817. }
  818. void AnycubicTFTClass::StartPrint() {
  819. #if ENABLED(SDSUPPORT)
  820. if (!ExtUI::isPrinting() && strlen(SelectedFile) > 0) {
  821. #if ENABLED(ANYCUBIC_LCD_DEBUG)
  822. SERIAL_ECHOPGM("TFT Serial Debug: About to print file ... ");
  823. SERIAL_ECHO(ExtUI::isPrinting());
  824. SERIAL_ECHOPGM(" ");
  825. SERIAL_ECHOLN(SelectedFile);
  826. #endif
  827. mediaPrintingState = AMPRINTSTATE_PRINTING;
  828. mediaPauseState = AMPAUSESTATE_NOT_PAUSED;
  829. ExtUI::printFile(SelectedFile);
  830. }
  831. #endif // SDUPPORT
  832. }
  833. void AnycubicTFTClass::PausePrint() {
  834. #if ENABLED(SDSUPPORT)
  835. if (ExtUI::isPrintingFromMedia() && mediaPrintingState != AMPRINTSTATE_STOP_REQUESTED && mediaPauseState == AMPAUSESTATE_NOT_PAUSED) {
  836. mediaPrintingState = AMPRINTSTATE_PAUSE_REQUESTED;
  837. mediaPauseState = AMPAUSESTATE_NOT_PAUSED; // need the userconfirm method to update pause state
  838. SENDLINE_DBG_PGM("J05", "TFT Serial Debug: SD print pause started... J05"); // J05 printing pause
  839. // for some reason pausing the print doesn't retract the extruder so force a manual one here
  840. ExtUI::injectCommands_P(PSTR("G91\nG1 E-2 F1800\nG90"));
  841. ExtUI::pausePrint();
  842. }
  843. #endif
  844. }
  845. void AnycubicTFTClass::ResumePrint() {
  846. #if ENABLED(SDSUPPORT)
  847. #if ENABLED(FILAMENT_RUNOUT_SENSOR)
  848. if (READ(FIL_RUNOUT1_PIN)) {
  849. #if ENABLED(ANYCUBIC_LCD_DEBUG)
  850. SERIAL_ECHOLNPGM("TFT Serial Debug: Resume Print with filament sensor still tripped... ");
  851. #endif
  852. // trigger the user message box
  853. DoFilamentRunoutCheck();
  854. // re-enable the continue button
  855. SENDLINE_DBG_PGM("J18", "TFT Serial Debug: Resume Print with filament sensor still tripped... J18");
  856. return;
  857. }
  858. #endif
  859. if (mediaPauseState == AMPAUSESTATE_HEATER_TIMEOUT) {
  860. mediaPauseState = AMPAUSESTATE_REHEATING;
  861. // TODO: JBA I don't think J05 just disables the continue button, i think it injects a rogue M25. So taking this out
  862. // // disable the continue button
  863. // SENDLINE_DBG_PGM("J05", "TFT Serial Debug: Resume called with heater timeout... J05"); // J05 printing pause
  864. // reheat the nozzle
  865. ExtUI::setUserConfirmed();
  866. }
  867. else {
  868. mediaPrintingState = AMPRINTSTATE_PRINTING;
  869. mediaPauseState = AMPAUSESTATE_NOT_PAUSED;
  870. SENDLINE_DBG_PGM("J04", "TFT Serial Debug: SD print resumed... J04"); // J04 printing form sd card now
  871. ExtUI::resumePrint();
  872. }
  873. #endif
  874. }
  875. void AnycubicTFTClass::StopPrint() {
  876. #if ENABLED(SDSUPPORT)
  877. mediaPrintingState = AMPRINTSTATE_STOP_REQUESTED;
  878. mediaPauseState = AMPAUSESTATE_NOT_PAUSED;
  879. SENDLINE_DBG_PGM("J16", "TFT Serial Debug: SD print stop called... J16");
  880. // for some reason stopping the print doesn't retract the extruder so force a manual one here
  881. ExtUI::injectCommands_P(PSTR("G91\nG1 E-2 F1800\nG90"));
  882. ExtUI::stopPrint();
  883. #endif
  884. }
  885. #endif // ANYCUBIC_LCD_I3MEGA