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

malyan.cpp 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  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. * lcd/extui/malyan/malyan.cpp
  24. *
  25. * LCD implementation for Malyan's LCD, a separate ESP8266 MCU running
  26. * on Serial1 for the M200 board. This module outputs a pseudo-G-code
  27. * wrapped in curly braces which the LCD implementation translates into
  28. * actual G-code commands.
  29. *
  30. * Added to Marlin for Mini/Malyan M200
  31. * Unknown commands as of Jan 2018: {H:}
  32. * Not currently implemented:
  33. * {E:} when sent by LCD. Meaning unknown.
  34. *
  35. * Notes for connecting to boards that are not Malyan:
  36. * The LCD is 3.3v, so if powering from a RAMPS 1.4 board or
  37. * other 5v/12v board, use a buck converter to power the LCD and
  38. * the 3.3v side of a logic level shifter. Aux1 on the RAMPS board
  39. * has Serial1 and 12v, making it perfect for this.
  40. * Copyright (c) 2017 Jason Nelson (xC0000005)
  41. */
  42. #include "../../../inc/MarlinConfigPre.h"
  43. #if ENABLED(MALYAN_LCD)
  44. //#define DEBUG_MALYAN_LCD
  45. #include "malyan.h"
  46. #include "../ui_api.h"
  47. #include "../../marlinui.h"
  48. #include "../../../sd/cardreader.h"
  49. #include "../../../module/temperature.h"
  50. #include "../../../module/stepper.h"
  51. #include "../../../module/motion.h"
  52. #include "../../../libs/duration_t.h"
  53. #include "../../../module/printcounter.h"
  54. #include "../../../gcode/queue.h"
  55. #define DEBUG_OUT ENABLED(DEBUG_MALYAN_LCD)
  56. #include "../../../core/debug_out.h"
  57. // This is based on longest sys command + a filename, plus some buffer
  58. // in case we encounter some data we don't recognize
  59. // There is no evidence a line will ever be this long, but better safe than sorry
  60. #define MAX_CURLY_COMMAND (32 + LONG_FILENAME_LENGTH) * 2
  61. // Track incoming command bytes from the LCD
  62. uint16_t inbound_count;
  63. // For sending print completion messages
  64. bool last_printing_status = false;
  65. // Everything written needs the high bit set.
  66. void write_to_lcd(FSTR_P const fmsg) {
  67. PGM_P pmsg = FTOP(fmsg);
  68. char encoded_message[MAX_CURLY_COMMAND];
  69. uint8_t message_length = _MIN(strlen_P(pmsg), sizeof(encoded_message));
  70. LOOP_L_N(i, message_length)
  71. encoded_message[i] = pgm_read_byte(&pmsg[i]) | 0x80;
  72. LCD_SERIAL.Print::write(encoded_message, message_length);
  73. }
  74. void write_to_lcd(const char * const cmsg) {
  75. char encoded_message[MAX_CURLY_COMMAND];
  76. const uint8_t message_length = _MIN(strlen(cmsg), sizeof(encoded_message));
  77. LOOP_L_N(i, message_length)
  78. encoded_message[i] = cmsg[i] | 0x80;
  79. LCD_SERIAL.Print::write(encoded_message, message_length);
  80. }
  81. // {E:<msg>} is for error states.
  82. void set_lcd_error(FSTR_P const error, FSTR_P const component/*=nullptr*/) {
  83. write_to_lcd(F("{E:"));
  84. write_to_lcd(error);
  85. if (component) {
  86. write_to_lcd(F(" "));
  87. write_to_lcd(component);
  88. }
  89. write_to_lcd(F("}"));
  90. }
  91. /**
  92. * Process an LCD 'C' command.
  93. * These are currently all temperature commands
  94. * {C:T0190}
  95. * Set temp for hotend to 190
  96. * {C:P050}
  97. * Set temp for bed to 50
  98. *
  99. * {C:S09} set feedrate to 90 %.
  100. * {C:S12} set feedrate to 120 %.
  101. *
  102. * the command portion begins after the :
  103. */
  104. void process_lcd_c_command(const char *command) {
  105. const int target_val = command[1] ? atoi(command + 1) : -1;
  106. if (target_val < 0) {
  107. DEBUG_ECHOLNPGM("UNKNOWN C COMMAND ", command);
  108. return;
  109. }
  110. switch (command[0]) {
  111. case 'C': // Cope with both V1 early rev and later LCDs.
  112. case 'S':
  113. feedrate_percentage = target_val * 10;
  114. LIMIT(feedrate_percentage, 10, 999);
  115. break;
  116. case 'T':
  117. // Sometimes the LCD will send commands to turn off both extruder and bed, though
  118. // this should not happen since the printing screen is up. Better safe than sorry.
  119. if (!print_job_timer.isRunning() || target_val > 0)
  120. ExtUI::setTargetTemp_celsius(target_val, ExtUI::extruder_t::E0);
  121. break;
  122. #if HAS_HEATED_BED
  123. case 'P': ExtUI::setTargetTemp_celsius(target_val, ExtUI::heater_t::BED); break;
  124. #endif
  125. default: DEBUG_ECHOLNPGM("UNKNOWN C COMMAND ", command);
  126. }
  127. }
  128. /**
  129. * Process an LCD 'B' command.
  130. * {B:0} results in: {T0:008/195}{T1:000/000}{TP:000/000}{TQ:000C}{TT:000000}
  131. * T0/T1 are hot end temperatures, TP is bed, TQ is percent, and TT is probably
  132. * time remaining (HH:MM:SS). The UI can't handle displaying a second hotend,
  133. * but the stock firmware always sends it, and it's always zero.
  134. */
  135. void process_lcd_eb_command(const char *command) {
  136. char elapsed_buffer[10];
  137. static uint8_t iteration = 0;
  138. duration_t elapsed;
  139. switch (command[0]) {
  140. case '0': {
  141. elapsed = print_job_timer.duration();
  142. sprintf_P(elapsed_buffer, PSTR("%02u%02u%02u"), uint16_t(elapsed.hour()), uint16_t(elapsed.minute()) % 60, uint16_t(elapsed.second()) % 60);
  143. char message_buffer[MAX_CURLY_COMMAND];
  144. uint8_t done_pct = print_job_timer.isRunning() ? (iteration * 10) : 100;
  145. iteration = (iteration + 1) % 10; // Provide progress animation
  146. #if ENABLED(SDSUPPORT)
  147. if (ExtUI::isPrintingFromMedia() || ExtUI::isPrintingFromMediaPaused())
  148. done_pct = card.percentDone();
  149. #endif
  150. sprintf_P(message_buffer,
  151. PSTR("{T0:%03i/%03i}{T1:000/000}{TP:%03i/%03i}{TQ:%03i}{TT:%s}"),
  152. thermalManager.wholeDegHotend(0), thermalManager.degTargetHotend(0),
  153. #if HAS_HEATED_BED
  154. thermalManager.wholeDegBed(), thermalManager.degTargetBed(),
  155. #else
  156. 0, 0,
  157. #endif
  158. TERN(SDSUPPORT, done_pct, 0),
  159. elapsed_buffer
  160. );
  161. write_to_lcd(message_buffer);
  162. } break;
  163. default: DEBUG_ECHOLNPGM("UNKNOWN E/B COMMAND ", command);
  164. }
  165. }
  166. /**
  167. * Process an LCD 'J' command.
  168. * These are currently all movement commands.
  169. * The command portion begins after the :
  170. * Move X Axis
  171. *
  172. * {J:E}{J:X-200}{J:E}
  173. * {J:E}{J:X+200}{J:E}
  174. * X, Y, Z, A (extruder)
  175. */
  176. template<typename T>
  177. void j_move_axis(const char *command, const T axis) {
  178. const float dist = atof(command + 1) / 10.0;
  179. ExtUI::setAxisPosition_mm(ExtUI::getAxisPosition_mm(axis) + dist, axis);
  180. };
  181. void process_lcd_j_command(const char *command) {
  182. switch (command[0]) {
  183. case 'E': break;
  184. case 'A': j_move_axis<ExtUI::extruder_t>(command, ExtUI::extruder_t::E0); break;
  185. case 'Y': j_move_axis<ExtUI::axis_t>(command, ExtUI::axis_t::Y); break;
  186. case 'Z': j_move_axis<ExtUI::axis_t>(command, ExtUI::axis_t::Z); break;
  187. case 'X': j_move_axis<ExtUI::axis_t>(command, ExtUI::axis_t::X); break;
  188. default: DEBUG_ECHOLNPGM("UNKNOWN J COMMAND ", command);
  189. }
  190. }
  191. /**
  192. * Process an LCD 'P' command, related to homing and printing.
  193. * Cancel:
  194. * {P:X}
  195. *
  196. * Home all axes:
  197. * {P:H}
  198. *
  199. * Print a file:
  200. * {P:000}
  201. * The File number is specified as a three digit value.
  202. * Printer responds with:
  203. * {PRINTFILE:Mini_SNES_Bottom.gcode}
  204. * {SYS:BUILD}echo:Now fresh file: Mini_SNES_Bottom.gcode
  205. * File opened: Mini_SNES_Bottom.gcode Size: 5805813
  206. * File selected
  207. * {SYS:BUILD}
  208. * T:-2526.8 E:0
  209. * T:-2533.0 E:0
  210. * T:-2537.4 E:0
  211. * Note only the curly brace stuff matters.
  212. */
  213. void process_lcd_p_command(const char *command) {
  214. switch (command[0]) {
  215. case 'P':
  216. ExtUI::pausePrint();
  217. write_to_lcd(F("{SYS:PAUSED}"));
  218. break;
  219. case 'R':
  220. ExtUI::resumePrint();
  221. write_to_lcd(F("{SYS:RESUMED}"));
  222. break;
  223. case 'X':
  224. write_to_lcd(F("{SYS:CANCELING}"));
  225. ExtUI::stopPrint();
  226. write_to_lcd(F("{SYS:STARTED}"));
  227. break;
  228. case 'H': queue.enqueue_now_P(G28_STR); break; // Home all axes
  229. default: {
  230. #if ENABLED(SDSUPPORT)
  231. // Print file 000 - a three digit number indicating which
  232. // file to print in the SD card. If it's a directory,
  233. // then switch to the directory.
  234. // Find the name of the file to print.
  235. // It's needed to echo the PRINTFILE option.
  236. // The {S:L} command should've ensured the SD card was mounted.
  237. card.selectFileByIndex(atoi(command));
  238. // There may be a difference in how V1 and V2 LCDs handle subdirectory
  239. // prints. Investigate more. This matches the V1 motion controller actions
  240. // but the V2 LCD switches to "print" mode on {SYS:DIR} response.
  241. if (card.flag.filenameIsDir) {
  242. card.cd(card.filename);
  243. write_to_lcd(F("{SYS:DIR}"));
  244. }
  245. else {
  246. char message_buffer[MAX_CURLY_COMMAND];
  247. sprintf_P(message_buffer, PSTR("{PRINTFILE:%s}"), card.longest_filename());
  248. write_to_lcd(message_buffer);
  249. write_to_lcd(F("{SYS:BUILD}"));
  250. card.openAndPrintFile(card.filename);
  251. }
  252. #endif
  253. } break; // default
  254. } // switch
  255. }
  256. /**
  257. * Handle an lcd 'S' command
  258. * {S:I} - Temperature request
  259. * {T0:999/000}{T1:000/000}{TP:004/000}
  260. *
  261. * {S:L} - File Listing request
  262. * Printer Response:
  263. * {FILE:buttons.gcode}
  264. * {FILE:update.bin}
  265. * {FILE:nupdate.bin}
  266. * {FILE:fcupdate.flg}
  267. * {SYS:OK}
  268. */
  269. void process_lcd_s_command(const char *command) {
  270. switch (command[0]) {
  271. case 'I': {
  272. // temperature information
  273. char message_buffer[MAX_CURLY_COMMAND];
  274. sprintf_P(message_buffer, PSTR("{T0:%03i/%03i}{T1:000/000}{TP:%03i/%03i}"),
  275. thermalManager.wholeDegHotend(0), thermalManager.degTargetHotend(0),
  276. #if HAS_HEATED_BED
  277. thermalManager.wholeDegBed(), thermalManager.degTargetBed()
  278. #else
  279. 0, 0
  280. #endif
  281. );
  282. write_to_lcd(message_buffer);
  283. } break;
  284. case 'L': {
  285. #if ENABLED(SDSUPPORT)
  286. if (!card.isMounted()) card.mount();
  287. // A more efficient way to do this would be to
  288. // implement a callback in the ls_SerialPrint code, but
  289. // that requires changes to the core cardreader class that
  290. // would not benefit the majority of users. Since one can't
  291. // select a file for printing during a print, there's
  292. // little reason not to do it this way.
  293. char message_buffer[MAX_CURLY_COMMAND];
  294. uint16_t file_count = card.get_num_Files();
  295. for (uint16_t i = 0; i < file_count; i++) {
  296. card.selectFileByIndex(i);
  297. sprintf_P(message_buffer, card.flag.filenameIsDir ? PSTR("{DIR:%s}") : PSTR("{FILE:%s}"), card.longest_filename());
  298. write_to_lcd(message_buffer);
  299. }
  300. write_to_lcd(F("{SYS:OK}"));
  301. #endif
  302. } break;
  303. default: DEBUG_ECHOLNPGM("UNKNOWN S COMMAND ", command);
  304. }
  305. }
  306. /**
  307. * Receive a curly brace command and translate to G-code.
  308. * Currently {E:0} is not handled. Its function is unknown,
  309. * but it occurs during the temp window after a sys build.
  310. */
  311. void process_lcd_command(const char *command) {
  312. const char *current = command;
  313. byte command_code = *current++;
  314. if (*current == ':') {
  315. current++; // skip the :
  316. switch (command_code) {
  317. case 'S': process_lcd_s_command(current); break;
  318. case 'J': process_lcd_j_command(current); break;
  319. case 'P': process_lcd_p_command(current); break;
  320. case 'C': process_lcd_c_command(current); break;
  321. case 'B':
  322. case 'E': process_lcd_eb_command(current); break;
  323. default: DEBUG_ECHOLNPGM("UNKNOWN COMMAND ", command);
  324. }
  325. }
  326. else
  327. DEBUG_ECHOLNPGM("UNKNOWN COMMAND FORMAT ", command);
  328. }
  329. //
  330. // Parse LCD commands mixed with G-Code
  331. //
  332. void parse_lcd_byte(const byte b) {
  333. static char inbound_buffer[MAX_CURLY_COMMAND];
  334. static uint8_t parsing = 0; // Parsing state
  335. static bool prevcr = false; // Was the last c a CR?
  336. const char c = b & 0x7F;
  337. if (parsing) {
  338. const bool is_lcd = parsing == 1; // 1 for LCD
  339. if ( ( is_lcd && c == '}') // Closing brace on LCD command
  340. || (!is_lcd && c == '\n') // LF on a G-code command
  341. ) {
  342. inbound_buffer[inbound_count] = '\0'; // Reset before processing
  343. inbound_count = 0; // Reset buffer index
  344. if (parsing == 1)
  345. process_lcd_command(inbound_buffer); // Handle the LCD command
  346. else
  347. queue.enqueue_one_now(inbound_buffer); // Handle the G-code command
  348. parsing = 0; // Unflag and...
  349. }
  350. else if (inbound_count < MAX_CURLY_COMMAND - 2)
  351. inbound_buffer[inbound_count++] = is_lcd ? c : b; // Buffer while space remains
  352. }
  353. else {
  354. if (c == '{') parsing = 1; // Brace opens an LCD command
  355. else if (prevcr && c == '\n') parsing = 2; // CRLF indicates G-code
  356. prevcr = (c == '\r'); // Remember if it was a CR
  357. }
  358. }
  359. /**
  360. * UC means connected.
  361. * UD means disconnected
  362. * The stock firmware considers USB initialized as "connected."
  363. */
  364. void update_usb_status(const bool forceUpdate) {
  365. static bool last_usb_connected_status = false;
  366. // This is mildly different than stock, which
  367. // appears to use the usb discovery status.
  368. // This is more logical.
  369. if (last_usb_connected_status != MYSERIAL1.connected() || forceUpdate) {
  370. last_usb_connected_status = MYSERIAL1.connected();
  371. write_to_lcd(last_usb_connected_status ? F("{R:UC}\r\n") : F("{R:UD}\r\n"));
  372. }
  373. }
  374. #endif // MALYAN_LCD