Merge pull request #294 from wolfmanjm/upstreamedge
[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 "Panel.h"
10 #include "PanelScreen.h"
11 #include "MainMenuScreen.h"
12 #include "WatchScreen.h"
13 #include "libs/nuts_bolts.h"
14 #include "libs/utils.h"
15 #include "modules/tools/temperaturecontrol/TemperatureControlPublicAccess.h"
16 #include "modules/robot/RobotPublicAccess.h"
17 #include "modules/utils/player/PlayerPublicAccess.h"
18
19 #include <string>
20 using namespace std;
21 static const uint8_t icons[] = { // 115x19 - 3 bytes each: he1, he2, he3, bed, fan
22 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0,
23 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xE0,
24 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x0C, 0x60,
25 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x0E, 0x20,
26 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x0F, 0x20,
27 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x0F, 0xA0,
28 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x07, 0xA0,
29 0x7F, 0x80, 0x00, 0x3F, 0xC0, 0x00, 0x3F, 0xC0, 0x00, 0x41, 0x04, 0x00, 0x40, 0x60, 0x20,
30 0xFB, 0xC0, 0x00, 0x79, 0xE0, 0x00, 0x79, 0xE0, 0x00, 0x20, 0x82, 0x00, 0x40, 0xF0, 0x20,
31 0xF3, 0xC0, 0x00, 0x76, 0xE0, 0x00, 0x76, 0xE0, 0x00, 0x20, 0x82, 0x00, 0x40, 0xF0, 0x20,
32 0xEB, 0xC0, 0x00, 0x7E, 0xE0, 0x00, 0x7E, 0xE0, 0x00, 0x41, 0x04, 0x00, 0x40, 0x60, 0x20,
33 0x7B, 0x80, 0x00, 0x3D, 0xC0, 0x00, 0x39, 0xC0, 0x00, 0x82, 0x08, 0x00, 0x5E, 0x07, 0xA0,
34 0x7B, 0x80, 0x00, 0x3B, 0xC0, 0x00, 0x3E, 0xC0, 0x01, 0x04, 0x10, 0x00, 0x5F, 0x0F, 0xA0,
35 0xFB, 0xC0, 0x00, 0x77, 0xE0, 0x00, 0x76, 0xE0, 0x01, 0x04, 0x10, 0x00, 0x4F, 0x0F, 0x20,
36 0xFB, 0xC0, 0x00, 0x70, 0xE0, 0x00, 0x79, 0xE0, 0x00, 0x82, 0x08, 0x00, 0x47, 0x0E, 0x20,
37 0xFF, 0xC0, 0x00, 0x7F, 0xE0, 0x00, 0x7F, 0xE0, 0x00, 0x41, 0x04, 0x00, 0x63, 0x0C, 0x60,
38 0x3F, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xE0,
39 0x1E, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x0F, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xE0,
40 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00
41 };
42
43
44
45 WatchScreen::WatchScreen()
46 {
47 speed_changed = false;
48 issue_change_speed = false;
49 }
50
51 void WatchScreen::on_enter()
52 {
53 this->panel->lcd->clear();
54 this->panel->setup_menu(4);
55 get_temp_data();
56 get_current_pos(this->pos);
57 get_sd_play_info();
58 this->current_speed = lround(get_current_speed());
59 this->refresh_screen(false);
60 this->panel->enter_control_mode(1, 0.5);
61 this->panel->set_control_value(this->current_speed);
62 }
63
64 void WatchScreen::on_refresh()
65 {
66 // Exit if the button is clicked
67 if ( this->panel->click() ) {
68 this->panel->enter_screen(this->parent);
69 return;
70 }
71
72 // see if speed is being changed
73 if (this->panel->control_value_change()) {
74 this->current_speed = this->panel->get_control_value();
75 if (this->current_speed < 10) {
76 this->current_speed = 10;
77 this->panel->set_control_value(this->current_speed);
78 this->panel->reset_counter();
79 } else {
80 // flag the update to change the speed, we don't want to issue hundreds of M220s
81 // but we do want to display the change we are going to make
82 this->speed_changed = true; // flag indicating speed changed
83 this->refresh_screen(false);
84 }
85 }
86
87 // Update Only every 20 refreshes, 1 a second
88 static int update_counts = 0;
89 update_counts++;
90 if ( update_counts % 20 == 0 ) {
91 get_sd_play_info();
92 get_current_pos(this->pos);
93 get_temp_data();
94 if (this->speed_changed) {
95 this->issue_change_speed = true; // trigger actual command to change speed
96 this->speed_changed = false;
97 } else if (!this->issue_change_speed) { // change still queued
98 // read it in case it was changed via M220
99 this->current_speed = lround(get_current_speed());
100 this->panel->set_control_value(this->current_speed);
101 this->panel->reset_counter();
102 }
103
104 this->refresh_screen(this->panel->lcd->hasGraphics() ? true : false); // graphics screens should be cleared
105
106 // for LCDs with leds set them according to heater status
107 // TODO should be enabled and disabled and settable from config
108 this->panel->lcd->setLed(LED_BED_ON, this->bedtarget > 0);
109 this->panel->lcd->setLed(LED_HOTEND_ON, this->hotendtarget > 0);
110 //this->panel->lcd->setLed(LED_FAN_ON, this->fanon);
111
112 if (this->panel->lcd->hasGraphics()) {
113 // display the graphical icons below the status are
114 //this->panel->lcd->bltGlyph(0, 34, 115, 19, icons);
115 // for (int i = 0; i < 5; ++i) {
116 // this->panel->lcd->bltGlyph(i*24, 38, 23, 19, icons, 15, i*24, 0);
117 // }
118 if (this->hotendtarget > 0)
119 this->panel->lcd->bltGlyph(8, 38, 20, 19, icons, 15, 0, 0);
120
121 if (this->bedtarget > 0)
122 this->panel->lcd->bltGlyph(32, 38, 23, 19, icons, 15, 64, 0);
123
124 // fan appears always on for now
125 this->panel->lcd->bltGlyph(96, 38, 23, 19, icons, 15, 96, 0);
126 }
127 }
128 }
129
130 // queuing gcodes needs to be done from main loop
131 void WatchScreen::on_main_loop()
132 {
133 if (this->issue_change_speed) {
134 this->issue_change_speed = false;
135 set_speed();
136 }
137 }
138
139 // fetch the data we are displaying
140 void WatchScreen::get_temp_data()
141 {
142 void *returned_data;
143 bool ok;
144
145 ok = THEKERNEL->public_data->get_value( temperature_control_checksum, bed_checksum, current_temperature_checksum, &returned_data );
146 if (ok) {
147 struct pad_temperature temp = *static_cast<struct pad_temperature *>(returned_data);
148 this->bedtemp = round(temp.current_temperature);
149 if (this->bedtemp > 100000) this->bedtemp = -2;
150 this->bedtarget = round(temp.target_temperature);
151 //this->bedpwm= temp.pwm;
152 } else {
153 // temp probably disabled
154 this->bedtemp = -1;
155 this->bedtarget = -1;
156 }
157
158
159 ok = THEKERNEL->public_data->get_value( temperature_control_checksum, hotend_checksum, current_temperature_checksum, &returned_data );
160 if (ok) {
161 struct pad_temperature temp = *static_cast<struct pad_temperature *>(returned_data);
162 this->hotendtemp = round(temp.current_temperature);
163 if (this->hotendtemp > 100000) this->hotendtemp = -2;
164 this->hotendtarget = round(temp.target_temperature);
165 //this->hotendpwm= temp.pwm;
166 } else {
167 // temp probably disabled
168 this->hotendtemp = -1;
169 this->hotendtarget = -1;
170 }
171 }
172
173 // fetch the data we are displaying
174 double WatchScreen::get_current_speed()
175 {
176 void *returned_data;
177
178 bool ok = THEKERNEL->public_data->get_value( robot_checksum, speed_override_percent_checksum, &returned_data );
179 if (ok) {
180 double cs = *static_cast<double *>(returned_data);
181 return cs;
182 }
183 return 0.0;
184 }
185
186 void WatchScreen::get_current_pos(double *cp)
187 {
188 void *returned_data;
189
190 bool ok = THEKERNEL->public_data->get_value( robot_checksum, current_position_checksum, &returned_data );
191 if (ok) {
192 double *p = static_cast<double *>(returned_data);
193 cp[0] = p[0];
194 cp[1] = p[1];
195 cp[2] = p[2];
196 }
197 }
198
199 void WatchScreen::get_sd_play_info()
200 {
201 void *returned_data;
202 bool ok = THEKERNEL->public_data->get_value( player_checksum, get_progress_checksum, &returned_data );
203 if (ok) {
204 struct pad_progress p = *static_cast<struct pad_progress *>(returned_data);
205 this->elapsed_time = p.elapsed_secs;
206 this->sd_pcnt_played = p.percent_complete;
207 this->panel->set_playing_file(p.filename);
208
209 } else {
210 this->elapsed_time = 0;
211 this->sd_pcnt_played = 0;
212 }
213 }
214
215 void WatchScreen::display_menu_line(uint16_t line)
216 {
217 // in menu mode
218 switch ( line ) {
219 case 0: this->panel->lcd->printf("H%03d/%03dc B%03d/%03dc", this->hotendtemp, this->hotendtarget, this->bedtemp, this->bedtarget); break;
220 case 1: this->panel->lcd->printf("X%4d Y%4d Z%7.2f", (int)round(this->pos[0]), (int)round(this->pos[1]), this->pos[2]); break;
221 case 2: this->panel->lcd->printf("%3d%% %2lu:%02lu %3u%% sd", this->current_speed, this->elapsed_time / 60, this->elapsed_time % 60, this->sd_pcnt_played); break;
222 case 3: this->panel->lcd->printf("%19s", this->get_status()); break;
223 }
224 }
225
226 const char *WatchScreen::get_status()
227 {
228 if (panel->hasMessage()) {
229 return panel->getMessage().c_str();
230 }
231
232 if (THEKERNEL->pauser->paused())
233 return "Paused";
234
235 if (panel->is_playing())
236 return panel->get_playing_file();
237
238 return "Smoothie ready";
239 }
240
241 void WatchScreen::set_speed()
242 {
243 // change pos by issuing a M220 Snnn
244 char buf[32];
245 int n = snprintf(buf, sizeof(buf), "M220 S%d", this->current_speed);
246 string g(buf, n);
247 send_gcode(g);
248 }