interating temperature controllers was done wrong. refactoring that error.
[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"
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 34using namespace std;
ebc64506 35static 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
59WatchScreen::WatchScreen()
60{
61 speed_changed = false;
62 issue_change_speed = false;
383c9c1c 63 ipstr = nullptr;
c77d6dae 64 update_counts= 0;
383c9c1c
JM
65}
66
67WatchScreen::~WatchScreen()
68{
69 delete[] ipstr;
dcf86322 70}
35089dc7 71
862fc625
JM
72void 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
85void 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
172void 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 181void *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
194void 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 211float 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 223void 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
236void 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
252void 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
292const 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
320void WatchScreen::set_speed()
321{
2fa50ca0 322 send_gcode("M220", 'S', this->current_speed);
dcf86322 323}
d4ee6ee2
JM
324
325const 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}