Implement endstops using new motion control
[clinton/Smoothieware.git] / src / modules / utils / panel / screens / 3dprinter / 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 "Robot.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 "TemperatureControlPool.h"
25
26
27 #include <math.h>
28 #include <string.h>
29 #include <string>
30 #include <stdio.h>
31 #include <algorithm>
32
33 using namespace std;
34 static const uint8_t icons[] = { // 16x80 - he1, he2, he3, bed, fan
35 0x3f, 0xfc, 0x3f, 0xfc, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0x7f, 0x7f, 0x7e, 0x3f, 0x7c, 0x1f,
36 0x78, 0x0f, 0xf0, 0x07, 0xe0, 0x03, 0xc0, 0x01, 0x80, 0x00, 0x00, 0x01, 0x80, 0x01, 0x80,
37 0x01, 0x80, 0x3f, 0xfc, 0x3f, 0xfc, 0xff, 0xff, 0xfc, 0x7f, 0xff, 0x7f, 0x7c, 0x7e, 0x3d,
38 0xfc, 0x1c, 0x78, 0x0f, 0xf0, 0x07, 0xe0, 0x03, 0xc0, 0x01, 0x80, 0x00, 0x00, 0x01, 0x80,
39 0x01, 0x80, 0x01, 0x80, 0x3f, 0xfc, 0x3f, 0xfc, 0xff, 0xff, 0xfc, 0x7f, 0xff, 0x7f, 0x7c,
40 0x7e, 0x3f, 0x7c, 0x1c, 0x78, 0x0f, 0xf0, 0x07, 0xe0, 0x03, 0xc0, 0x01, 0x80, 0x00, 0x00,
41 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x00, 0x00, 0x08, 0x88, 0x11, 0x10, 0x22, 0x20, 0x22,
42 0x20, 0x11, 0x10, 0x08, 0x88, 0x04, 0x44, 0x04, 0x44, 0x08, 0x88, 0x11, 0x10, 0x22, 0x20,
43 0x00, 0x00, 0x7f, 0xfe, 0xff, 0xff, 0x7f, 0xfe, 0x39, 0xec, 0x43, 0xe2, 0x9b, 0xc9, 0xa3,
44 0x85, 0x03, 0x85, 0xc3, 0x00, 0xe0, 0x3e, 0xf9, 0xbf, 0xfd, 0x9f, 0x7c, 0x07, 0x00, 0xc3,
45 0xa1, 0xc0, 0xa1, 0xc5, 0x93, 0xd9, 0x47, 0xc2, 0x37, 0x9c
46 };
47
48 WatchScreen::WatchScreen()
49 {
50 speed_changed = false;
51 issue_change_speed = false;
52 ipstr = nullptr;
53 update_counts= 0;
54 }
55
56 WatchScreen::~WatchScreen()
57 {
58 delete[] ipstr;
59 }
60
61 void WatchScreen::on_enter()
62 {
63 THEPANEL->lcd->clear();
64 THEPANEL->setup_menu(4);
65 get_current_status();
66 get_current_pos(this->pos);
67 get_sd_play_info();
68 this->current_speed = lroundf(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 // enumerate temperature controls
74 temp_controllers.clear();
75 std::vector<struct pad_temperature> controllers;
76 bool ok = PublicData::get_value(temperature_control_checksum, poll_controls_checksum, &controllers);
77 if (ok) {
78 for (auto &c : controllers) {
79 temp_controllers.push_back(c.id);
80 }
81 }
82 }
83
84 static struct pad_temperature getTemperatures(uint16_t heater_cs)
85 {
86 struct pad_temperature temp;
87 PublicData::get_value( temperature_control_checksum, current_temperature_checksum, heater_cs, &temp );
88 return temp;
89 }
90
91 void WatchScreen::on_refresh()
92 {
93 // Exit if the button is clicked
94 if ( THEPANEL->click() ) {
95 THEPANEL->enter_screen(this->parent);
96 return;
97 }
98
99 // see if speed is being changed
100 if (THEPANEL->control_value_change()) {
101 this->current_speed = THEPANEL->get_control_value();
102 if (this->current_speed < 10) {
103 this->current_speed = 10;
104 THEPANEL->set_control_value(this->current_speed);
105 THEPANEL->reset_counter();
106 } else {
107 // flag the update to change the speed, we don't want to issue hundreds of M220s
108 // but we do want to display the change we are going to make
109 this->speed_changed = true; // flag indicating speed changed
110 this->refresh_screen(false);
111 }
112 }
113
114 // Update Only every 20 refreshes, 1 a second
115 update_counts++;
116 if ( update_counts % 20 == 0 ) {
117 get_sd_play_info();
118 get_current_pos(this->pos);
119 get_current_status();
120 if (this->speed_changed) {
121 this->issue_change_speed = true; // trigger actual command to change speed
122 this->speed_changed = false;
123 } else if (!this->issue_change_speed) { // change still queued
124 // read it in case it was changed via M220
125 this->current_speed = lroundf(get_current_speed());
126 THEPANEL->set_control_value(this->current_speed);
127 THEPANEL->reset_counter();
128 }
129
130 this->refresh_screen(THEPANEL->lcd->hasGraphics() ? true : false); // graphics screens should be cleared
131
132 // for LCDs with leds set them according to heater status
133 bool bed_on= false, hotend_on= false, is_hot= false;
134 uint8_t heon=0, hemsk= 0x01; // bit set for which hotend is on bit0: hotend1, bit1: hotend2 etc
135 for(auto id : temp_controllers) {
136 struct pad_temperature c= getTemperatures(id);
137 if(c.current_temperature > 50) is_hot= true; // anything is hot
138 if(c.designator.front() == 'B' && c.target_temperature > 0) bed_on= true; // bed on/off
139 if(c.designator.front() == 'T') { // a hotend by convention
140 if(c.target_temperature > 0){
141 hotend_on= true;// hotend on/off (anyone)
142 heon |= hemsk;
143 }
144 hemsk <<= 1;
145 }
146 }
147
148 THEPANEL->lcd->setLed(LED_BED_ON, bed_on);
149 THEPANEL->lcd->setLed(LED_HOTEND_ON, hotend_on);
150 THEPANEL->lcd->setLed(LED_HOT, is_hot);
151
152 THEPANEL->lcd->setLed(LED_FAN_ON, this->fan_state);
153
154 if (THEPANEL->lcd->hasGraphics()) {
155 // display the graphical icons below the status are
156 // for (int i = 0; i < 5; ++i) {
157 // THEPANEL->lcd->bltGlyph(i*24, 42, 16, 16, icons, 15, i*24, 0);
158 // }
159 if(heon&0x01) THEPANEL->lcd->bltGlyph(0, 42, 16, 16, icons, 2, 0, 0);
160 if(heon&0x02) THEPANEL->lcd->bltGlyph(27, 42, 16, 16, icons, 2, 0, 16);
161 if(heon&0x04) THEPANEL->lcd->bltGlyph(55, 42, 16, 16, icons, 2, 0, 32);
162
163 if (bed_on)
164 THEPANEL->lcd->bltGlyph(83, 42, 16, 16, icons, 2, 0, 48);
165
166 if(this->fan_state)
167 THEPANEL->lcd->bltGlyph(111, 42, 16, 16, icons, 2, 0, 64);
168 }
169 }
170 }
171
172 // queuing gcodes needs to be done from main loop
173 void WatchScreen::on_main_loop()
174 {
175 if (this->issue_change_speed) {
176 this->issue_change_speed = false;
177 set_speed();
178 }
179 PanelScreen::on_main_loop(); // in case any queued commands left
180 }
181
182 // fetch the data we are displaying
183 void WatchScreen::get_current_status()
184 {
185 // get fan status
186 struct pad_switch s;
187 bool ok = PublicData::get_value( switch_checksum, fan_checksum, 0, &s );
188 if (ok) {
189 this->fan_state = s.state;
190 } else {
191 // fan probably disabled
192 this->fan_state = false;
193 }
194 }
195
196 // fetch the data we are displaying
197 float WatchScreen::get_current_speed()
198 {
199 // in percent
200 return 6000.0F / THEROBOT->get_seconds_per_minute();
201 }
202
203 void WatchScreen::get_sd_play_info()
204 {
205 void *returned_data;
206 bool ok = PublicData::get_value( player_checksum, get_progress_checksum, &returned_data );
207 if (ok) {
208 struct pad_progress p = *static_cast<struct pad_progress *>(returned_data);
209 this->elapsed_time = p.elapsed_secs;
210 this->sd_pcnt_played = p.percent_complete;
211 THEPANEL->set_playing_file(p.filename);
212
213 } else {
214 this->elapsed_time = 0;
215 this->sd_pcnt_played = 0;
216 }
217 }
218
219 void WatchScreen::display_menu_line(uint16_t line)
220 {
221 // in menu mode
222 switch ( line ) {
223 case 0:
224 {
225 auto& tm= this->temp_controllers;
226 if(tm.size() > 0) {
227 // only if we detected heaters in config
228 int n= 0;
229 if(tm.size() > 2) {
230 // more than two temps we need to cycle between them
231 n= update_counts/100; // increments every 5 seconds
232 int ntemps= (tm.size()+1)/2;
233 n= n%ntemps; // which of the pairs of temps to display
234 }
235
236 int off= 0;
237 for (size_t i = 0; i < 2; ++i) {
238 size_t o= i+(n*2);
239 if(o>tm.size()-1) break;
240 struct pad_temperature temp= getTemperatures(tm[o]);
241 int t= std::min(999, (int)roundf(temp.current_temperature));
242 int tt= roundf(temp.target_temperature);
243 THEPANEL->lcd->setCursor(off, 0); // col, row
244 off += THEPANEL->lcd->printf("%s:%03d/%03d ", temp.designator.substr(0, 2).c_str(), t, tt);
245 }
246
247 }else{
248 //THEPANEL->lcd->printf("No Heaters");
249 }
250 break;
251 }
252 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;
253 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;
254 case 3: THEPANEL->lcd->printf("%19s", this->get_status()); break;
255 }
256 }
257
258 const char *WatchScreen::get_status()
259 {
260 if (THEPANEL->hasMessage())
261 return THEPANEL->getMessage().c_str();
262
263 if (THEKERNEL->is_halted())
264 return "HALTED Reset or M999";
265
266 if (THEPANEL->is_suspended())
267 return "Suspended";
268
269 if (THEPANEL->is_playing())
270 return THEPANEL->get_playing_file();
271
272 if (!THEKERNEL->conveyor->is_queue_empty())
273 return "Printing";
274
275 const char *ip = get_network();
276 if (ip == NULL) {
277 return "Smoothie ready";
278 } else {
279 return ip;
280 }
281 }
282
283 void WatchScreen::set_speed()
284 {
285 send_gcode("M220", 'S', this->current_speed);
286 }
287
288 const char *WatchScreen::get_network()
289 {
290 void *returned_data;
291
292 bool ok = PublicData::get_value( network_checksum, get_ip_checksum, &returned_data );
293 if (ok) {
294 uint8_t *ipaddr = (uint8_t *)returned_data;
295 char buf[20];
296 int n = snprintf(buf, sizeof(buf), "IP %d.%d.%d.%d", ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]);
297 buf[n] = 0;
298 if (this->ipstr == nullptr) {
299 this->ipstr = new char[n + 1];
300 }
301 strcpy(this->ipstr, buf);
302
303 return this->ipstr;
304 }
305
306 return NULL;
307 }