Commit | Line | Data |
---|---|---|
3105a7be | 1 | /* |
35089dc7 JM |
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. | |
3105a7be | 5 | You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. |
35089dc7 JM |
6 | */ |
7 | ||
8 | #include "libs/Kernel.h" | |
383c9c1c | 9 | #include "LcdBase.h" |
35089dc7 JM |
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" | |
691dcad3 | 18 | #include "modules/robot/Conveyor.h" |
35089dc7 | 19 | #include "modules/utils/player/PlayerPublicAccess.h" |
d4ee6ee2 | 20 | #include "NetworkPublicAccess.h" |
61134a65 | 21 | #include "PublicData.h" |
f1aac8df | 22 | #include "SwitchPublicAccess.h" |
61134a65 JM |
23 | #include "checksumm.h" |
24 | #include "Pauser.h" | |
02e4b295 JM |
25 | #include "TemperatureControlPool.h" |
26 | ||
27 | ||
61134a65 JM |
28 | #include <math.h> |
29 | #include <string.h> | |
35089dc7 | 30 | #include <string> |
383c9c1c | 31 | #include <stdio.h> |
373d0bf1 | 32 | #include <algorithm> |
61134a65 | 33 | |
35089dc7 | 34 | using namespace std; |
ebc64506 | 35 | static const uint8_t icons[] = { // 115x19 - 3 bytes each: he1, he2, he3, bed, fan |
862fc625 JM |
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 | |
ebc64506 | 55 | }; |
35089dc7 | 56 | |
deb26bcd JM |
57 | |
58 | ||
862fc625 JM |
59 | WatchScreen::WatchScreen() |
60 | { | |
61 | speed_changed = false; | |
62 | issue_change_speed = false; | |
383c9c1c | 63 | ipstr = nullptr; |
c77d6dae | 64 | update_counts= 0; |
383c9c1c JM |
65 | } |
66 | ||
67 | WatchScreen::~WatchScreen() | |
68 | { | |
69 | delete[] ipstr; | |
dcf86322 | 70 | } |
35089dc7 | 71 | |
862fc625 JM |
72 | void WatchScreen::on_enter() |
73 | { | |
cee1bb2d JM |
74 | THEPANEL->lcd->clear(); |
75 | THEPANEL->setup_menu(4); | |
c77d6dae | 76 | get_current_status(); |
58d6d841 JM |
77 | get_current_pos(this->pos); |
78 | get_sd_play_info(); | |
862fc625 | 79 | this->current_speed = lround(get_current_speed()); |
58d6d841 | 80 | this->refresh_screen(false); |
cee1bb2d JM |
81 | THEPANEL->enter_control_mode(1, 0.5); |
82 | THEPANEL->set_control_value(this->current_speed); | |
35089dc7 JM |
83 | } |
84 | ||
862fc625 JM |
85 | void WatchScreen::on_refresh() |
86 | { | |
3105a7be | 87 | // Exit if the button is clicked |
cee1bb2d JM |
88 | if ( THEPANEL->click() ) { |
89 | THEPANEL->enter_screen(this->parent); | |
35089dc7 | 90 | return; |
58d6d841 JM |
91 | } |
92 | ||
93 | // see if speed is being changed | |
cee1bb2d JM |
94 | if (THEPANEL->control_value_change()) { |
95 | this->current_speed = THEPANEL->get_control_value(); | |
862fc625 JM |
96 | if (this->current_speed < 10) { |
97 | this->current_speed = 10; | |
cee1bb2d JM |
98 | THEPANEL->set_control_value(this->current_speed); |
99 | THEPANEL->reset_counter(); | |
862fc625 | 100 | } else { |
3f038f58 JM |
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 | |
862fc625 | 103 | this->speed_changed = true; // flag indicating speed changed |
7a522ccc JM |
104 | this->refresh_screen(false); |
105 | } | |
58d6d841 | 106 | } |
3105a7be | 107 | |
35089dc7 | 108 | // Update Only every 20 refreshes, 1 a second |
35089dc7 | 109 | update_counts++; |
862fc625 | 110 | if ( update_counts % 20 == 0 ) { |
58d6d841 JM |
111 | get_sd_play_info(); |
112 | get_current_pos(this->pos); | |
c77d6dae | 113 | get_current_status(); |
862fc625 JM |
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 | |
3f038f58 | 118 | // read it in case it was changed via M220 |
862fc625 | 119 | this->current_speed = lround(get_current_speed()); |
cee1bb2d JM |
120 | THEPANEL->set_control_value(this->current_speed); |
121 | THEPANEL->reset_counter(); | |
3f038f58 | 122 | } |
7a522ccc | 123 | |
cee1bb2d | 124 | this->refresh_screen(THEPANEL->lcd->hasGraphics() ? true : false); // graphics screens should be cleared |
91c45256 JM |
125 | |
126 | // for LCDs with leds set them according to heater status | |
c77d6dae | 127 | bool bed_on= false, hotend_on= false, is_hot= false; |
226d08ee | 128 | uint8_t heon=0, hemsk= 0x01; // bit set for which hotend is on bit0: hotend1, bit1: hotend2 etc |
bab4e1bd JM |
129 | temp_controllers.clear(); |
130 | std::vector<struct pad_temperature*> *controllers; | |
131 | bool ok = PublicData::get_value(temperature_control_checksum, poll_controls_checksum, (void**)&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){ | |
226d08ee JM |
139 | hotend_on= true;// hotend on/off (anyone) |
140 | heon |= hemsk; | |
141 | } | |
142 | hemsk <<= 1; | |
143 | } | |
c77d6dae JM |
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 | ||
cee1bb2d | 150 | THEPANEL->lcd->setLed(LED_FAN_ON, this->fan_state); |
ebc64506 | 151 | |
cee1bb2d | 152 | if (THEPANEL->lcd->hasGraphics()) { |
ebc64506 | 153 | // display the graphical icons below the status are |
cee1bb2d | 154 | //THEPANEL->lcd->bltGlyph(0, 34, 115, 19, icons); |
ebc64506 | 155 | // for (int i = 0; i < 5; ++i) { |
cee1bb2d | 156 | // THEPANEL->lcd->bltGlyph(i*24, 38, 23, 19, icons, 15, i*24, 0); |
ebc64506 | 157 | // } |
226d08ee JM |
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); | |
ebc64506 | 161 | |
c77d6dae | 162 | if (bed_on) |
226d08ee | 163 | THEPANEL->lcd->bltGlyph(60, 38, 23, 19, icons, 15, 64, 0); |
3105a7be | 164 | |
f1aac8df | 165 | if(this->fan_state) |
cee1bb2d | 166 | THEPANEL->lcd->bltGlyph(96, 38, 23, 19, icons, 15, 96, 0); |
ebc64506 | 167 | } |
35089dc7 JM |
168 | } |
169 | } | |
170 | ||
dcf86322 | 171 | // queuing gcodes needs to be done from main loop |
862fc625 JM |
172 | void WatchScreen::on_main_loop() |
173 | { | |
174 | if (this->issue_change_speed) { | |
175 | this->issue_change_speed = false; | |
3f038f58 JM |
176 | set_speed(); |
177 | } | |
f15e6f4b | 178 | PanelScreen::on_main_loop(); // in case any queued commands left |
dcf86322 JM |
179 | } |
180 | ||
c77d6dae | 181 | void *WatchScreen::getTemperatures(uint16_t heater_cs) |
862fc625 | 182 | { |
58d6d841 | 183 | void *returned_data; |
c77d6dae | 184 | bool ok = PublicData::get_value( temperature_control_checksum, heater_cs, current_temperature_checksum, &returned_data ); |
58d6d841 | 185 | |
862fc625 | 186 | if (ok) { |
c77d6dae | 187 | return returned_data; |
58d6d841 JM |
188 | } |
189 | ||
c77d6dae JM |
190 | return nullptr; |
191 | } | |
206167cf | 192 | |
c77d6dae JM |
193 | // fetch the data we are displaying |
194 | void WatchScreen::get_current_status() | |
195 | { | |
196 | void *returned_data; | |
197 | bool ok; | |
f1aac8df JM |
198 | |
199 | // get fan status | |
75e6428d | 200 | ok = PublicData::get_value( switch_checksum, fan_checksum, 0, &returned_data ); |
f1aac8df JM |
201 | if (ok) { |
202 | struct pad_switch s = *static_cast<struct pad_switch *>(returned_data); | |
203 | this->fan_state = s.state; | |
204 | } else { | |
205 | // fan probably disabled | |
206 | this->fan_state = false; | |
207 | } | |
35089dc7 JM |
208 | } |
209 | ||
210 | // fetch the data we are displaying | |
1ad23cd3 | 211 | float WatchScreen::get_current_speed() |
862fc625 | 212 | { |
58d6d841 | 213 | void *returned_data; |
3105a7be | 214 | |
75e6428d | 215 | bool ok = PublicData::get_value( robot_checksum, speed_override_percent_checksum, &returned_data ); |
862fc625 | 216 | if (ok) { |
1ad23cd3 | 217 | float cs = *static_cast<float *>(returned_data); |
58d6d841 JM |
218 | return cs; |
219 | } | |
220 | return 0.0; | |
35089dc7 JM |
221 | } |
222 | ||
1ad23cd3 | 223 | void WatchScreen::get_current_pos(float *cp) |
862fc625 | 224 | { |
58d6d841 JM |
225 | void *returned_data; |
226 | ||
75e6428d | 227 | bool ok = PublicData::get_value( robot_checksum, current_position_checksum, &returned_data ); |
862fc625 | 228 | if (ok) { |
1ad23cd3 | 229 | float *p = static_cast<float *>(returned_data); |
862fc625 JM |
230 | cp[0] = p[0]; |
231 | cp[1] = p[1]; | |
232 | cp[2] = p[2]; | |
58d6d841 | 233 | } |
35089dc7 JM |
234 | } |
235 | ||
862fc625 JM |
236 | void WatchScreen::get_sd_play_info() |
237 | { | |
58d6d841 | 238 | void *returned_data; |
75e6428d | 239 | bool ok = PublicData::get_value( player_checksum, get_progress_checksum, &returned_data ); |
862fc625 JM |
240 | if (ok) { |
241 | struct pad_progress p = *static_cast<struct pad_progress *>(returned_data); | |
242 | this->elapsed_time = p.elapsed_secs; | |
243 | this->sd_pcnt_played = p.percent_complete; | |
cee1bb2d | 244 | THEPANEL->set_playing_file(p.filename); |
4c8afa75 | 245 | |
862fc625 JM |
246 | } else { |
247 | this->elapsed_time = 0; | |
248 | this->sd_pcnt_played = 0; | |
58d6d841 | 249 | } |
35089dc7 JM |
250 | } |
251 | ||
862fc625 JM |
252 | void WatchScreen::display_menu_line(uint16_t line) |
253 | { | |
58d6d841 | 254 | // in menu mode |
862fc625 | 255 | switch ( line ) { |
e0437082 | 256 | case 0: |
02e4b295 | 257 | { |
bab4e1bd | 258 | auto& tm= this->temp_controllers; |
02e4b295 | 259 | if(tm.size() > 0) { |
e0437082 | 260 | // only if we detected heaters in config |
c77d6dae JM |
261 | int n= 0; |
262 | if(tm.size() > 2) { | |
263 | // more than two temps we need to cycle between them | |
264 | n= update_counts/100; // increments every 5 seconds | |
265 | int ntemps= (tm.size()+1)/2; | |
266 | n= n%ntemps; // which of the pairs of temps to display | |
267 | } | |
268 | ||
269 | int off= 0; | |
270 | for (size_t i = 0; i < 2; ++i) { | |
271 | size_t o= i+(n*2); | |
272 | if(o>tm.size()-1) break; | |
273 | struct pad_temperature *temp= static_cast<struct pad_temperature *>(getTemperatures(tm[o])); | |
274 | if(temp == nullptr) continue; | |
373d0bf1 JM |
275 | int t= std::min(999, (int)roundf(temp->current_temperature)); |
276 | int tt= roundf(temp->target_temperature); | |
c77d6dae | 277 | THEPANEL->lcd->setCursor(off, 0); // col, row |
373d0bf1 | 278 | off += THEPANEL->lcd->printf("%s:%03d/%03d ", temp->designator.substr(0, 2).c_str(), t, tt); |
c77d6dae JM |
279 | } |
280 | ||
e0437082 JM |
281 | }else{ |
282 | //THEPANEL->lcd->printf("No Heaters"); | |
283 | } | |
284 | break; | |
02e4b295 | 285 | } |
cee1bb2d JM |
286 | 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; |
287 | 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; | |
288 | case 3: THEPANEL->lcd->printf("%19s", this->get_status()); break; | |
58d6d841 | 289 | } |
35089dc7 | 290 | } |
84267f43 | 291 | |
862fc625 JM |
292 | const char *WatchScreen::get_status() |
293 | { | |
5f1a896b | 294 | if (THEPANEL->hasMessage()) |
cee1bb2d | 295 | return THEPANEL->getMessage().c_str(); |
399cb110 | 296 | |
5f1a896b | 297 | if (THEPANEL->is_halted()) |
76217df5 | 298 | return "HALTED Reset or M999"; |
76217df5 | 299 | |
862fc625 | 300 | if (THEKERNEL->pauser->paused()) |
84267f43 | 301 | return "Paused"; |
3105a7be | 302 | |
5f1a896b JM |
303 | if (THEPANEL->is_suspended()) |
304 | return "Suspended"; | |
305 | ||
cee1bb2d JM |
306 | if (THEPANEL->is_playing()) |
307 | return THEPANEL->get_playing_file(); | |
84267f43 | 308 | |
5f1a896b | 309 | if (!THEKERNEL->conveyor->is_queue_empty()) |
691dcad3 | 310 | return "Printing"; |
691dcad3 | 311 | |
d4ee6ee2 JM |
312 | const char *ip = get_network(); |
313 | if (ip == NULL) { | |
314 | return "Smoothie ready"; | |
315 | } else { | |
316 | return ip; | |
317 | } | |
84267f43 | 318 | } |
dcf86322 | 319 | |
862fc625 JM |
320 | void WatchScreen::set_speed() |
321 | { | |
2fa50ca0 | 322 | send_gcode("M220", 'S', this->current_speed); |
dcf86322 | 323 | } |
d4ee6ee2 JM |
324 | |
325 | const char *WatchScreen::get_network() | |
326 | { | |
327 | void *returned_data; | |
328 | ||
75e6428d | 329 | bool ok = PublicData::get_value( network_checksum, get_ip_checksum, &returned_data ); |
d4ee6ee2 JM |
330 | if (ok) { |
331 | uint8_t *ipaddr = (uint8_t *)returned_data; | |
332 | char buf[20]; | |
333 | int n = snprintf(buf, sizeof(buf), "IP %d.%d.%d.%d", ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]); | |
334 | buf[n] = 0; | |
383c9c1c JM |
335 | if (this->ipstr == nullptr) { |
336 | this->ipstr = new char[n + 1]; | |
d4ee6ee2 JM |
337 | } |
338 | strcpy(this->ipstr, buf); | |
339 | ||
340 | return this->ipstr; | |
341 | } | |
342 | ||
343 | return NULL; | |
344 | } |