don't use safe_delay when initing lcds, as on_idle can't be called yet
[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 "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 = 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 // 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 = lround(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 / THEKERNEL->robot->get_seconds_per_minute();
201 }
202
203 void WatchScreen::get_current_pos(float *cp)
204 {
205 THEKERNEL->robot->get_axis_position(cp);
206 }
207
208 void WatchScreen::get_sd_play_info()
209 {
210 void *returned_data;
211 bool ok = PublicData::get_value( player_checksum, get_progress_checksum, &returned_data );
212 if (ok) {
213 struct pad_progress p = *static_cast<struct pad_progress *>(returned_data);
214 this->elapsed_time = p.elapsed_secs;
215 this->sd_pcnt_played = p.percent_complete;
216 THEPANEL->set_playing_file(p.filename);
217
218 } else {
219 this->elapsed_time = 0;
220 this->sd_pcnt_played = 0;
221 }
222 }
223
224 void WatchScreen::display_menu_line(uint16_t line)
225 {
226 // in menu mode
227 switch ( line ) {
228 case 0:
229 {
230 auto& tm= this->temp_controllers;
231 if(tm.size() > 0) {
232 // only if we detected heaters in config
233 int n= 0;
234 if(tm.size() > 2) {
235 // more than two temps we need to cycle between them
236 n= update_counts/100; // increments every 5 seconds
237 int ntemps= (tm.size()+1)/2;
238 n= n%ntemps; // which of the pairs of temps to display
239 }
240
241 int off= 0;
242 for (size_t i = 0; i < 2; ++i) {
243 size_t o= i+(n*2);
244 if(o>tm.size()-1) break;
245 struct pad_temperature temp= getTemperatures(tm[o]);
246 int t= std::min(999, (int)roundf(temp.current_temperature));
247 int tt= roundf(temp.target_temperature);
248 THEPANEL->lcd->setCursor(off, 0); // col, row
249 off += THEPANEL->lcd->printf("%s:%03d/%03d ", temp.designator.substr(0, 2).c_str(), t, tt);
250 }
251
252 }else{
253 //THEPANEL->lcd->printf("No Heaters");
254 }
255 break;
256 }
257 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;
258 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;
259 case 3: THEPANEL->lcd->printf("%19s", this->get_status()); break;
260 }
261 }
262
263 const char *WatchScreen::get_status()
264 {
265 if (THEPANEL->hasMessage())
266 return THEPANEL->getMessage().c_str();
267
268 if (THEKERNEL->is_halted())
269 return "HALTED Reset or M999";
270
271 if (THEPANEL->is_suspended())
272 return "Suspended";
273
274 if (THEPANEL->is_playing())
275 return THEPANEL->get_playing_file();
276
277 if (!THEKERNEL->conveyor->is_queue_empty())
278 return "Printing";
279
280 const char *ip = get_network();
281 if (ip == NULL) {
282 return "Smoothie ready";
283 } else {
284 return ip;
285 }
286 }
287
288 void WatchScreen::set_speed()
289 {
290 send_gcode("M220", 'S', this->current_speed);
291 }
292
293 const char *WatchScreen::get_network()
294 {
295 void *returned_data;
296
297 bool ok = PublicData::get_value( network_checksum, get_ip_checksum, &returned_data );
298 if (ok) {
299 uint8_t *ipaddr = (uint8_t *)returned_data;
300 char buf[20];
301 int n = snprintf(buf, sizeof(buf), "IP %d.%d.%d.%d", ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]);
302 buf[n] = 0;
303 if (this->ipstr == nullptr) {
304 this->ipstr = new char[n + 1];
305 }
306 strcpy(this->ipstr, buf);
307
308 return this->ipstr;
309 }
310
311 return NULL;
312 }