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