My Marlin configs for Fabrikator Mini and CTC i3 Pro B
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ui_api.cpp 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726
  1. /**
  2. * Marlin 3D Printer Firmware
  3. * Copyright (C) 2016 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 <http://www.gnu.org/licenses/>.
  20. *
  21. */
  22. /**************
  23. * ui_api.cpp *
  24. **************/
  25. /****************************************************************************
  26. * Written By Marcio Teixeira 2018 - Aleph Objects, Inc. *
  27. * *
  28. * This program is free software: you can redistribute it and/or modify *
  29. * it under the terms of the GNU General Public License as published by *
  30. * the Free Software Foundation, either version 3 of the License, or *
  31. * (at your option) any later version. *
  32. * *
  33. * This program is distributed in the hope that it will be useful, *
  34. * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  35. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
  36. * GNU General Public License for more details. *
  37. * *
  38. * To view a copy of the GNU General Public License, go to the following *
  39. * location: <http://www.gnu.org/licenses/>. *
  40. ****************************************************************************/
  41. #include "../../inc/MarlinConfigPre.h"
  42. #if ENABLED(EXTENSIBLE_UI)
  43. #include "../ultralcd.h"
  44. #include "../../gcode/queue.h"
  45. #include "../../module/motion.h"
  46. #include "../../module/planner.h"
  47. #include "../../module/probe.h"
  48. #include "../../module/temperature.h"
  49. #include "../../libs/duration_t.h"
  50. #include "../../HAL/shared/Delay.h"
  51. #if DO_SWITCH_EXTRUDER || ENABLED(SWITCHING_NOZZLE) || ENABLED(PARKING_EXTRUDER)
  52. #include "../../module/tool_change.h"
  53. #endif
  54. #if ENABLED(SDSUPPORT)
  55. #include "../../sd/cardreader.h"
  56. #include "../../feature/emergency_parser.h"
  57. #define IFSD(A,B) (A)
  58. #else
  59. #define IFSD(A,B) (B)
  60. #endif
  61. #if ENABLED(PRINTCOUNTER)
  62. #include "../../core/utility.h"
  63. #include "../../module/printcounter.h"
  64. #endif
  65. #include "ui_api.h"
  66. #if ENABLED(BACKLASH_GCODE)
  67. extern float backlash_distance_mm[XYZ], backlash_correction;
  68. #ifdef BACKLASH_SMOOTHING_MM
  69. extern float backlash_smoothing_mm;
  70. #endif
  71. #endif
  72. #if ENABLED(FILAMENT_RUNOUT_SENSOR)
  73. #include "../../feature/runout.h"
  74. #endif
  75. inline float clamp(const float value, const float minimum, const float maximum) {
  76. return MAX(MIN(value, maximum), minimum);
  77. }
  78. static struct {
  79. uint8_t printer_killed : 1;
  80. uint8_t manual_motion : 1;
  81. } flags;
  82. namespace ExtUI {
  83. #ifdef __SAM3X8E__
  84. /**
  85. * Implement a special millis() to allow time measurement
  86. * within an ISR (such as when the printer is killed).
  87. *
  88. * To keep proper time, must be called at least every 1s.
  89. */
  90. uint32_t safe_millis() {
  91. // Not killed? Just call millis()
  92. if (!flags.printer_killed) return millis();
  93. static uint32_t currTimeHI = 0; /* Current time */
  94. // Machine was killed, reinit SysTick so we are able to compute time without ISRs
  95. if (currTimeHI == 0) {
  96. // Get the last time the Arduino time computed (from CMSIS) and convert it to SysTick
  97. currTimeHI = (uint32_t)((GetTickCount() * (uint64_t)(F_CPU / 8000)) >> 24);
  98. // Reinit the SysTick timer to maximize its period
  99. SysTick->LOAD = SysTick_LOAD_RELOAD_Msk; // get the full range for the systick timer
  100. SysTick->VAL = 0; // Load the SysTick Counter Value
  101. SysTick->CTRL = // MCLK/8 as source
  102. // No interrupts
  103. SysTick_CTRL_ENABLE_Msk; // Enable SysTick Timer
  104. }
  105. // Check if there was a timer overflow from the last read
  106. if (SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) {
  107. // There was. This means (SysTick_LOAD_RELOAD_Msk * 1000 * 8)/F_CPU ms has elapsed
  108. currTimeHI++;
  109. }
  110. // Calculate current time in milliseconds
  111. uint32_t currTimeLO = SysTick_LOAD_RELOAD_Msk - SysTick->VAL; // (in MCLK/8)
  112. uint64_t currTime = ((uint64_t)currTimeLO) | (((uint64_t)currTimeHI) << 24);
  113. // The ms count is
  114. return (uint32_t)(currTime / (F_CPU / 8000));
  115. }
  116. #endif // __SAM3X8E__
  117. void delay_us(unsigned long us) {
  118. DELAY_US(us);
  119. }
  120. void delay_ms(unsigned long ms) {
  121. if (flags.printer_killed)
  122. DELAY_US(ms * 1000);
  123. else
  124. safe_delay(ms);
  125. }
  126. void yield() {
  127. if (!flags.printer_killed)
  128. thermalManager.manage_heater();
  129. }
  130. float getActualTemp_celsius(const heater_t heater) {
  131. return heater == BED ?
  132. #if HAS_HEATED_BED
  133. thermalManager.degBed()
  134. #else
  135. 0
  136. #endif
  137. : thermalManager.degHotend(heater - H0);
  138. }
  139. float getActualTemp_celsius(const extruder_t extruder) {
  140. return thermalManager.degHotend(extruder - E0);
  141. }
  142. float getTargetTemp_celsius(const heater_t heater) {
  143. return heater == BED ?
  144. #if HAS_HEATED_BED
  145. thermalManager.degTargetBed()
  146. #else
  147. 0
  148. #endif
  149. : thermalManager.degTargetHotend(heater - H0);
  150. }
  151. float getTargetTemp_celsius(const extruder_t extruder) {
  152. return thermalManager.degTargetHotend(extruder - E0);
  153. }
  154. float getFan_percent(const fan_t fan) { return ((float(fan_speed[fan - FAN0]) + 1) * 100) / 256; }
  155. float getAxisPosition_mm(const axis_t axis) {
  156. return flags.manual_motion ? destination[axis] : current_position[axis];
  157. }
  158. float getAxisPosition_mm(const extruder_t extruder) {
  159. return flags.manual_motion ? destination[E_AXIS] : current_position[E_AXIS];
  160. }
  161. void setAxisPosition_mm(const float position, const axis_t axis) {
  162. // Start with no limits to movement
  163. float min = current_position[axis] - 1000,
  164. max = current_position[axis] + 1000;
  165. // Limit to software endstops, if enabled
  166. #if HAS_SOFTWARE_ENDSTOPS
  167. if (soft_endstops_enabled) switch (axis) {
  168. case X_AXIS:
  169. #if ENABLED(MIN_SOFTWARE_ENDSTOP_X)
  170. min = soft_endstop_min[X_AXIS];
  171. #endif
  172. #if ENABLED(MAX_SOFTWARE_ENDSTOP_X)
  173. max = soft_endstop_max[X_AXIS];
  174. #endif
  175. break;
  176. case Y_AXIS:
  177. #if ENABLED(MIN_SOFTWARE_ENDSTOP_Y)
  178. min = soft_endstop_min[Y_AXIS];
  179. #endif
  180. #if ENABLED(MAX_SOFTWARE_ENDSTOP_Y)
  181. max = soft_endstop_max[Y_AXIS];
  182. #endif
  183. break;
  184. case Z_AXIS:
  185. #if ENABLED(MIN_SOFTWARE_ENDSTOP_Z)
  186. min = soft_endstop_min[Z_AXIS];
  187. #endif
  188. #if ENABLED(MAX_SOFTWARE_ENDSTOP_Z)
  189. max = soft_endstop_max[Z_AXIS];
  190. #endif
  191. default: break;
  192. }
  193. #endif // HAS_SOFTWARE_ENDSTOPS
  194. // Delta limits XY based on the current offset from center
  195. // This assumes the center is 0,0
  196. #if ENABLED(DELTA)
  197. if (axis != Z_AXIS) {
  198. max = SQRT(sq((float)(DELTA_PRINTABLE_RADIUS)) - sq(current_position[Y_AXIS - axis])); // (Y_AXIS - axis) == the other axis
  199. min = -max;
  200. }
  201. #endif
  202. if (!flags.manual_motion)
  203. set_destination_from_current();
  204. destination[axis] = clamp(position, min, max);
  205. flags.manual_motion = true;
  206. }
  207. void setAxisPosition_mm(const float position, const extruder_t extruder) {
  208. setActiveTool(extruder, true);
  209. if (!flags.manual_motion)
  210. set_destination_from_current();
  211. destination[E_AXIS] = position;
  212. flags.manual_motion = true;
  213. }
  214. void _processManualMoveToDestination() {
  215. // Lower max_response_lag makes controls more responsive, but makes CPU work harder
  216. constexpr float max_response_lag = 0.1; // seconds
  217. constexpr uint8_t segments_to_buffer = 4; // keep planner filled with this many segments
  218. if (flags.manual_motion && planner.movesplanned() < segments_to_buffer) {
  219. float saved_destination[XYZ];
  220. COPY(saved_destination, destination);
  221. // Compute direction vector from current_position towards destination.
  222. destination[X_AXIS] -= current_position[X_AXIS];
  223. destination[Y_AXIS] -= current_position[Y_AXIS];
  224. destination[Z_AXIS] -= current_position[Z_AXIS];
  225. const float inv_length = RSQRT(sq(destination[X_AXIS]) + sq(destination[Y_AXIS]) + sq(destination[Z_AXIS]));
  226. // Find move segment length so that all segments can execute in less time than max_response_lag
  227. const float scale = inv_length * feedrate_mm_s * max_response_lag / segments_to_buffer;
  228. if (scale < 1) {
  229. // Move a small bit towards the destination.
  230. destination[X_AXIS] = scale * destination[X_AXIS] + current_position[X_AXIS];
  231. destination[Y_AXIS] = scale * destination[Y_AXIS] + current_position[Y_AXIS];
  232. destination[Z_AXIS] = scale * destination[Z_AXIS] + current_position[Z_AXIS];
  233. prepare_move_to_destination();
  234. COPY(destination, saved_destination);
  235. }
  236. else {
  237. // We are close enough to finish off the move.
  238. COPY(destination, saved_destination);
  239. prepare_move_to_destination();
  240. flags.manual_motion = false;
  241. }
  242. }
  243. }
  244. void setActiveTool(const extruder_t extruder, bool no_move) {
  245. #if EXTRUDERS > 1
  246. const uint8_t e = extruder - E0;
  247. #if DO_SWITCH_EXTRUDER || ENABLED(SWITCHING_NOZZLE) || ENABLED(PARKING_EXTRUDER)
  248. if (e != active_extruder)
  249. tool_change(e, 0, no_move);
  250. #endif
  251. active_extruder = e;
  252. #endif
  253. }
  254. extruder_t getActiveTool() {
  255. switch (active_extruder) {
  256. case 5: return E5;
  257. case 4: return E4;
  258. case 3: return E3;
  259. case 2: return E2;
  260. case 1: return E1;
  261. default: return E0;
  262. }
  263. }
  264. bool isMoving() { return planner.has_blocks_queued(); }
  265. bool canMove(const axis_t axis) {
  266. switch (axis) {
  267. #if IS_KINEMATIC || ENABLED(NO_MOTION_BEFORE_HOMING)
  268. case X: return TEST(axis_homed, X_AXIS);
  269. case Y: return TEST(axis_homed, Y_AXIS);
  270. case Z: return TEST(axis_homed, Z_AXIS);
  271. #else
  272. case X: case Y: case Z: return true;
  273. #endif
  274. default: return false;
  275. }
  276. }
  277. bool canMove(const extruder_t extruder) {
  278. return !thermalManager.tooColdToExtrude(extruder - E0);
  279. }
  280. float getAxisSteps_per_mm(const axis_t axis) {
  281. return planner.settings.axis_steps_per_mm[axis];
  282. }
  283. float getAxisSteps_per_mm(const extruder_t extruder) {
  284. return planner.settings.axis_steps_per_mm[E_AXIS_N(extruder - E0)];
  285. }
  286. void setAxisSteps_per_mm(const float value, const axis_t axis) {
  287. planner.settings.axis_steps_per_mm[axis] = value;
  288. }
  289. void setAxisSteps_per_mm(const float value, const extruder_t extruder) {
  290. planner.settings.axis_steps_per_mm[E_AXIS_N(axis - E0)] = value;
  291. }
  292. float getAxisMaxFeedrate_mm_s(const axis_t axis) {
  293. return planner.settings.max_feedrate_mm_s[axis];
  294. }
  295. float getAxisMaxFeedrate_mm_s(const extruder_t extruder) {
  296. return planner.settings.max_feedrate_mm_s[E_AXIS_N(axis - E0)];
  297. }
  298. void setAxisMaxFeedrate_mm_s(const float value, const axis_t axis) {
  299. planner.settings.max_feedrate_mm_s[axis] = value;
  300. }
  301. void setAxisMaxFeedrate_mm_s(const float value, const extruder_t extruder) {
  302. planner.settings.max_feedrate_mm_s[E_AXIS_N(axis - E0)] = value;
  303. }
  304. float getAxisMaxAcceleration_mm_s2(const axis_t axis) {
  305. return planner.settings.max_acceleration_mm_per_s2[axis];
  306. }
  307. float getAxisMaxAcceleration_mm_s2(const extruder_t extruder) {
  308. return planner.settings.max_acceleration_mm_per_s2[E_AXIS_N(extruder - E0)];
  309. }
  310. void setAxisMaxAcceleration_mm_s2(const float value, const axis_t axis) {
  311. planner.settings.max_acceleration_mm_per_s2[axis] = value;
  312. }
  313. void setAxisMaxAcceleration_mm_s2(const float value, const extruder_t extruder) {
  314. planner.settings.max_acceleration_mm_per_s2[E_AXIS_N(extruder - E0)] = value;
  315. }
  316. #if ENABLED(FILAMENT_RUNOUT_SENSOR)
  317. bool getFilamentRunoutEnabled() { return runout.enabled; }
  318. void setFilamentRunoutEnabled(const bool value) { runout.enabled = value; }
  319. #if FILAMENT_RUNOUT_DISTANCE_MM > 0
  320. float getFilamentRunoutDistance_mm() {
  321. return RunoutResponseDelayed::runout_distance_mm;
  322. }
  323. void setFilamentRunoutDistance_mm(const float value) {
  324. RunoutResponseDelayed::runout_distance_mm = clamp(value, 0, 999);
  325. }
  326. #endif
  327. #endif
  328. #if ENABLED(LIN_ADVANCE)
  329. float getLinearAdvance_mm_mm_s(const extruder_t extruder) {
  330. return (extruder < EXTRUDERS) ? planner.extruder_advance_K[extruder - E0] : 0;
  331. }
  332. void setLinearAdvance_mm_mm_s(const float value, const extruder_t extruder) {
  333. if (extruder < EXTRUDERS)
  334. planner.extruder_advance_K[extruder - E0] = clamp(value, 0, 999);
  335. }
  336. #endif
  337. #if ENABLED(JUNCTION_DEVIATION)
  338. float getJunctionDeviation_mm() {
  339. return planner.junction_deviation_mm;
  340. }
  341. void setJunctionDeviation_mm(const float value) {
  342. planner.junction_deviation_mm = clamp(value, 0.01, 0.3);
  343. planner.recalculate_max_e_jerk();
  344. }
  345. #else
  346. float getAxisMaxJerk_mm_s(const axis_t axis) {
  347. return planner.max_jerk[axis];
  348. }
  349. float getAxisMaxJerk_mm_s(const extruder_t extruder) {
  350. return planner.max_jerk[E_AXIS];
  351. }
  352. void setAxisMaxJerk_mm_s(const float value, const axis_t axis) {
  353. planner.max_jerk[axis] = value;
  354. }
  355. void setAxisMaxJerk_mm_s(const float value, const extruder_t extruder) {
  356. planner.max_jerk[E_AXIS] = value;
  357. }
  358. #endif
  359. float getFeedrate_mm_s() { return feedrate_mm_s; }
  360. float getMinFeedrate_mm_s() { return planner.settings.min_feedrate_mm_s; }
  361. float getMinTravelFeedrate_mm_s() { return planner.settings.min_travel_feedrate_mm_s; }
  362. float getPrintingAcceleration_mm_s2() { return planner.settings.acceleration; }
  363. float getRetractAcceleration_mm_s2() { return planner.settings.retract_acceleration; }
  364. float getTravelAcceleration_mm_s2() { return planner.settings.travel_acceleration; }
  365. void setFeedrate_mm_s(const float fr) { feedrate_mm_s = fr; }
  366. void setMinFeedrate_mm_s(const float fr) { planner.settings.min_feedrate_mm_s = fr; }
  367. void setMinTravelFeedrate_mm_s(const float fr) { planner.settings.min_travel_feedrate_mm_s = fr; }
  368. void setPrintingAcceleration_mm_s2(const float acc) { planner.settings.acceleration = acc; }
  369. void setRetractAcceleration_mm_s2(const float acc) { planner.settings.retract_acceleration = acc; }
  370. void setTravelAcceleration_mm_s2(const float acc) { planner.settings.travel_acceleration = acc; }
  371. #if ENABLED(BABYSTEP_ZPROBE_OFFSET)
  372. float getZOffset_mm() {
  373. #if ENABLED(BABYSTEP_HOTEND_Z_OFFSET)
  374. if (active_extruder != 0)
  375. return hotend_offset[Z_AXIS][active_extruder];
  376. else
  377. #endif
  378. return zprobe_zoffset;
  379. }
  380. void setZOffset_mm(const float value) {
  381. const float diff = (value - getZOffset_mm()) / planner.steps_to_mm[Z_AXIS];
  382. addZOffset_steps(diff > 0 ? ceil(diff) : floor(diff));
  383. }
  384. void addZOffset_steps(int16_t babystep_increment) {
  385. #if ENABLED(BABYSTEP_HOTEND_Z_OFFSET)
  386. const bool do_probe = (active_extruder == 0);
  387. #else
  388. constexpr bool do_probe = true;
  389. #endif
  390. const float diff = planner.steps_to_mm[Z_AXIS] * babystep_increment,
  391. new_probe_offset = zprobe_zoffset + diff,
  392. new_offs =
  393. #if ENABLED(BABYSTEP_HOTEND_Z_OFFSET)
  394. do_probe ? new_probe_offset : hotend_offset[Z_AXIS][active_extruder] - diff
  395. #else
  396. new_probe_offset
  397. #endif
  398. ;
  399. if (WITHIN(new_offs, Z_PROBE_OFFSET_RANGE_MIN, Z_PROBE_OFFSET_RANGE_MAX)) {
  400. thermalManager.babystep_axis(Z_AXIS, babystep_increment);
  401. if (do_probe) zprobe_zoffset = new_offs;
  402. #if ENABLED(BABYSTEP_HOTEND_Z_OFFSET)
  403. else hotend_offset[Z_AXIS][active_extruder] = new_offs;
  404. #endif
  405. }
  406. }
  407. #endif // ENABLED(BABYSTEP_ZPROBE_OFFSET)
  408. #if HOTENDS > 1
  409. float getNozzleOffset_mm(const axis_t axis, const extruder_t extruder) {
  410. if (extruder - E0 >= HOTENDS) return 0;
  411. return hotend_offset[axis][extruder - E0];
  412. }
  413. void setNozzleOffset_mm(const float value, const axis_t axis, const extruder_t extruder) {
  414. if (extruder - E0 >= HOTENDS) return;
  415. hotend_offset[axis][extruder - E0] = value;
  416. }
  417. #endif
  418. #if ENABLED(BACKLASH_GCODE)
  419. float getAxisBacklash_mm(const axis_t axis) { return backlash_distance_mm[axis]; }
  420. void setAxisBacklash_mm(const float value, const axis_t axis)
  421. { backlash_distance_mm[axis] = clamp(value,0,5); }
  422. float getBacklashCorrection_percent() { return backlash_correction * 100; }
  423. void setBacklashCorrection_percent(const float value) { backlash_correction = clamp(value, 0, 100) / 100.0f; }
  424. #ifdef BACKLASH_SMOOTHING_MM
  425. float getBacklashSmoothing_mm() { return backlash_smoothing_mm; }
  426. void setBacklashSmoothing_mm(const float value) { backlash_smoothing_mm = clamp(value, 0, 999); }
  427. #endif
  428. #endif
  429. uint8_t getProgress_percent() {
  430. return IFSD(card.percentDone(), 0);
  431. }
  432. uint32_t getProgress_seconds_elapsed() {
  433. const duration_t elapsed = print_job_timer.duration();
  434. return elapsed.value;
  435. }
  436. #if ENABLED(PRINTCOUNTER)
  437. char* getTotalPrints_str(char buffer[21]) { strcpy(buffer,itostr3left(print_job_timer.getStats().totalPrints)); return buffer; }
  438. char* getFinishedPrints_str(char buffer[21]) { strcpy(buffer,itostr3left(print_job_timer.getStats().finishedPrints)); return buffer; }
  439. char* getTotalPrintTime_str(char buffer[21]) { duration_t(print_job_timer.getStats().printTime).toString(buffer); return buffer; }
  440. char* getLongestPrint_str(char buffer[21]) { duration_t(print_job_timer.getStats().printTime).toString(buffer); return buffer; }
  441. char* getFilamentUsed_str(char buffer[21]) {
  442. printStatistics stats = print_job_timer.getStats();
  443. sprintf_P(buffer, PSTR("%ld.%im"), long(stats.filamentUsed / 1000), int16_t(stats.filamentUsed / 100) % 10);
  444. return buffer;
  445. }
  446. #endif
  447. float getFeedrate_percent() { return feedrate_percentage; }
  448. void enqueueCommands_P(PGM_P const gcode) {
  449. enqueue_and_echo_commands_P(gcode);
  450. }
  451. bool isAxisPositionKnown(const axis_t axis) {
  452. return TEST(axis_known_position, axis);
  453. }
  454. PGM_P getFirmwareName_str() {
  455. static const char firmware_name[] PROGMEM = "Marlin " SHORT_BUILD_VERSION;
  456. return firmware_name;
  457. }
  458. void setTargetTemp_celsius(float value, const heater_t heater) {
  459. #if HAS_HEATED_BED
  460. if (heater == BED)
  461. thermalManager.setTargetBed(clamp(value,0,200));
  462. else
  463. #endif
  464. thermalManager.setTargetHotend(clamp(value,0,500), heater - H0);
  465. }
  466. void setTargetTemp_celsius(float value, const extruder_t extruder) {
  467. thermalManager.setTargetHotend(clamp(value,0,500), extruder - E0);
  468. }
  469. void setFan_percent(float value, const fan_t fan) {
  470. if (fan < FAN_COUNT)
  471. fan_speed[fan - FAN0] = clamp(round(value * 255 / 100), 0, 255);
  472. }
  473. void setFeedrate_percent(const float value) {
  474. feedrate_percentage = clamp(value, 10, 500);
  475. }
  476. void printFile(const char *filename) {
  477. IFSD(card.openAndPrintFile(filename), NOOP);
  478. }
  479. bool isPrintingFromMediaPaused() {
  480. return IFSD(isPrintingFromMedia() && !IS_SD_PRINTING(), false);
  481. }
  482. bool isPrintingFromMedia() {
  483. return IFSD(card.flag.cardOK && card.isFileOpen(), false);
  484. }
  485. bool isPrinting() {
  486. return (planner.movesplanned() || isPrintingFromMedia() || IFSD(IS_SD_PRINTING(), false));
  487. }
  488. bool isMediaInserted() {
  489. return IFSD(IS_SD_INSERTED() && card.flag.cardOK, false);
  490. }
  491. void pausePrint() {
  492. #if ENABLED(SDSUPPORT)
  493. card.pauseSDPrint();
  494. print_job_timer.pause();
  495. #if ENABLED(PARK_HEAD_ON_PAUSE)
  496. enqueue_and_echo_commands_P(PSTR("M125"));
  497. #endif
  498. ui.set_status_P(PSTR(MSG_PRINT_PAUSED));
  499. #endif
  500. }
  501. void resumePrint() {
  502. #if ENABLED(SDSUPPORT)
  503. ui.set_status_P(PSTR(MSG_FILAMENT_CHANGE_RESUME_1));
  504. #if ENABLED(PARK_HEAD_ON_PAUSE)
  505. wait_for_heatup = wait_for_user = false;
  506. enqueue_and_echo_commands_P(PSTR("M24"));
  507. #else
  508. card.startFileprint();
  509. print_job_timer.start();
  510. #endif
  511. #endif
  512. }
  513. void stopPrint() {
  514. #if ENABLED(SDSUPPORT)
  515. wait_for_heatup = wait_for_user = false;
  516. card.flag.abort_sd_printing = true;
  517. ui.set_status_P(PSTR(MSG_PRINT_ABORTED));
  518. #endif
  519. }
  520. FileList::FileList() { refresh(); }
  521. void FileList::refresh() { num_files = 0xFFFF; }
  522. bool FileList::seek(const uint16_t pos, const bool skip_range_check) {
  523. #if ENABLED(SDSUPPORT)
  524. if (!skip_range_check && pos > (count() - 1)) return false;
  525. const uint16_t nr =
  526. #if ENABLED(SDCARD_RATHERRECENTFIRST) && DISABLED(SDCARD_SORT_ALPHA)
  527. count() - 1 -
  528. #endif
  529. pos;
  530. card.getfilename_sorted(nr);
  531. return card.filename && card.filename[0] != '\0';
  532. #else
  533. return false;
  534. #endif
  535. }
  536. const char* FileList::filename() {
  537. return IFSD(card.longFilename && card.longFilename[0] ? card.longFilename : card.filename, "");
  538. }
  539. const char* FileList::shortFilename() {
  540. return IFSD(card.filename, "");
  541. }
  542. const char* FileList::longFilename() {
  543. return IFSD(card.longFilename, "");
  544. }
  545. bool FileList::isDir() {
  546. return IFSD(card.flag.filenameIsDir, false);
  547. }
  548. uint16_t FileList::count() {
  549. return IFSD((num_files = (num_files == 0xFFFF ? card.get_num_Files() : num_files)), 0);
  550. }
  551. bool FileList::isAtRootDir() {
  552. #if ENABLED(SDSUPPORT)
  553. card.getWorkDirName();
  554. return card.filename[0] == '/';
  555. #else
  556. return true;
  557. #endif
  558. }
  559. void FileList::upDir() {
  560. #if ENABLED(SDSUPPORT)
  561. card.updir();
  562. num_files = 0xFFFF;
  563. #endif
  564. }
  565. void FileList::changeDir(const char * const dirname) {
  566. #if ENABLED(SDSUPPORT)
  567. card.chdir(dirname);
  568. num_files = 0xFFFF;
  569. #endif
  570. }
  571. } // namespace ExtUI
  572. // At the moment, we piggy-back off the ultralcd calls, but this could be cleaned up in the future
  573. void MarlinUI::init() {
  574. #if ENABLED(SDSUPPORT) && PIN_EXISTS(SD_DETECT)
  575. SET_INPUT_PULLUP(SD_DETECT_PIN);
  576. #endif
  577. ExtUI::onStartup();
  578. }
  579. void MarlinUI::update() {
  580. #if ENABLED(SDSUPPORT)
  581. static bool last_sd_status;
  582. const bool sd_status = IS_SD_INSERTED();
  583. if (sd_status != last_sd_status) {
  584. last_sd_status = sd_status;
  585. if (sd_status) {
  586. card.initsd();
  587. if (card.flag.cardOK)
  588. ExtUI::onMediaInserted();
  589. else
  590. ExtUI::onMediaError();
  591. }
  592. else {
  593. const bool ok = card.flag.cardOK;
  594. card.release();
  595. if (ok) ExtUI::onMediaRemoved();
  596. }
  597. }
  598. #endif // SDSUPPORT
  599. ExtUI::_processManualMoveToDestination();
  600. ExtUI::onIdle();
  601. }
  602. void MarlinUI::kill_screen(PGM_P const msg) {
  603. if (!flags.printer_killed) {
  604. flags.printer_killed = true;
  605. ExtUI::onPrinterKilled(msg);
  606. }
  607. }
  608. #endif // EXTENSIBLE_UI