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"
32 static const uint8_t icons
[] = { // 115x19 - 3 bytes each: he1, he2, he3, bed, fan
33 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0,
34 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xE0,
35 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x0C, 0x60,
36 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x0E, 0x20,
37 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x0F, 0x20,
38 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x0F, 0xA0,
39 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x07, 0xA0,
40 0x7F, 0x80, 0x00, 0x3F, 0xC0, 0x00, 0x3F, 0xC0, 0x00, 0x41, 0x04, 0x00, 0x40, 0x60, 0x20,
41 0xFB, 0xC0, 0x00, 0x79, 0xE0, 0x00, 0x79, 0xE0, 0x00, 0x20, 0x82, 0x00, 0x40, 0xF0, 0x20,
42 0xF3, 0xC0, 0x00, 0x76, 0xE0, 0x00, 0x76, 0xE0, 0x00, 0x20, 0x82, 0x00, 0x40, 0xF0, 0x20,
43 0xEB, 0xC0, 0x00, 0x7E, 0xE0, 0x00, 0x7E, 0xE0, 0x00, 0x41, 0x04, 0x00, 0x40, 0x60, 0x20,
44 0x7B, 0x80, 0x00, 0x3D, 0xC0, 0x00, 0x39, 0xC0, 0x00, 0x82, 0x08, 0x00, 0x5E, 0x07, 0xA0,
45 0x7B, 0x80, 0x00, 0x3B, 0xC0, 0x00, 0x3E, 0xC0, 0x01, 0x04, 0x10, 0x00, 0x5F, 0x0F, 0xA0,
46 0xFB, 0xC0, 0x00, 0x77, 0xE0, 0x00, 0x76, 0xE0, 0x01, 0x04, 0x10, 0x00, 0x4F, 0x0F, 0x20,
47 0xFB, 0xC0, 0x00, 0x70, 0xE0, 0x00, 0x79, 0xE0, 0x00, 0x82, 0x08, 0x00, 0x47, 0x0E, 0x20,
48 0xFF, 0xC0, 0x00, 0x7F, 0xE0, 0x00, 0x7F, 0xE0, 0x00, 0x41, 0x04, 0x00, 0x63, 0x0C, 0x60,
49 0x3F, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xE0,
50 0x1E, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x0F, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xE0,
51 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00
56 WatchScreen::WatchScreen()
58 speed_changed
= false;
59 issue_change_speed
= false;
64 WatchScreen::~WatchScreen()
69 void WatchScreen::on_enter()
71 THEPANEL
->lcd
->clear();
72 THEPANEL
->setup_menu(4);
74 get_current_pos(this->pos
);
76 this->current_speed
= lround(get_current_speed());
77 this->refresh_screen(false);
78 THEPANEL
->enter_control_mode(1, 0.5);
79 THEPANEL
->set_control_value(this->current_speed
);
82 void WatchScreen::on_refresh()
84 // Exit if the button is clicked
85 if ( THEPANEL
->click() ) {
86 THEPANEL
->enter_screen(this->parent
);
90 // see if speed is being changed
91 if (THEPANEL
->control_value_change()) {
92 this->current_speed
= THEPANEL
->get_control_value();
93 if (this->current_speed
< 10) {
94 this->current_speed
= 10;
95 THEPANEL
->set_control_value(this->current_speed
);
96 THEPANEL
->reset_counter();
98 // flag the update to change the speed, we don't want to issue hundreds of M220s
99 // but we do want to display the change we are going to make
100 this->speed_changed
= true; // flag indicating speed changed
101 this->refresh_screen(false);
105 // Update Only every 20 refreshes, 1 a second
107 if ( update_counts
% 20 == 0 ) {
109 get_current_pos(this->pos
);
110 get_current_status();
111 if (this->speed_changed
) {
112 this->issue_change_speed
= true; // trigger actual command to change speed
113 this->speed_changed
= false;
114 } else if (!this->issue_change_speed
) { // change still queued
115 // read it in case it was changed via M220
116 this->current_speed
= lround(get_current_speed());
117 THEPANEL
->set_control_value(this->current_speed
);
118 THEPANEL
->reset_counter();
121 this->refresh_screen(THEPANEL
->lcd
->hasGraphics() ? true : false); // graphics screens should be cleared
123 // for LCDs with leds set them according to heater status
124 bool bed_on
= false, hotend_on
= false, is_hot
= false;
125 uint8_t heon
=0, hemsk
= 0x01; // bit set for which hotend is on bit0: hotend1, bit1: hotend2 etc
126 for(auto m
: THEPANEL
->temperature_modules
) {
128 void *p
= getTemperatures(m
);
129 struct pad_temperature
*temp
= static_cast<struct pad_temperature
*>(p
);
130 if(temp
!= nullptr) {
131 if(temp
->current_temperature
> 50) is_hot
= true; // anything is hot
132 if(temp
->designator
.front() == 'B' && temp
->target_temperature
> 0) bed_on
= true; // bed on/off
133 if(temp
->designator
.front() == 'T') { // a hotend by convention
134 if(temp
->target_temperature
> 0){
135 hotend_on
= true;// hotend on/off (anyone)
142 THEPANEL
->lcd
->setLed(LED_BED_ON
, bed_on
);
143 THEPANEL
->lcd
->setLed(LED_HOTEND_ON
, hotend_on
);
144 THEPANEL
->lcd
->setLed(LED_HOT
, is_hot
);
146 THEPANEL
->lcd
->setLed(LED_FAN_ON
, this->fan_state
);
148 if (THEPANEL
->lcd
->hasGraphics()) {
149 // display the graphical icons below the status are
150 //THEPANEL->lcd->bltGlyph(0, 34, 115, 19, icons);
151 // for (int i = 0; i < 5; ++i) {
152 // THEPANEL->lcd->bltGlyph(i*24, 38, 23, 19, icons, 15, i*24, 0);
154 if(heon
&0x01) THEPANEL
->lcd
->bltGlyph(0, 38, 20, 19, icons
, 15, 0, 0);
155 if(heon
&0x02) THEPANEL
->lcd
->bltGlyph(20, 38, 20, 19, icons
, 15, 24, 0);
156 if(heon
&0x04) THEPANEL
->lcd
->bltGlyph(40, 38, 20, 19, icons
, 15, 48, 0);
159 THEPANEL
->lcd
->bltGlyph(60, 38, 23, 19, icons
, 15, 64, 0);
162 THEPANEL
->lcd
->bltGlyph(96, 38, 23, 19, icons
, 15, 96, 0);
167 // queuing gcodes needs to be done from main loop
168 void WatchScreen::on_main_loop()
170 if (this->issue_change_speed
) {
171 this->issue_change_speed
= false;
176 void *WatchScreen::getTemperatures(uint16_t heater_cs
)
179 bool ok
= PublicData::get_value( temperature_control_checksum
, heater_cs
, current_temperature_checksum
, &returned_data
);
182 return returned_data
;
188 // fetch the data we are displaying
189 void WatchScreen::get_current_status()
195 ok
= PublicData::get_value( switch_checksum
, fan_checksum
, 0, &returned_data
);
197 struct pad_switch s
= *static_cast<struct pad_switch
*>(returned_data
);
198 this->fan_state
= s
.state
;
200 // fan probably disabled
201 this->fan_state
= false;
205 // fetch the data we are displaying
206 float WatchScreen::get_current_speed()
210 bool ok
= PublicData::get_value( robot_checksum
, speed_override_percent_checksum
, &returned_data
);
212 float cs
= *static_cast<float *>(returned_data
);
218 void WatchScreen::get_current_pos(float *cp
)
222 bool ok
= PublicData::get_value( robot_checksum
, current_position_checksum
, &returned_data
);
224 float *p
= static_cast<float *>(returned_data
);
231 void WatchScreen::get_sd_play_info()
234 bool ok
= PublicData::get_value( player_checksum
, get_progress_checksum
, &returned_data
);
236 struct pad_progress p
= *static_cast<struct pad_progress
*>(returned_data
);
237 this->elapsed_time
= p
.elapsed_secs
;
238 this->sd_pcnt_played
= p
.percent_complete
;
239 THEPANEL
->set_playing_file(p
.filename
);
242 this->elapsed_time
= 0;
243 this->sd_pcnt_played
= 0;
247 void WatchScreen::display_menu_line(uint16_t line
)
252 if(THEPANEL
->temperature_modules
.size() > 0) {
253 // only if we detected heaters in config
254 auto tm
= THEPANEL
->temperature_modules
;
257 // more than two temps we need to cycle between them
258 n
= update_counts
/100; // increments every 5 seconds
259 int ntemps
= (tm
.size()+1)/2;
260 n
= n
%ntemps
; // which of the pairs of temps to display
264 for (size_t i
= 0; i
< 2; ++i
) {
266 if(o
>tm
.size()-1) break;
267 struct pad_temperature
*temp
= static_cast<struct pad_temperature
*>(getTemperatures(tm
[o
]));
268 if(temp
== nullptr) continue;
269 int t
= std::min(999, (int)roundf(temp
->current_temperature
));
270 int tt
= roundf(temp
->target_temperature
);
271 THEPANEL
->lcd
->setCursor(off
, 0); // col, row
272 off
+= THEPANEL
->lcd
->printf("%s:%03d/%03d ", temp
->designator
.substr(0, 2).c_str(), t
, tt
);
276 //THEPANEL->lcd->printf("No Heaters");
279 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;
280 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;
281 case 3: THEPANEL
->lcd
->printf("%19s", this->get_status()); break;
285 const char *WatchScreen::get_status()
287 if (THEPANEL
->hasMessage()) {
288 return THEPANEL
->getMessage().c_str();
291 if (THEPANEL
->is_halted()) {
292 return "HALTED Reset or M999";
295 if (THEKERNEL
->pauser
->paused())
298 if (THEPANEL
->is_playing())
299 return THEPANEL
->get_playing_file();
301 if (!THEKERNEL
->conveyor
->is_queue_empty()) {
305 const char *ip
= get_network();
307 return "Smoothie ready";
313 void WatchScreen::set_speed()
315 send_gcode("M220", 'S', this->current_speed
);
318 const char *WatchScreen::get_network()
322 bool ok
= PublicData::get_value( network_checksum
, get_ip_checksum
, &returned_data
);
324 uint8_t *ipaddr
= (uint8_t *)returned_data
;
326 int n
= snprintf(buf
, sizeof(buf
), "IP %d.%d.%d.%d", ipaddr
[0], ipaddr
[1], ipaddr
[2], ipaddr
[3]);
328 if (this->ipstr
== nullptr) {
329 this->ipstr
= new char[n
+ 1];
331 strcpy(this->ipstr
, buf
);