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"
31 static const uint8_t icons
[] = { // 115x19 - 3 bytes each: he1, he2, he3, bed, fan
32 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0,
33 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xE0,
34 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x0C, 0x60,
35 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x0E, 0x20,
36 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x0F, 0x20,
37 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x0F, 0xA0,
38 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x07, 0xA0,
39 0x7F, 0x80, 0x00, 0x3F, 0xC0, 0x00, 0x3F, 0xC0, 0x00, 0x41, 0x04, 0x00, 0x40, 0x60, 0x20,
40 0xFB, 0xC0, 0x00, 0x79, 0xE0, 0x00, 0x79, 0xE0, 0x00, 0x20, 0x82, 0x00, 0x40, 0xF0, 0x20,
41 0xF3, 0xC0, 0x00, 0x76, 0xE0, 0x00, 0x76, 0xE0, 0x00, 0x20, 0x82, 0x00, 0x40, 0xF0, 0x20,
42 0xEB, 0xC0, 0x00, 0x7E, 0xE0, 0x00, 0x7E, 0xE0, 0x00, 0x41, 0x04, 0x00, 0x40, 0x60, 0x20,
43 0x7B, 0x80, 0x00, 0x3D, 0xC0, 0x00, 0x39, 0xC0, 0x00, 0x82, 0x08, 0x00, 0x5E, 0x07, 0xA0,
44 0x7B, 0x80, 0x00, 0x3B, 0xC0, 0x00, 0x3E, 0xC0, 0x01, 0x04, 0x10, 0x00, 0x5F, 0x0F, 0xA0,
45 0xFB, 0xC0, 0x00, 0x77, 0xE0, 0x00, 0x76, 0xE0, 0x01, 0x04, 0x10, 0x00, 0x4F, 0x0F, 0x20,
46 0xFB, 0xC0, 0x00, 0x70, 0xE0, 0x00, 0x79, 0xE0, 0x00, 0x82, 0x08, 0x00, 0x47, 0x0E, 0x20,
47 0xFF, 0xC0, 0x00, 0x7F, 0xE0, 0x00, 0x7F, 0xE0, 0x00, 0x41, 0x04, 0x00, 0x63, 0x0C, 0x60,
48 0x3F, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xE0,
49 0x1E, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x0F, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xE0,
50 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00
55 WatchScreen::WatchScreen()
57 speed_changed
= false;
58 issue_change_speed
= false;
63 WatchScreen::~WatchScreen()
68 void WatchScreen::on_enter()
70 THEPANEL
->lcd
->clear();
71 THEPANEL
->setup_menu(4);
73 get_current_pos(this->pos
);
75 this->current_speed
= lround(get_current_speed());
76 this->refresh_screen(false);
77 THEPANEL
->enter_control_mode(1, 0.5);
78 THEPANEL
->set_control_value(this->current_speed
);
81 void WatchScreen::on_refresh()
83 // Exit if the button is clicked
84 if ( THEPANEL
->click() ) {
85 THEPANEL
->enter_screen(this->parent
);
89 // see if speed is being changed
90 if (THEPANEL
->control_value_change()) {
91 this->current_speed
= THEPANEL
->get_control_value();
92 if (this->current_speed
< 10) {
93 this->current_speed
= 10;
94 THEPANEL
->set_control_value(this->current_speed
);
95 THEPANEL
->reset_counter();
97 // flag the update to change the speed, we don't want to issue hundreds of M220s
98 // but we do want to display the change we are going to make
99 this->speed_changed
= true; // flag indicating speed changed
100 this->refresh_screen(false);
104 // Update Only every 20 refreshes, 1 a second
106 if ( update_counts
% 20 == 0 ) {
108 get_current_pos(this->pos
);
109 get_current_status();
110 if (this->speed_changed
) {
111 this->issue_change_speed
= true; // trigger actual command to change speed
112 this->speed_changed
= false;
113 } else if (!this->issue_change_speed
) { // change still queued
114 // read it in case it was changed via M220
115 this->current_speed
= lround(get_current_speed());
116 THEPANEL
->set_control_value(this->current_speed
);
117 THEPANEL
->reset_counter();
120 this->refresh_screen(THEPANEL
->lcd
->hasGraphics() ? true : false); // graphics screens should be cleared
122 // for LCDs with leds set them according to heater status
123 bool bed_on
= false, hotend_on
= false, is_hot
= false;
124 uint8_t heon
=0, hemsk
= 0x01; // bit set for which hotend is on bit0: hotend1, bit1: hotend2 etc
125 for(auto m
: THEPANEL
->temperature_modules
) {
127 void *p
= getTemperatures(m
);
128 struct pad_temperature
*temp
= static_cast<struct pad_temperature
*>(p
);
129 if(temp
!= nullptr) {
130 if(temp
->current_temperature
> 50) is_hot
= true; // anything is hot
131 if(temp
->designator
.front() == 'B' && temp
->target_temperature
> 0) bed_on
= true; // bed on/off
132 if(temp
->designator
.front() == 'T') { // a hotend by convention
133 if(temp
->target_temperature
> 0){
134 hotend_on
= true;// hotend on/off (anyone)
141 THEPANEL
->lcd
->setLed(LED_BED_ON
, bed_on
);
142 THEPANEL
->lcd
->setLed(LED_HOTEND_ON
, hotend_on
);
143 THEPANEL
->lcd
->setLed(LED_HOT
, is_hot
);
145 THEPANEL
->lcd
->setLed(LED_FAN_ON
, this->fan_state
);
147 if (THEPANEL
->lcd
->hasGraphics()) {
148 // display the graphical icons below the status are
149 //THEPANEL->lcd->bltGlyph(0, 34, 115, 19, icons);
150 // for (int i = 0; i < 5; ++i) {
151 // THEPANEL->lcd->bltGlyph(i*24, 38, 23, 19, icons, 15, i*24, 0);
153 if(heon
&0x01) THEPANEL
->lcd
->bltGlyph(0, 38, 20, 19, icons
, 15, 0, 0);
154 if(heon
&0x02) THEPANEL
->lcd
->bltGlyph(20, 38, 20, 19, icons
, 15, 24, 0);
155 if(heon
&0x04) THEPANEL
->lcd
->bltGlyph(40, 38, 20, 19, icons
, 15, 48, 0);
158 THEPANEL
->lcd
->bltGlyph(60, 38, 23, 19, icons
, 15, 64, 0);
161 THEPANEL
->lcd
->bltGlyph(96, 38, 23, 19, icons
, 15, 96, 0);
166 // queuing gcodes needs to be done from main loop
167 void WatchScreen::on_main_loop()
169 if (this->issue_change_speed
) {
170 this->issue_change_speed
= false;
175 void *WatchScreen::getTemperatures(uint16_t heater_cs
)
178 bool ok
= PublicData::get_value( temperature_control_checksum
, heater_cs
, current_temperature_checksum
, &returned_data
);
181 return returned_data
;
187 // fetch the data we are displaying
188 void WatchScreen::get_current_status()
194 ok
= PublicData::get_value( switch_checksum
, fan_checksum
, 0, &returned_data
);
196 struct pad_switch s
= *static_cast<struct pad_switch
*>(returned_data
);
197 this->fan_state
= s
.state
;
199 // fan probably disabled
200 this->fan_state
= false;
204 // fetch the data we are displaying
205 float WatchScreen::get_current_speed()
209 bool ok
= PublicData::get_value( robot_checksum
, speed_override_percent_checksum
, &returned_data
);
211 float cs
= *static_cast<float *>(returned_data
);
217 void WatchScreen::get_current_pos(float *cp
)
221 bool ok
= PublicData::get_value( robot_checksum
, current_position_checksum
, &returned_data
);
223 float *p
= static_cast<float *>(returned_data
);
230 void WatchScreen::get_sd_play_info()
233 bool ok
= PublicData::get_value( player_checksum
, get_progress_checksum
, &returned_data
);
235 struct pad_progress p
= *static_cast<struct pad_progress
*>(returned_data
);
236 this->elapsed_time
= p
.elapsed_secs
;
237 this->sd_pcnt_played
= p
.percent_complete
;
238 THEPANEL
->set_playing_file(p
.filename
);
241 this->elapsed_time
= 0;
242 this->sd_pcnt_played
= 0;
246 void WatchScreen::display_menu_line(uint16_t line
)
251 if(THEPANEL
->temperature_modules
.size() > 0) {
252 // only if we detected heaters in config
253 auto tm
= THEPANEL
->temperature_modules
;
256 // more than two temps we need to cycle between them
257 n
= update_counts
/100; // increments every 5 seconds
258 int ntemps
= (tm
.size()+1)/2;
259 n
= n
%ntemps
; // which of the pairs of temps to display
263 for (size_t i
= 0; i
< 2; ++i
) {
265 if(o
>tm
.size()-1) break;
266 struct pad_temperature
*temp
= static_cast<struct pad_temperature
*>(getTemperatures(tm
[o
]));
267 if(temp
== nullptr) continue;
268 THEPANEL
->lcd
->setCursor(off
, 0); // col, row
269 off
+= THEPANEL
->lcd
->printf("%s:%03d/%03d ", temp
->designator
.substr(0, 2).c_str(), (int)roundf(temp
->current_temperature
), (int)roundf(temp
->target_temperature
));
273 //THEPANEL->lcd->printf("No Heaters");
276 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;
277 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;
278 case 3: THEPANEL
->lcd
->printf("%19s", this->get_status()); break;
282 const char *WatchScreen::get_status()
284 if (THEPANEL
->hasMessage()) {
285 return THEPANEL
->getMessage().c_str();
288 if (THEPANEL
->is_halted()) {
289 return "HALTED Reset or M999";
292 if (THEKERNEL
->pauser
->paused())
295 if (THEPANEL
->is_playing())
296 return THEPANEL
->get_playing_file();
298 if (!THEKERNEL
->conveyor
->is_queue_empty()) {
302 const char *ip
= get_network();
304 return "Smoothie ready";
310 void WatchScreen::set_speed()
312 send_gcode("M220", 'S', this->current_speed
);
315 const char *WatchScreen::get_network()
319 bool ok
= PublicData::get_value( network_checksum
, get_ip_checksum
, &returned_data
);
321 uint8_t *ipaddr
= (uint8_t *)returned_data
;
323 int n
= snprintf(buf
, sizeof(buf
), "IP %d.%d.%d.%d", ipaddr
[0], ipaddr
[1], ipaddr
[2], ipaddr
[3]);
325 if (this->ipstr
== nullptr) {
326 this->ipstr
= new char[n
+ 1];
328 strcpy(this->ipstr
, buf
);