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"
24 #include "TemperatureControlPool.h"
34 static const uint8_t icons
[] = { // 16x80 - he1, he2, he3, bed, fan
35 0x3f, 0xfc, 0x3f, 0xfc, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0x7f, 0x7f, 0x7e, 0x3f, 0x7c, 0x1f,
36 0x78, 0x0f, 0xf0, 0x07, 0xe0, 0x03, 0xc0, 0x01, 0x80, 0x00, 0x00, 0x01, 0x80, 0x01, 0x80,
37 0x01, 0x80, 0x3f, 0xfc, 0x3f, 0xfc, 0xff, 0xff, 0xfc, 0x7f, 0xff, 0x7f, 0x7c, 0x7e, 0x3d,
38 0xfc, 0x1c, 0x78, 0x0f, 0xf0, 0x07, 0xe0, 0x03, 0xc0, 0x01, 0x80, 0x00, 0x00, 0x01, 0x80,
39 0x01, 0x80, 0x01, 0x80, 0x3f, 0xfc, 0x3f, 0xfc, 0xff, 0xff, 0xfc, 0x7f, 0xff, 0x7f, 0x7c,
40 0x7e, 0x3f, 0x7c, 0x1c, 0x78, 0x0f, 0xf0, 0x07, 0xe0, 0x03, 0xc0, 0x01, 0x80, 0x00, 0x00,
41 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x00, 0x00, 0x08, 0x88, 0x11, 0x10, 0x22, 0x20, 0x22,
42 0x20, 0x11, 0x10, 0x08, 0x88, 0x04, 0x44, 0x04, 0x44, 0x08, 0x88, 0x11, 0x10, 0x22, 0x20,
43 0x00, 0x00, 0x7f, 0xfe, 0xff, 0xff, 0x7f, 0xfe, 0x39, 0xec, 0x43, 0xe2, 0x9b, 0xc9, 0xa3,
44 0x85, 0x03, 0x85, 0xc3, 0x00, 0xe0, 0x3e, 0xf9, 0xbf, 0xfd, 0x9f, 0x7c, 0x07, 0x00, 0xc3,
45 0xa1, 0xc0, 0xa1, 0xc5, 0x93, 0xd9, 0x47, 0xc2, 0x37, 0x9c
48 WatchScreen::WatchScreen()
50 speed_changed
= false;
51 issue_change_speed
= false;
56 WatchScreen::~WatchScreen()
61 void WatchScreen::on_enter()
63 THEPANEL
->lcd
->clear();
64 THEPANEL
->setup_menu(4);
66 get_current_pos(this->pos
);
68 this->current_speed
= lround(get_current_speed());
69 this->refresh_screen(false);
70 THEPANEL
->enter_control_mode(1, 0.5);
71 THEPANEL
->set_control_value(this->current_speed
);
73 // enumerate temperature controls
74 temp_controllers
.clear();
75 std::vector
<struct pad_temperature
> controllers
;
76 bool ok
= PublicData::get_value(temperature_control_checksum
, poll_controls_checksum
, &controllers
);
78 for (auto &c
: controllers
) {
79 temp_controllers
.push_back(c
.id
);
84 static struct pad_temperature
getTemperatures(uint16_t heater_cs
)
86 struct pad_temperature temp
;
87 PublicData::get_value( temperature_control_checksum
, current_temperature_checksum
, heater_cs
, &temp
);
91 void WatchScreen::on_refresh()
93 // Exit if the button is clicked
94 if ( THEPANEL
->click() ) {
95 THEPANEL
->enter_screen(this->parent
);
99 // see if speed is being changed
100 if (THEPANEL
->control_value_change()) {
101 this->current_speed
= THEPANEL
->get_control_value();
102 if (this->current_speed
< 10) {
103 this->current_speed
= 10;
104 THEPANEL
->set_control_value(this->current_speed
);
105 THEPANEL
->reset_counter();
107 // flag the update to change the speed, we don't want to issue hundreds of M220s
108 // but we do want to display the change we are going to make
109 this->speed_changed
= true; // flag indicating speed changed
110 this->refresh_screen(false);
114 // Update Only every 20 refreshes, 1 a second
116 if ( update_counts
% 20 == 0 ) {
118 get_current_pos(this->pos
);
119 get_current_status();
120 if (this->speed_changed
) {
121 this->issue_change_speed
= true; // trigger actual command to change speed
122 this->speed_changed
= false;
123 } else if (!this->issue_change_speed
) { // change still queued
124 // read it in case it was changed via M220
125 this->current_speed
= lround(get_current_speed());
126 THEPANEL
->set_control_value(this->current_speed
);
127 THEPANEL
->reset_counter();
130 this->refresh_screen(THEPANEL
->lcd
->hasGraphics() ? true : false); // graphics screens should be cleared
132 // for LCDs with leds set them according to heater status
133 bool bed_on
= false, hotend_on
= false, is_hot
= false;
134 uint8_t heon
=0, hemsk
= 0x01; // bit set for which hotend is on bit0: hotend1, bit1: hotend2 etc
135 for(auto id
: temp_controllers
) {
136 struct pad_temperature c
= getTemperatures(id
);
137 if(c
.current_temperature
> 50) is_hot
= true; // anything is hot
138 if(c
.designator
.front() == 'B' && c
.target_temperature
> 0) bed_on
= true; // bed on/off
139 if(c
.designator
.front() == 'T') { // a hotend by convention
140 if(c
.target_temperature
> 0){
141 hotend_on
= true;// hotend on/off (anyone)
148 THEPANEL
->lcd
->setLed(LED_BED_ON
, bed_on
);
149 THEPANEL
->lcd
->setLed(LED_HOTEND_ON
, hotend_on
);
150 THEPANEL
->lcd
->setLed(LED_HOT
, is_hot
);
152 THEPANEL
->lcd
->setLed(LED_FAN_ON
, this->fan_state
);
154 if (THEPANEL
->lcd
->hasGraphics()) {
155 // display the graphical icons below the status are
156 // for (int i = 0; i < 5; ++i) {
157 // THEPANEL->lcd->bltGlyph(i*24, 42, 16, 16, icons, 15, i*24, 0);
159 if(heon
&0x01) THEPANEL
->lcd
->bltGlyph(0, 42, 16, 16, icons
, 2, 0, 0);
160 if(heon
&0x02) THEPANEL
->lcd
->bltGlyph(27, 42, 16, 16, icons
, 2, 0, 16);
161 if(heon
&0x04) THEPANEL
->lcd
->bltGlyph(55, 42, 16, 16, icons
, 2, 0, 32);
164 THEPANEL
->lcd
->bltGlyph(83, 42, 16, 16, icons
, 2, 0, 48);
167 THEPANEL
->lcd
->bltGlyph(111, 42, 16, 16, icons
, 2, 0, 64);
172 // queuing gcodes needs to be done from main loop
173 void WatchScreen::on_main_loop()
175 if (this->issue_change_speed
) {
176 this->issue_change_speed
= false;
179 PanelScreen::on_main_loop(); // in case any queued commands left
182 // fetch the data we are displaying
183 void WatchScreen::get_current_status()
187 bool ok
= PublicData::get_value( switch_checksum
, fan_checksum
, 0, &s
);
189 this->fan_state
= s
.state
;
191 // fan probably disabled
192 this->fan_state
= false;
196 // fetch the data we are displaying
197 float WatchScreen::get_current_speed()
200 return 6000.0F
/ THEKERNEL
->robot
->get_seconds_per_minute();
203 void WatchScreen::get_current_pos(float *cp
)
205 THEKERNEL
->robot
->get_axis_position(cp
);
208 void WatchScreen::get_sd_play_info()
211 bool ok
= PublicData::get_value( player_checksum
, get_progress_checksum
, &returned_data
);
213 struct pad_progress p
= *static_cast<struct pad_progress
*>(returned_data
);
214 this->elapsed_time
= p
.elapsed_secs
;
215 this->sd_pcnt_played
= p
.percent_complete
;
216 THEPANEL
->set_playing_file(p
.filename
);
219 this->elapsed_time
= 0;
220 this->sd_pcnt_played
= 0;
224 void WatchScreen::display_menu_line(uint16_t line
)
230 auto& tm
= this->temp_controllers
;
232 // only if we detected heaters in config
235 // more than two temps we need to cycle between them
236 n
= update_counts
/100; // increments every 5 seconds
237 int ntemps
= (tm
.size()+1)/2;
238 n
= n
%ntemps
; // which of the pairs of temps to display
242 for (size_t i
= 0; i
< 2; ++i
) {
244 if(o
>tm
.size()-1) break;
245 struct pad_temperature temp
= getTemperatures(tm
[o
]);
246 int t
= std::min(999, (int)roundf(temp
.current_temperature
));
247 int tt
= roundf(temp
.target_temperature
);
248 THEPANEL
->lcd
->setCursor(off
, 0); // col, row
249 off
+= THEPANEL
->lcd
->printf("%s:%03d/%03d ", temp
.designator
.substr(0, 2).c_str(), t
, tt
);
253 //THEPANEL->lcd->printf("No Heaters");
257 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;
258 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;
259 case 3: THEPANEL
->lcd
->printf("%19s", this->get_status()); break;
263 const char *WatchScreen::get_status()
265 if (THEPANEL
->hasMessage())
266 return THEPANEL
->getMessage().c_str();
268 if (THEKERNEL
->is_halted())
269 return "HALTED Reset or M999";
271 if (THEPANEL
->is_suspended())
274 if (THEPANEL
->is_playing())
275 return THEPANEL
->get_playing_file();
277 if (!THEKERNEL
->conveyor
->is_queue_empty())
280 const char *ip
= get_network();
282 return "Smoothie ready";
288 void WatchScreen::set_speed()
290 send_gcode("M220", 'S', this->current_speed
);
293 const char *WatchScreen::get_network()
297 bool ok
= PublicData::get_value( network_checksum
, get_ip_checksum
, &returned_data
);
299 uint8_t *ipaddr
= (uint8_t *)returned_data
;
301 int n
= snprintf(buf
, sizeof(buf
), "IP %d.%d.%d.%d", ipaddr
[0], ipaddr
[1], ipaddr
[2], ipaddr
[3]);
303 if (this->ipstr
== nullptr) {
304 this->ipstr
= new char[n
+ 1];
306 strcpy(this->ipstr
, buf
);