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.

DGUSScreenHandler.cpp 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. /**
  2. * Marlin 3D Printer Firmware
  3. * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
  4. *
  5. * Based on Sprinter and grbl.
  6. * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
  7. *
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. *
  21. */
  22. #include "../../../inc/MarlinConfigPre.h"
  23. #if ENABLED(DGUS_LCD_UI_RELOADED)
  24. #include "DGUSScreenHandler.h"
  25. #include "DGUSDisplay.h"
  26. #include "definition/DGUS_ScreenAddrList.h"
  27. #include "definition/DGUS_ScreenSetup.h"
  28. #include "../../../gcode/queue.h"
  29. uint8_t DGUSScreenHandler::debug_count = 0;
  30. #if ENABLED(SDSUPPORT)
  31. ExtUI::FileList DGUSScreenHandler::filelist;
  32. uint16_t DGUSScreenHandler::filelist_offset = 0;
  33. int16_t DGUSScreenHandler::filelist_selected = -1;
  34. #endif
  35. DGUS_Data::StepSize DGUSScreenHandler::offset_steps = DGUS_Data::StepSize::MMP1;
  36. DGUS_Data::StepSize DGUSScreenHandler::move_steps = DGUS_Data::StepSize::MM10;
  37. uint16_t DGUSScreenHandler::probing_icons[] = { 0, 0 };
  38. DGUS_Data::Extruder DGUSScreenHandler::filament_extruder = DGUS_Data::Extruder::CURRENT;
  39. uint16_t DGUSScreenHandler::filament_length = DGUS_DEFAULT_FILAMENT_LEN;
  40. char DGUSScreenHandler::gcode[] = "";
  41. DGUS_Data::Heater DGUSScreenHandler::pid_heater = DGUS_Data::Heater::H0;
  42. uint16_t DGUSScreenHandler::pid_temp = DGUS_PLA_TEMP_HOTEND;
  43. uint8_t DGUSScreenHandler::pid_cycles = 5;
  44. bool DGUSScreenHandler::settings_ready = false;
  45. bool DGUSScreenHandler::booted = false;
  46. DGUS_Screen DGUSScreenHandler::current_screen = DGUS_Screen::BOOT;
  47. DGUS_Screen DGUSScreenHandler::new_screen = DGUS_Screen::BOOT;
  48. bool DGUSScreenHandler::full_update = false;
  49. DGUS_Screen DGUSScreenHandler::wait_return_screen = DGUS_Screen::HOME;
  50. bool DGUSScreenHandler::wait_continue = false;
  51. bool DGUSScreenHandler::leveling_active = false;
  52. millis_t DGUSScreenHandler::status_expire = 0;
  53. millis_t DGUSScreenHandler::eeprom_save = 0;
  54. const char DGUS_MSG_HOMING_REQUIRED[] PROGMEM = "Homing required",
  55. DGUS_MSG_BUSY[] PROGMEM = "Busy",
  56. DGUS_MSG_UNDEF[] PROGMEM = "-",
  57. DGUS_MSG_HOMING[] PROGMEM = "Homing...",
  58. DGUS_MSG_FW_OUTDATED[] PROGMEM = "DWIN GUI/OS update required",
  59. DGUS_MSG_ABL_REQUIRED[] PROGMEM = "Auto bed leveling required";
  60. const char DGUS_CMD_HOME[] PROGMEM = "G28",
  61. DGUS_CMD_EEPROM_SAVE[] PROGMEM = "M500";
  62. void DGUSScreenHandler::Init() {
  63. dgus_display.Init();
  64. MoveToScreen(DGUS_Screen::BOOT, true);
  65. }
  66. void DGUSScreenHandler::Ready() {
  67. dgus_display.PlaySound(1);
  68. }
  69. void DGUSScreenHandler::Loop() {
  70. if (!settings_ready || current_screen == DGUS_Screen::KILL) {
  71. return;
  72. }
  73. const millis_t ms = ExtUI::safe_millis();
  74. static millis_t next_event_ms = 0;
  75. if (new_screen != DGUS_Screen::BOOT) {
  76. const DGUS_Screen screen = new_screen;
  77. new_screen = DGUS_Screen::BOOT;
  78. if (current_screen == screen)
  79. TriggerFullUpdate();
  80. else
  81. MoveToScreen(screen);
  82. return;
  83. }
  84. if (!booted && ELAPSED(ms, 3000)) {
  85. booted = true;
  86. dgus_display.ReadVersions();
  87. if (current_screen == DGUS_Screen::BOOT)
  88. MoveToScreen(DGUS_Screen::HOME);
  89. return;
  90. }
  91. if (ELAPSED(ms, next_event_ms) || full_update) {
  92. next_event_ms = ms + DGUS_UPDATE_INTERVAL_MS;
  93. if (!SendScreenVPData(current_screen, full_update))
  94. DEBUG_ECHOLNPGM("SendScreenVPData failed");
  95. return;
  96. }
  97. if (current_screen == DGUS_Screen::WAIT
  98. && ((wait_continue && !wait_for_user)
  99. || (!wait_continue && IsPrinterIdle()))
  100. ) {
  101. MoveToScreen(wait_return_screen, true);
  102. return;
  103. }
  104. if (current_screen == DGUS_Screen::LEVELING_PROBING && IsPrinterIdle()) {
  105. dgus_display.PlaySound(3);
  106. SetStatusMessage(ExtUI::getMeshValid() ? F("Probing successful") : F("Probing failed"));
  107. MoveToScreen(DGUS_Screen::LEVELING_AUTOMATIC);
  108. return;
  109. }
  110. if (status_expire > 0 && ELAPSED(ms, status_expire)) {
  111. SetStatusMessage(FPSTR(NUL_STR), 0);
  112. return;
  113. }
  114. if (eeprom_save > 0 && ELAPSED(ms, eeprom_save) && IsPrinterIdle()) {
  115. eeprom_save = 0;
  116. queue.enqueue_now_P(DGUS_CMD_EEPROM_SAVE);
  117. return;
  118. }
  119. dgus_display.Loop();
  120. }
  121. void DGUSScreenHandler::PrinterKilled(FSTR_P const error, FSTR_P const component) {
  122. SetMessageLinePGM(FTOP(error), 1);
  123. SetMessageLinePGM(FTOP(component), 2);
  124. SetMessageLinePGM(NUL_STR, 3);
  125. SetMessageLinePGM(GET_TEXT(MSG_PLEASE_RESET), 4);
  126. dgus_display.PlaySound(3, 1, 200);
  127. MoveToScreen(DGUS_Screen::KILL, true);
  128. }
  129. void DGUSScreenHandler::UserConfirmRequired(const char * const msg) {
  130. dgus_screen_handler.SetMessageLinePGM(NUL_STR, 1);
  131. dgus_screen_handler.SetMessageLine(msg, 2);
  132. dgus_screen_handler.SetMessageLinePGM(NUL_STR, 3);
  133. dgus_screen_handler.SetMessageLinePGM(NUL_STR, 4);
  134. dgus_display.PlaySound(3);
  135. dgus_screen_handler.ShowWaitScreen(current_screen, true);
  136. }
  137. void DGUSScreenHandler::SettingsReset() {
  138. dgus_display.SetVolume(DGUS_DEFAULT_VOLUME);
  139. dgus_display.SetBrightness(DGUS_DEFAULT_BRIGHTNESS);
  140. if (!settings_ready) {
  141. settings_ready = true;
  142. Ready();
  143. }
  144. SetStatusMessage(F("EEPROM reset"));
  145. }
  146. void DGUSScreenHandler::StoreSettings(char *buff) {
  147. eeprom_data_t data;
  148. static_assert(sizeof(data) <= ExtUI::eeprom_data_size, "sizeof(eeprom_data_t) > eeprom_data_size.");
  149. data.initialized = true;
  150. data.volume = dgus_display.GetVolume();
  151. data.brightness = dgus_display.GetBrightness();
  152. data.abl_okay = (ExtUI::getLevelingActive() && ExtUI::getMeshValid());
  153. memcpy(buff, &data, sizeof(data));
  154. }
  155. void DGUSScreenHandler::LoadSettings(const char *buff) {
  156. eeprom_data_t data;
  157. static_assert(sizeof(data) <= ExtUI::eeprom_data_size, "sizeof(eeprom_data_t) > eeprom_data_size.");
  158. memcpy(&data, buff, sizeof(data));
  159. dgus_display.SetVolume(data.initialized ? data.volume : DGUS_DEFAULT_VOLUME);
  160. dgus_display.SetBrightness(data.initialized ? data.brightness : DGUS_DEFAULT_BRIGHTNESS);
  161. if (data.initialized) {
  162. leveling_active = (data.abl_okay && ExtUI::getMeshValid());
  163. ExtUI::setLevelingActive(leveling_active);
  164. }
  165. }
  166. void DGUSScreenHandler::ConfigurationStoreWritten(bool success) {
  167. if (!success)
  168. SetStatusMessage(F("EEPROM write failed"));
  169. }
  170. void DGUSScreenHandler::ConfigurationStoreRead(bool success) {
  171. if (!success) {
  172. SetStatusMessage(F("EEPROM read failed"));
  173. }
  174. else if (!settings_ready) {
  175. settings_ready = true;
  176. Ready();
  177. }
  178. }
  179. void DGUSScreenHandler::PlayTone(const uint16_t frequency, const uint16_t duration) {
  180. UNUSED(duration);
  181. if (frequency >= 1 && frequency <= 255) {
  182. if (duration >= 1 && duration <= 255)
  183. dgus_display.PlaySound((uint8_t)frequency, (uint8_t)duration);
  184. else
  185. dgus_display.PlaySound((uint8_t)frequency);
  186. }
  187. }
  188. void DGUSScreenHandler::MeshUpdate(const int8_t xpos, const int8_t ypos) {
  189. if (current_screen != DGUS_Screen::LEVELING_PROBING) {
  190. if (current_screen == DGUS_Screen::LEVELING_AUTOMATIC)
  191. TriggerFullUpdate();
  192. return;
  193. }
  194. uint8_t point = ypos * GRID_MAX_POINTS_X + xpos;
  195. probing_icons[point < 16 ? 0 : 1] |= (1U << (point % 16));
  196. if (xpos >= GRID_MAX_POINTS_X - 1 && ypos >= GRID_MAX_POINTS_Y - 1 && !ExtUI::getMeshValid())
  197. probing_icons[0] = probing_icons[1] = 0;
  198. TriggerFullUpdate();
  199. }
  200. void DGUSScreenHandler::PrintTimerStarted() {
  201. TriggerScreenChange(DGUS_Screen::PRINT_STATUS);
  202. }
  203. void DGUSScreenHandler::PrintTimerPaused() {
  204. dgus_display.PlaySound(3);
  205. TriggerFullUpdate();
  206. }
  207. void DGUSScreenHandler::PrintTimerStopped() {
  208. if (current_screen != DGUS_Screen::PRINT_STATUS && current_screen != DGUS_Screen::PRINT_ADJUST)
  209. return;
  210. dgus_display.PlaySound(3);
  211. TriggerScreenChange(DGUS_Screen::PRINT_FINISHED);
  212. }
  213. void DGUSScreenHandler::FilamentRunout(const ExtUI::extruder_t extruder) {
  214. char buffer[21];
  215. snprintf_P(buffer, sizeof(buffer), PSTR("Filament runout E%d"), extruder);
  216. SetStatusMessage(buffer);
  217. dgus_display.PlaySound(3);
  218. }
  219. #if ENABLED(SDSUPPORT)
  220. void DGUSScreenHandler::SDCardInserted() {
  221. if (current_screen == DGUS_Screen::HOME)
  222. TriggerScreenChange(DGUS_Screen::PRINT);
  223. }
  224. void DGUSScreenHandler::SDCardRemoved() {
  225. if (current_screen == DGUS_Screen::PRINT)
  226. TriggerScreenChange(DGUS_Screen::HOME);
  227. }
  228. void DGUSScreenHandler::SDCardError() {
  229. SetStatusMessage(GET_TEXT_F(MSG_MEDIA_READ_ERROR));
  230. if (current_screen == DGUS_Screen::PRINT)
  231. TriggerScreenChange(DGUS_Screen::HOME);
  232. }
  233. #endif // SDSUPPORT
  234. #if ENABLED(POWER_LOSS_RECOVERY)
  235. void DGUSScreenHandler::PowerLossResume() {
  236. MoveToScreen(DGUS_Screen::POWERLOSS, true);
  237. }
  238. #endif // POWER_LOSS_RECOVERY
  239. #if HAS_PID_HEATING
  240. void DGUSScreenHandler::PidTuning(const ExtUI::result_t rst) {
  241. switch (rst) {
  242. case ExtUI::PID_STARTED:
  243. SetStatusMessage(GET_TEXT_F(MSG_PID_AUTOTUNE));
  244. break;
  245. case ExtUI::PID_BAD_EXTRUDER_NUM:
  246. SetStatusMessage(GET_TEXT_F(MSG_PID_BAD_EXTRUDER_NUM));
  247. break;
  248. case ExtUI::PID_TEMP_TOO_HIGH:
  249. SetStatusMessage(GET_TEXT_F(MSG_PID_TEMP_TOO_HIGH));
  250. break;
  251. case ExtUI::PID_TUNING_TIMEOUT:
  252. SetStatusMessage(GET_TEXT_F(MSG_PID_TIMEOUT));
  253. break;
  254. case ExtUI::PID_DONE:
  255. SetStatusMessage(GET_TEXT_F(MSG_PID_AUTOTUNE_DONE));
  256. break;
  257. default:
  258. return;
  259. }
  260. dgus_display.PlaySound(3);
  261. }
  262. #endif // HAS_PID_HEATING
  263. void DGUSScreenHandler::SetMessageLine(const char* msg, uint8_t line) {
  264. switch (line) {
  265. default: return;
  266. case 1:
  267. dgus_display.WriteString((uint16_t)DGUS_Addr::MESSAGE_Line1, msg, DGUS_LINE_LEN, true, true);
  268. break;
  269. case 2:
  270. dgus_display.WriteString((uint16_t)DGUS_Addr::MESSAGE_Line2, msg, DGUS_LINE_LEN, true, true);
  271. break;
  272. case 3:
  273. dgus_display.WriteString((uint16_t)DGUS_Addr::MESSAGE_Line3, msg, DGUS_LINE_LEN, true, true);
  274. break;
  275. case 4:
  276. dgus_display.WriteString((uint16_t)DGUS_Addr::MESSAGE_Line4, msg, DGUS_LINE_LEN, true, true);
  277. break;
  278. }
  279. }
  280. void DGUSScreenHandler::SetMessageLinePGM(PGM_P msg, uint8_t line) {
  281. switch (line) {
  282. default: return;
  283. case 1:
  284. dgus_display.WriteStringPGM((uint16_t)DGUS_Addr::MESSAGE_Line1, msg, DGUS_LINE_LEN, true, true);
  285. break;
  286. case 2:
  287. dgus_display.WriteStringPGM((uint16_t)DGUS_Addr::MESSAGE_Line2, msg, DGUS_LINE_LEN, true, true);
  288. break;
  289. case 3:
  290. dgus_display.WriteStringPGM((uint16_t)DGUS_Addr::MESSAGE_Line3, msg, DGUS_LINE_LEN, true, true);
  291. break;
  292. case 4:
  293. dgus_display.WriteStringPGM((uint16_t)DGUS_Addr::MESSAGE_Line4, msg, DGUS_LINE_LEN, true, true);
  294. break;
  295. }
  296. }
  297. void DGUSScreenHandler::SetStatusMessage(const char* msg, const millis_t duration) {
  298. dgus_display.WriteString((uint16_t)DGUS_Addr::MESSAGE_Status, msg, DGUS_STATUS_LEN, false, true);
  299. status_expire = (duration > 0 ? ExtUI::safe_millis() + duration : 0);
  300. }
  301. void DGUSScreenHandler::SetStatusMessage(FSTR_P const fmsg, const millis_t duration) {
  302. dgus_display.WriteStringPGM((uint16_t)DGUS_Addr::MESSAGE_Status, FTOP(fmsg), DGUS_STATUS_LEN, false, true);
  303. status_expire = (duration > 0 ? ExtUI::safe_millis() + duration : 0);
  304. }
  305. void DGUSScreenHandler::ShowWaitScreen(DGUS_Screen return_screen, bool has_continue) {
  306. if (return_screen != DGUS_Screen::WAIT) {
  307. wait_return_screen = return_screen;
  308. }
  309. wait_continue = has_continue;
  310. TriggerScreenChange(DGUS_Screen::WAIT);
  311. }
  312. DGUS_Screen DGUSScreenHandler::GetCurrentScreen() {
  313. return current_screen;
  314. }
  315. void DGUSScreenHandler::TriggerScreenChange(DGUS_Screen screen) {
  316. new_screen = screen;
  317. }
  318. void DGUSScreenHandler::TriggerFullUpdate() {
  319. full_update = true;
  320. }
  321. void DGUSScreenHandler::TriggerEEPROMSave() {
  322. eeprom_save = ExtUI::safe_millis() + 500;
  323. }
  324. bool DGUSScreenHandler::IsPrinterIdle() {
  325. return (!ExtUI::commandsInQueue()
  326. && !ExtUI::isMoving());
  327. }
  328. const DGUS_Addr* DGUSScreenHandler::FindScreenAddrList(DGUS_Screen screen) {
  329. DGUS_ScreenAddrList list;
  330. const DGUS_ScreenAddrList *map = screen_addr_list_map;
  331. do {
  332. memcpy_P(&list, map, sizeof(*map));
  333. if (!list.addr_list) break;
  334. if (list.screen == screen) {
  335. return list.addr_list;
  336. }
  337. } while (++map);
  338. return nullptr;
  339. }
  340. bool DGUSScreenHandler::CallScreenSetup(DGUS_Screen screen) {
  341. DGUS_ScreenSetup setup;
  342. const DGUS_ScreenSetup *list = screen_setup_list;
  343. do {
  344. memcpy_P(&setup, list, sizeof(*list));
  345. if (!setup.setup_fn) break;
  346. if (setup.screen == screen) {
  347. return setup.setup_fn();
  348. }
  349. } while (++list);
  350. return true;
  351. }
  352. void DGUSScreenHandler::MoveToScreen(DGUS_Screen screen, bool abort_wait) {
  353. if (current_screen == DGUS_Screen::KILL) {
  354. return;
  355. }
  356. if (current_screen == DGUS_Screen::WAIT) {
  357. if (screen != DGUS_Screen::WAIT) {
  358. wait_return_screen = screen;
  359. }
  360. if (!abort_wait) return;
  361. if (wait_continue && wait_for_user) {
  362. ExtUI::setUserConfirmed();
  363. }
  364. }
  365. if (!CallScreenSetup(screen)) return;
  366. if (!SendScreenVPData(screen, true)) {
  367. DEBUG_ECHOLNPGM("SendScreenVPData failed");
  368. return;
  369. }
  370. current_screen = screen;
  371. dgus_display.SwitchScreen(current_screen);
  372. }
  373. bool DGUSScreenHandler::SendScreenVPData(DGUS_Screen screen, bool complete_update) {
  374. if (complete_update) {
  375. full_update = false;
  376. }
  377. const DGUS_Addr *list = FindScreenAddrList(screen);
  378. while (true) {
  379. if (!list) return true; // Nothing left to send
  380. const uint16_t addr = pgm_read_word(list++);
  381. if (!addr) return true; // Nothing left to send
  382. DGUS_VP vp;
  383. if (!DGUS_PopulateVP((DGUS_Addr)addr, &vp)) continue; // Invalid VP
  384. if (!vp.tx_handler) continue; // Nothing to send
  385. if (!complete_update && !(vp.flags & VPFLAG_AUTOUPLOAD)) continue; // Unnecessary VP
  386. uint8_t expected_tx = 6 + vp.size; // 6 bytes header + payload.
  387. const millis_t try_until = ExtUI::safe_millis() + 1000;
  388. while (expected_tx > dgus_display.GetFreeTxBuffer()) {
  389. if (ELAPSED(ExtUI::safe_millis(), try_until)) return false; // Stop trying after 1 second
  390. dgus_display.FlushTx(); // Flush the TX buffer
  391. delay(50);
  392. }
  393. vp.tx_handler(vp);
  394. }
  395. }
  396. #endif // DGUS_LCD_UI_RELOADED