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

DGUSDisplay.cpp 45KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348
  1. /**
  2. * Marlin 3D Printer Firmware
  3. * Copyright (c) 2019 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. /* DGUS implementation written by coldtobi in 2019 for Marlin */
  23. #include "../../../../inc/MarlinConfigPre.h"
  24. #if HAS_DGUS_LCD
  25. #if HOTENDS > 2
  26. #error "More than 2 hotends not implemented on the Display UI design."
  27. #endif
  28. #include "DGUSDisplay.h"
  29. #include "DGUSVPVariable.h"
  30. #include "DGUSDisplayDefinition.h"
  31. #include "../../ui_api.h"
  32. #include "../../../../MarlinCore.h"
  33. #include "../../../../module/temperature.h"
  34. #include "../../../../module/motion.h"
  35. #include "../../../../gcode/queue.h"
  36. #include "../../../../module/planner.h"
  37. #include "../../../../sd/cardreader.h"
  38. #include "../../../../libs/duration_t.h"
  39. #include "../../../../module/printcounter.h"
  40. #if ENABLED(POWER_LOSS_RECOVERY)
  41. #include "../../../../feature/power_loss_recovery.h"
  42. #endif
  43. // Preamble... 2 Bytes, usually 0x5A 0xA5, but configurable
  44. constexpr uint8_t DGUS_HEADER1 = 0x5A;
  45. constexpr uint8_t DGUS_HEADER2 = 0xA5;
  46. constexpr uint8_t DGUS_CMD_WRITEVAR = 0x82;
  47. constexpr uint8_t DGUS_CMD_READVAR = 0x83;
  48. #if ENABLED(DEBUG_DGUSLCD)
  49. bool dguslcd_local_debug; // = false;
  50. #endif
  51. #if ENABLED(DGUS_FILAMENT_LOADUNLOAD)
  52. typedef struct {
  53. ExtUI::extruder_t extruder; // which extruder to operate
  54. uint8_t action; // load or unload
  55. bool heated; // heating done ?
  56. float purge_length; // the length to extrude before unload, prevent filament jam
  57. } filament_data_t;
  58. static filament_data_t filament_data;
  59. #endif
  60. uint16_t DGUSScreenVariableHandler::ConfirmVP;
  61. #if ENABLED(SDSUPPORT)
  62. int16_t DGUSScreenVariableHandler::top_file = 0;
  63. int16_t DGUSScreenVariableHandler::file_to_print = 0;
  64. static ExtUI::FileList filelist;
  65. #endif
  66. void (*DGUSScreenVariableHandler::confirm_action_cb)() = nullptr;
  67. //DGUSScreenVariableHandler ScreenHandler;
  68. DGUSLCD_Screens DGUSScreenVariableHandler::current_screen;
  69. DGUSLCD_Screens DGUSScreenVariableHandler::past_screens[NUM_PAST_SCREENS];
  70. uint8_t DGUSScreenVariableHandler::update_ptr;
  71. uint16_t DGUSScreenVariableHandler::skipVP;
  72. bool DGUSScreenVariableHandler::ScreenComplete;
  73. //DGUSDisplay dgusdisplay;
  74. rx_datagram_state_t DGUSDisplay::rx_datagram_state = DGUS_IDLE;
  75. uint8_t DGUSDisplay::rx_datagram_len = 0;
  76. bool DGUSDisplay::Initialized = false;
  77. bool DGUSDisplay::no_reentrance = false;
  78. #define dgusserial DGUS_SERIAL
  79. // endianness swap
  80. uint16_t swap16(const uint16_t value) { return (value & 0xffU) << 8U | (value >> 8U); }
  81. bool populate_VPVar(const uint16_t VP, DGUS_VP_Variable * const ramcopy) {
  82. // DEBUG_ECHOPAIR("populate_VPVar ", VP);
  83. const DGUS_VP_Variable *pvp = DGUSLCD_FindVPVar(VP);
  84. // DEBUG_ECHOLNPAIR(" pvp ", (uint16_t )pvp);
  85. if (!pvp) return false;
  86. memcpy_P(ramcopy, pvp, sizeof(DGUS_VP_Variable));
  87. return true;
  88. }
  89. void DGUSScreenVariableHandler::sendinfoscreen(const char* line1, const char* line2, const char* line3, const char* line4, bool l1inflash, bool l2inflash, bool l3inflash, bool l4inflash) {
  90. DGUS_VP_Variable ramcopy;
  91. if (populate_VPVar(VP_MSGSTR1, &ramcopy)) {
  92. ramcopy.memadr = (void*) line1;
  93. l1inflash ? DGUSScreenVariableHandler::DGUSLCD_SendStringToDisplayPGM(ramcopy) : DGUSScreenVariableHandler::DGUSLCD_SendStringToDisplay(ramcopy);
  94. }
  95. if (populate_VPVar(VP_MSGSTR2, &ramcopy)) {
  96. ramcopy.memadr = (void*) line2;
  97. l2inflash ? DGUSScreenVariableHandler::DGUSLCD_SendStringToDisplayPGM(ramcopy) : DGUSScreenVariableHandler::DGUSLCD_SendStringToDisplay(ramcopy);
  98. }
  99. if (populate_VPVar(VP_MSGSTR3, &ramcopy)) {
  100. ramcopy.memadr = (void*) line3;
  101. l3inflash ? DGUSScreenVariableHandler::DGUSLCD_SendStringToDisplayPGM(ramcopy) : DGUSScreenVariableHandler::DGUSLCD_SendStringToDisplay(ramcopy);
  102. }
  103. if (populate_VPVar(VP_MSGSTR4, &ramcopy)) {
  104. ramcopy.memadr = (void*) line4;
  105. l4inflash ? DGUSScreenVariableHandler::DGUSLCD_SendStringToDisplayPGM(ramcopy) : DGUSScreenVariableHandler::DGUSLCD_SendStringToDisplay(ramcopy);
  106. }
  107. }
  108. void DGUSScreenVariableHandler::HandleUserConfirmationPopUp(uint16_t VP, const char* line1, const char* line2, const char* line3, const char* line4, bool l1, bool l2, bool l3, bool l4) {
  109. if (current_screen == DGUSLCD_SCREEN_CONFIRM) {
  110. // Already showing a pop up, so we need to cancel that first.
  111. PopToOldScreen();
  112. }
  113. ConfirmVP = VP;
  114. sendinfoscreen(line1, line2, line3, line4, l1, l2, l3, l4);
  115. ScreenHandler.GotoScreen(DGUSLCD_SCREEN_CONFIRM);
  116. }
  117. void DGUSScreenVariableHandler::setstatusmessage(const char *msg) {
  118. DGUS_VP_Variable ramcopy;
  119. if (populate_VPVar(VP_M117, &ramcopy)) {
  120. ramcopy.memadr = (void*) msg;
  121. DGUSLCD_SendStringToDisplay(ramcopy);
  122. }
  123. }
  124. void DGUSScreenVariableHandler::setstatusmessagePGM(PGM_P const msg) {
  125. DGUS_VP_Variable ramcopy;
  126. if (populate_VPVar(VP_M117, &ramcopy)) {
  127. ramcopy.memadr = (void*) msg;
  128. DGUSLCD_SendStringToDisplayPGM(ramcopy);
  129. }
  130. }
  131. // Send an 8 bit or 16 bit value to the display.
  132. void DGUSScreenVariableHandler::DGUSLCD_SendWordValueToDisplay(DGUS_VP_Variable &var) {
  133. if (var.memadr) {
  134. //DEBUG_ECHOPAIR(" DGUS_LCD_SendWordValueToDisplay ", var.VP);
  135. //DEBUG_ECHOLNPAIR(" data ", *(uint16_t *)var.memadr);
  136. uint8_t *tmp = (uint8_t *) var.memadr;
  137. uint16_t data_to_send = (tmp[0] << 8);
  138. if (var.size >= 1) data_to_send |= tmp[1];
  139. dgusdisplay.WriteVariable(var.VP, data_to_send);
  140. }
  141. }
  142. // Send an uint8_t between 0 and 255 to the display, but scale to a percentage (0..100)
  143. void DGUSScreenVariableHandler::DGUSLCD_SendPercentageToDisplay(DGUS_VP_Variable &var) {
  144. if (var.memadr) {
  145. //DEBUG_ECHOPAIR(" DGUS_LCD_SendWordValueToDisplay ", var.VP);
  146. //DEBUG_ECHOLNPAIR(" data ", *(uint16_t *)var.memadr);
  147. uint16_t tmp = *(uint8_t *) var.memadr +1 ; // +1 -> avoid rounding issues for the display.
  148. tmp = map(tmp, 0, 255, 0, 100);
  149. uint16_t data_to_send = swap16(tmp);
  150. dgusdisplay.WriteVariable(var.VP, data_to_send);
  151. }
  152. }
  153. // Send the current print time to the display.
  154. // It is using a hex display for that: It expects BSD coded data in the format xxyyzz
  155. void DGUSScreenVariableHandler::DGUSLCD_SendPrintTimeToDisplay(DGUS_VP_Variable &var) {
  156. duration_t elapsed = print_job_timer.duration();
  157. char buf[32];
  158. elapsed.toString(buf);
  159. dgusdisplay.WriteVariable(VP_PrintTime, buf, var.size, true);
  160. }
  161. // Send an uint8_t between 0 and 100 to a variable scale to 0..255
  162. void DGUSScreenVariableHandler::DGUSLCD_PercentageToUint8(DGUS_VP_Variable &var, void *val_ptr) {
  163. if (var.memadr) {
  164. uint16_t value = swap16(*(uint16_t*)val_ptr);
  165. *(uint8_t*)var.memadr = map(constrain(value, 0, 100), 0, 100, 0, 255);
  166. }
  167. }
  168. // Sends a (RAM located) string to the DGUS Display
  169. // (Note: The DGUS Display does not clear after the \0, you have to
  170. // overwrite the remainings with spaces.// var.size has the display buffer size!
  171. void DGUSScreenVariableHandler::DGUSLCD_SendStringToDisplay(DGUS_VP_Variable &var) {
  172. char *tmp = (char*) var.memadr;
  173. dgusdisplay.WriteVariable(var.VP, tmp, var.size, true);
  174. }
  175. // Sends a (flash located) string to the DGUS Display
  176. // (Note: The DGUS Display does not clear after the \0, you have to
  177. // overwrite the remainings with spaces.// var.size has the display buffer size!
  178. void DGUSScreenVariableHandler::DGUSLCD_SendStringToDisplayPGM(DGUS_VP_Variable &var) {
  179. char *tmp = (char*) var.memadr;
  180. dgusdisplay.WriteVariablePGM(var.VP, tmp, var.size, true);
  181. }
  182. #if HAS_PID_HEATING
  183. void DGUSScreenVariableHandler::DGUSLCD_SendTemperaturePID(DGUS_VP_Variable &var) {
  184. float value = *(float *)var.memadr;
  185. float valuesend = 0;
  186. switch (var.VP) {
  187. default: return;
  188. #if HOTENDS >= 1
  189. case VP_E0_PID_P: valuesend = value; break;
  190. case VP_E0_PID_I: valuesend = unscalePID_i(value); break;
  191. case VP_E0_PID_D: valuesend = unscalePID_d(value); break;
  192. #endif
  193. #if HOTENDS >= 2
  194. case VP_E1_PID_P: valuesend = value; break;
  195. case VP_E1_PID_I: valuesend = unscalePID_i(value); break;
  196. case VP_E1_PID_D: valuesend = unscalePID_d(value); break;
  197. #endif
  198. #if HAS_HEATED_BED
  199. case VP_BED_PID_P: valuesend = value; break;
  200. case VP_BED_PID_I: valuesend = unscalePID_i(value); break;
  201. case VP_BED_PID_D: valuesend = unscalePID_d(value); break;
  202. #endif
  203. }
  204. valuesend *= cpow(10, 1);
  205. union { int16_t i; char lb[2]; } endian;
  206. char tmp[2];
  207. endian.i = valuesend;
  208. tmp[0] = endian.lb[1];
  209. tmp[1] = endian.lb[0];
  210. dgusdisplay.WriteVariable(var.VP, tmp, 2);
  211. }
  212. #endif
  213. #if ENABLED(PRINTCOUNTER)
  214. // Send the accumulate print time to the display.
  215. // It is using a hex display for that: It expects BSD coded data in the format xxyyzz
  216. void DGUSScreenVariableHandler::DGUSLCD_SendPrintAccTimeToDisplay(DGUS_VP_Variable &var) {
  217. printStatistics state = print_job_timer.getStats();
  218. char buf[21];
  219. duration_t elapsed = state.printTime;
  220. elapsed.toString(buf);
  221. dgusdisplay.WriteVariable(VP_PrintAccTime, buf, var.size, true);
  222. }
  223. void DGUSScreenVariableHandler::DGUSLCD_SendPrintsTotalToDisplay(DGUS_VP_Variable &var) {
  224. printStatistics state = print_job_timer.getStats();
  225. char buf[21];
  226. sprintf_P(buf, PSTR("%u"), state.totalPrints);
  227. dgusdisplay.WriteVariable(VP_PrintsTotal, buf, var.size, true);
  228. }
  229. #endif
  230. // Send fan status value to the display.
  231. #if FAN_COUNT > 0
  232. void DGUSScreenVariableHandler::DGUSLCD_SendFanStatusToDisplay(DGUS_VP_Variable &var) {
  233. if (var.memadr) {
  234. DEBUG_ECHOPAIR(" DGUSLCD_SendFanStatusToDisplay ", var.VP);
  235. DEBUG_ECHOLNPAIR(" data ", *(uint8_t *)var.memadr);
  236. uint16_t data_to_send = 0;
  237. if (*(uint8_t *) var.memadr) data_to_send = 1;
  238. data_to_send = swap16(data_to_send);
  239. dgusdisplay.WriteVariable(var.VP, data_to_send);
  240. }
  241. }
  242. #endif
  243. // Send heater status value to the display.
  244. void DGUSScreenVariableHandler::DGUSLCD_SendHeaterStatusToDisplay(DGUS_VP_Variable &var) {
  245. if (var.memadr) {
  246. DEBUG_ECHOPAIR(" DGUSLCD_SendHeaterStatusToDisplay ", var.VP);
  247. DEBUG_ECHOLNPAIR(" data ", *(int16_t *)var.memadr);
  248. uint16_t data_to_send = 0;
  249. if (*(int16_t *) var.memadr) data_to_send = 1;
  250. data_to_send = swap16(data_to_send);
  251. dgusdisplay.WriteVariable(var.VP, data_to_send);
  252. }
  253. }
  254. #if ENABLED(DGUS_UI_WAITING)
  255. void DGUSScreenVariableHandler::DGUSLCD_SendWaitingStatusToDisplay(DGUS_VP_Variable &var) {
  256. // In FYSETC UI design there are 10 statuses to loop
  257. static uint16_t period = 0;
  258. static uint16_t index = 0;
  259. //DEBUG_ECHOPAIR(" DGUSLCD_SendWaitingStatusToDisplay ", var.VP);
  260. //DEBUG_ECHOLNPAIR(" data ", swap16(index));
  261. if (period++ > DGUS_UI_WAITING_STATUS_PERIOD) {
  262. dgusdisplay.WriteVariable(var.VP, swap16(index));
  263. //DEBUG_ECHOLNPAIR(" data ", swap16(index));
  264. if (++index >= DGUS_UI_WAITING_STATUS) index = 0;
  265. period = 0;
  266. }
  267. }
  268. #endif
  269. #if ENABLED(SDSUPPORT)
  270. void DGUSScreenVariableHandler::ScreenChangeHookIfSD(DGUS_VP_Variable &var, void *val_ptr) {
  271. // default action executed when there is a SD card, but not printing
  272. if (ExtUI::isMediaInserted() && !ExtUI::isPrintingFromMedia()) {
  273. ScreenChangeHook(var, val_ptr);
  274. dgusdisplay.RequestScreen(current_screen);
  275. return;
  276. }
  277. // if we are printing, we jump to two screens after the requested one.
  278. // This should host e.g a print pause / print abort / print resume dialog.
  279. // This concept allows to recycle this hook for other file
  280. if (ExtUI::isPrintingFromMedia() && !card.flag.abort_sd_printing) {
  281. GotoScreen(DGUSLCD_SCREEN_SDPRINTMANIPULATION);
  282. return;
  283. }
  284. // Don't let the user in the dark why there is no reaction.
  285. if (!ExtUI::isMediaInserted()) {
  286. setstatusmessagePGM(GET_TEXT(MSG_NO_MEDIA));
  287. return;
  288. }
  289. if (card.flag.abort_sd_printing) {
  290. setstatusmessagePGM(GET_TEXT(MSG_MEDIA_ABORTING));
  291. return;
  292. }
  293. }
  294. void DGUSScreenVariableHandler::DGUSLCD_SD_ScrollFilelist(DGUS_VP_Variable& var, void *val_ptr) {
  295. auto old_top = top_file;
  296. const int16_t scroll = (int16_t)swap16(*(uint16_t*)val_ptr);
  297. if (scroll) {
  298. top_file += scroll;
  299. DEBUG_ECHOPAIR("new topfile calculated:", top_file);
  300. if (top_file < 0) {
  301. top_file = 0;
  302. DEBUG_ECHOLNPGM("Top of filelist reached");
  303. }
  304. else {
  305. int16_t max_top = filelist.count() - DGUS_SD_FILESPERSCREEN;
  306. NOLESS(max_top, 0);
  307. NOMORE(top_file, max_top);
  308. }
  309. DEBUG_ECHOPAIR("new topfile adjusted:", top_file);
  310. }
  311. else if (!filelist.isAtRootDir()) {
  312. filelist.upDir();
  313. top_file = 0;
  314. ForceCompleteUpdate();
  315. }
  316. if (old_top != top_file) ForceCompleteUpdate();
  317. }
  318. void DGUSScreenVariableHandler::DGUSLCD_SD_FileSelected(DGUS_VP_Variable &var, void *val_ptr) {
  319. uint16_t touched_nr = (int16_t)swap16(*(uint16_t*)val_ptr) + top_file;
  320. if (touched_nr > filelist.count()) return;
  321. if (!filelist.seek(touched_nr)) return;
  322. if (filelist.isDir()) {
  323. filelist.changeDir(filelist.filename());
  324. top_file = 0;
  325. ForceCompleteUpdate();
  326. return;
  327. }
  328. #if ENABLED(DGUS_PRINT_FILENAME)
  329. // Send print filename
  330. dgusdisplay.WriteVariable(VP_SD_Print_Filename, filelist.filename(), VP_SD_FileName_LEN, true);
  331. #endif
  332. // Setup Confirmation screen
  333. file_to_print = touched_nr;
  334. HandleUserConfirmationPopUp(VP_SD_FileSelectConfirm, nullptr, PSTR("Print file"), filelist.filename(), PSTR("from SD Card?"), true, true, false, true);
  335. }
  336. void DGUSScreenVariableHandler::DGUSLCD_SD_StartPrint(DGUS_VP_Variable &var, void *val_ptr) {
  337. if (!filelist.seek(file_to_print)) return;
  338. ExtUI::printFile(filelist.shortFilename());
  339. ScreenHandler.GotoScreen(
  340. #if ENABLED(DGUS_LCD_UI_ORIGIN)
  341. DGUSLCD_SCREEN_STATUS
  342. #else
  343. DGUSLCD_SCREEN_SDPRINTMANIPULATION
  344. #endif
  345. );
  346. }
  347. void DGUSScreenVariableHandler::DGUSLCD_SD_ResumePauseAbort(DGUS_VP_Variable &var, void *val_ptr) {
  348. if (!ExtUI::isPrintingFromMedia()) return; // avoid race condition when user stays in this menu and printer finishes.
  349. switch (swap16(*(uint16_t*)val_ptr)) {
  350. case 0: // Resume
  351. if (ExtUI::isPrintingFromMediaPaused()) ExtUI::resumePrint();
  352. break;
  353. case 1: // Pause
  354. if (!ExtUI::isPrintingFromMediaPaused()) ExtUI::pausePrint();
  355. break;
  356. case 2: // Abort
  357. ScreenHandler.HandleUserConfirmationPopUp(VP_SD_AbortPrintConfirmed, nullptr, PSTR("Abort printing"), filelist.filename(), PSTR("?"), true, true, false, true);
  358. break;
  359. }
  360. }
  361. void DGUSScreenVariableHandler::DGUSLCD_SD_ReallyAbort(DGUS_VP_Variable &var, void *val_ptr) {
  362. ExtUI::stopPrint();
  363. GotoScreen(DGUSLCD_SCREEN_MAIN);
  364. }
  365. void DGUSScreenVariableHandler::DGUSLCD_SD_PrintTune(DGUS_VP_Variable &var, void *val_ptr) {
  366. if (!ExtUI::isPrintingFromMedia()) return; // avoid race condition when user stays in this menu and printer finishes.
  367. GotoScreen(DGUSLCD_SCREEN_SDPRINTTUNE);
  368. }
  369. void DGUSScreenVariableHandler::DGUSLCD_SD_SendFilename(DGUS_VP_Variable& var) {
  370. uint16_t target_line = (var.VP - VP_SD_FileName0) / VP_SD_FileName_LEN;
  371. if (target_line > DGUS_SD_FILESPERSCREEN) return;
  372. char tmpfilename[VP_SD_FileName_LEN + 1] = "";
  373. var.memadr = (void*)tmpfilename;
  374. if (filelist.seek(top_file + target_line))
  375. snprintf_P(tmpfilename, VP_SD_FileName_LEN, PSTR("%s%c"), filelist.filename(), filelist.isDir() ? '/' : 0);
  376. DGUSLCD_SendStringToDisplay(var);
  377. }
  378. void DGUSScreenVariableHandler::SDCardInserted() {
  379. top_file = 0;
  380. auto cs = ScreenHandler.getCurrentScreen();
  381. if (cs == DGUSLCD_SCREEN_MAIN || cs == DGUSLCD_SCREEN_STATUS)
  382. ScreenHandler.GotoScreen(DGUSLCD_SCREEN_SDFILELIST);
  383. }
  384. void DGUSScreenVariableHandler::SDCardRemoved() {
  385. if (current_screen == DGUSLCD_SCREEN_SDFILELIST
  386. || (current_screen == DGUSLCD_SCREEN_CONFIRM && (ConfirmVP == VP_SD_AbortPrintConfirmed || ConfirmVP == VP_SD_FileSelectConfirm))
  387. || current_screen == DGUSLCD_SCREEN_SDPRINTMANIPULATION
  388. ) ScreenHandler.GotoScreen(DGUSLCD_SCREEN_MAIN);
  389. }
  390. void DGUSScreenVariableHandler::SDCardError() {
  391. DGUSScreenVariableHandler::SDCardRemoved();
  392. ScreenHandler.sendinfoscreen(PSTR("NOTICE"), nullptr, PSTR("SD card error"), nullptr, true, true, true, true);
  393. ScreenHandler.SetupConfirmAction(nullptr);
  394. ScreenHandler.GotoScreen(DGUSLCD_SCREEN_POPUP);
  395. }
  396. #endif // SDSUPPORT
  397. void DGUSScreenVariableHandler::ScreenConfirmedOK(DGUS_VP_Variable &var, void *val_ptr) {
  398. DGUS_VP_Variable ramcopy;
  399. if (!populate_VPVar(ConfirmVP, &ramcopy)) return;
  400. if (ramcopy.set_by_display_handler) ramcopy.set_by_display_handler(ramcopy, val_ptr);
  401. }
  402. const uint16_t* DGUSLCD_FindScreenVPMapList(uint8_t screen) {
  403. const uint16_t *ret;
  404. const struct VPMapping *map = VPMap;
  405. while (ret = (uint16_t*) pgm_read_word(&(map->VPList))) {
  406. if (pgm_read_byte(&(map->screen)) == screen) return ret;
  407. map++;
  408. }
  409. return nullptr;
  410. }
  411. const DGUS_VP_Variable* DGUSLCD_FindVPVar(const uint16_t vp) {
  412. const DGUS_VP_Variable *ret = ListOfVP;
  413. do {
  414. const uint16_t vpcheck = pgm_read_word(&(ret->VP));
  415. if (vpcheck == 0) break;
  416. if (vpcheck == vp) return ret;
  417. ++ret;
  418. } while (1);
  419. DEBUG_ECHOLNPAIR("FindVPVar NOT FOUND ", vp);
  420. return nullptr;
  421. }
  422. void DGUSScreenVariableHandler::ScreenChangeHookIfIdle(DGUS_VP_Variable &var, void *val_ptr) {
  423. if (!ExtUI::isPrinting()) {
  424. ScreenChangeHook(var, val_ptr);
  425. dgusdisplay.RequestScreen(current_screen);
  426. }
  427. }
  428. void DGUSScreenVariableHandler::ScreenChangeHook(DGUS_VP_Variable &var, void *val_ptr) {
  429. uint8_t *tmp = (uint8_t*)val_ptr;
  430. // The keycode in target is coded as <from-frame><to-frame>, so 0x0100A means
  431. // from screen 1 (main) to 10 (temperature). DGUSLCD_SCREEN_POPUP is special,
  432. // meaning "return to previous screen"
  433. DGUSLCD_Screens target = (DGUSLCD_Screens)tmp[1];
  434. if (target == DGUSLCD_SCREEN_POPUP) {
  435. // special handling for popup is to return to previous menu
  436. if (current_screen == DGUSLCD_SCREEN_POPUP && confirm_action_cb) confirm_action_cb();
  437. PopToOldScreen();
  438. return;
  439. }
  440. UpdateNewScreen(target);
  441. #ifdef DEBUG_DGUSLCD
  442. if (!DGUSLCD_FindScreenVPMapList(target)) DEBUG_ECHOLNPAIR("WARNING: No screen Mapping found for ", target);
  443. #endif
  444. }
  445. void DGUSScreenVariableHandler::HandleAllHeatersOff(DGUS_VP_Variable &var, void *val_ptr) {
  446. thermalManager.disable_all_heaters();
  447. ScreenHandler.ForceCompleteUpdate(); // hint to send all data.
  448. }
  449. void DGUSScreenVariableHandler::HandleTemperatureChanged(DGUS_VP_Variable &var, void *val_ptr) {
  450. uint16_t newvalue = swap16(*(uint16_t*)val_ptr);
  451. uint16_t acceptedvalue;
  452. switch (var.VP) {
  453. default: return;
  454. #if HOTENDS >= 1
  455. case VP_T_E0_Set:
  456. thermalManager.setTargetHotend(newvalue, 0);
  457. acceptedvalue = thermalManager.temp_hotend[0].target;
  458. break;
  459. #endif
  460. #if HOTENDS >= 2
  461. case VP_T_E1_Set:
  462. thermalManager.setTargetHotend(newvalue, 1);
  463. acceptedvalue = thermalManager.temp_hotend[1].target;
  464. break;
  465. #endif
  466. #if HAS_HEATED_BED
  467. case VP_T_Bed_Set:
  468. thermalManager.setTargetBed(newvalue);
  469. acceptedvalue = thermalManager.temp_bed.target;
  470. break;
  471. #endif
  472. }
  473. // reply to display the new value to update the view if the new value was rejected by the Thermal Manager.
  474. if (newvalue != acceptedvalue && var.send_to_display_handler) var.send_to_display_handler(var);
  475. ScreenHandler.skipVP = var.VP; // don't overwrite value the next update time as the display might autoincrement in parallel
  476. }
  477. void DGUSScreenVariableHandler::HandleFlowRateChanged(DGUS_VP_Variable &var, void *val_ptr) {
  478. #if EXTRUDERS
  479. uint16_t newvalue = swap16(*(uint16_t*)val_ptr);
  480. uint8_t target_extruder;
  481. switch (var.VP) {
  482. default: return;
  483. #if HOTENDS >= 1
  484. case VP_Flowrate_E0: target_extruder = 0; break;
  485. #endif
  486. #if HOTENDS >= 2
  487. case VP_Flowrate_E1: target_extruder = 1; break;
  488. #endif
  489. }
  490. planner.flow_percentage[target_extruder] = newvalue;
  491. planner.refresh_e_factor(target_extruder);
  492. ScreenHandler.skipVP = var.VP; // don't overwrite value the next update time as the display might autoincrement in parallel
  493. #else
  494. UNUSED(var); UNUSED(val_ptr);
  495. #endif
  496. }
  497. void DGUSScreenVariableHandler::HandleManualExtrude(DGUS_VP_Variable &var, void *val_ptr) {
  498. DEBUG_ECHOLNPGM("HandleManualExtrude");
  499. int16_t movevalue = swap16(*(uint16_t*)val_ptr);
  500. float target = movevalue * 0.01f;
  501. ExtUI::extruder_t target_extruder;
  502. switch (var.VP) {
  503. #if HOTENDS >= 1
  504. case VP_MOVE_E0: target_extruder = ExtUI::extruder_t::E0; break;
  505. #endif
  506. #if HOTENDS >= 2
  507. case VP_MOVE_E1: target_extruder = ExtUI::extruder_t::E1; break
  508. #endif
  509. default: return;
  510. }
  511. target += ExtUI::getAxisPosition_mm(target_extruder);
  512. ExtUI::setAxisPosition_mm(target, target_extruder);
  513. skipVP = var.VP;
  514. }
  515. #if ENABLED(DUGS_UI_MOVE_DIS_OPTION)
  516. void DGUSScreenVariableHandler::HandleManualMoveOption(DGUS_VP_Variable &var, void *val_ptr) {
  517. DEBUG_ECHOLNPGM("HandleManualMoveOption");
  518. *(uint16_t*)var.memadr = swap16(*(uint16_t*)val_ptr);
  519. }
  520. #endif
  521. void DGUSScreenVariableHandler::HandleManualMove(DGUS_VP_Variable &var, void *val_ptr) {
  522. DEBUG_ECHOLNPGM("HandleManualMove");
  523. int16_t movevalue = swap16(*(uint16_t*)val_ptr);
  524. #if ENABLED(DUGS_UI_MOVE_DIS_OPTION)
  525. const uint16_t choice = *(uint16_t*)var.memadr;
  526. movevalue = movevalue > 0 ? choice : -choice;
  527. #endif
  528. char axiscode;
  529. unsigned int speed = 1500; //FIXME: get default feedrate for manual moves, dont hardcode.
  530. switch (var.VP) {
  531. default: return;
  532. case VP_MOVE_X:
  533. axiscode = 'X';
  534. if (!ExtUI::canMove(ExtUI::axis_t::X)) goto cannotmove;
  535. break;
  536. case VP_MOVE_Y:
  537. axiscode = 'Y';
  538. if (!ExtUI::canMove(ExtUI::axis_t::Y)) goto cannotmove;
  539. break;
  540. case VP_MOVE_Z:
  541. axiscode = 'Z';
  542. speed = 300; // default to 5mm/s
  543. if (!ExtUI::canMove(ExtUI::axis_t::Z)) goto cannotmove;
  544. break;
  545. case VP_HOME_ALL: // only used for homing
  546. axiscode = '\0';
  547. movevalue = 0; // ignore value sent from display, this VP is _ONLY_ for homing.
  548. break;
  549. }
  550. if (!movevalue) {
  551. // homing
  552. DEBUG_ECHOPAIR(" homing ", axiscode);
  553. char buf[6] = "G28 X";
  554. buf[4] = axiscode;
  555. //DEBUG_ECHOPAIR(" ", buf);
  556. queue.enqueue_one_now(buf);
  557. //DEBUG_ECHOLNPGM(" ✓");
  558. ScreenHandler.ForceCompleteUpdate();
  559. return;
  560. }
  561. else {
  562. //movement
  563. DEBUG_ECHOPAIR(" move ", axiscode);
  564. bool old_relative_mode = relative_mode;
  565. if (!relative_mode) {
  566. //DEBUG_ECHOPGM(" G91");
  567. queue.enqueue_now_P(PSTR("G91"));
  568. //DEBUG_ECHOPGM(" ✓ ");
  569. }
  570. char buf[32]; // G1 X9999.99 F12345
  571. unsigned int backup_speed = MMS_TO_MMM(feedrate_mm_s);
  572. char sign[]="\0";
  573. int16_t value = movevalue / 100;
  574. if (movevalue < 0) { value = -value; sign[0] = '-'; }
  575. int16_t fraction = ABS(movevalue) % 100;
  576. snprintf_P(buf, 32, PSTR("G0 %c%s%d.%02d F%d"), axiscode, sign, value, fraction, speed);
  577. //DEBUG_ECHOPAIR(" ", buf);
  578. queue.enqueue_one_now(buf);
  579. //DEBUG_ECHOLNPGM(" ✓ ");
  580. if (backup_speed != speed) {
  581. snprintf_P(buf, 32, PSTR("G0 F%d"), backup_speed);
  582. queue.enqueue_one_now(buf);
  583. //DEBUG_ECHOPAIR(" ", buf);
  584. }
  585. //while (!enqueue_and_echo_command(buf)) idle();
  586. //DEBUG_ECHOLNPGM(" ✓ ");
  587. if (!old_relative_mode) {
  588. //DEBUG_ECHOPGM("G90");
  589. queue.enqueue_now_P(PSTR("G90"));
  590. //DEBUG_ECHOPGM(" ✓ ");
  591. }
  592. }
  593. ScreenHandler.ForceCompleteUpdate();
  594. DEBUG_ECHOLNPGM("manmv done.");
  595. return;
  596. cannotmove:
  597. DEBUG_ECHOLNPAIR(" cannot move ", axiscode);
  598. return;
  599. }
  600. void DGUSScreenVariableHandler::HandleMotorLockUnlock(DGUS_VP_Variable &var, void *val_ptr) {
  601. DEBUG_ECHOLNPGM("HandleMotorLockUnlock");
  602. char buf[4];
  603. const int16_t lock = swap16(*(uint16_t*)val_ptr);
  604. strcpy_P(buf, lock ? PSTR("M18") : PSTR("M17"));
  605. //DEBUG_ECHOPAIR(" ", buf);
  606. queue.enqueue_one_now(buf);
  607. }
  608. #if ENABLED(POWER_LOSS_RECOVERY)
  609. void DGUSScreenVariableHandler::HandlePowerLossRecovery(DGUS_VP_Variable &var, void *val_ptr) {
  610. uint16_t value = swap16(*(uint16_t*)val_ptr);
  611. if (value) {
  612. queue.inject_P(PSTR("M1000"));
  613. ScreenHandler.GotoScreen(DGUSLCD_SCREEN_SDPRINTMANIPULATION);
  614. }
  615. else {
  616. card.removeJobRecoveryFile();
  617. card.autostart_index = 0;
  618. ScreenHandler.GotoScreen(DGUSLCD_SCREEN_STATUS);
  619. }
  620. }
  621. #endif
  622. void DGUSScreenVariableHandler::HandleSettings(DGUS_VP_Variable &var, void *val_ptr) {
  623. DEBUG_ECHOLNPGM("HandleSettings");
  624. uint16_t value = swap16(*(uint16_t*)val_ptr);
  625. switch (value) {
  626. default: break;
  627. case 1:
  628. #if ENABLED(PRINTCOUNTER)
  629. print_job_timer.initStats();
  630. #endif
  631. queue.enqueue_now_P(PSTR("M502\nM500"));
  632. break;
  633. case 2: queue.enqueue_now_P(PSTR("M501")); break;
  634. case 3: queue.enqueue_now_P(PSTR("M500")); break;
  635. }
  636. }
  637. void DGUSScreenVariableHandler::HandleStepPerMMChanged(DGUS_VP_Variable &var, void *val_ptr) {
  638. DEBUG_ECHOLNPGM("HandleStepPerMMChanged");
  639. uint16_t value_raw = swap16(*(uint16_t*)val_ptr);
  640. DEBUG_ECHOLNPAIR("value_raw:", value_raw);
  641. float value = (float)value_raw/10;
  642. ExtUI::axis_t axis;
  643. switch (var.VP) {
  644. case VP_X_STEP_PER_MM: axis = ExtUI::axis_t::X; break;
  645. case VP_Y_STEP_PER_MM: axis = ExtUI::axis_t::Y; break;
  646. case VP_Z_STEP_PER_MM: axis = ExtUI::axis_t::Z; break;
  647. default: return;
  648. }
  649. DEBUG_ECHOLNPAIR_F("value:", value);
  650. ExtUI::setAxisSteps_per_mm(value, axis);
  651. DEBUG_ECHOLNPAIR_F("value_set:", ExtUI::getAxisSteps_per_mm(axis));
  652. ScreenHandler.skipVP = var.VP; // don't overwrite value the next update time as the display might autoincrement in parallel
  653. return;
  654. }
  655. void DGUSScreenVariableHandler::HandleStepPerMMExtruderChanged(DGUS_VP_Variable &var, void *val_ptr) {
  656. DEBUG_ECHOLNPGM("HandleStepPerMMExtruderChanged");
  657. uint16_t value_raw = swap16(*(uint16_t*)val_ptr);
  658. DEBUG_ECHOLNPAIR("value_raw:", value_raw);
  659. float value = (float)value_raw/10;
  660. ExtUI::extruder_t extruder;
  661. switch (var.VP) {
  662. default: return;
  663. #if HOTENDS >= 1
  664. case VP_E0_STEP_PER_MM: extruder = ExtUI::extruder_t::E0; break;
  665. #endif
  666. #if HOTENDS >= 2
  667. case VP_E1_STEP_PER_MM: extruder = ExtUI::extruder_t::E1; break;
  668. #endif
  669. }
  670. DEBUG_ECHOLNPAIR_F("value:", value);
  671. ExtUI::setAxisSteps_per_mm(value,extruder);
  672. DEBUG_ECHOLNPAIR_F("value_set:", ExtUI::getAxisSteps_per_mm(extruder));
  673. ScreenHandler.skipVP = var.VP; // don't overwrite value the next update time as the display might autoincrement in parallel
  674. return;
  675. }
  676. #if HAS_PID_HEATING
  677. void DGUSScreenVariableHandler::HandleTemperaturePIDChanged(DGUS_VP_Variable &var, void *val_ptr) {
  678. uint16_t rawvalue = swap16(*(uint16_t*)val_ptr);
  679. DEBUG_ECHOLNPAIR("V1:", rawvalue);
  680. float value = (float)rawvalue / 10;
  681. DEBUG_ECHOLNPAIR("V2:", value);
  682. float newvalue = 0;
  683. switch (var.VP) {
  684. default: return;
  685. #if HOTENDS >= 1
  686. case VP_E0_PID_P: newvalue = value; break;
  687. case VP_E0_PID_I: newvalue = scalePID_i(value); break;
  688. case VP_E0_PID_D: newvalue = scalePID_d(value); break;
  689. #endif
  690. #if HOTENDS >= 2
  691. case VP_E1_PID_P: newvalue = value; break;
  692. case VP_E1_PID_I: newvalue = scalePID_i(value); break;
  693. case VP_E1_PID_D: newvalue = scalePID_d(value); break;
  694. #endif
  695. #if HAS_HEATED_BED
  696. case VP_BED_PID_P: newvalue = value; break;
  697. case VP_BED_PID_I: newvalue = scalePID_i(value); break;
  698. case VP_BED_PID_D: newvalue = scalePID_d(value); break;
  699. #endif
  700. }
  701. DEBUG_ECHOLNPAIR_F("V3:", newvalue);
  702. *(float *)var.memadr = newvalue;
  703. ScreenHandler.skipVP = var.VP; // don't overwrite value the next update time as the display might autoincrement in parallel
  704. }
  705. void DGUSScreenVariableHandler::HandlePIDAutotune(DGUS_VP_Variable &var, void *val_ptr) {
  706. DEBUG_ECHOLNPGM("HandlePIDAutotune");
  707. char buf[32] = {0};
  708. switch (var.VP) {
  709. default: break;
  710. #if ENABLED(PIDTEMP)
  711. #if HOTENDS >= 1
  712. case VP_PID_AUTOTUNE_E0: // Autotune Extruder 0
  713. sprintf(buf, "M303 E%d C5 S210 U1", ExtUI::extruder_t::E0);
  714. break;
  715. #endif
  716. #if HOTENDS >= 2
  717. case VP_PID_AUTOTUNE_E1:
  718. sprintf(buf, "M303 E%d C5 S210 U1", ExtUI::extruder_t::E1);
  719. break;
  720. #endif
  721. #endif
  722. #if ENABLED(PIDTEMPBED)
  723. case VP_PID_AUTOTUNE_BED:
  724. sprintf(buf, "M303 E-1 C5 S70 U1");
  725. break;
  726. #endif
  727. }
  728. if (buf[0]) queue.enqueue_one_now(buf);
  729. #if ENABLED(DGUS_UI_WAITING)
  730. sendinfoscreen(PSTR("PID is autotuning"), PSTR("please wait"), NUL_STR, NUL_STR, true, true, true, true);
  731. GotoScreen(DGUSLCD_SCREEN_WAITING);
  732. #endif
  733. }
  734. #endif
  735. void DGUSScreenVariableHandler::HandleProbeOffsetZChanged(DGUS_VP_Variable &var, void *val_ptr) {
  736. DEBUG_ECHOLNPGM("HandleProbeOffsetZChanged");
  737. uint16_t value = swap16(*(uint16_t*)val_ptr)/100;
  738. ExtUI::setZOffset_mm(value);
  739. ScreenHandler.skipVP = var.VP; // don't overwrite value the next update time as the display might autoincrement in parallel
  740. return;
  741. }
  742. #if ENABLED(BABYSTEPPING)
  743. void DGUSScreenVariableHandler::HandleLiveAdjustZ(DGUS_VP_Variable &var, void *val_ptr) {
  744. DEBUG_ECHOLNPGM("HandleLiveAdjustZ");
  745. int16_t flag = swap16(*(uint16_t*)val_ptr);
  746. int16_t steps = flag ? -20 : 20;
  747. ExtUI::smartAdjustAxis_steps(steps,ExtUI::axis_t::Z,true);
  748. ScreenHandler.ForceCompleteUpdate();
  749. return;
  750. }
  751. #endif
  752. #if FAN_COUNT
  753. void DGUSScreenVariableHandler::HandleFanControl(DGUS_VP_Variable &var, void *val_ptr) {
  754. DEBUG_ECHOLNPGM("HandleFanControl");
  755. *(uint8_t*)var.memadr = *(uint8_t*)var.memadr > 0 ? 0 : 255;
  756. }
  757. #endif
  758. void DGUSScreenVariableHandler::HandleHeaterControl(DGUS_VP_Variable &var, void *val_ptr) {
  759. DEBUG_ECHOLNPGM("HandleHeaterControl");
  760. uint8_t preheat_temp = 0;
  761. switch (var.VP) {
  762. #if HOTENDS >= 1
  763. case VP_E0_CONTROL:
  764. #endif
  765. #if HOTENDS >= 2
  766. case VP_E1_CONTROL:
  767. #endif
  768. #if HOTENDS >= 3
  769. case VP_E2_CONTROL:
  770. #endif
  771. preheat_temp = PREHEAT_1_TEMP_HOTEND;
  772. break;
  773. case VP_BED_CONTROL:
  774. preheat_temp = PREHEAT_1_TEMP_BED;
  775. break;
  776. }
  777. *(int16_t*)var.memadr = *(int16_t*)var.memadr > 0 ? 0 : preheat_temp;
  778. }
  779. #if ENABLED(DGUS_PREHEAT_UI)
  780. void DGUSScreenVariableHandler::HandlePreheat(DGUS_VP_Variable &var, void *val_ptr) {
  781. DEBUG_ECHOLNPGM("HandlePreheat");
  782. uint8_t e_temp = 0;
  783. uint8_t bed_temp = 0;
  784. const uint16_t preheat_option = swap16(*(uint16_t*)val_ptr);
  785. switch (preheat_option) {
  786. case 0: // Preheat PLA
  787. #if defined(PREHEAT_1_TEMP_HOTEND) && defined(PREHEAT_1_TEMP_BED)
  788. e_temp = PREHEAT_1_TEMP_HOTEND;
  789. bed_temp = PREHEAT_1_TEMP_BED;
  790. #endif
  791. break;
  792. case 1: // Preheat ABS
  793. #if defined(PREHEAT_2_TEMP_HOTEND) && defined(PREHEAT_2_TEMP_BED)
  794. e_temp = PREHEAT_2_TEMP_HOTEND;
  795. bed_temp = PREHEAT_2_TEMP_BED;
  796. #endif
  797. break;
  798. case 2: // Preheat PET
  799. #if defined(PREHEAT_3_TEMP_HOTEND) && defined(PREHEAT_3_TEMP_BED)
  800. e_temp = PREHEAT_3_TEMP_HOTEND;
  801. bed_temp = PREHEAT_3_TEMP_BED;
  802. #endif
  803. break;
  804. case 3: // Preheat FLEX
  805. #if defined(PREHEAT_4_TEMP_HOTEND) && defined(PREHEAT_4_TEMP_BED)
  806. e_temp = PREHEAT_4_TEMP_HOTEND;
  807. bed_temp = PREHEAT_4_TEMP_BED;
  808. #endif
  809. break;
  810. case 7: // Custom preheat
  811. break;
  812. case 9: // Cool down
  813. e_temp = 0;
  814. bed_temp = 0;
  815. break;
  816. default:
  817. #if defined(PREHEAT_1_TEMP_HOTEND) && defined(PREHEAT_1_TEMP_BED)
  818. e_temp = PREHEAT_1_TEMP_HOTEND;
  819. bed_temp = PREHEAT_1_TEMP_BED;
  820. #endif
  821. break;
  822. }
  823. switch (var.VP) {
  824. default: return;
  825. #if HOTENDS >= 1
  826. case VP_E0_BED_PREHEAT:
  827. thermalManager.setTargetHotend(e_temp, 0);
  828. #if HAS_HEATED_BED
  829. thermalManager.setTargetBed(bed_temp);
  830. #endif
  831. break;
  832. #endif
  833. #if HOTENDS >= 2
  834. case VP_E1_BED_PREHEAT:
  835. thermalManager.setTargetHotend(e_temp, 1);
  836. #if HAS_HEATED_BED
  837. thermalManager.setTargetBed(bed_temp);
  838. #endif
  839. break;
  840. #endif
  841. }
  842. // Go to the preheat screen to show the heating progress
  843. GotoScreen(DGUSLCD_SCREEN_PREHEAT);
  844. }
  845. #endif
  846. #if ENABLED(DGUS_FILAMENT_LOADUNLOAD)
  847. void DGUSScreenVariableHandler::HandleFilamentOption(DGUS_VP_Variable &var, void *val_ptr) {
  848. DEBUG_ECHOLNPGM("HandleFilamentOption");
  849. uint8_t e_temp = 0;
  850. filament_data.heated = false;
  851. uint16_t preheat_option = swap16(*(uint16_t*)val_ptr);
  852. if (preheat_option <= 8) // Load filament type
  853. filament_data.action = 1;
  854. else if (preheat_option >= 10) { // Unload filament type
  855. preheat_option -= 10;
  856. filament_data.action = 2;
  857. filament_data.purge_length = DGUS_FILAMENT_PURGE_LENGTH;
  858. }
  859. else // Cancel filament operation
  860. filament_data.action = 0;
  861. switch (preheat_option) {
  862. case 0: // Load PLA
  863. #ifdef PREHEAT_1_TEMP_HOTEND
  864. e_temp = PREHEAT_1_TEMP_HOTEND;
  865. #endif
  866. break;
  867. case 1: // Load ABS
  868. #if ENABLED(PREHEAT_2_TEMP_HOTEND)
  869. e_temp = PREHEAT_2_TEMP_HOTEND;
  870. #endif
  871. break;
  872. case 2: // Load PET
  873. #ifdef PREHEAT_3_TEMP_HOTEND
  874. e_temp = PREHEAT_3_TEMP_HOTEND;
  875. #endif
  876. break;
  877. case 3: // Load FLEX
  878. #ifdef PREHEAT_4_TEMP_HOTEND
  879. e_temp = PREHEAT_4_TEMP_HOTEND;
  880. #endif
  881. break;
  882. case 9: // Cool down
  883. default:
  884. e_temp = 0;
  885. break;
  886. }
  887. if (filament_data.action == 0) { // Go back to utility screen
  888. #if HOTENDS >= 1
  889. thermalManager.setTargetHotend(e_temp, ExtUI::extruder_t::E0);
  890. #endif
  891. #if HOTENDS >= 2
  892. thermalManager.setTargetHotend(e_temp, ExtUI::extruder_t::E1);
  893. #endif
  894. GotoScreen(DGUSLCD_SCREEN_UTILITY);
  895. }
  896. else { // Go to the preheat screen to show the heating progress
  897. switch (var.VP) {
  898. default: return;
  899. #if HOTENDS >= 1
  900. case VP_E0_FILAMENT_LOAD_UNLOAD:
  901. filament_data.extruder = ExtUI::extruder_t::E0;
  902. thermalManager.setTargetHotend(e_temp, filament_data.extruder);
  903. break;
  904. #endif
  905. #if HOTENDS >= 2
  906. case VP_E1_FILAMENT_LOAD_UNLOAD:
  907. filament_data.extruder = ExtUI::extruder_t::E1;
  908. thermalManager.setTargetHotend(e_temp, filament_data.extruder);
  909. break;
  910. #endif
  911. }
  912. GotoScreen(DGUSLCD_SCREEN_FILAMENT_HEATING);
  913. }
  914. }
  915. void DGUSScreenVariableHandler::HandleFilamentLoadUnload(DGUS_VP_Variable &var) {
  916. DEBUG_ECHOLNPGM("HandleFilamentLoadUnload");
  917. if (filament_data.action <= 0) return;
  918. // If we close to the target temperature, we can start load or unload the filament
  919. if (thermalManager.hotEnoughToExtrude(filament_data.extruder) && \
  920. thermalManager.targetHotEnoughToExtrude(filament_data.extruder)) {
  921. float movevalue = DGUS_FILAMENT_LOAD_LENGTH_PER_TIME;
  922. if (filament_data.action == 1) { // load filament
  923. if (!filament_data.heated) {
  924. GotoScreen(DGUSLCD_SCREEN_FILAMENT_LOADING);
  925. filament_data.heated = true;
  926. }
  927. movevalue = ExtUI::getAxisPosition_mm(filament_data.extruder)+movevalue;
  928. }
  929. else { // unload filament
  930. if (!filament_data.heated) {
  931. GotoScreen(DGUSLCD_SCREEN_FILAMENT_UNLOADING);
  932. filament_data.heated = true;
  933. }
  934. // Before unloading extrude to prevent jamming
  935. if (filament_data.purge_length >= 0) {
  936. movevalue = ExtUI::getAxisPosition_mm(filament_data.extruder) + movevalue;
  937. filament_data.purge_length -= movevalue;
  938. }
  939. else
  940. movevalue = ExtUI::getAxisPosition_mm(filament_data.extruder) - movevalue;
  941. }
  942. ExtUI::setAxisPosition_mm(movevalue, filament_data.extruder);
  943. }
  944. }
  945. #endif
  946. void DGUSScreenVariableHandler::UpdateNewScreen(DGUSLCD_Screens newscreen, bool popup) {
  947. DEBUG_ECHOLNPAIR("SetNewScreen: ", newscreen);
  948. if (!popup) {
  949. memmove(&past_screens[1], &past_screens[0], sizeof(past_screens) - 1);
  950. past_screens[0] = current_screen;
  951. }
  952. current_screen = newscreen;
  953. skipVP = 0;
  954. ForceCompleteUpdate();
  955. }
  956. void DGUSScreenVariableHandler::PopToOldScreen() {
  957. DEBUG_ECHOLNPAIR("PopToOldScreen s=", past_screens[0]);
  958. GotoScreen(past_screens[0], true);
  959. memmove(&past_screens[0], &past_screens[1], sizeof(past_screens) - 1);
  960. past_screens[sizeof(past_screens) - 1] = DGUSLCD_SCREEN_MAIN;
  961. }
  962. void DGUSScreenVariableHandler::UpdateScreenVPData() {
  963. DEBUG_ECHOPAIR(" UpdateScreenVPData Screen: ", current_screen);
  964. const uint16_t *VPList = DGUSLCD_FindScreenVPMapList(current_screen);
  965. if (!VPList) {
  966. DEBUG_ECHOLNPAIR(" NO SCREEN FOR: ", current_screen);
  967. ScreenComplete = true;
  968. return; // nothing to do, likely a bug or boring screen.
  969. }
  970. // Round-robin updating of all VPs.
  971. VPList += update_ptr;
  972. bool sent_one = false;
  973. do {
  974. uint16_t VP = pgm_read_word(VPList);
  975. DEBUG_ECHOPAIR(" VP: ", VP);
  976. if (!VP) {
  977. update_ptr = 0;
  978. DEBUG_ECHOLNPGM(" UpdateScreenVPData done");
  979. ScreenComplete = true;
  980. return; // Screen completed.
  981. }
  982. if (VP == skipVP) { skipVP = 0; continue; }
  983. DGUS_VP_Variable rcpy;
  984. if (populate_VPVar(VP, &rcpy)) {
  985. uint8_t expected_tx = 6 + rcpy.size; // expected overhead is 6 bytes + payload.
  986. // Send the VP to the display, but try to avoid overrunning the Tx Buffer.
  987. // But send at least one VP, to avoid getting stalled.
  988. if (rcpy.send_to_display_handler && (!sent_one || expected_tx <= dgusdisplay.GetFreeTxBuffer())) {
  989. //DEBUG_ECHOPAIR(" calling handler for ", rcpy.VP);
  990. sent_one = true;
  991. rcpy.send_to_display_handler(rcpy);
  992. }
  993. else {
  994. //auto x=dgusdisplay.GetFreeTxBuffer();
  995. //DEBUG_ECHOLNPAIR(" tx almost full: ", x);
  996. //DEBUG_ECHOPAIR(" update_ptr ", update_ptr);
  997. ScreenComplete = false;
  998. return; // please call again!
  999. }
  1000. }
  1001. } while (++update_ptr, ++VPList, true);
  1002. }
  1003. void DGUSDisplay::loop() {
  1004. // protect against recursion… ProcessRx() may indirectly call idle() when injecting gcode commands.
  1005. if (!no_reentrance) {
  1006. no_reentrance = true;
  1007. ProcessRx();
  1008. no_reentrance = false;
  1009. }
  1010. }
  1011. void DGUSDisplay::InitDisplay() {
  1012. dgusserial.begin(DGUS_BAUDRATE);
  1013. if (true
  1014. #if ENABLED(POWER_LOSS_RECOVERY)
  1015. && !recovery.valid()
  1016. #endif
  1017. )
  1018. RequestScreen(
  1019. #if ENABLED(SHOW_BOOTSCREEN)
  1020. DGUSLCD_SCREEN_BOOT
  1021. #else
  1022. DGUSLCD_SCREEN_MAIN
  1023. #endif
  1024. );
  1025. }
  1026. void DGUSDisplay::WriteVariable(uint16_t adr, const void* values, uint8_t valueslen, bool isstr) {
  1027. const char* myvalues = static_cast<const char*>(values);
  1028. bool strend = !myvalues;
  1029. WriteHeader(adr, DGUS_CMD_WRITEVAR, valueslen);
  1030. while (valueslen--) {
  1031. char x;
  1032. if (!strend) x = *myvalues++;
  1033. if ((isstr && !x) || strend) {
  1034. strend = true;
  1035. x = ' ';
  1036. }
  1037. dgusserial.write(x);
  1038. }
  1039. }
  1040. void DGUSDisplay::WriteVariablePGM(uint16_t adr, const void* values, uint8_t valueslen, bool isstr) {
  1041. const char* myvalues = static_cast<const char*>(values);
  1042. bool strend = !myvalues;
  1043. WriteHeader(adr, DGUS_CMD_WRITEVAR, valueslen);
  1044. while (valueslen--) {
  1045. char x;
  1046. if (!strend) x = pgm_read_byte(myvalues++);
  1047. if ((isstr && !x) || strend) {
  1048. strend = true;
  1049. x = ' ';
  1050. }
  1051. dgusserial.write(x);
  1052. }
  1053. }
  1054. void DGUSScreenVariableHandler::GotoScreen(DGUSLCD_Screens screen, bool ispopup) {
  1055. dgusdisplay.RequestScreen(screen);
  1056. UpdateNewScreen(screen, ispopup);
  1057. }
  1058. bool DGUSScreenVariableHandler::loop() {
  1059. dgusdisplay.loop();
  1060. const millis_t ms = millis();
  1061. static millis_t next_event_ms = 0;
  1062. if (!IsScreenComplete() || ELAPSED(ms, next_event_ms)) {
  1063. next_event_ms = ms + DGUS_UPDATE_INTERVAL_MS;
  1064. UpdateScreenVPData();
  1065. }
  1066. #if ENABLED(SHOW_BOOTSCREEN)
  1067. static bool booted = false;
  1068. #if ENABLED(POWER_LOSS_RECOVERY)
  1069. if (!booted && recovery.valid()) booted = true;
  1070. #endif
  1071. if (!booted && ELAPSED(ms, BOOTSCREEN_TIMEOUT)) {
  1072. booted = true;
  1073. GotoScreen(DGUSLCD_SCREEN_MAIN);
  1074. }
  1075. #endif
  1076. return IsScreenComplete();
  1077. }
  1078. void DGUSDisplay::RequestScreen(DGUSLCD_Screens screen) {
  1079. DEBUG_ECHOLNPAIR("GotoScreen ", screen);
  1080. const unsigned char gotoscreen[] = { 0x5A, 0x01, (unsigned char) (screen >> 8U), (unsigned char) (screen & 0xFFU) };
  1081. WriteVariable(0x84, gotoscreen, sizeof(gotoscreen));
  1082. }
  1083. void DGUSDisplay::ProcessRx() {
  1084. #if ENABLED(DGUS_SERIAL_STATS_RX_BUFFER_OVERRUNS)
  1085. if (!dgusserial.available() && dgusserial.buffer_overruns()) {
  1086. // Overrun, but reset the flag only when the buffer is empty
  1087. // We want to extract as many as valid datagrams possible...
  1088. DEBUG_ECHOPGM("OVFL");
  1089. rx_datagram_state = DGUS_IDLE;
  1090. //dgusserial.reset_rx_overun();
  1091. dgusserial.flush();
  1092. }
  1093. #endif
  1094. uint8_t receivedbyte;
  1095. while (dgusserial.available()) {
  1096. switch (rx_datagram_state) {
  1097. case DGUS_IDLE: // Waiting for the first header byte
  1098. receivedbyte = dgusserial.read();
  1099. //DEBUG_ECHOPAIR("< ",x);
  1100. if (DGUS_HEADER1 == receivedbyte) rx_datagram_state = DGUS_HEADER1_SEEN;
  1101. break;
  1102. case DGUS_HEADER1_SEEN: // Waiting for the second header byte
  1103. receivedbyte = dgusserial.read();
  1104. //DEBUG_ECHOPAIR(" ",x);
  1105. rx_datagram_state = (DGUS_HEADER2 == receivedbyte) ? DGUS_HEADER2_SEEN : DGUS_IDLE;
  1106. break;
  1107. case DGUS_HEADER2_SEEN: // Waiting for the length byte
  1108. rx_datagram_len = dgusserial.read();
  1109. DEBUG_ECHOPAIR(" (", rx_datagram_len, ") ");
  1110. // Telegram min len is 3 (command and one word of payload)
  1111. rx_datagram_state = WITHIN(rx_datagram_len, 3, DGUS_RX_BUFFER_SIZE) ? DGUS_WAIT_TELEGRAM : DGUS_IDLE;
  1112. break;
  1113. case DGUS_WAIT_TELEGRAM: // wait for complete datagram to arrive.
  1114. if (dgusserial.available() < rx_datagram_len) return;
  1115. Initialized = true; // We've talked to it, so we defined it as initialized.
  1116. uint8_t command = dgusserial.read();
  1117. DEBUG_ECHOPAIR("# ", command);
  1118. uint8_t readlen = rx_datagram_len - 1; // command is part of len.
  1119. unsigned char tmp[rx_datagram_len - 1];
  1120. unsigned char *ptmp = tmp;
  1121. while (readlen--) {
  1122. receivedbyte = dgusserial.read();
  1123. DEBUG_ECHOPAIR(" ", receivedbyte);
  1124. *ptmp++ = receivedbyte;
  1125. }
  1126. DEBUG_ECHOPGM(" # ");
  1127. // mostly we'll get this: 5A A5 03 82 4F 4B -- ACK on 0x82, so discard it.
  1128. if (command == DGUS_CMD_WRITEVAR && 'O' == tmp[0] && 'K' == tmp[1]) {
  1129. DEBUG_ECHOLNPGM(">");
  1130. rx_datagram_state = DGUS_IDLE;
  1131. break;
  1132. }
  1133. /* AutoUpload, (and answer to) Command 0x83 :
  1134. | tmp[0 1 2 3 4 ... ]
  1135. | Example 5A A5 06 83 20 01 01 78 01 ……
  1136. | / / | | \ / | \ \
  1137. | Header | | | | \_____\_ DATA (Words!)
  1138. | DatagramLen / VPAdr |
  1139. | Command DataLen (in Words) */
  1140. if (command == DGUS_CMD_READVAR) {
  1141. const uint16_t vp = tmp[0] << 8 | tmp[1];
  1142. const uint8_t dlen = tmp[2] << 1; // Convert to Bytes. (Display works with words)
  1143. //DEBUG_ECHOPAIR(" vp=", vp, " dlen=", dlen);
  1144. DGUS_VP_Variable ramcopy;
  1145. if (populate_VPVar(vp, &ramcopy)) {
  1146. if (!(dlen == ramcopy.size || (dlen == 2 && ramcopy.size == 1)))
  1147. DEBUG_ECHOLNPGM("SIZE MISMATCH");
  1148. else if (ramcopy.set_by_display_handler) {
  1149. ramcopy.set_by_display_handler(ramcopy, &tmp[3]);
  1150. }
  1151. else
  1152. DEBUG_ECHOLNPGM(" VPVar found, no handler.");
  1153. }
  1154. else
  1155. DEBUG_ECHOLNPAIR(" VPVar not found:", vp);
  1156. rx_datagram_state = DGUS_IDLE;
  1157. break;
  1158. }
  1159. // discard anything else
  1160. rx_datagram_state = DGUS_IDLE;
  1161. }
  1162. }
  1163. }
  1164. size_t DGUSDisplay::GetFreeTxBuffer() { return DGUS_SERIAL_GET_TX_BUFFER_FREE(); }
  1165. void DGUSDisplay::WriteHeader(uint16_t adr, uint8_t cmd, uint8_t payloadlen) {
  1166. dgusserial.write(DGUS_HEADER1);
  1167. dgusserial.write(DGUS_HEADER2);
  1168. dgusserial.write(payloadlen + 3);
  1169. dgusserial.write(cmd);
  1170. dgusserial.write(adr >> 8);
  1171. dgusserial.write(adr & 0xFF);
  1172. }
  1173. void DGUSDisplay::WritePGM(const char str[], uint8_t len) {
  1174. while (len--) dgusserial.write(pgm_read_byte(str++));
  1175. }
  1176. // A SW memory barrier, to ensure GCC does not overoptimize loops
  1177. #define sw_barrier() asm volatile("": : :"memory");
  1178. #endif // HAS_DGUS_LCD