add M1910.2 to move by the specified number of actuator units
[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;
21384474
RP
34static 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
ebc64506 46};
35089dc7 47
862fc625
JM
48WatchScreen::WatchScreen()
49{
50 speed_changed = false;
51 issue_change_speed = false;
383c9c1c 52 ipstr = nullptr;
c77d6dae 53 update_counts= 0;
383c9c1c
JM
54}
55
56WatchScreen::~WatchScreen()
57{
58 delete[] ipstr;
dcf86322 59}
35089dc7 60
862fc625
JM
61void WatchScreen::on_enter()
62{
cee1bb2d
JM
63 THEPANEL->lcd->clear();
64 THEPANEL->setup_menu(4);
c77d6dae 65 get_current_status();
58d6d841
JM
66 get_current_pos(this->pos);
67 get_sd_play_info();
586cc733 68 this->current_speed = lroundf(get_current_speed());
58d6d841 69 this->refresh_screen(false);
cee1bb2d
JM
70 THEPANEL->enter_control_mode(1, 0.5);
71 THEPANEL->set_control_value(this->current_speed);
56a6c8c1
JM
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
84static 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;
35089dc7
JM
89}
90
862fc625
JM
91void WatchScreen::on_refresh()
92{
3105a7be 93 // Exit if the button is clicked
cee1bb2d
JM
94 if ( THEPANEL->click() ) {
95 THEPANEL->enter_screen(this->parent);
35089dc7 96 return;
58d6d841
JM
97 }
98
99 // see if speed is being changed
cee1bb2d
JM
100 if (THEPANEL->control_value_change()) {
101 this->current_speed = THEPANEL->get_control_value();
862fc625
JM
102 if (this->current_speed < 10) {
103 this->current_speed = 10;
cee1bb2d
JM
104 THEPANEL->set_control_value(this->current_speed);
105 THEPANEL->reset_counter();
862fc625 106 } else {
3f038f58
JM
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
862fc625 109 this->speed_changed = true; // flag indicating speed changed
7a522ccc
JM
110 this->refresh_screen(false);
111 }
58d6d841 112 }
3105a7be 113
35089dc7 114 // Update Only every 20 refreshes, 1 a second
35089dc7 115 update_counts++;
862fc625 116 if ( update_counts % 20 == 0 ) {
58d6d841
JM
117 get_sd_play_info();
118 get_current_pos(this->pos);
c77d6dae 119 get_current_status();
862fc625
JM
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
3f038f58 124 // read it in case it was changed via M220
586cc733 125 this->current_speed = lroundf(get_current_speed());
cee1bb2d
JM
126 THEPANEL->set_control_value(this->current_speed);
127 THEPANEL->reset_counter();
3f038f58 128 }
7a522ccc 129
cee1bb2d 130 this->refresh_screen(THEPANEL->lcd->hasGraphics() ? true : false); // graphics screens should be cleared
91c45256
JM
131
132 // for LCDs with leds set them according to heater status
c77d6dae 133 bool bed_on= false, hotend_on= false, is_hot= false;
226d08ee 134 uint8_t heon=0, hemsk= 0x01; // bit set for which hotend is on bit0: hotend1, bit1: hotend2 etc
56a6c8c1
JM
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;
226d08ee 143 }
56a6c8c1 144 hemsk <<= 1;
c77d6dae
JM
145 }
146 }
56a6c8c1 147
c77d6dae
JM
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
cee1bb2d 152 THEPANEL->lcd->setLed(LED_FAN_ON, this->fan_state);
ebc64506 153
cee1bb2d 154 if (THEPANEL->lcd->hasGraphics()) {
ebc64506 155 // display the graphical icons below the status are
ebc64506 156 // for (int i = 0; i < 5; ++i) {
21384474 157 // THEPANEL->lcd->bltGlyph(i*24, 42, 16, 16, icons, 15, i*24, 0);
ebc64506 158 // }
21384474
RP
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);
ebc64506 162
c77d6dae 163 if (bed_on)
21384474 164 THEPANEL->lcd->bltGlyph(83, 42, 16, 16, icons, 2, 0, 48);
3105a7be 165
f1aac8df 166 if(this->fan_state)
21384474 167 THEPANEL->lcd->bltGlyph(111, 42, 16, 16, icons, 2, 0, 64);
ebc64506 168 }
35089dc7
JM
169 }
170}
171
dcf86322 172// queuing gcodes needs to be done from main loop
862fc625
JM
173void WatchScreen::on_main_loop()
174{
175 if (this->issue_change_speed) {
176 this->issue_change_speed = false;
3f038f58
JM
177 set_speed();
178 }
f15e6f4b 179 PanelScreen::on_main_loop(); // in case any queued commands left
dcf86322
JM
180}
181
c77d6dae
JM
182// fetch the data we are displaying
183void WatchScreen::get_current_status()
184{
f1aac8df 185 // get fan status
1ae7e276
JM
186 struct pad_switch s;
187 bool ok = PublicData::get_value( switch_checksum, fan_checksum, 0, &s );
f1aac8df 188 if (ok) {
f1aac8df
JM
189 this->fan_state = s.state;
190 } else {
191 // fan probably disabled
192 this->fan_state = false;
193 }
35089dc7
JM
194}
195
196// fetch the data we are displaying
1ad23cd3 197float WatchScreen::get_current_speed()
862fc625 198{
564cf1f0
JM
199 // in percent
200 return 6000.0F / THEKERNEL->robot->get_seconds_per_minute();
35089dc7
JM
201}
202
1ad23cd3 203void WatchScreen::get_current_pos(float *cp)
862fc625 204{
564cf1f0 205 THEKERNEL->robot->get_axis_position(cp);
35089dc7
JM
206}
207
862fc625
JM
208void WatchScreen::get_sd_play_info()
209{
58d6d841 210 void *returned_data;
75e6428d 211 bool ok = PublicData::get_value( player_checksum, get_progress_checksum, &returned_data );
862fc625
JM
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;
cee1bb2d 216 THEPANEL->set_playing_file(p.filename);
4c8afa75 217
862fc625
JM
218 } else {
219 this->elapsed_time = 0;
220 this->sd_pcnt_played = 0;
58d6d841 221 }
35089dc7
JM
222}
223
862fc625
JM
224void WatchScreen::display_menu_line(uint16_t line)
225{
58d6d841 226 // in menu mode
862fc625 227 switch ( line ) {
e0437082 228 case 0:
02e4b295 229 {
bab4e1bd 230 auto& tm= this->temp_controllers;
02e4b295 231 if(tm.size() > 0) {
e0437082 232 // only if we detected heaters in config
c77d6dae
JM
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;
3bfb2639
JM
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);
c77d6dae 248 THEPANEL->lcd->setCursor(off, 0); // col, row
3bfb2639 249 off += THEPANEL->lcd->printf("%s:%03d/%03d ", temp.designator.substr(0, 2).c_str(), t, tt);
c77d6dae
JM
250 }
251
e0437082
JM
252 }else{
253 //THEPANEL->lcd->printf("No Heaters");
254 }
255 break;
02e4b295 256 }
cee1bb2d
JM
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;
58d6d841 260 }
35089dc7 261}
84267f43 262
862fc625
JM
263const char *WatchScreen::get_status()
264{
5f1a896b 265 if (THEPANEL->hasMessage())
cee1bb2d 266 return THEPANEL->getMessage().c_str();
399cb110 267
73706276 268 if (THEKERNEL->is_halted())
76217df5 269 return "HALTED Reset or M999";
76217df5 270
5f1a896b
JM
271 if (THEPANEL->is_suspended())
272 return "Suspended";
273
cee1bb2d
JM
274 if (THEPANEL->is_playing())
275 return THEPANEL->get_playing_file();
84267f43 276
5f1a896b 277 if (!THEKERNEL->conveyor->is_queue_empty())
691dcad3 278 return "Printing";
691dcad3 279
d4ee6ee2
JM
280 const char *ip = get_network();
281 if (ip == NULL) {
282 return "Smoothie ready";
283 } else {
284 return ip;
285 }
84267f43 286}
dcf86322 287
862fc625
JM
288void WatchScreen::set_speed()
289{
2fa50ca0 290 send_gcode("M220", 'S', this->current_speed);
dcf86322 291}
d4ee6ee2
JM
292
293const char *WatchScreen::get_network()
294{
295 void *returned_data;
296
75e6428d 297 bool ok = PublicData::get_value( network_checksum, get_ip_checksum, &returned_data );
d4ee6ee2
JM
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;
383c9c1c
JM
303 if (this->ipstr == nullptr) {
304 this->ipstr = new char[n + 1];
d4ee6ee2
JM
305 }
306 strcpy(this->ipstr, buf);
307
308 return this->ipstr;
309 }
310
311 return NULL;
312}