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 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759
  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 "../../gcode/queue.h"
  44. #include "../../module/motion.h"
  45. #include "../../module/planner.h"
  46. #include "../../module/probe.h"
  47. #include "../../module/temperature.h"
  48. #include "../../libs/duration_t.h"
  49. #include "../../HAL/shared/Delay.h"
  50. #if DO_SWITCH_EXTRUDER || ENABLED(SWITCHING_NOZZLE) || ENABLED(PARKING_EXTRUDER)
  51. #include "../../module/tool_change.h"
  52. #endif
  53. #if ENABLED(SDSUPPORT)
  54. #include "../../sd/cardreader.h"
  55. #include "../../feature/emergency_parser.h"
  56. #define IFSD(A,B) (A)
  57. #else
  58. #define IFSD(A,B) (B)
  59. #endif
  60. #if ENABLED(PRINTCOUNTER)
  61. #include "../../core/utility.h"
  62. #include "../../module/printcounter.h"
  63. #endif
  64. #include "ui_api.h"
  65. #if ENABLED(BACKLASH_GCODE)
  66. extern float backlash_distance_mm[XYZ], backlash_correction;
  67. #ifdef BACKLASH_SMOOTHING_MM
  68. extern float backlash_smoothing_mm;
  69. #endif
  70. #endif
  71. #if ENABLED(FILAMENT_RUNOUT_SENSOR)
  72. #include "../../feature/runout.h"
  73. #endif
  74. inline float clamp(const float value, const float minimum, const float maximum) {
  75. return MAX(MIN(value, maximum), minimum);
  76. }
  77. static struct {
  78. uint8_t printer_killed : 1;
  79. uint8_t manual_motion : 1;
  80. } flags;
  81. namespace UI {
  82. #ifdef __SAM3X8E__
  83. /**
  84. * Implement a special millis() to allow time measurement
  85. * within an ISR (such as when the printer is killed).
  86. *
  87. * To keep proper time, must be called at least every 1s.
  88. */
  89. uint32_t safe_millis() {
  90. // Not killed? Just call millis()
  91. if (!flags.printer_killed) return millis();
  92. static uint32_t currTimeHI = 0; /* Current time */
  93. // Machine was killed, reinit SysTick so we are able to compute time without ISRs
  94. if (currTimeHI == 0) {
  95. // Get the last time the Arduino time computed (from CMSIS) and convert it to SysTick
  96. currTimeHI = (uint32_t)((GetTickCount() * (uint64_t)(F_CPU / 8000)) >> 24);
  97. // Reinit the SysTick timer to maximize its period
  98. SysTick->LOAD = SysTick_LOAD_RELOAD_Msk; // get the full range for the systick timer
  99. SysTick->VAL = 0; // Load the SysTick Counter Value
  100. SysTick->CTRL = // MCLK/8 as source
  101. // No interrupts
  102. SysTick_CTRL_ENABLE_Msk; // Enable SysTick Timer
  103. }
  104. // Check if there was a timer overflow from the last read
  105. if (SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) {
  106. // There was. This means (SysTick_LOAD_RELOAD_Msk * 1000 * 8)/F_CPU ms has elapsed
  107. currTimeHI++;
  108. }
  109. // Calculate current time in milliseconds
  110. uint32_t currTimeLO = SysTick_LOAD_RELOAD_Msk - SysTick->VAL; // (in MCLK/8)
  111. uint64_t currTime = ((uint64_t)currTimeLO) | (((uint64_t)currTimeHI) << 24);
  112. // The ms count is
  113. return (uint32_t)(currTime / (F_CPU / 8000));
  114. }
  115. #else
  116. // TODO: Implement for AVR
  117. FORCE_INLINE uint32_t safe_millis() { return millis(); }
  118. #endif
  119. void delay_us(unsigned long us) {
  120. DELAY_US(us);
  121. }
  122. void delay_ms(unsigned long ms) {
  123. if (flags.printer_killed)
  124. DELAY_US(ms * 1000);
  125. else
  126. safe_delay(ms);
  127. }
  128. void yield() {
  129. if (!flags.printer_killed)
  130. thermalManager.manage_heater();
  131. }
  132. float getActualTemp_celsius(const heater_t heater) {
  133. return heater == BED ?
  134. #if HAS_HEATED_BED
  135. thermalManager.degBed()
  136. #else
  137. 0
  138. #endif
  139. : thermalManager.degHotend(heater - H0);
  140. }
  141. float getActualTemp_celsius(const extruder_t extruder) {
  142. return thermalManager.degHotend(extruder - E0);
  143. }
  144. float getTargetTemp_celsius(const heater_t heater) {
  145. return heater == BED ?
  146. #if HAS_HEATED_BED
  147. thermalManager.degTargetBed()
  148. #else
  149. 0
  150. #endif
  151. : thermalManager.degTargetHotend(heater - H0);
  152. }
  153. float getTargetTemp_celsius(const extruder_t extruder) {
  154. return thermalManager.degTargetHotend(extruder - E0);
  155. }
  156. float getFan_percent(const fan_t fan) { return ((float(fan_speed[fan - FAN0]) + 1) * 100) / 256; }
  157. float getAxisPosition_mm(const axis_t axis) {
  158. return flags.manual_motion ? destination[axis] : current_position[axis];
  159. }
  160. float getAxisPosition_mm(const extruder_t extruder) {
  161. return flags.manual_motion ? destination[E_AXIS] : current_position[E_AXIS];
  162. }
  163. void setAxisPosition_mm(const float position, const axis_t axis) {
  164. // Start with no limits to movement
  165. float min = current_position[axis] - 1000,
  166. max = current_position[axis] + 1000;
  167. // Limit to software endstops, if enabled
  168. #if HAS_SOFTWARE_ENDSTOPS
  169. if (soft_endstops_enabled) switch (axis) {
  170. case X_AXIS:
  171. #if ENABLED(MIN_SOFTWARE_ENDSTOP_X)
  172. min = soft_endstop_min[X_AXIS];
  173. #endif
  174. #if ENABLED(MAX_SOFTWARE_ENDSTOP_X)
  175. max = soft_endstop_max[X_AXIS];
  176. #endif
  177. break;
  178. case Y_AXIS:
  179. #if ENABLED(MIN_SOFTWARE_ENDSTOP_Y)
  180. min = soft_endstop_min[Y_AXIS];
  181. #endif
  182. #if ENABLED(MAX_SOFTWARE_ENDSTOP_Y)
  183. max = soft_endstop_max[Y_AXIS];
  184. #endif
  185. break;
  186. case Z_AXIS:
  187. #if ENABLED(MIN_SOFTWARE_ENDSTOP_Z)
  188. min = soft_endstop_min[Z_AXIS];
  189. #endif
  190. #if ENABLED(MAX_SOFTWARE_ENDSTOP_Z)
  191. max = soft_endstop_max[Z_AXIS];
  192. #endif
  193. default: break;
  194. }
  195. #endif // HAS_SOFTWARE_ENDSTOPS
  196. // Delta limits XY based on the current offset from center
  197. // This assumes the center is 0,0
  198. #if ENABLED(DELTA)
  199. if (axis != Z_AXIS) {
  200. max = SQRT(sq((float)(DELTA_PRINTABLE_RADIUS)) - sq(current_position[Y_AXIS - axis])); // (Y_AXIS - axis) == the other axis
  201. min = -max;
  202. }
  203. #endif
  204. if (!flags.manual_motion)
  205. set_destination_from_current();
  206. destination[axis] = clamp(position, min, max);
  207. flags.manual_motion = true;
  208. }
  209. void setAxisPosition_mm(const float position, const extruder_t extruder) {
  210. setActiveTool(extruder, true);
  211. if (!flags.manual_motion)
  212. set_destination_from_current();
  213. destination[E_AXIS] = position;
  214. flags.manual_motion = true;
  215. }
  216. void _processManualMoveToDestination() {
  217. // Lower max_response_lag makes controls more responsive, but makes CPU work harder
  218. constexpr float max_response_lag = 0.1; // seconds
  219. constexpr uint8_t segments_to_buffer = 4; // keep planner filled with this many segments
  220. if (flags.manual_motion && planner.movesplanned() < segments_to_buffer) {
  221. float saved_destination[XYZ];
  222. COPY(saved_destination, destination);
  223. // Compute direction vector from current_position towards destination.
  224. destination[X_AXIS] -= current_position[X_AXIS];
  225. destination[Y_AXIS] -= current_position[Y_AXIS];
  226. destination[Z_AXIS] -= current_position[Z_AXIS];
  227. const float inv_length = RSQRT(sq(destination[X_AXIS]) + sq(destination[Y_AXIS]) + sq(destination[Z_AXIS]));
  228. // Find move segment length so that all segments can execute in less time than max_response_lag
  229. const float scale = inv_length * feedrate_mm_s * max_response_lag / segments_to_buffer;
  230. if (scale < 1) {
  231. // Move a small bit towards the destination.
  232. destination[X_AXIS] = scale * destination[X_AXIS] + current_position[X_AXIS];
  233. destination[Y_AXIS] = scale * destination[Y_AXIS] + current_position[Y_AXIS];
  234. destination[Z_AXIS] = scale * destination[Z_AXIS] + current_position[Z_AXIS];
  235. prepare_move_to_destination();
  236. COPY(destination, saved_destination);
  237. }
  238. else {
  239. // We are close enough to finish off the move.
  240. COPY(destination, saved_destination);
  241. prepare_move_to_destination();
  242. flags.manual_motion = false;
  243. }
  244. }
  245. }
  246. void setActiveTool(const extruder_t extruder, bool no_move) {
  247. const uint8_t e = extruder - E0;
  248. #if DO_SWITCH_EXTRUDER || ENABLED(SWITCHING_NOZZLE) || ENABLED(PARKING_EXTRUDER)
  249. if (e != active_extruder)
  250. tool_change(e, 0, no_move);
  251. #endif
  252. active_extruder = e;
  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(progmem_str gcode) {
  449. enqueue_and_echo_commands_P((PGM_P)gcode);
  450. }
  451. bool isAxisPositionKnown(const axis_t axis) {
  452. return TEST(axis_known_position, axis);
  453. }
  454. progmem_str getFirmwareName_str() {
  455. return F("Marlin " SHORT_BUILD_VERSION);
  456. }
  457. void setTargetTemp_celsius(float value, const heater_t heater) {
  458. #if HAS_HEATED_BED
  459. if (heater == BED)
  460. thermalManager.setTargetBed(clamp(value,0,200));
  461. #endif
  462. thermalManager.setTargetHotend(clamp(value,0,500), heater - H0);
  463. }
  464. void setTargetTemp_celsius(float value, const extruder_t extruder) {
  465. thermalManager.setTargetHotend(clamp(value,0,500), extruder - E0);
  466. }
  467. void setFan_percent(float value, const fan_t fan) {
  468. if (fan < FAN_COUNT)
  469. fan_speed[fan - FAN0] = clamp(round(value * 255 / 100), 0, 255);
  470. }
  471. void setFeedrate_percent(const float value) {
  472. feedrate_percentage = clamp(value, 10, 500);
  473. }
  474. void printFile(const char *filename) {
  475. IFSD(card.openAndPrintFile(filename), NOOP);
  476. }
  477. bool isPrintingFromMediaPaused() {
  478. return IFSD(isPrintingFromMedia() && !card.sdprinting, false);
  479. }
  480. bool isPrintingFromMedia() {
  481. return IFSD(card.cardOK && card.isFileOpen(), false);
  482. }
  483. bool isPrinting() {
  484. return (planner.movesplanned() || IS_SD_PRINTING() || isPrintingFromMedia());
  485. }
  486. bool isMediaInserted() {
  487. return IFSD(IS_SD_INSERTED() && card.cardOK, false);
  488. }
  489. void pausePrint() {
  490. #if ENABLED(SDSUPPORT)
  491. card.pauseSDPrint();
  492. print_job_timer.pause();
  493. #if ENABLED(PARK_HEAD_ON_PAUSE)
  494. enqueue_and_echo_commands_P(PSTR("M125"));
  495. #endif
  496. UI::onStatusChanged(PSTR(MSG_PRINT_PAUSED));
  497. #endif
  498. }
  499. void resumePrint() {
  500. #if ENABLED(SDSUPPORT)
  501. #if ENABLED(PARK_HEAD_ON_PAUSE)
  502. enqueue_and_echo_commands_P(PSTR("M24"));
  503. #else
  504. card.startFileprint();
  505. print_job_timer.start();
  506. #endif
  507. UI::onStatusChanged(PSTR(MSG_PRINTING));
  508. #endif
  509. }
  510. void stopPrint() {
  511. #if ENABLED(SDSUPPORT)
  512. wait_for_heatup = wait_for_user = false;
  513. card.abort_sd_printing = true;
  514. UI::onStatusChanged(PSTR(MSG_PRINT_ABORTED));
  515. #endif
  516. }
  517. FileList::FileList() { refresh(); }
  518. void FileList::refresh() { num_files = 0xFFFF; }
  519. bool FileList::seek(uint16_t pos, bool skip_range_check) {
  520. #if ENABLED(SDSUPPORT)
  521. if (!skip_range_check && pos > (count() - 1)) return false;
  522. const uint16_t nr =
  523. #if ENABLED(SDCARD_RATHERRECENTFIRST) && DISABLED(SDCARD_SORT_ALPHA)
  524. count() - 1 -
  525. #endif
  526. pos;
  527. card.getfilename_sorted(nr);
  528. return card.filename && card.filename[0] != '\0';
  529. #endif
  530. }
  531. const char* FileList::filename() {
  532. return IFSD(card.longFilename && card.longFilename[0] ? card.longFilename : card.filename, "");
  533. }
  534. const char* FileList::shortFilename() {
  535. return IFSD(card.filename, "");
  536. }
  537. const char* FileList::longFilename() {
  538. return IFSD(card.longFilename, "");
  539. }
  540. bool FileList::isDir() {
  541. return IFSD(card.filenameIsDir, false);
  542. }
  543. uint16_t FileList::count() {
  544. return IFSD((num_files = (num_files == 0xFFFF ? card.get_num_Files() : num_files)), 0);
  545. }
  546. bool FileList::isAtRootDir() {
  547. #if ENABLED(SDSUPPORT)
  548. card.getWorkDirName();
  549. return card.filename[0] == '/';
  550. #else
  551. return true;
  552. #endif
  553. }
  554. void FileList::upDir() {
  555. #if ENABLED(SDSUPPORT)
  556. card.updir();
  557. num_files = 0xFFFF;
  558. #endif
  559. }
  560. void FileList::changeDir(const char *dirname) {
  561. #if ENABLED(SDSUPPORT)
  562. card.chdir(dirname);
  563. num_files = 0xFFFF;
  564. #endif
  565. }
  566. } // namespace UI
  567. // At the moment, we piggy-back off the ultralcd calls, but this could be cleaned up in the future
  568. void lcd_init() {
  569. #if ENABLED(SDSUPPORT) && PIN_EXISTS(SD_DETECT)
  570. SET_INPUT_PULLUP(SD_DETECT_PIN);
  571. #endif
  572. UI::onStartup();
  573. }
  574. void lcd_update() {
  575. #if ENABLED(SDSUPPORT)
  576. static bool last_sd_status;
  577. const bool sd_status = IS_SD_INSERTED();
  578. if (sd_status != last_sd_status) {
  579. last_sd_status = sd_status;
  580. if (sd_status) {
  581. card.initsd();
  582. if (card.cardOK)
  583. UI::onMediaInserted();
  584. else
  585. UI::onMediaError();
  586. }
  587. else {
  588. const bool ok = card.cardOK;
  589. card.release();
  590. if (ok) UI::onMediaRemoved();
  591. }
  592. }
  593. #endif // SDSUPPORT
  594. UI::_processManualMoveToDestination();
  595. UI::onIdle();
  596. }
  597. bool lcd_hasstatus() { return true; }
  598. bool lcd_detected() { return true; }
  599. void lcd_reset_alert_level() { }
  600. void lcd_refresh() { }
  601. void lcd_setstatus(const char * const message, const bool persist /* = false */) { UI::onStatusChanged(message); }
  602. void lcd_setstatusPGM(const char * const message, int8_t level /* = 0 */) { UI::onStatusChanged((progmem_str)message); }
  603. void lcd_setalertstatusPGM(const char * const message) { lcd_setstatusPGM(message, 0); }
  604. void lcd_reset_status() {
  605. static const char paused[] PROGMEM = MSG_PRINT_PAUSED;
  606. static const char printing[] PROGMEM = MSG_PRINTING;
  607. static const char welcome[] PROGMEM = WELCOME_MSG;
  608. PGM_P msg;
  609. if (print_job_timer.isPaused())
  610. msg = paused;
  611. #if ENABLED(SDSUPPORT)
  612. else if (card.sdprinting)
  613. return lcd_setstatus(card.longest_filename(), true);
  614. #endif
  615. else if (print_job_timer.isRunning())
  616. msg = printing;
  617. else
  618. msg = welcome;
  619. lcd_setstatusPGM(msg, -1);
  620. }
  621. void lcd_status_printf_P(const uint8_t level, const char * const fmt, ...) {
  622. char buff[64];
  623. va_list args;
  624. va_start(args, fmt);
  625. vsnprintf_P(buff, sizeof(buff), fmt, args);
  626. va_end(args);
  627. buff[63] = '\0';
  628. UI::onStatusChanged(buff);
  629. }
  630. void kill_screen(PGM_P msg) {
  631. if (!flags.printer_killed) {
  632. flags.printer_killed = true;
  633. UI::onPrinterKilled(msg);
  634. }
  635. }
  636. #endif // EXTENSIBLE_UI