12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094 |
- /**
- * Marlin 3D Printer Firmware
- * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
- *
- * Based on Sprinter and grbl.
- * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- */
-
- #include "../../inc/MarlinConfigPre.h"
-
- #if IS_TFTGLCD_PANEL
-
- /**
- * marlinui_TFTGLCD.cpp
- *
- * Implementation of the LCD display routines for a TFT GLCD displays with external controller.
- * This display looks like a REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER but has good text font
- * and supports color output.
- */
-
- #if NONE(__AVR__, TARGET_LPC1768, STM32F1, STM32F4xx)
- #warning "Selected platform not yet tested. Please contribute your good pin mappings."
- #endif
-
- #if ENABLED(TFTGLCD_PANEL_SPI)
- #include <SPI.h>
- #else
- #include <Wire.h>
- #endif
-
- #include "marlinui_TFTGLCD.h"
- #include "../marlinui.h"
- #include "../../libs/numtostr.h"
-
- #include "../../sd/cardreader.h"
- #include "../../module/temperature.h"
- #include "../../module/printcounter.h"
- #include "../../module/planner.h"
- #include "../../module/motion.h"
-
- #if DISABLED(LCD_PROGRESS_BAR) && BOTH(FILAMENT_LCD_DISPLAY, SDSUPPORT)
- #include "../../feature/filwidth.h"
- #include "../../gcode/parser.h"
- #endif
-
- #if EITHER(HAS_COOLER, LASER_COOLANT_FLOW_METER)
- #include "../../feature/cooler.h"
- #endif
-
- #if ENABLED(I2C_AMMETER)
- #include "../../feature/ammeter.h"
- #endif
-
- #if HAS_CUTTER
- #include "../../feature/spindle_laser.h"
- #endif
-
- #if ENABLED(AUTO_BED_LEVELING_UBL)
- #include "../../feature/bedlevel/bedlevel.h"
- #endif
-
- TFTGLCD lcd;
-
- #define ICON_LOGO B00000001
- #define ICON_TEMP1 B00000010 // Hotend 1
- #define ICON_TEMP2 B00000100 // Hotend 2
- #define ICON_TEMP3 B00001000 // Hotend 3
- #define ICON_BED B00010000
- #define ICON_FAN B00100000
- #define ICON_HOT B01000000 // When any T > 50deg
- #define PIC_MASK 0x7F
-
- // LEDs not used, for compatibility with Smoothieware
- #define LED_HOTEND_ON B00000001
- #define LED_BED_ON B00000010
- #define LED_FAN_ON B00000100
- #define LED_HOT B00001000
- #define LED_MASK 0x0F
-
- #define FBSIZE (LCD_WIDTH * LCD_HEIGHT + 2)
- #define MIDDLE_Y ((LCD_HEIGHT - 1) / 2)
-
- // Markers for change line colors
- #define COLOR_EDIT '#'
- #define COLOR_ERROR '!'
-
- #ifdef CONVERT_TO_EXT_ASCII //use standard pseudographic symbols in ASCII table
- #define LR 179 //vertical line
- #define TRC 191 //top right corner
- #define BLC 192 //bottom left corner
- #define GL 196 //horizontal line
- #define BRC 217 //bottom right corner, should be replaced to 12 for some languages
- #define TLC 218 //top left corner, should be replaced to 13 for some languages
- #else //next symbols must be present in panel font
- #define LR 8 //equal to 179
- #define TRC 9 //equal to 191
- #define BLC 10 //equal to 192
- #define GL 11 //equal to 196
- #define BRC 12 //equal to 217
- #define TLC 13 //equal to 218
- #endif
-
- #define Marlin 0x01
-
- enum Commands { // based on Smoothieware commands
- GET_SPI_DATA = 0,
- READ_BUTTONS, // read buttons
- READ_ENCODER, // read encoder
- LCD_WRITE, // write all screen to LCD
- BUZZER, // beep buzzer
- CONTRAST, // set contrast (brightnes)
- // Other commands... 0xE0 thru 0xFF
- GET_LCD_ROW = 0xE0, // for detect panel
- GET_LCD_COL, // reserved for compatibility with Smoothieware, not used
- LCD_PUT, // write one line to LCD
- CLR_SCREEN,
- INIT_SCREEN = 0xFE // clear panel buffer
- };
-
- static unsigned char framebuffer[FBSIZE];
- static unsigned char *fb;
- static uint8_t cour_line;
- static uint8_t picBits, ledBits, hotBits;
- static uint8_t PanelDetected = 0;
-
- // Different platforms use different SPI methods
- #if ANY(__AVR__, TARGET_LPC1768, __STM32F1__, ARDUINO_ARCH_SAM, __SAMD51__, __MK20DX256__, __MK64FX512__)
- #define SPI_SEND_ONE(V) SPI.transfer(V);
- #define SPI_SEND_TWO(V) SPI.transfer16(V);
- #elif EITHER(STM32F4xx, STM32F1xx)
- #define SPI_SEND_ONE(V) SPI.transfer(V, SPI_CONTINUE);
- #define SPI_SEND_TWO(V) SPI.transfer16(V, SPI_CONTINUE);
- #elif defined(ARDUINO_ARCH_ESP32)
- #define SPI_SEND_ONE(V) SPI.write(V);
- #define SPI_SEND_TWO(V) SPI.write16(V);
- #endif
-
- #if ANY(__AVR__, ARDUINO_ARCH_SAM, __SAMD51__, __MK20DX256__, __MK64FX512__)
- #define SPI_SEND_SOME(V,L,Z) SPI.transfer(&V[Z], L);
- #elif EITHER(STM32F4xx, STM32F1xx)
- #define SPI_SEND_SOME(V,L,Z) SPI.transfer(&V[Z], L, SPI_CONTINUE);
- #elif ANY(TARGET_LPC1768, __STM32F1__, ARDUINO_ARCH_ESP32)
- #define SPI_SEND_SOME(V,L,Z) do{ for (uint16_t i = 0; i < L; i++) SPI_SEND_ONE(V[(Z)+i]); }while(0)
- #endif
-
- // Constructor
- TFTGLCD::TFTGLCD() {}
-
- // Clear local buffer
- void TFTGLCD::clear_buffer() {
- memset(&framebuffer[0], ' ', FBSIZE - 2);
- framebuffer[FBSIZE - 1] = framebuffer[FBSIZE - 2] = 0;
- picBits = ledBits = 0;
- }
-
- // Clear panel's screen
- void TFTGLCD::clr_screen() {
- if (!PanelDetected) return;
- #if ENABLED(TFTGLCD_PANEL_SPI)
- WRITE(TFTGLCD_CS, LOW);
- SPI_SEND_ONE(CLR_SCREEN);
- WRITE(TFTGLCD_CS, HIGH);
- #else
- Wire.beginTransmission((uint8_t)LCD_I2C_ADDRESS); //set I2C device address
- Wire.write(CLR_SCREEN);
- Wire.endTransmission(); //transmit data
- #endif
- }
-
- // Set new text cursor position
- void TFTGLCD::setCursor(uint8_t col, uint8_t row) {
- fb = &framebuffer[0] + col + row * LCD_WIDTH;
- cour_line = row;
- }
-
- // Send char to buffer
- void TFTGLCD::write(char c) {
- *fb++ = c;
- }
-
- // Send text line to buffer
- void TFTGLCD::print(const char *line) {
- while (*line) *fb++ = *line++;
- }
-
- // For menu
- void TFTGLCD::print_line() {
- if (!PanelDetected) return;
- #if ENABLED(TFTGLCD_PANEL_SPI)
- WRITE(TFTGLCD_CS, LOW);
- SPI_SEND_ONE(LCD_PUT);
- SPI_SEND_ONE(cour_line);
- SPI_SEND_SOME(framebuffer, LCD_WIDTH, cour_line * LCD_WIDTH);
- WRITE(TFTGLCD_CS, HIGH);
- #else
- Wire.beginTransmission((uint8_t)LCD_I2C_ADDRESS); //set I2C device address
- Wire.write(LCD_PUT);
- Wire.write(cour_line);
- Wire.write(&framebuffer[cour_line * LCD_WIDTH], LCD_WIDTH); //transfer 1 line to txBuffer
- Wire.endTransmission(); //transmit data
- safe_delay(1);
- #endif
- }
-
- void TFTGLCD::print_screen() {
- if (!PanelDetected) return;
- framebuffer[FBSIZE - 2] = picBits & PIC_MASK;
- framebuffer[FBSIZE - 1] = ledBits;
- #if ENABLED(TFTGLCD_PANEL_SPI)
- // Send all framebuffer to panel
- WRITE(TFTGLCD_CS, LOW);
- SPI_SEND_ONE(LCD_WRITE);
- SPI_SEND_SOME(framebuffer, FBSIZE, 0);
- WRITE(TFTGLCD_CS, HIGH);
- #else
- uint8_t r;
- // Send framebuffer to panel by line
- Wire.beginTransmission((uint8_t)LCD_I2C_ADDRESS);
- // First line
- Wire.write(LCD_WRITE);
- Wire.write(&framebuffer[0], LCD_WIDTH);
- Wire.endTransmission();
- for (r = 1; r < (LCD_HEIGHT - 1); r++) {
- Wire.beginTransmission((uint8_t)LCD_I2C_ADDRESS);
- Wire.write(&framebuffer[r * LCD_WIDTH], LCD_WIDTH);
- Wire.endTransmission();
- }
- // Last line
- Wire.beginTransmission((uint8_t)LCD_I2C_ADDRESS);
- Wire.write(&framebuffer[r * LCD_WIDTH], LCD_WIDTH);
- Wire.write(&framebuffer[FBSIZE - 2], 2);
- Wire.endTransmission();
- #endif
- }
-
- void TFTGLCD::setContrast(uint16_t contrast) {
- if (!PanelDetected) return;
- #if ENABLED(TFTGLCD_PANEL_SPI)
- WRITE(TFTGLCD_CS, LOW);
- SPI_SEND_ONE(CONTRAST);
- SPI_SEND_ONE((uint8_t)contrast);
- WRITE(TFTGLCD_CS, HIGH);
- #else
- Wire.beginTransmission((uint8_t)LCD_I2C_ADDRESS);
- Wire.write(CONTRAST);
- Wire.write((uint8_t)contrast);
- Wire.endTransmission();
- #endif
- }
-
- extern volatile int8_t encoderDiff;
-
- // Read buttons and encoder states
- uint8_t MarlinUI::read_slow_buttons() {
- if (!PanelDetected) return 0;
- #if ENABLED(TFTGLCD_PANEL_SPI)
- uint8_t b = 0;
- WRITE(TFTGLCD_CS, LOW);
- SPI_SEND_ONE(READ_ENCODER);
- #ifndef STM32F4xx
- WRITE(TFTGLCD_CS, LOW); // for delay
- #endif
- encoderDiff += SPI_SEND_ONE(READ_BUTTONS);
- #ifndef STM32F4xx
- WRITE(TFTGLCD_CS, LOW); // for delay
- WRITE(TFTGLCD_CS, LOW);
- #endif
- b = SPI_SEND_ONE(GET_SPI_DATA);
- WRITE(TFTGLCD_CS, HIGH);
- return b;
- #else
- Wire.beginTransmission((uint8_t)LCD_I2C_ADDRESS);
- Wire.write(READ_ENCODER);
- Wire.endTransmission();
- #ifdef __AVR__
- Wire.requestFrom((uint8_t)LCD_I2C_ADDRESS, 2, 0, 0, 1);
- #elif defined(STM32F1)
- Wire.requestFrom((uint8_t)LCD_I2C_ADDRESS, (uint8_t)2);
- #elif EITHER(STM32F4xx, TARGET_LPC1768)
- Wire.requestFrom(LCD_I2C_ADDRESS, 2);
- #endif
- encoderDiff += Wire.read();
- return Wire.read(); //buttons
- #endif
- }
-
- // Duration in ms, freq in Hz
- void MarlinUI::buzz(const long duration, const uint16_t freq) {
- if (!PanelDetected) return;
- if (!sound_on) return;
- #if ENABLED(TFTGLCD_PANEL_SPI)
- WRITE(TFTGLCD_CS, LOW);
- SPI_SEND_ONE(BUZZER);
- SPI_SEND_TWO((uint16_t)duration);
- SPI_SEND_TWO(freq);
- WRITE(TFTGLCD_CS, HIGH);
- #else
- Wire.beginTransmission((uint8_t)LCD_I2C_ADDRESS);
- Wire.write(BUZZER);
- Wire.write((uint8_t)(duration >> 8));
- Wire.write((uint8_t)duration);
- Wire.write((uint8_t)(freq >> 8));
- Wire.write((uint8_t)freq);
- Wire.endTransmission();
- #endif
- }
-
- void MarlinUI::init_lcd() {
- uint8_t t;
- lcd.clear_buffer();
- t = 0;
- #if ENABLED(TFTGLCD_PANEL_SPI)
- // SPI speed must be less 10MHz
- SET_OUTPUT(TFTGLCD_CS);
- WRITE(TFTGLCD_CS, HIGH);
- spiInit(TERN(__STM32F1__, SPI_QUARTER_SPEED, SPI_FULL_SPEED));
- WRITE(TFTGLCD_CS, LOW);
- SPI_SEND_ONE(GET_LCD_ROW);
- t = SPI_SEND_ONE(GET_SPI_DATA);
- #else
- #ifdef TARGET_LPC1768
- Wire.begin(); //init twi/I2C
- #else
- Wire.begin((uint8_t)LCD_I2C_ADDRESS); //init twi/I2C
- #endif
- Wire.beginTransmission((uint8_t)LCD_I2C_ADDRESS);
- Wire.write((uint8_t)GET_LCD_ROW); // put command to buffer
- Wire.endTransmission(); // send buffer
- #ifdef __AVR__
- Wire.requestFrom((uint8_t)LCD_I2C_ADDRESS, 1, 0, 0, 1);
- #elif ANY(STM32F1, STM32F4xx, TARGET_LPC1768)
- Wire.requestFrom(LCD_I2C_ADDRESS, 1);
- #endif
- t = (uint8_t)Wire.read();
- #endif
-
- if (t == LCD_HEIGHT) {
- PanelDetected = 1;
- #if ENABLED(TFTGLCD_PANEL_SPI)
- SPI_SEND_ONE(INIT_SCREEN);
- SPI_SEND_ONE(Marlin);
- WRITE(TFTGLCD_CS, HIGH);
- #else
- Wire.beginTransmission((uint8_t)LCD_I2C_ADDRESS);
- Wire.write((uint8_t)INIT_SCREEN);
- Wire.write(Marlin);
- Wire.endTransmission();
- #endif
- }
- else
- PanelDetected = 0;
- safe_delay(100);
- }
-
- bool MarlinUI::detected() {
- return PanelDetected;
- }
-
- void MarlinUI::clear_lcd() {
- if (!PanelDetected) return;
- lcd.clr_screen();
- lcd.clear_buffer();
- }
-
- #if HAS_LCD_CONTRAST
- void MarlinUI::_set_contrast() { lcd.setContrast(contrast); }
- #endif
-
- #if !IS_TFTGLCD_PANEL
- void lcd_moveto(const uint8_t col, const uint8_t row) { lcd.setCursor(col, row); }
- #endif
-
- static void center_text(FSTR_P const fstart, const uint8_t y) {
- const uint8_t len = utf8_strlen(fstart);
- lcd_moveto(len < LCD_WIDTH ? (LCD_WIDTH - len) / 2 : 0, y);
- lcd_put_u8str(fstart);
- }
-
- #if ENABLED(SHOW_BOOTSCREEN)
-
- void MarlinUI::show_bootscreen() {
- if (!PanelDetected) return;
- //
- // Show the Marlin logo, splash line1, and splash line 2
- //
- uint8_t indent = (LCD_WIDTH - 8) / 2;
- // symbols 217 (bottom right corner) and 218 (top left corner) are using for letters in some languages
- // and they should be moved to beginning ASCII table as special symbols
- lcd_moveto(indent, 0); lcd.write(TLC); lcd_put_u8str(F("------")); lcd.write(TRC);
- lcd_moveto(indent, 1); lcd.write(LR); lcd_put_u8str(F("Marlin")); lcd.write(LR);
- lcd_moveto(indent, 2); lcd.write(BLC); lcd_put_u8str(F("------")); lcd.write(BRC);
- center_text(F(SHORT_BUILD_VERSION), 3);
- center_text(F(MARLIN_WEBSITE_URL), 4);
- picBits = ICON_LOGO;
- lcd.print_screen();
- }
-
- void MarlinUI::bootscreen_completion(const millis_t sofar) {
- if ((BOOTSCREEN_TIMEOUT) > sofar) safe_delay((BOOTSCREEN_TIMEOUT) - sofar);
- }
-
- #endif // SHOW_BOOTSCREEN
-
- void MarlinUI::draw_kill_screen() {
- if (!PanelDetected) return;
- lcd.clear_buffer();
- lcd_moveto(0, 3); lcd.write(COLOR_ERROR);
- lcd_moveto((LCD_WIDTH - utf8_strlen(status_message)) / 2 + 1, 3);
- lcd_put_u8str(status_message);
- center_text(GET_TEXT_F(MSG_HALTED), 5);
- center_text(GET_TEXT_F(MSG_PLEASE_RESET), 6);
- lcd.print_screen();
- }
-
- //
- // Before homing, blink '123' <-> '???'.
- // Homed but unknown... '123' <-> ' '.
- // Homed and known, display constantly.
- //
- FORCE_INLINE void _draw_axis_value(const AxisEnum axis, const char *value, const bool blink) {
- lcd.write('X' + uint8_t(axis));
- if (blink)
- lcd.print(value);
- else if (axis_should_home(axis))
- while (const char c = *value++) lcd.write(c <= '.' ? c : '?');
- else if (NONE(HOME_AFTER_DEACTIVATE, DISABLE_REDUCED_ACCURACY_WARNING) && !axis_is_trusted(axis))
- lcd_put_u8str(axis == Z_AXIS ? F(" ") : F(" "));
- else
- lcd_put_u8str(value);
- }
-
- #if HAS_HOTEND || HAS_HEATED_BED
-
- FORCE_INLINE void _draw_heater_status(const heater_id_t heater_id, const char *prefix, const bool blink) {
- uint8_t pic_hot_bits;
- #if HAS_HEATED_BED
- const bool isBed = heater_id < 0;
- const celsius_t t1 = (isBed ? thermalManager.wholeDegBed() : thermalManager.wholeDegHotend(heater_id)),
- t2 = (isBed ? thermalManager.degTargetBed() : thermalManager.degTargetHotend(heater_id));
- #else
- const celsius_t t1 = thermalManager.wholeDegHotend(heater_id), t2 = thermalManager.degTargetHotend(heater_id);
- #endif
-
- #if HOTENDS < 2
- if (heater_id == H_E0) {
- lcd_moveto(2, 5); lcd.print(prefix); //HE
- lcd_moveto(1, 6); lcd.print(i16tostr3rj(t1));
- lcd_moveto(1, 7);
- }
- else {
- lcd_moveto(6, 5); lcd.print(prefix); //BED
- lcd_moveto(6, 6); lcd.print(i16tostr3rj(t1));
- lcd_moveto(6, 7);
- }
- #else
- if (heater_id > H_BED) {
- lcd_moveto(heater_id * 4, 5); lcd.print(prefix); // HE1 or HE2 or HE3
- lcd_moveto(heater_id * 4, 6); lcd.print(i16tostr3rj(t1));
- lcd_moveto(heater_id * 4, 7);
- }
- else {
- lcd_moveto(13, 5); lcd.print(prefix); //BED
- lcd_moveto(13, 6); lcd.print(i16tostr3rj(t1));
- lcd_moveto(13, 7);
- }
- #endif // HOTENDS <= 1
-
- #if !HEATER_IDLE_HANDLER
- UNUSED(blink);
- #else
- if (!blink && thermalManager.heater_idle[thermalManager.idle_index_for_id(heater_id)].timed_out) {
- lcd.write(' ');
- if (t2 >= 10) lcd.write(' ');
- if (t2 >= 100) lcd.write(' ');
- }
- else
- #endif // !HEATER_IDLE_HANDLER
- lcd.print(i16tostr3rj(t2));
-
- switch (heater_id) {
- case H_BED: pic_hot_bits = ICON_BED; break;
- case H_E0: pic_hot_bits = ICON_TEMP1; break;
- case H_E1: pic_hot_bits = ICON_TEMP2; break;
- case H_E2: pic_hot_bits = ICON_TEMP3;
- default: break;
- }
-
- if (t2) picBits |= pic_hot_bits;
- else picBits &= ~pic_hot_bits;
-
- if (t1 > 50) hotBits |= pic_hot_bits;
- else hotBits &= ~pic_hot_bits;
-
- if (hotBits) picBits |= ICON_HOT;
- else picBits &= ~ICON_HOT;
- }
-
- #endif // HAS_HOTEND || HAS_HEATED_BED
-
- #if HAS_COOLER
-
- FORCE_INLINE void _draw_cooler_status(const bool blink) {
- const celsius_t t2 = thermalManager.degTargetCooler();
-
- lcd_moveto(0, 5); lcd_put_u8str(F("COOL"));
- lcd_moveto(1, 6); lcd_put_u8str(i16tostr3rj(thermalManager.wholeDegCooler()));
- lcd_moveto(1, 7);
-
- #if !HEATER_IDLE_HANDLER
- UNUSED(blink);
- #else
- if (!blink && thermalManager.heater_idle[thermalManager.idle_index_for_id(heater_id)].timed_out) {
- lcd_put_lchar(' ');
- if (t2 >= 10) lcd_put_lchar(' ');
- if (t2 >= 100) lcd_put_lchar(' ');
- }
- else
- #endif
- lcd_put_u8str(i16tostr3left(t2));
-
- lcd_put_lchar(' ');
- if (t2 < 10) lcd_put_lchar(' ');
-
- if (t2) picBits |= ICON_TEMP1;
- else picBits &= ~ICON_TEMP1;
- }
-
- #endif // HAS_COOLER
-
- #if ENABLED(LASER_COOLANT_FLOW_METER)
-
- FORCE_INLINE void _draw_flowmeter_status() {
- lcd_moveto(5, 5); lcd_put_u8str(F("FLOW"));
- lcd_moveto(7, 6); lcd_put_lchar('L');
- lcd_moveto(6, 7); lcd_put_u8str(ftostr11ns(cooler.flowrate));
-
- if (cooler.flowrate) picBits |= ICON_FAN;
- else picBits &= ~ICON_FAN;
- }
-
- #endif
-
- #if ENABLED(I2C_AMMETER)
-
- FORCE_INLINE void _draw_ammeter_status() {
- lcd_moveto(10, 5); lcd_put_u8str(F("ILAZ"));
- ammeter.read();
- lcd_moveto(11, 6);
- if (ammeter.current <= 0.999f)
- {
- lcd_put_u8str("mA");
- lcd_moveto(10, 7);
- lcd_put_lchar(' '); lcd_put_u8str(ui16tostr3rj(uint16_t(ammeter.current * 1000 + 0.5f)));
- }
- else {
- lcd_put_u8str(" A");
- lcd_moveto(10, 7);
- lcd_put_u8str(ftostr12ns(ammeter.current));
- }
-
- if (ammeter.current) picBits |= ICON_BED;
- else picBits &= ~ICON_BED;
- }
-
- #endif // I2C_AMMETER
-
- #if HAS_CUTTER
-
- FORCE_INLINE void _draw_cutter_status() {
- lcd_moveto(15, 5); lcd_put_u8str(F("CUTT"));
- #if CUTTER_UNIT_IS(RPM)
- lcd_moveto(16, 6); lcd_put_u8str(F("RPM"));
- lcd_moveto(15, 7); lcd_put_u8str(ftostr31ns(float(cutter.unitPower) / 1000));
- lcd_put_lchar('K');
- #elif CUTTER_UNIT_IS(PERCENT)
- lcd_moveto(17, 6); lcd_put_lchar('%');
- lcd_moveto(18, 7); lcd_put_u8str(cutter_power2str(cutter.unitPower));
- #else
- lcd_moveto(17, 7); lcd_put_u8str(cutter_power2str(cutter.unitPower));
- #endif
-
- if (cutter.unitPower) picBits |= ICON_HOT;
- else picBits &= ~ICON_HOT;
- }
-
- #endif // HAS_CUTTER
-
- #if HAS_PRINT_PROGRESS
-
- FORCE_INLINE void _draw_print_progress() {
- if (!PanelDetected) return;
- const uint8_t progress = ui._get_progress();
- #if ENABLED(SDSUPPORT)
- lcd_put_u8str(F("SD"));
- #elif ENABLED(SET_PROGRESS_PERCENT)
- lcd_put_u8str(F("P:"));
- #endif
- if (progress)
- lcd.print(ui8tostr3rj(progress));
- else
- lcd_put_u8str(F("---"));
- lcd.write('%');
- }
-
- #endif // HAS_PRINT_PROGRESS
-
- #if ENABLED(LCD_PROGRESS_BAR)
-
- void MarlinUI::draw_progress_bar(const uint8_t percent) {
- if (!PanelDetected) return;
- if (fb == &framebuffer[0] + LCD_WIDTH * 2) { // For status screen
- lcd.write('%'); lcd.write(percent);
- }
- else { // For progress bar test
- lcd_moveto(LCD_WIDTH / 2 - 2, MIDDLE_Y);
- lcd.print(i16tostr3rj(percent)); lcd.write('%');
- lcd.print_line();
- lcd_moveto(0, MIDDLE_Y + 1);
- lcd.write('%'); lcd.write(percent);
- lcd.print_line();
- }
- }
-
- #endif // LCD_PROGRESS_BAR
-
- void MarlinUI::draw_status_message(const bool blink) {
- if (!PanelDetected) return;
- lcd_moveto(0, 3);
- #if BOTH(FILAMENT_LCD_DISPLAY, SDSUPPORT)
-
- // Alternate Status message and Filament display
- if (ELAPSED(millis(), next_filament_display)) {
- lcd_put_u8str(F("Dia "));
- lcd.print(ftostr12ns(filament_width_meas));
- lcd_put_u8str(F(" V"));
- lcd.print(i16tostr3rj(100.0 * (
- parser.volumetric_enabled
- ? planner.volumetric_area_nominal / planner.volumetric_multiplier[FILAMENT_SENSOR_EXTRUDER_NUM]
- : planner.volumetric_multiplier[FILAMENT_SENSOR_EXTRUDER_NUM]
- )
- ));
- lcd.write('%');
- return;
- }
-
- #endif // FILAMENT_LCD_DISPLAY && SDSUPPORT
-
- // Get the UTF8 character count of the string
- uint8_t slen = utf8_strlen(status_message);
-
- #if ENABLED(STATUS_MESSAGE_SCROLLING)
-
- static bool last_blink = false;
-
- // If the string fits into the LCD, just print it and do not scroll it
- if (slen <= LCD_WIDTH) {
-
- // The string isn't scrolling and may not fill the screen
- lcd_put_u8str(status_message);
-
- // Fill the rest with spaces
- while (slen < LCD_WIDTH) { lcd.write(' '); ++slen; }
- }
- else {
- // String is larger than the available space in screen.
-
- // Get a pointer to the next valid UTF8 character
- // and the string remaining length
- uint8_t rlen;
- const char *stat = status_and_len(rlen);
- lcd_put_u8str_max(stat, LCD_WIDTH); // The string leaves space
-
- // If the remaining string doesn't completely fill the screen
- if (rlen < LCD_WIDTH) {
- uint8_t chars = LCD_WIDTH - rlen; // Amount of space left in characters
- lcd.write(' '); // Always at 1+ spaces left, draw a space
- if (--chars) { // Draw a second space if there's room
- lcd.write(' ');
- if (--chars) { // Draw a third space if there's room
- lcd.write(' ');
- if (--chars)
- lcd_put_u8str_max(status_message, chars); // Print a second copy of the message
- }
- }
- }
- if (last_blink != blink) {
- last_blink = blink;
- advance_status_scroll();
- }
- }
-
- #else
-
- UNUSED(blink);
-
- // Just print the string to the LCD
- lcd_put_u8str_max(status_message, LCD_WIDTH);
-
- // Fill the rest with spaces if there are missing spaces
- while (slen < LCD_WIDTH) {
- lcd.write(' ');
- ++slen;
- }
-
- #endif
- }
-
- /**
- Possible status screens:
-
- Equal to 20x10 text LCD
-
- |X 000 Y 000 Z 000.00|
- |FR100% SD100% C--:--|
- | Progress bar line |
- |Status message |
- | |
- | HE BED FAN |
- | ttc ttc % | ttc - current temperature
- | tts tts %%% | tts - set temperature, %%% - percent for FAN
- | ICO ICO ICO ICO | ICO - icon 48x48, placed in 2 text lines
- | ICO ICO ICO ICO | ICO
-
- or
-
- |X 000 Y 000 Z 000.00|
- |FR100% SD100% C--:--|
- | Progress bar line |
- |Status message |
- | |
- |HE1 HE2 HE3 BED ICO|
- |ttc ttc ttc ttc ICO|
- |tts tts tts tts %%%|
- |ICO ICO ICO ICO ICO|
- |ICO ICO ICO ICO ICO|
-
- or
-
- |X 000 Y 000 Z 000.00|
- |FR100% SD100% C--:--|
- | Progress bar line |
- |Status message |
- | |
- |COOL FLOW ILAZ CUTT |
- | ttc L mA RPM |
- | tts f.f aaa rr.rK|
- | ICO ICO ICO ICO |
- | ICO ICO ICO ICO |
-
- or
-
- Equal to 24x10 text LCD
-
- |X 000 Y 000 Z 000.00 |
- |FR100% SD100% C--:--|
- | Progress bar line |
- |Status message |
- | |
- |HE1 HE2 HE3 BED FAN |
- |ttc ttc ttc ttc % |
- |tts tts tts tts %%% |
- |ICO ICO ICO ICO ICO ICO|
- |ICO ICO ICO ICO ICO ICO|
- */
-
- void MarlinUI::draw_status_screen() {
- if (!PanelDetected) return;
-
- const bool blink = get_blink();
-
- lcd.clear_buffer();
-
- //
- // Line 1 - XYZ coordinates
- //
-
- lcd_moveto(0, 0);
- const xyz_pos_t lpos = current_position.asLogical();
- _draw_axis_value(X_AXIS, ftostr4sign(lpos.x), blink); lcd.write(' ');
- _draw_axis_value(Y_AXIS, ftostr4sign(lpos.y), blink); lcd.write(' ');
- _draw_axis_value(Z_AXIS, ftostr52sp(lpos.z), blink);
-
- #if HAS_LEVELING && !HAS_HEATED_BED
- lcd.write(planner.leveling_active || blink ? '_' : ' ');
- #endif
-
- //
- // Line 2 - feedrate, , time
- //
-
- lcd_moveto(0, 1);
- lcd_put_u8str(F("FR")); lcd.print(i16tostr3rj(feedrate_percentage)); lcd.write('%');
-
- #if BOTH(SDSUPPORT, HAS_PRINT_PROGRESS)
- lcd_moveto(LCD_WIDTH / 2 - 3, 1);
- _draw_print_progress();
- #endif
-
- char buffer[10];
- duration_t elapsed = print_job_timer.duration();
- uint8_t len = elapsed.toDigital(buffer);
-
- lcd_moveto((LCD_WIDTH - 1) - len, 1);
- lcd.write(LCD_STR_CLOCK[0]); lcd.print(buffer);
-
- //
- // Line 3 - progressbar
- //
-
- lcd_moveto(0, 2);
- #if ENABLED(LCD_PROGRESS_BAR)
- draw_progress_bar(_get_progress());
- #else
- lcd.write('%'); lcd.write(0);
- #endif
-
- //
- // Line 4 - Status Message (which may be a Filament display)
- //
-
- draw_status_message(blink);
-
- //
- // Line 5
- //
-
- #if HOTENDS <= 1 || (HOTENDS <= 2 && !HAS_HEATED_BED)
- #if DUAL_MIXING_EXTRUDER
- lcd_moveto(0, 4);
- // Two-component mix / gradient instead of XY
- char mixer_messages[12];
- const char *mix_label;
- #if ENABLED(GRADIENT_MIX)
- if (mixer.gradient.enabled) {
- mixer.update_mix_from_gradient();
- mix_label = "Gr";
- }
- else
- #endif
- {
- mixer.update_mix_from_vtool();
- mix_label = "Mx";
- }
- sprintf_P(mixer_messages, PSTR("%s %d;%d%% "), mix_label, int(mixer.mix[0]), int(mixer.mix[1]));
- lcd_put_u8str(mixer_messages);
- #endif
- #endif
-
- //
- // Line 6..8 Temperatures, FAN for printer or Cooler, Flowmetter, Ampermeter, Cutter for laser/spindle
- //
-
- #if HAS_HOTEND
-
- #if HOTENDS < 2
- _draw_heater_status(H_E0, "HE", blink); // Hotend Temperature
- #else
- _draw_heater_status(H_E0, "HE1", blink); // Hotend 1 Temperature
- _draw_heater_status(H_E1, "HE2", blink); // Hotend 2 Temperature
- #if HOTENDS > 2
- _draw_heater_status(H_E2, "HE3", blink); // Hotend 3 Temperature
- #endif
- #endif
-
- #if HAS_HEATED_BED
- #if HAS_LEVELING
- _draw_heater_status(H_BED, (planner.leveling_active && blink ? "___" : "BED"), blink);
- #else
- _draw_heater_status(H_BED, "BED", blink);
- #endif
- #endif
-
- #if HAS_FAN
- uint16_t spd = thermalManager.fan_speed[0];
- #if ENABLED(ADAPTIVE_FAN_SLOWING)
- if (!blink) spd = thermalManager.scaledFanSpeed(0, spd);
- #endif
- uint16_t per = thermalManager.pwmToPercent(spd);
-
- #if HOTENDS < 2
- #define FANX 11
- #else
- #define FANX 17
- #endif
- lcd_moveto(FANX, 5); lcd_put_u8str(F("FAN"));
- lcd_moveto(FANX + 1, 6); lcd.write('%');
- lcd_moveto(FANX, 7);
- lcd.print(i16tostr3rj(per));
-
- if (TERN0(HAS_FAN0, thermalManager.fan_speed[0]) || TERN0(HAS_FAN1, thermalManager.fan_speed[1]) || TERN0(HAS_FAN2, thermalManager.fan_speed[2]))
- picBits |= ICON_FAN;
- else
- picBits &= ~ICON_FAN;
-
- #endif // HAS_FAN
-
- #else
-
- TERN_(HAS_COOLER, _draw_cooler_status(blink));
- TERN_(LASER_COOLANT_FLOW_METER, _draw_flowmeter_status());
- TERN_(I2C_AMMETER, _draw_ammeter_status());
- TERN_(HAS_CUTTER, _draw_cutter_status());
-
- #endif
-
- //
- // Line 9, 10 - icons
- //
- lcd.print_screen();
- }
-
- #if HAS_MARLINUI_MENU
-
- #include "../menu/menu.h"
-
- #if ENABLED(ADVANCED_PAUSE_FEATURE)
-
- void MarlinUI::draw_hotend_status(const uint8_t row, const uint8_t extruder) {
- if (!PanelDetected) return;
- lcd_moveto((LCD_WIDTH - 14) / 2, row + 1);
- lcd.write(LCD_STR_THERMOMETER[0]); lcd_put_u8str(F(" E")); lcd.write('1' + extruder); lcd.write(' ');
- lcd.print(i16tostr3rj(thermalManager.wholeDegHotend(extruder))); lcd.write(LCD_STR_DEGREE[0]); lcd.write('/');
- lcd.print(i16tostr3rj(thermalManager.degTargetHotend(extruder))); lcd.write(LCD_STR_DEGREE[0]);
- lcd.print_line();
- }
-
- #endif
-
- // Draw a static item with no left-right margin required. Centered by default.
- void MenuItem_static::draw(const uint8_t row, FSTR_P const fstr, const uint8_t style/*=SS_DEFAULT*/, const char * const valstr/*=nullptr*/) {
- if (!PanelDetected) return;
- uint8_t n = LCD_WIDTH;
- lcd_moveto(0, row);
- if ((style & SS_CENTER) && !valstr) {
- int8_t pad = (LCD_WIDTH - utf8_strlen(fstr)) / 2;
- while (--pad >= 0) { lcd.write(' '); n--; }
- }
- n = lcd_put_u8str(fstr, itemIndex, itemStringC, itemStringF, n);
- if (valstr) n -= lcd_put_u8str_max(valstr, n);
- for (; n; --n) lcd.write(' ');
- lcd.print_line();
- }
-
- // Draw a generic menu item with pre_char (if selected) and post_char
- void MenuItemBase::_draw(const bool sel, const uint8_t row, FSTR_P const fstr, const char pre_char, const char post_char) {
- if (!PanelDetected) return;
- lcd_moveto(0, row);
- lcd.write(sel ? pre_char : ' ');
- uint8_t n = lcd_put_u8str(fstr, itemIndex, itemStringC, itemStringF, LCD_WIDTH - 2);
- for (; n; --n) lcd.write(' ');
- lcd.write(post_char);
- lcd.print_line();
- }
-
- // Draw a menu item with a (potentially) editable value
- void MenuEditItemBase::draw(const bool sel, const uint8_t row, FSTR_P const fstr, const char * const inStr, const bool pgm) {
- if (!PanelDetected) return;
- const uint8_t vlen = inStr ? (pgm ? utf8_strlen_P(inStr) : utf8_strlen(inStr)) : 0;
- lcd_moveto(0, row);
- lcd.write(sel ? LCD_STR_ARROW_RIGHT[0] : ' ');
- uint8_t n = lcd_put_u8str(fstr, itemIndex, itemStringC, itemStringF, LCD_WIDTH - 2 - vlen);
- if (vlen) {
- lcd.write(':');
- for (; n; --n) lcd.write(' ');
- if (pgm) lcd_put_u8str_P(inStr); else lcd_put_u8str(inStr);
- }
- lcd.print_line();
- }
-
- // Low-level draw_edit_screen can be used to draw an edit screen from anyplace
- // This line moves to the last line of the screen for UBL plot screen on the panel side
- void MenuEditItemBase::draw_edit_screen(FSTR_P const fstr, const char * const value/*=nullptr*/) {
- if (!PanelDetected) return;
- ui.encoder_direction_normal();
- const uint8_t y = TERN0(AUTO_BED_LEVELING_UBL, ui.external_control) ? LCD_HEIGHT - 1 : MIDDLE_Y;
- lcd_moveto(0, y);
- lcd.write(COLOR_EDIT);
- lcd_put_u8str(fstr);
- if (value) {
- lcd.write(':');
- lcd_moveto((LCD_WIDTH - 1) - (utf8_strlen(value) + 1), y); // Right-justified, padded by spaces
- lcd.write(' '); // Overwrite char if value gets shorter
- lcd.print(value);
- lcd.write(' ');
- lcd.print_line();
- }
- }
-
- // The Select Screen presents a prompt and two "buttons"
- void MenuItem_confirm::draw_select_screen(FSTR_P const yes, FSTR_P const no, const bool yesno, FSTR_P const pref, const char * const string, FSTR_P const suff) {
- if (!PanelDetected) return;
- ui.draw_select_screen_prompt(pref, string, suff);
- lcd.write(COLOR_EDIT);
- if (no) {
- lcd_moveto(0, MIDDLE_Y);
- lcd.write(yesno ? ' ' : '['); lcd_put_u8str(no); lcd.write(yesno ? ' ' : ']');
- }
- if (yes) {
- lcd_moveto(LCD_WIDTH - utf8_strlen(yes) - 3, MIDDLE_Y);
- lcd.write(yesno ? '[' : ' '); lcd_put_u8str(yes); lcd.write(yesno ? ']' : ' ');
- }
- lcd.print_line();
- }
-
- #if ENABLED(SDSUPPORT)
-
- void MenuItem_sdbase::draw(const bool sel, const uint8_t row, FSTR_P const, CardReader &theCard, const bool isDir) {
- if (!PanelDetected) return;
- lcd_moveto(0, row);
- lcd.write(sel ? LCD_STR_ARROW_RIGHT[0] : ' ');
- constexpr uint8_t maxlen = LCD_WIDTH - 2;
- uint8_t n = maxlen - lcd_put_u8str_max(ui.scrolled_filename(theCard, maxlen, row, sel), maxlen);
- for (; n; --n) lcd.write(' ');
- lcd.write(isDir ? LCD_STR_FOLDER[0] : ' ');
- lcd.print_line();
- }
-
- #endif // SDSUPPORT
-
- #if ENABLED(LCD_HAS_STATUS_INDICATORS)
-
- void MarlinUI::update_indicators() {}
-
- #endif // LCD_HAS_STATUS_INDICATORS
-
- #if ENABLED(AUTO_BED_LEVELING_UBL)
- /**
- * Map screen:
- * |/---------\ (00,00) |
- * || . . . . | X:000.00|
- * || . . . . | Y:000.00|
- * || . . . . | Z:00.000|
- * || . . . . | |
- * || . . . . | |
- * || . . . . | |
- * |+---------/ |
- * | |
- * |____________________|
- */
- void MarlinUI::ubl_plot(const uint8_t x_plot, const uint8_t y_plot) {
- if (!PanelDetected) return;
-
- #define _LCD_W_POS 12
-
- lcd.clear_buffer();
-
- //print only top left corner. All frame with grid points will be printed by panel
- lcd_moveto(0, 0);
- *fb++ = TLC; //top left corner - marker for plot parameters
- *fb = (GRID_MAX_POINTS_X << 4) + GRID_MAX_POINTS_Y; //set mesh size
-
- // Print plot position
- lcd_moveto(_LCD_W_POS, 0);
- *fb++ = '('; lcd.print(i16tostr3left(x_plot));
- *fb++ = ','; lcd.print(i16tostr3left(y_plot)); *fb = ')';
-
- // Show all values
- lcd_moveto(_LCD_W_POS, 1); lcd_put_u8str(F("X:"));
- lcd.print(ftostr52(LOGICAL_X_POSITION(pgm_read_float(&bedlevel._mesh_index_to_xpos[x_plot]))));
- lcd_moveto(_LCD_W_POS, 2); lcd_put_u8str(F("Y:"));
- lcd.print(ftostr52(LOGICAL_Y_POSITION(pgm_read_float(&bedlevel._mesh_index_to_ypos[y_plot]))));
-
- // Show the location value
- lcd_moveto(_LCD_W_POS, 3); lcd_put_u8str(F("Z:"));
-
- if (!isnan(bedlevel.z_values[x_plot][y_plot]))
- lcd.print(ftostr43sign(bedlevel.z_values[x_plot][y_plot]));
- else
- lcd_put_u8str(F(" -----"));
-
- center_text(GET_TEXT_F(MSG_UBL_FINE_TUNE_MESH), 8);
-
- lcd.print_screen();
- }
-
- #endif // AUTO_BED_LEVELING_UBL
-
- #endif // HAS_MARLINUI_MENU
-
- #endif // IS_TFTGLCD_PANEL
|