change the temperature public data access to pass in the pad_temp for the result...
[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 "TemperatureControlPool.h"
26
27
28 #include <math.h>
29 #include <string.h>
30 #include <string>
31 #include <stdio.h>
32 #include <algorithm>
33
34 using namespace std;
35 static const uint8_t icons[] = { // 115x19 - 3 bytes each: he1, he2, he3, bed, fan
36 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0,
37 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xE0,
38 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x0C, 0x60,
39 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x0E, 0x20,
40 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x0F, 0x20,
41 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x0F, 0xA0,
42 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x07, 0xA0,
43 0x7F, 0x80, 0x00, 0x3F, 0xC0, 0x00, 0x3F, 0xC0, 0x00, 0x41, 0x04, 0x00, 0x40, 0x60, 0x20,
44 0xFB, 0xC0, 0x00, 0x79, 0xE0, 0x00, 0x79, 0xE0, 0x00, 0x20, 0x82, 0x00, 0x40, 0xF0, 0x20,
45 0xF3, 0xC0, 0x00, 0x76, 0xE0, 0x00, 0x76, 0xE0, 0x00, 0x20, 0x82, 0x00, 0x40, 0xF0, 0x20,
46 0xEB, 0xC0, 0x00, 0x7E, 0xE0, 0x00, 0x7E, 0xE0, 0x00, 0x41, 0x04, 0x00, 0x40, 0x60, 0x20,
47 0x7B, 0x80, 0x00, 0x3D, 0xC0, 0x00, 0x39, 0xC0, 0x00, 0x82, 0x08, 0x00, 0x5E, 0x07, 0xA0,
48 0x7B, 0x80, 0x00, 0x3B, 0xC0, 0x00, 0x3E, 0xC0, 0x01, 0x04, 0x10, 0x00, 0x5F, 0x0F, 0xA0,
49 0xFB, 0xC0, 0x00, 0x77, 0xE0, 0x00, 0x76, 0xE0, 0x01, 0x04, 0x10, 0x00, 0x4F, 0x0F, 0x20,
50 0xFB, 0xC0, 0x00, 0x70, 0xE0, 0x00, 0x79, 0xE0, 0x00, 0x82, 0x08, 0x00, 0x47, 0x0E, 0x20,
51 0xFF, 0xC0, 0x00, 0x7F, 0xE0, 0x00, 0x7F, 0xE0, 0x00, 0x41, 0x04, 0x00, 0x63, 0x0C, 0x60,
52 0x3F, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xE0,
53 0x1E, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x0F, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xE0,
54 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00
55 };
56
57
58
59 WatchScreen::WatchScreen()
60 {
61 speed_changed = false;
62 issue_change_speed = false;
63 ipstr = nullptr;
64 update_counts= 0;
65 }
66
67 WatchScreen::~WatchScreen()
68 {
69 delete[] ipstr;
70 }
71
72 void WatchScreen::on_enter()
73 {
74 THEPANEL->lcd->clear();
75 THEPANEL->setup_menu(4);
76 get_current_status();
77 get_current_pos(this->pos);
78 get_sd_play_info();
79 this->current_speed = lround(get_current_speed());
80 this->refresh_screen(false);
81 THEPANEL->enter_control_mode(1, 0.5);
82 THEPANEL->set_control_value(this->current_speed);
83 }
84
85 void WatchScreen::on_refresh()
86 {
87 // Exit if the button is clicked
88 if ( THEPANEL->click() ) {
89 THEPANEL->enter_screen(this->parent);
90 return;
91 }
92
93 // see if speed is being changed
94 if (THEPANEL->control_value_change()) {
95 this->current_speed = THEPANEL->get_control_value();
96 if (this->current_speed < 10) {
97 this->current_speed = 10;
98 THEPANEL->set_control_value(this->current_speed);
99 THEPANEL->reset_counter();
100 } else {
101 // flag the update to change the speed, we don't want to issue hundreds of M220s
102 // but we do want to display the change we are going to make
103 this->speed_changed = true; // flag indicating speed changed
104 this->refresh_screen(false);
105 }
106 }
107
108 // Update Only every 20 refreshes, 1 a second
109 update_counts++;
110 if ( update_counts % 20 == 0 ) {
111 get_sd_play_info();
112 get_current_pos(this->pos);
113 get_current_status();
114 if (this->speed_changed) {
115 this->issue_change_speed = true; // trigger actual command to change speed
116 this->speed_changed = false;
117 } else if (!this->issue_change_speed) { // change still queued
118 // read it in case it was changed via M220
119 this->current_speed = lround(get_current_speed());
120 THEPANEL->set_control_value(this->current_speed);
121 THEPANEL->reset_counter();
122 }
123
124 this->refresh_screen(THEPANEL->lcd->hasGraphics() ? true : false); // graphics screens should be cleared
125
126 // for LCDs with leds set them according to heater status
127 bool bed_on= false, hotend_on= false, is_hot= false;
128 uint8_t heon=0, hemsk= 0x01; // bit set for which hotend is on bit0: hotend1, bit1: hotend2 etc
129 temp_controllers.clear();
130 std::vector<struct pad_temperature> controllers;
131 bool ok = PublicData::get_value(temperature_control_checksum, poll_controls_checksum, &controllers);
132 if (ok) {
133 for (auto &c : controllers) {
134 temp_controllers.push_back(c.id);
135 if(c.current_temperature > 50) is_hot= true; // anything is hot
136 if(c.designator.front() == 'B' && c.target_temperature > 0) bed_on= true; // bed on/off
137 if(c.designator.front() == 'T') { // a hotend by convention
138 if(c.target_temperature > 0){
139 hotend_on= true;// hotend on/off (anyone)
140 heon |= hemsk;
141 }
142 hemsk <<= 1;
143 }
144 }
145 }
146 THEPANEL->lcd->setLed(LED_BED_ON, bed_on);
147 THEPANEL->lcd->setLed(LED_HOTEND_ON, hotend_on);
148 THEPANEL->lcd->setLed(LED_HOT, is_hot);
149
150 THEPANEL->lcd->setLed(LED_FAN_ON, this->fan_state);
151
152 if (THEPANEL->lcd->hasGraphics()) {
153 // display the graphical icons below the status are
154 //THEPANEL->lcd->bltGlyph(0, 34, 115, 19, icons);
155 // for (int i = 0; i < 5; ++i) {
156 // THEPANEL->lcd->bltGlyph(i*24, 38, 23, 19, icons, 15, i*24, 0);
157 // }
158 if(heon&0x01) THEPANEL->lcd->bltGlyph(0, 38, 20, 19, icons, 15, 0, 0);
159 if(heon&0x02) THEPANEL->lcd->bltGlyph(20, 38, 20, 19, icons, 15, 24, 0);
160 if(heon&0x04) THEPANEL->lcd->bltGlyph(40, 38, 20, 19, icons, 15, 48, 0);
161
162 if (bed_on)
163 THEPANEL->lcd->bltGlyph(60, 38, 23, 19, icons, 15, 64, 0);
164
165 if(this->fan_state)
166 THEPANEL->lcd->bltGlyph(96, 38, 23, 19, icons, 15, 96, 0);
167 }
168 }
169 }
170
171 // queuing gcodes needs to be done from main loop
172 void WatchScreen::on_main_loop()
173 {
174 if (this->issue_change_speed) {
175 this->issue_change_speed = false;
176 set_speed();
177 }
178 PanelScreen::on_main_loop(); // in case any queued commands left
179 }
180
181 // fetch the data we are displaying
182 void WatchScreen::get_current_status()
183 {
184 void *returned_data;
185 bool ok;
186
187 // get fan status
188 ok = PublicData::get_value( switch_checksum, fan_checksum, 0, &returned_data );
189 if (ok) {
190 struct pad_switch s = *static_cast<struct pad_switch *>(returned_data);
191 this->fan_state = s.state;
192 } else {
193 // fan probably disabled
194 this->fan_state = false;
195 }
196 }
197
198 // fetch the data we are displaying
199 float WatchScreen::get_current_speed()
200 {
201 void *returned_data;
202
203 bool ok = PublicData::get_value( robot_checksum, speed_override_percent_checksum, &returned_data );
204 if (ok) {
205 float cs = *static_cast<float *>(returned_data);
206 return cs;
207 }
208 return 0.0;
209 }
210
211 void WatchScreen::get_current_pos(float *cp)
212 {
213 void *returned_data;
214
215 bool ok = PublicData::get_value( robot_checksum, current_position_checksum, &returned_data );
216 if (ok) {
217 float *p = static_cast<float *>(returned_data);
218 cp[0] = p[0];
219 cp[1] = p[1];
220 cp[2] = p[2];
221 }
222 }
223
224 void WatchScreen::get_sd_play_info()
225 {
226 void *returned_data;
227 bool ok = PublicData::get_value( player_checksum, get_progress_checksum, &returned_data );
228 if (ok) {
229 struct pad_progress p = *static_cast<struct pad_progress *>(returned_data);
230 this->elapsed_time = p.elapsed_secs;
231 this->sd_pcnt_played = p.percent_complete;
232 THEPANEL->set_playing_file(p.filename);
233
234 } else {
235 this->elapsed_time = 0;
236 this->sd_pcnt_played = 0;
237 }
238 }
239
240 static struct pad_temperature getTemperatures(uint16_t heater_cs)
241 {
242 struct pad_temperature temp;
243 PublicData::get_value( temperature_control_checksum, heater_cs, current_temperature_checksum, &temp );
244 return temp;
245 }
246
247 void WatchScreen::display_menu_line(uint16_t line)
248 {
249 // in menu mode
250 switch ( line ) {
251 case 0:
252 {
253 auto& tm= this->temp_controllers;
254 if(tm.size() > 0) {
255 // only if we detected heaters in config
256 int n= 0;
257 if(tm.size() > 2) {
258 // more than two temps we need to cycle between them
259 n= update_counts/100; // increments every 5 seconds
260 int ntemps= (tm.size()+1)/2;
261 n= n%ntemps; // which of the pairs of temps to display
262 }
263
264 int off= 0;
265 for (size_t i = 0; i < 2; ++i) {
266 size_t o= i+(n*2);
267 if(o>tm.size()-1) break;
268 struct pad_temperature temp= getTemperatures(tm[o]);
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 }
280 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;
281 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;
282 case 3: THEPANEL->lcd->printf("%19s", this->get_status()); break;
283 }
284 }
285
286 const char *WatchScreen::get_status()
287 {
288 if (THEPANEL->hasMessage())
289 return THEPANEL->getMessage().c_str();
290
291 if (THEPANEL->is_halted())
292 return "HALTED Reset or M999";
293
294 if (THEKERNEL->pauser->paused())
295 return "Paused";
296
297 if (THEPANEL->is_suspended())
298 return "Suspended";
299
300 if (THEPANEL->is_playing())
301 return THEPANEL->get_playing_file();
302
303 if (!THEKERNEL->conveyor->is_queue_empty())
304 return "Printing";
305
306 const char *ip = get_network();
307 if (ip == NULL) {
308 return "Smoothie ready";
309 } else {
310 return ip;
311 }
312 }
313
314 void WatchScreen::set_speed()
315 {
316 send_gcode("M220", 'S', this->current_speed);
317 }
318
319 const char *WatchScreen::get_network()
320 {
321 void *returned_data;
322
323 bool ok = PublicData::get_value( network_checksum, get_ip_checksum, &returned_data );
324 if (ok) {
325 uint8_t *ipaddr = (uint8_t *)returned_data;
326 char buf[20];
327 int n = snprintf(buf, sizeof(buf), "IP %d.%d.%d.%d", ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]);
328 buf[n] = 0;
329 if (this->ipstr == nullptr) {
330 this->ipstr = new char[n + 1];
331 }
332 strcpy(this->ipstr, buf);
333
334 return this->ipstr;
335 }
336
337 return NULL;
338 }