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
= lroundf(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
= lroundf(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_sd_play_info()
206 bool ok
= PublicData::get_value( player_checksum
, get_progress_checksum
, &returned_data
);
208 struct pad_progress p
= *static_cast<struct pad_progress
*>(returned_data
);
209 this->elapsed_time
= p
.elapsed_secs
;
210 this->sd_pcnt_played
= p
.percent_complete
;
211 THEPANEL
->set_playing_file(p
.filename
);
214 this->elapsed_time
= 0;
215 this->sd_pcnt_played
= 0;
219 void WatchScreen::display_menu_line(uint16_t line
)
225 auto& tm
= this->temp_controllers
;
227 // only if we detected heaters in config
230 // more than two temps we need to cycle between them
231 n
= update_counts
/100; // increments every 5 seconds
232 int ntemps
= (tm
.size()+1)/2;
233 n
= n
%ntemps
; // which of the pairs of temps to display
237 for (size_t i
= 0; i
< 2; ++i
) {
239 if(o
>tm
.size()-1) break;
240 struct pad_temperature temp
= getTemperatures(tm
[o
]);
241 int t
= std::min(999, (int)roundf(temp
.current_temperature
));
242 int tt
= roundf(temp
.target_temperature
);
243 THEPANEL
->lcd
->setCursor(off
, 0); // col, row
244 off
+= THEPANEL
->lcd
->printf("%s:%03d/%03d ", temp
.designator
.substr(0, 2).c_str(), t
, tt
);
248 //THEPANEL->lcd->printf("No Heaters");
252 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;
253 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;
254 case 3: THEPANEL
->lcd
->printf("%19s", this->get_status()); break;
258 const char *WatchScreen::get_status()
260 if (THEPANEL
->hasMessage())
261 return THEPANEL
->getMessage().c_str();
263 if (THEKERNEL
->is_halted())
264 return "HALTED Reset or M999";
266 if (THEPANEL
->is_suspended())
269 if (THEPANEL
->is_playing())
270 return THEPANEL
->get_playing_file();
272 if (!THEKERNEL
->conveyor
->is_queue_empty())
275 const char *ip
= get_network();
277 return "Smoothie ready";
283 void WatchScreen::set_speed()
285 send_gcode("M220", 'S', this->current_speed
);
288 const char *WatchScreen::get_network()
292 bool ok
= PublicData::get_value( network_checksum
, get_ip_checksum
, &returned_data
);
294 uint8_t *ipaddr
= (uint8_t *)returned_data
;
296 int n
= snprintf(buf
, sizeof(buf
), "IP %d.%d.%d.%d", ipaddr
[0], ipaddr
[1], ipaddr
[2], ipaddr
[3]);
298 if (this->ipstr
== nullptr) {
299 this->ipstr
= new char[n
+ 1];
301 strcpy(this->ipstr
, buf
);