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