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"
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
57 WatchScreen::WatchScreen()
59 speed_changed
= false;
60 issue_change_speed
= false;
65 WatchScreen::~WatchScreen()
70 void WatchScreen::on_enter()
72 THEPANEL
->lcd
->clear();
73 THEPANEL
->setup_menu(4);
75 get_current_pos(this->pos
);
77 this->current_speed
= lround(get_current_speed());
78 this->refresh_screen(false);
79 THEPANEL
->enter_control_mode(1, 0.5);
80 THEPANEL
->set_control_value(this->current_speed
);
82 // enumerate temperature controls
83 temp_controllers
.clear();
84 std::vector
<struct pad_temperature
> controllers
;
85 bool ok
= PublicData::get_value(temperature_control_checksum
, poll_controls_checksum
, &controllers
);
87 for (auto &c
: controllers
) {
88 temp_controllers
.push_back(c
.id
);
93 static struct pad_temperature
getTemperatures(uint16_t heater_cs
)
95 struct pad_temperature temp
;
96 PublicData::get_value( temperature_control_checksum
, current_temperature_checksum
, heater_cs
, &temp
);
100 void WatchScreen::on_refresh()
102 // Exit if the button is clicked
103 if ( THEPANEL
->click() ) {
104 THEPANEL
->enter_screen(this->parent
);
108 // see if speed is being changed
109 if (THEPANEL
->control_value_change()) {
110 this->current_speed
= THEPANEL
->get_control_value();
111 if (this->current_speed
< 10) {
112 this->current_speed
= 10;
113 THEPANEL
->set_control_value(this->current_speed
);
114 THEPANEL
->reset_counter();
116 // flag the update to change the speed, we don't want to issue hundreds of M220s
117 // but we do want to display the change we are going to make
118 this->speed_changed
= true; // flag indicating speed changed
119 this->refresh_screen(false);
123 // Update Only every 20 refreshes, 1 a second
125 if ( update_counts
% 20 == 0 ) {
127 get_current_pos(this->pos
);
128 get_current_status();
129 if (this->speed_changed
) {
130 this->issue_change_speed
= true; // trigger actual command to change speed
131 this->speed_changed
= false;
132 } else if (!this->issue_change_speed
) { // change still queued
133 // read it in case it was changed via M220
134 this->current_speed
= lround(get_current_speed());
135 THEPANEL
->set_control_value(this->current_speed
);
136 THEPANEL
->reset_counter();
139 this->refresh_screen(THEPANEL
->lcd
->hasGraphics() ? true : false); // graphics screens should be cleared
141 // for LCDs with leds set them according to heater status
142 bool bed_on
= false, hotend_on
= false, is_hot
= false;
143 uint8_t heon
=0, hemsk
= 0x01; // bit set for which hotend is on bit0: hotend1, bit1: hotend2 etc
144 for(auto id
: temp_controllers
) {
145 struct pad_temperature c
= getTemperatures(id
);
146 if(c
.current_temperature
> 50) is_hot
= true; // anything is hot
147 if(c
.designator
.front() == 'B' && c
.target_temperature
> 0) bed_on
= true; // bed on/off
148 if(c
.designator
.front() == 'T') { // a hotend by convention
149 if(c
.target_temperature
> 0){
150 hotend_on
= true;// hotend on/off (anyone)
157 THEPANEL
->lcd
->setLed(LED_BED_ON
, bed_on
);
158 THEPANEL
->lcd
->setLed(LED_HOTEND_ON
, hotend_on
);
159 THEPANEL
->lcd
->setLed(LED_HOT
, is_hot
);
161 THEPANEL
->lcd
->setLed(LED_FAN_ON
, this->fan_state
);
163 if (THEPANEL
->lcd
->hasGraphics()) {
164 // display the graphical icons below the status are
165 //THEPANEL->lcd->bltGlyph(0, 34, 115, 19, icons);
166 // for (int i = 0; i < 5; ++i) {
167 // THEPANEL->lcd->bltGlyph(i*24, 38, 23, 19, icons, 15, i*24, 0);
169 if(heon
&0x01) THEPANEL
->lcd
->bltGlyph(0, 38, 20, 19, icons
, 15, 0, 0);
170 if(heon
&0x02) THEPANEL
->lcd
->bltGlyph(20, 38, 20, 19, icons
, 15, 24, 0);
171 if(heon
&0x04) THEPANEL
->lcd
->bltGlyph(40, 38, 20, 19, icons
, 15, 48, 0);
174 THEPANEL
->lcd
->bltGlyph(60, 38, 23, 19, icons
, 15, 64, 0);
177 THEPANEL
->lcd
->bltGlyph(96, 38, 23, 19, icons
, 15, 96, 0);
182 // queuing gcodes needs to be done from main loop
183 void WatchScreen::on_main_loop()
185 if (this->issue_change_speed
) {
186 this->issue_change_speed
= false;
189 PanelScreen::on_main_loop(); // in case any queued commands left
192 // fetch the data we are displaying
193 void WatchScreen::get_current_status()
197 bool ok
= PublicData::get_value( switch_checksum
, fan_checksum
, 0, &s
);
199 this->fan_state
= s
.state
;
201 // fan probably disabled
202 this->fan_state
= false;
206 // fetch the data we are displaying
207 float WatchScreen::get_current_speed()
210 return 6000.0F
/ THEKERNEL
->robot
->get_seconds_per_minute();
213 void WatchScreen::get_current_pos(float *cp
)
215 THEKERNEL
->robot
->get_axis_position(cp
);
218 void WatchScreen::get_sd_play_info()
221 bool ok
= PublicData::get_value( player_checksum
, get_progress_checksum
, &returned_data
);
223 struct pad_progress p
= *static_cast<struct pad_progress
*>(returned_data
);
224 this->elapsed_time
= p
.elapsed_secs
;
225 this->sd_pcnt_played
= p
.percent_complete
;
226 THEPANEL
->set_playing_file(p
.filename
);
229 this->elapsed_time
= 0;
230 this->sd_pcnt_played
= 0;
234 void WatchScreen::display_menu_line(uint16_t line
)
240 auto& tm
= this->temp_controllers
;
242 // only if we detected heaters in config
245 // more than two temps we need to cycle between them
246 n
= update_counts
/100; // increments every 5 seconds
247 int ntemps
= (tm
.size()+1)/2;
248 n
= n
%ntemps
; // which of the pairs of temps to display
252 for (size_t i
= 0; i
< 2; ++i
) {
254 if(o
>tm
.size()-1) break;
255 struct pad_temperature temp
= getTemperatures(tm
[o
]);
256 int t
= std::min(999, (int)roundf(temp
.current_temperature
));
257 int tt
= roundf(temp
.target_temperature
);
258 THEPANEL
->lcd
->setCursor(off
, 0); // col, row
259 off
+= THEPANEL
->lcd
->printf("%s:%03d/%03d ", temp
.designator
.substr(0, 2).c_str(), t
, tt
);
263 //THEPANEL->lcd->printf("No Heaters");
267 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;
268 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;
269 case 3: THEPANEL
->lcd
->printf("%19s", this->get_status()); break;
273 const char *WatchScreen::get_status()
275 if (THEPANEL
->hasMessage())
276 return THEPANEL
->getMessage().c_str();
278 if (THEPANEL
->is_halted())
279 return "HALTED Reset or M999";
281 if (THEKERNEL
->pauser
->paused())
284 if (THEPANEL
->is_suspended())
287 if (THEPANEL
->is_playing())
288 return THEPANEL
->get_playing_file();
290 if (!THEKERNEL
->conveyor
->is_queue_empty())
293 const char *ip
= get_network();
295 return "Smoothie ready";
301 void WatchScreen::set_speed()
303 send_gcode("M220", 'S', this->current_speed
);
306 const char *WatchScreen::get_network()
310 bool ok
= PublicData::get_value( network_checksum
, get_ip_checksum
, &returned_data
);
312 uint8_t *ipaddr
= (uint8_t *)returned_data
;
314 int n
= snprintf(buf
, sizeof(buf
), "IP %d.%d.%d.%d", ipaddr
[0], ipaddr
[1], ipaddr
[2], ipaddr
[3]);
316 if (this->ipstr
== nullptr) {
317 this->ipstr
= new char[n
+ 1];
319 strcpy(this->ipstr
, buf
);