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
, (void**)&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 void *WatchScreen::getTemperatures(uint16_t heater_cs
)
184 bool ok
= PublicData::get_value( temperature_control_checksum
, heater_cs
, current_temperature_checksum
, &returned_data
);
187 return returned_data
;
193 // fetch the data we are displaying
194 void WatchScreen::get_current_status()
200 ok
= PublicData::get_value( switch_checksum
, fan_checksum
, 0, &returned_data
);
202 struct pad_switch s
= *static_cast<struct pad_switch
*>(returned_data
);
203 this->fan_state
= s
.state
;
205 // fan probably disabled
206 this->fan_state
= false;
210 // fetch the data we are displaying
211 float WatchScreen::get_current_speed()
215 bool ok
= PublicData::get_value( robot_checksum
, speed_override_percent_checksum
, &returned_data
);
217 float cs
= *static_cast<float *>(returned_data
);
223 void WatchScreen::get_current_pos(float *cp
)
227 bool ok
= PublicData::get_value( robot_checksum
, current_position_checksum
, &returned_data
);
229 float *p
= static_cast<float *>(returned_data
);
236 void WatchScreen::get_sd_play_info()
239 bool ok
= PublicData::get_value( player_checksum
, get_progress_checksum
, &returned_data
);
241 struct pad_progress p
= *static_cast<struct pad_progress
*>(returned_data
);
242 this->elapsed_time
= p
.elapsed_secs
;
243 this->sd_pcnt_played
= p
.percent_complete
;
244 THEPANEL
->set_playing_file(p
.filename
);
247 this->elapsed_time
= 0;
248 this->sd_pcnt_played
= 0;
252 void WatchScreen::display_menu_line(uint16_t line
)
258 auto& tm
= this->temp_controllers
;
260 // only if we detected heaters in config
263 // more than two temps we need to cycle between them
264 n
= update_counts
/100; // increments every 5 seconds
265 int ntemps
= (tm
.size()+1)/2;
266 n
= n
%ntemps
; // which of the pairs of temps to display
270 for (size_t i
= 0; i
< 2; ++i
) {
272 if(o
>tm
.size()-1) break;
273 struct pad_temperature
*temp
= static_cast<struct pad_temperature
*>(getTemperatures(tm
[o
]));
274 if(temp
== nullptr) continue;
275 int t
= std::min(999, (int)roundf(temp
->current_temperature
));
276 int tt
= roundf(temp
->target_temperature
);
277 THEPANEL
->lcd
->setCursor(off
, 0); // col, row
278 off
+= THEPANEL
->lcd
->printf("%s:%03d/%03d ", temp
->designator
.substr(0, 2).c_str(), t
, tt
);
282 //THEPANEL->lcd->printf("No Heaters");
286 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;
287 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;
288 case 3: THEPANEL
->lcd
->printf("%19s", this->get_status()); break;
292 const char *WatchScreen::get_status()
294 if (THEPANEL
->hasMessage())
295 return THEPANEL
->getMessage().c_str();
297 if (THEPANEL
->is_halted())
298 return "HALTED Reset or M999";
300 if (THEKERNEL
->pauser
->paused())
303 if (THEPANEL
->is_suspended())
306 if (THEPANEL
->is_playing())
307 return THEPANEL
->get_playing_file();
309 if (!THEKERNEL
->conveyor
->is_queue_empty())
312 const char *ip
= get_network();
314 return "Smoothie ready";
320 void WatchScreen::set_speed()
322 send_gcode("M220", 'S', this->current_speed
);
325 const char *WatchScreen::get_network()
329 bool ok
= PublicData::get_value( network_checksum
, get_ip_checksum
, &returned_data
);
331 uint8_t *ipaddr
= (uint8_t *)returned_data
;
333 int n
= snprintf(buf
, sizeof(buf
), "IP %d.%d.%d.%d", ipaddr
[0], ipaddr
[1], ipaddr
[2], ipaddr
[3]);
335 if (this->ipstr
== nullptr) {
336 this->ipstr
= new char[n
+ 1];
338 strcpy(this->ipstr
, buf
);