2 This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
3 Smoothie 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.
4 Smoothie 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.
5 You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>.
8 #include "libs/Kernel.h"
11 #include "PanelScreen.h"
12 #include "MainMenuScreen.h"
13 #include "WatchScreen.h"
14 #include "libs/nuts_bolts.h"
15 #include "libs/utils.h"
16 #include "modules/tools/temperaturecontrol/TemperatureControlPublicAccess.h"
17 #include "modules/robot/RobotPublicAccess.h"
18 #include "modules/robot/Conveyor.h"
19 #include "modules/utils/player/PlayerPublicAccess.h"
20 #include "NetworkPublicAccess.h"
21 #include "PublicData.h"
22 #include "SwitchPublicAccess.h"
23 #include "checksumm.h"
25 #include "TemperatureControlPool.h"
35 static const uint8_t icons
[] = { // 115x19 - 3 bytes each: he1, he2, he3, bed, fan
36 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0,
37 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xE0,
38 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x0C, 0x60,
39 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x0E, 0x20,
40 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x0F, 0x20,
41 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x0F, 0xA0,
42 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x07, 0xA0,
43 0x7F, 0x80, 0x00, 0x3F, 0xC0, 0x00, 0x3F, 0xC0, 0x00, 0x41, 0x04, 0x00, 0x40, 0x60, 0x20,
44 0xFB, 0xC0, 0x00, 0x79, 0xE0, 0x00, 0x79, 0xE0, 0x00, 0x20, 0x82, 0x00, 0x40, 0xF0, 0x20,
45 0xF3, 0xC0, 0x00, 0x76, 0xE0, 0x00, 0x76, 0xE0, 0x00, 0x20, 0x82, 0x00, 0x40, 0xF0, 0x20,
46 0xEB, 0xC0, 0x00, 0x7E, 0xE0, 0x00, 0x7E, 0xE0, 0x00, 0x41, 0x04, 0x00, 0x40, 0x60, 0x20,
47 0x7B, 0x80, 0x00, 0x3D, 0xC0, 0x00, 0x39, 0xC0, 0x00, 0x82, 0x08, 0x00, 0x5E, 0x07, 0xA0,
48 0x7B, 0x80, 0x00, 0x3B, 0xC0, 0x00, 0x3E, 0xC0, 0x01, 0x04, 0x10, 0x00, 0x5F, 0x0F, 0xA0,
49 0xFB, 0xC0, 0x00, 0x77, 0xE0, 0x00, 0x76, 0xE0, 0x01, 0x04, 0x10, 0x00, 0x4F, 0x0F, 0x20,
50 0xFB, 0xC0, 0x00, 0x70, 0xE0, 0x00, 0x79, 0xE0, 0x00, 0x82, 0x08, 0x00, 0x47, 0x0E, 0x20,
51 0xFF, 0xC0, 0x00, 0x7F, 0xE0, 0x00, 0x7F, 0xE0, 0x00, 0x41, 0x04, 0x00, 0x63, 0x0C, 0x60,
52 0x3F, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xE0,
53 0x1E, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x0F, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xE0,
54 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00
59 WatchScreen::WatchScreen()
61 speed_changed
= false;
62 issue_change_speed
= false;
67 WatchScreen::~WatchScreen()
72 void WatchScreen::on_enter()
74 THEPANEL
->lcd
->clear();
75 THEPANEL
->setup_menu(4);
77 get_current_pos(this->pos
);
79 this->current_speed
= lround(get_current_speed());
80 this->refresh_screen(false);
81 THEPANEL
->enter_control_mode(1, 0.5);
82 THEPANEL
->set_control_value(this->current_speed
);
85 void WatchScreen::on_refresh()
87 // Exit if the button is clicked
88 if ( THEPANEL
->click() ) {
89 THEPANEL
->enter_screen(this->parent
);
93 // see if speed is being changed
94 if (THEPANEL
->control_value_change()) {
95 this->current_speed
= THEPANEL
->get_control_value();
96 if (this->current_speed
< 10) {
97 this->current_speed
= 10;
98 THEPANEL
->set_control_value(this->current_speed
);
99 THEPANEL
->reset_counter();
101 // flag the update to change the speed, we don't want to issue hundreds of M220s
102 // but we do want to display the change we are going to make
103 this->speed_changed
= true; // flag indicating speed changed
104 this->refresh_screen(false);
108 // Update Only every 20 refreshes, 1 a second
110 if ( update_counts
% 20 == 0 ) {
112 get_current_pos(this->pos
);
113 get_current_status();
114 if (this->speed_changed
) {
115 this->issue_change_speed
= true; // trigger actual command to change speed
116 this->speed_changed
= false;
117 } else if (!this->issue_change_speed
) { // change still queued
118 // read it in case it was changed via M220
119 this->current_speed
= lround(get_current_speed());
120 THEPANEL
->set_control_value(this->current_speed
);
121 THEPANEL
->reset_counter();
124 this->refresh_screen(THEPANEL
->lcd
->hasGraphics() ? true : false); // graphics screens should be cleared
126 // for LCDs with leds set them according to heater status
127 bool bed_on
= false, hotend_on
= false, is_hot
= false;
128 uint8_t heon
=0, hemsk
= 0x01; // bit set for which hotend is on bit0: hotend1, bit1: hotend2 etc
129 temp_controllers
.clear();
130 std::vector
<struct pad_temperature
> controllers
;
131 bool ok
= PublicData::get_value(temperature_control_checksum
, poll_controls_checksum
, &controllers
);
133 for (auto &c
: controllers
) {
134 temp_controllers
.push_back(c
.id
);
135 if(c
.current_temperature
> 50) is_hot
= true; // anything is hot
136 if(c
.designator
.front() == 'B' && c
.target_temperature
> 0) bed_on
= true; // bed on/off
137 if(c
.designator
.front() == 'T') { // a hotend by convention
138 if(c
.target_temperature
> 0){
139 hotend_on
= true;// hotend on/off (anyone)
146 THEPANEL
->lcd
->setLed(LED_BED_ON
, bed_on
);
147 THEPANEL
->lcd
->setLed(LED_HOTEND_ON
, hotend_on
);
148 THEPANEL
->lcd
->setLed(LED_HOT
, is_hot
);
150 THEPANEL
->lcd
->setLed(LED_FAN_ON
, this->fan_state
);
152 if (THEPANEL
->lcd
->hasGraphics()) {
153 // display the graphical icons below the status are
154 //THEPANEL->lcd->bltGlyph(0, 34, 115, 19, icons);
155 // for (int i = 0; i < 5; ++i) {
156 // THEPANEL->lcd->bltGlyph(i*24, 38, 23, 19, icons, 15, i*24, 0);
158 if(heon
&0x01) THEPANEL
->lcd
->bltGlyph(0, 38, 20, 19, icons
, 15, 0, 0);
159 if(heon
&0x02) THEPANEL
->lcd
->bltGlyph(20, 38, 20, 19, icons
, 15, 24, 0);
160 if(heon
&0x04) THEPANEL
->lcd
->bltGlyph(40, 38, 20, 19, icons
, 15, 48, 0);
163 THEPANEL
->lcd
->bltGlyph(60, 38, 23, 19, icons
, 15, 64, 0);
166 THEPANEL
->lcd
->bltGlyph(96, 38, 23, 19, icons
, 15, 96, 0);
171 // queuing gcodes needs to be done from main loop
172 void WatchScreen::on_main_loop()
174 if (this->issue_change_speed
) {
175 this->issue_change_speed
= false;
178 PanelScreen::on_main_loop(); // in case any queued commands left
181 // fetch the data we are displaying
182 void WatchScreen::get_current_status()
188 ok
= PublicData::get_value( switch_checksum
, fan_checksum
, 0, &returned_data
);
190 struct pad_switch s
= *static_cast<struct pad_switch
*>(returned_data
);
191 this->fan_state
= s
.state
;
193 // fan probably disabled
194 this->fan_state
= false;
198 // fetch the data we are displaying
199 float WatchScreen::get_current_speed()
203 bool ok
= PublicData::get_value( robot_checksum
, speed_override_percent_checksum
, &returned_data
);
205 float cs
= *static_cast<float *>(returned_data
);
211 void WatchScreen::get_current_pos(float *cp
)
215 bool ok
= PublicData::get_value( robot_checksum
, current_position_checksum
, &returned_data
);
217 float *p
= static_cast<float *>(returned_data
);
224 void WatchScreen::get_sd_play_info()
227 bool ok
= PublicData::get_value( player_checksum
, get_progress_checksum
, &returned_data
);
229 struct pad_progress p
= *static_cast<struct pad_progress
*>(returned_data
);
230 this->elapsed_time
= p
.elapsed_secs
;
231 this->sd_pcnt_played
= p
.percent_complete
;
232 THEPANEL
->set_playing_file(p
.filename
);
235 this->elapsed_time
= 0;
236 this->sd_pcnt_played
= 0;
240 static struct pad_temperature
getTemperatures(uint16_t heater_cs
)
242 struct pad_temperature temp
;
243 PublicData::get_value( temperature_control_checksum
, heater_cs
, current_temperature_checksum
, &temp
);
247 void WatchScreen::display_menu_line(uint16_t line
)
253 auto& tm
= this->temp_controllers
;
255 // only if we detected heaters in config
258 // more than two temps we need to cycle between them
259 n
= update_counts
/100; // increments every 5 seconds
260 int ntemps
= (tm
.size()+1)/2;
261 n
= n
%ntemps
; // which of the pairs of temps to display
265 for (size_t i
= 0; i
< 2; ++i
) {
267 if(o
>tm
.size()-1) break;
268 struct pad_temperature temp
= getTemperatures(tm
[o
]);
269 int t
= std::min(999, (int)roundf(temp
.current_temperature
));
270 int tt
= roundf(temp
.target_temperature
);
271 THEPANEL
->lcd
->setCursor(off
, 0); // col, row
272 off
+= THEPANEL
->lcd
->printf("%s:%03d/%03d ", temp
.designator
.substr(0, 2).c_str(), t
, tt
);
276 //THEPANEL->lcd->printf("No Heaters");
280 case 1: THEPANEL
->lcd
->printf("X%4d Y%4d Z%7.2f", (int)round(this->pos
[0]), (int)round(this->pos
[1]), this->pos
[2]); break;
281 case 2: THEPANEL
->lcd
->printf("%3d%% %2lu:%02lu %3u%% sd", this->current_speed
, this->elapsed_time
/ 60, this->elapsed_time
% 60, this->sd_pcnt_played
); break;
282 case 3: THEPANEL
->lcd
->printf("%19s", this->get_status()); break;
286 const char *WatchScreen::get_status()
288 if (THEPANEL
->hasMessage())
289 return THEPANEL
->getMessage().c_str();
291 if (THEPANEL
->is_halted())
292 return "HALTED Reset or M999";
294 if (THEKERNEL
->pauser
->paused())
297 if (THEPANEL
->is_suspended())
300 if (THEPANEL
->is_playing())
301 return THEPANEL
->get_playing_file();
303 if (!THEKERNEL
->conveyor
->is_queue_empty())
306 const char *ip
= get_network();
308 return "Smoothie ready";
314 void WatchScreen::set_speed()
316 send_gcode("M220", 'S', this->current_speed
);
319 const char *WatchScreen::get_network()
323 bool ok
= PublicData::get_value( network_checksum
, get_ip_checksum
, &returned_data
);
325 uint8_t *ipaddr
= (uint8_t *)returned_data
;
327 int n
= snprintf(buf
, sizeof(buf
), "IP %d.%d.%d.%d", ipaddr
[0], ipaddr
[1], ipaddr
[2], ipaddr
[3]);
329 if (this->ipstr
== nullptr) {
330 this->ipstr
= new char[n
+ 1];
332 strcpy(this->ipstr
, buf
);