Enumerate hotends for display in panel temperature settings
[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 "Panel.h"
10 #include "PanelScreen.h"
11 #include "MainMenuScreen.h"
12 #include "WatchScreen.h"
13 #include "libs/nuts_bolts.h"
14 #include "libs/utils.h"
15 #include "modules/tools/temperaturecontrol/TemperatureControlPublicAccess.h"
16 #include "modules/robot/RobotPublicAccess.h"
17 #include "modules/robot/Conveyor.h"
18 #include "modules/utils/player/PlayerPublicAccess.h"
19 #include "NetworkPublicAccess.h"
20 #include "PublicData.h"
21 #include "SwitchPublicAccess.h"
22 #include "checksumm.h"
23 #include "Pauser.h"
24
25 #include <math.h>
26 #include <string.h>
27 #include <string>
28
29 using namespace std;
30 static const uint8_t icons[] = { // 115x19 - 3 bytes each: he1, he2, he3, bed, fan
31 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0,
32 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xE0,
33 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x0C, 0x60,
34 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x0E, 0x20,
35 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x0F, 0x20,
36 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x0F, 0xA0,
37 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x07, 0xA0,
38 0x7F, 0x80, 0x00, 0x3F, 0xC0, 0x00, 0x3F, 0xC0, 0x00, 0x41, 0x04, 0x00, 0x40, 0x60, 0x20,
39 0xFB, 0xC0, 0x00, 0x79, 0xE0, 0x00, 0x79, 0xE0, 0x00, 0x20, 0x82, 0x00, 0x40, 0xF0, 0x20,
40 0xF3, 0xC0, 0x00, 0x76, 0xE0, 0x00, 0x76, 0xE0, 0x00, 0x20, 0x82, 0x00, 0x40, 0xF0, 0x20,
41 0xEB, 0xC0, 0x00, 0x7E, 0xE0, 0x00, 0x7E, 0xE0, 0x00, 0x41, 0x04, 0x00, 0x40, 0x60, 0x20,
42 0x7B, 0x80, 0x00, 0x3D, 0xC0, 0x00, 0x39, 0xC0, 0x00, 0x82, 0x08, 0x00, 0x5E, 0x07, 0xA0,
43 0x7B, 0x80, 0x00, 0x3B, 0xC0, 0x00, 0x3E, 0xC0, 0x01, 0x04, 0x10, 0x00, 0x5F, 0x0F, 0xA0,
44 0xFB, 0xC0, 0x00, 0x77, 0xE0, 0x00, 0x76, 0xE0, 0x01, 0x04, 0x10, 0x00, 0x4F, 0x0F, 0x20,
45 0xFB, 0xC0, 0x00, 0x70, 0xE0, 0x00, 0x79, 0xE0, 0x00, 0x82, 0x08, 0x00, 0x47, 0x0E, 0x20,
46 0xFF, 0xC0, 0x00, 0x7F, 0xE0, 0x00, 0x7F, 0xE0, 0x00, 0x41, 0x04, 0x00, 0x63, 0x0C, 0x60,
47 0x3F, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xE0,
48 0x1E, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x0F, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xE0,
49 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00
50 };
51
52
53
54 WatchScreen::WatchScreen()
55 {
56 speed_changed = false;
57 issue_change_speed = false;
58 ipstr = NULL;
59 }
60
61 void WatchScreen::on_enter()
62 {
63 THEPANEL->lcd->clear();
64 THEPANEL->setup_menu(4);
65 get_temp_data();
66 get_current_pos(this->pos);
67 get_sd_play_info();
68 this->current_speed = lround(get_current_speed());
69 this->refresh_screen(false);
70 THEPANEL->enter_control_mode(1, 0.5);
71 THEPANEL->set_control_value(this->current_speed);
72 }
73
74 void WatchScreen::on_refresh()
75 {
76 // Exit if the button is clicked
77 if ( THEPANEL->click() ) {
78 THEPANEL->enter_screen(this->parent);
79 return;
80 }
81
82 // see if speed is being changed
83 if (THEPANEL->control_value_change()) {
84 this->current_speed = THEPANEL->get_control_value();
85 if (this->current_speed < 10) {
86 this->current_speed = 10;
87 THEPANEL->set_control_value(this->current_speed);
88 THEPANEL->reset_counter();
89 } else {
90 // flag the update to change the speed, we don't want to issue hundreds of M220s
91 // but we do want to display the change we are going to make
92 this->speed_changed = true; // flag indicating speed changed
93 this->refresh_screen(false);
94 }
95 }
96
97 // Update Only every 20 refreshes, 1 a second
98 static int update_counts = 0;
99 update_counts++;
100 if ( update_counts % 20 == 0 ) {
101 get_sd_play_info();
102 get_current_pos(this->pos);
103 get_temp_data();
104 if (this->speed_changed) {
105 this->issue_change_speed = true; // trigger actual command to change speed
106 this->speed_changed = false;
107 } else if (!this->issue_change_speed) { // change still queued
108 // read it in case it was changed via M220
109 this->current_speed = lround(get_current_speed());
110 THEPANEL->set_control_value(this->current_speed);
111 THEPANEL->reset_counter();
112 }
113
114 this->refresh_screen(THEPANEL->lcd->hasGraphics() ? true : false); // graphics screens should be cleared
115
116 // for LCDs with leds set them according to heater status
117 // TODO should be enabled and disabled and settable from config
118 THEPANEL->lcd->setLed(LED_BED_ON, this->bedtarget > 0);
119 THEPANEL->lcd->setLed(LED_HOTEND_ON, this->hotendtarget > 0);
120 THEPANEL->lcd->setLed(LED_FAN_ON, this->fan_state);
121
122 if (THEPANEL->lcd->hasGraphics()) {
123 // display the graphical icons below the status are
124 //THEPANEL->lcd->bltGlyph(0, 34, 115, 19, icons);
125 // for (int i = 0; i < 5; ++i) {
126 // THEPANEL->lcd->bltGlyph(i*24, 38, 23, 19, icons, 15, i*24, 0);
127 // }
128 if (this->hotendtarget > 0)
129 THEPANEL->lcd->bltGlyph(8, 38, 20, 19, icons, 15, 0, 0);
130
131 if (this->bedtarget > 0)
132 THEPANEL->lcd->bltGlyph(32, 38, 23, 19, icons, 15, 64, 0);
133
134 if(this->fan_state)
135 THEPANEL->lcd->bltGlyph(96, 38, 23, 19, icons, 15, 96, 0);
136 }
137 }
138 }
139
140 // queuing gcodes needs to be done from main loop
141 void WatchScreen::on_main_loop()
142 {
143 if (this->issue_change_speed) {
144 this->issue_change_speed = false;
145 set_speed();
146 }
147 }
148
149 // fetch the data we are displaying
150 void WatchScreen::get_temp_data()
151 {
152 void *returned_data;
153 bool ok;
154
155 ok = THEKERNEL->public_data->get_value( temperature_control_checksum, bed_checksum, current_temperature_checksum, &returned_data );
156 if (ok) {
157 struct pad_temperature temp = *static_cast<struct pad_temperature *>(returned_data);
158 this->bedtemp = round(temp.current_temperature);
159 if (this->bedtemp > 100000) this->bedtemp = -2;
160 this->bedtarget = round(temp.target_temperature);
161 //this->bedpwm= temp.pwm;
162 } else {
163 // temp probably disabled
164 this->bedtemp = -1;
165 this->bedtarget = -1;
166 }
167
168
169 ok = THEKERNEL->public_data->get_value( temperature_control_checksum, hotend_checksum, current_temperature_checksum, &returned_data );
170 if (ok) {
171 struct pad_temperature temp = *static_cast<struct pad_temperature *>(returned_data);
172 this->hotendtemp = round(temp.current_temperature);
173 if (this->hotendtemp > 100000) this->hotendtemp = -2;
174 this->hotendtarget = round(temp.target_temperature);
175 //this->hotendpwm= temp.pwm;
176 } else {
177 // temp probably disabled
178 this->hotendtemp = -1;
179 this->hotendtarget = -1;
180 }
181
182 // get fan status
183 ok = THEKERNEL->public_data->get_value( switch_checksum, fan_checksum, 0, &returned_data );
184 if (ok) {
185 struct pad_switch s = *static_cast<struct pad_switch *>(returned_data);
186 this->fan_state = s.state;
187 } else {
188 // fan probably disabled
189 this->fan_state = false;
190 }
191 }
192
193 // fetch the data we are displaying
194 float WatchScreen::get_current_speed()
195 {
196 void *returned_data;
197
198 bool ok = THEKERNEL->public_data->get_value( robot_checksum, speed_override_percent_checksum, &returned_data );
199 if (ok) {
200 float cs = *static_cast<float *>(returned_data);
201 return cs;
202 }
203 return 0.0;
204 }
205
206 void WatchScreen::get_current_pos(float *cp)
207 {
208 void *returned_data;
209
210 bool ok = THEKERNEL->public_data->get_value( robot_checksum, current_position_checksum, &returned_data );
211 if (ok) {
212 float *p = static_cast<float *>(returned_data);
213 cp[0] = p[0];
214 cp[1] = p[1];
215 cp[2] = p[2];
216 }
217 }
218
219 void WatchScreen::get_sd_play_info()
220 {
221 void *returned_data;
222 bool ok = THEKERNEL->public_data->get_value( player_checksum, get_progress_checksum, &returned_data );
223 if (ok) {
224 struct pad_progress p = *static_cast<struct pad_progress *>(returned_data);
225 this->elapsed_time = p.elapsed_secs;
226 this->sd_pcnt_played = p.percent_complete;
227 THEPANEL->set_playing_file(p.filename);
228
229 } else {
230 this->elapsed_time = 0;
231 this->sd_pcnt_played = 0;
232 }
233 }
234
235 void WatchScreen::display_menu_line(uint16_t line)
236 {
237 // in menu mode
238 switch ( line ) {
239 case 0: THEPANEL->lcd->printf("H%03d/%03dc B%03d/%03dc", this->hotendtemp, this->hotendtarget, this->bedtemp, this->bedtarget); break;
240 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;
241 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;
242 case 3: THEPANEL->lcd->printf("%19s", this->get_status()); break;
243 }
244 }
245
246 const char *WatchScreen::get_status()
247 {
248 if (THEPANEL->hasMessage()) {
249 return THEPANEL->getMessage().c_str();
250 }
251
252 if (THEKERNEL->pauser->paused())
253 return "Paused";
254
255 if (THEPANEL->is_playing())
256 return THEPANEL->get_playing_file();
257
258 if (!THEKERNEL->conveyor->is_queue_empty()) {
259 return "Printing";
260 }
261
262 const char *ip = get_network();
263 if (ip == NULL) {
264 return "Smoothie ready";
265 } else {
266 return ip;
267 }
268 }
269
270 void WatchScreen::set_speed()
271 {
272 send_gcode("M220", 'S', this->current_speed);
273 }
274
275 const char *WatchScreen::get_network()
276 {
277 void *returned_data;
278
279 bool ok = THEKERNEL->public_data->get_value( network_checksum, get_ip_checksum, &returned_data );
280 if (ok) {
281 uint8_t *ipaddr = (uint8_t *)returned_data;
282 char buf[20];
283 int n = snprintf(buf, sizeof(buf), "IP %d.%d.%d.%d", ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]);
284 buf[n] = 0;
285 if (this->ipstr == NULL) {
286 this->ipstr = (char *)malloc(n + 1);
287 }
288 strcpy(this->ipstr, buf);
289
290 return this->ipstr;
291 }
292
293 return NULL;
294 }