Add halt status to panel and a way to clear it
[clinton/Smoothieware.git] / src / modules / utils / panel / screens / WatchScreen.cpp
1 /*
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/>.
6 */
7
8 #include "libs/Kernel.h"
9 #include "LcdBase.h"
10 #include "Panel.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"
24 #include "Pauser.h"
25 #include <math.h>
26 #include <string.h>
27 #include <string>
28 #include <stdio.h>
29
30 using namespace std;
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
51 };
52
53
54
55 WatchScreen::WatchScreen()
56 {
57 speed_changed = false;
58 issue_change_speed = false;
59 ipstr = nullptr;
60 update_counts= 0;
61 }
62
63 WatchScreen::~WatchScreen()
64 {
65 delete[] ipstr;
66 }
67
68 void WatchScreen::on_enter()
69 {
70 THEPANEL->lcd->clear();
71 THEPANEL->setup_menu(4);
72 get_current_status();
73 get_current_pos(this->pos);
74 get_sd_play_info();
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);
79 }
80
81 void WatchScreen::on_refresh()
82 {
83 // Exit if the button is clicked
84 if ( THEPANEL->click() ) {
85 THEPANEL->enter_screen(this->parent);
86 return;
87 }
88
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();
96 } else {
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);
101 }
102 }
103
104 // Update Only every 20 refreshes, 1 a second
105 update_counts++;
106 if ( update_counts % 20 == 0 ) {
107 get_sd_play_info();
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();
118 }
119
120 this->refresh_screen(THEPANEL->lcd->hasGraphics() ? true : false); // graphics screens should be cleared
121
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) {
126 // query each heater
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)
135 heon |= hemsk;
136 }
137 hemsk <<= 1;
138 }
139 }
140 }
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);
144
145 THEPANEL->lcd->setLed(LED_FAN_ON, this->fan_state);
146
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);
152 // }
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);
156
157 if (bed_on)
158 THEPANEL->lcd->bltGlyph(60, 38, 23, 19, icons, 15, 64, 0);
159
160 if(this->fan_state)
161 THEPANEL->lcd->bltGlyph(96, 38, 23, 19, icons, 15, 96, 0);
162 }
163 }
164 }
165
166 // queuing gcodes needs to be done from main loop
167 void WatchScreen::on_main_loop()
168 {
169 if (this->issue_change_speed) {
170 this->issue_change_speed = false;
171 set_speed();
172 }
173 }
174
175 void *WatchScreen::getTemperatures(uint16_t heater_cs)
176 {
177 void *returned_data;
178 bool ok = PublicData::get_value( temperature_control_checksum, heater_cs, current_temperature_checksum, &returned_data );
179
180 if (ok) {
181 return returned_data;
182 }
183
184 return nullptr;
185 }
186
187 // fetch the data we are displaying
188 void WatchScreen::get_current_status()
189 {
190 void *returned_data;
191 bool ok;
192
193 // get fan status
194 ok = PublicData::get_value( switch_checksum, fan_checksum, 0, &returned_data );
195 if (ok) {
196 struct pad_switch s = *static_cast<struct pad_switch *>(returned_data);
197 this->fan_state = s.state;
198 } else {
199 // fan probably disabled
200 this->fan_state = false;
201 }
202 }
203
204 // fetch the data we are displaying
205 float WatchScreen::get_current_speed()
206 {
207 void *returned_data;
208
209 bool ok = PublicData::get_value( robot_checksum, speed_override_percent_checksum, &returned_data );
210 if (ok) {
211 float cs = *static_cast<float *>(returned_data);
212 return cs;
213 }
214 return 0.0;
215 }
216
217 void WatchScreen::get_current_pos(float *cp)
218 {
219 void *returned_data;
220
221 bool ok = PublicData::get_value( robot_checksum, current_position_checksum, &returned_data );
222 if (ok) {
223 float *p = static_cast<float *>(returned_data);
224 cp[0] = p[0];
225 cp[1] = p[1];
226 cp[2] = p[2];
227 }
228 }
229
230 void WatchScreen::get_sd_play_info()
231 {
232 void *returned_data;
233 bool ok = PublicData::get_value( player_checksum, get_progress_checksum, &returned_data );
234 if (ok) {
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);
239
240 } else {
241 this->elapsed_time = 0;
242 this->sd_pcnt_played = 0;
243 }
244 }
245
246 void WatchScreen::display_menu_line(uint16_t line)
247 {
248 // in menu mode
249 switch ( line ) {
250 case 0:
251 if(THEPANEL->temperature_modules.size() > 0) {
252 // only if we detected heaters in config
253 auto tm= THEPANEL->temperature_modules;
254 int n= 0;
255 if(tm.size() > 2) {
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
260 }
261
262 int off= 0;
263 for (size_t i = 0; i < 2; ++i) {
264 size_t o= i+(n*2);
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));
270 }
271
272 }else{
273 //THEPANEL->lcd->printf("No Heaters");
274 }
275 break;
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;
279 }
280 }
281
282 const char *WatchScreen::get_status()
283 {
284 if (THEPANEL->hasMessage()) {
285 return THEPANEL->getMessage().c_str();
286 }
287
288 if (THEPANEL->is_halted()) {
289 return "HALTED Reset or M999";
290 }
291
292 if (THEKERNEL->pauser->paused())
293 return "Paused";
294
295 if (THEPANEL->is_playing())
296 return THEPANEL->get_playing_file();
297
298 if (!THEKERNEL->conveyor->is_queue_empty()) {
299 return "Printing";
300 }
301
302 const char *ip = get_network();
303 if (ip == NULL) {
304 return "Smoothie ready";
305 } else {
306 return ip;
307 }
308 }
309
310 void WatchScreen::set_speed()
311 {
312 send_gcode("M220", 'S', this->current_speed);
313 }
314
315 const char *WatchScreen::get_network()
316 {
317 void *returned_data;
318
319 bool ok = PublicData::get_value( network_checksum, get_ip_checksum, &returned_data );
320 if (ok) {
321 uint8_t *ipaddr = (uint8_t *)returned_data;
322 char buf[20];
323 int n = snprintf(buf, sizeof(buf), "IP %d.%d.%d.%d", ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]);
324 buf[n] = 0;
325 if (this->ipstr == nullptr) {
326 this->ipstr = new char[n + 1];
327 }
328 strcpy(this->ipstr, buf);
329
330 return this->ipstr;
331 }
332
333 return NULL;
334 }