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/>.
8 #include "libs/Kernel.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 #include "NetworkPublicAccess.h"
22 static const uint8_t icons
[] = { // 115x19 - 3 bytes each: he1, he2, he3, bed, fan
23 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0,
24 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xE0,
25 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x0C, 0x60,
26 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x0E, 0x20,
27 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x0F, 0x20,
28 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x0F, 0xA0,
29 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x07, 0xA0,
30 0x7F, 0x80, 0x00, 0x3F, 0xC0, 0x00, 0x3F, 0xC0, 0x00, 0x41, 0x04, 0x00, 0x40, 0x60, 0x20,
31 0xFB, 0xC0, 0x00, 0x79, 0xE0, 0x00, 0x79, 0xE0, 0x00, 0x20, 0x82, 0x00, 0x40, 0xF0, 0x20,
32 0xF3, 0xC0, 0x00, 0x76, 0xE0, 0x00, 0x76, 0xE0, 0x00, 0x20, 0x82, 0x00, 0x40, 0xF0, 0x20,
33 0xEB, 0xC0, 0x00, 0x7E, 0xE0, 0x00, 0x7E, 0xE0, 0x00, 0x41, 0x04, 0x00, 0x40, 0x60, 0x20,
34 0x7B, 0x80, 0x00, 0x3D, 0xC0, 0x00, 0x39, 0xC0, 0x00, 0x82, 0x08, 0x00, 0x5E, 0x07, 0xA0,
35 0x7B, 0x80, 0x00, 0x3B, 0xC0, 0x00, 0x3E, 0xC0, 0x01, 0x04, 0x10, 0x00, 0x5F, 0x0F, 0xA0,
36 0xFB, 0xC0, 0x00, 0x77, 0xE0, 0x00, 0x76, 0xE0, 0x01, 0x04, 0x10, 0x00, 0x4F, 0x0F, 0x20,
37 0xFB, 0xC0, 0x00, 0x70, 0xE0, 0x00, 0x79, 0xE0, 0x00, 0x82, 0x08, 0x00, 0x47, 0x0E, 0x20,
38 0xFF, 0xC0, 0x00, 0x7F, 0xE0, 0x00, 0x7F, 0xE0, 0x00, 0x41, 0x04, 0x00, 0x63, 0x0C, 0x60,
39 0x3F, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xE0,
40 0x1E, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x0F, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xE0,
41 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00
46 WatchScreen::WatchScreen()
48 speed_changed
= false;
49 issue_change_speed
= false;
53 void WatchScreen::on_enter()
55 this->panel
->lcd
->clear();
56 this->panel
->setup_menu(4);
58 get_current_pos(this->pos
);
60 this->current_speed
= lround(get_current_speed());
61 this->refresh_screen(false);
62 this->panel
->enter_control_mode(1, 0.5);
63 this->panel
->set_control_value(this->current_speed
);
66 void WatchScreen::on_refresh()
68 // Exit if the button is clicked
69 if ( this->panel
->click() ) {
70 this->panel
->enter_screen(this->parent
);
74 // see if speed is being changed
75 if (this->panel
->control_value_change()) {
76 this->current_speed
= this->panel
->get_control_value();
77 if (this->current_speed
< 10) {
78 this->current_speed
= 10;
79 this->panel
->set_control_value(this->current_speed
);
80 this->panel
->reset_counter();
82 // flag the update to change the speed, we don't want to issue hundreds of M220s
83 // but we do want to display the change we are going to make
84 this->speed_changed
= true; // flag indicating speed changed
85 this->refresh_screen(false);
89 // Update Only every 20 refreshes, 1 a second
90 static int update_counts
= 0;
92 if ( update_counts
% 20 == 0 ) {
94 get_current_pos(this->pos
);
96 if (this->speed_changed
) {
97 this->issue_change_speed
= true; // trigger actual command to change speed
98 this->speed_changed
= false;
99 } else if (!this->issue_change_speed
) { // change still queued
100 // read it in case it was changed via M220
101 this->current_speed
= lround(get_current_speed());
102 this->panel
->set_control_value(this->current_speed
);
103 this->panel
->reset_counter();
106 this->refresh_screen(this->panel
->lcd
->hasGraphics() ? true : false); // graphics screens should be cleared
108 // for LCDs with leds set them according to heater status
109 // TODO should be enabled and disabled and settable from config
110 this->panel
->lcd
->setLed(LED_BED_ON
, this->bedtarget
> 0);
111 this->panel
->lcd
->setLed(LED_HOTEND_ON
, this->hotendtarget
> 0);
112 //this->panel->lcd->setLed(LED_FAN_ON, this->fanon);
114 if (this->panel
->lcd
->hasGraphics()) {
115 // display the graphical icons below the status are
116 //this->panel->lcd->bltGlyph(0, 34, 115, 19, icons);
117 // for (int i = 0; i < 5; ++i) {
118 // this->panel->lcd->bltGlyph(i*24, 38, 23, 19, icons, 15, i*24, 0);
120 if (this->hotendtarget
> 0)
121 this->panel
->lcd
->bltGlyph(8, 38, 20, 19, icons
, 15, 0, 0);
123 if (this->bedtarget
> 0)
124 this->panel
->lcd
->bltGlyph(32, 38, 23, 19, icons
, 15, 64, 0);
126 // fan appears always on for now
127 this->panel
->lcd
->bltGlyph(96, 38, 23, 19, icons
, 15, 96, 0);
132 // queuing gcodes needs to be done from main loop
133 void WatchScreen::on_main_loop()
135 if (this->issue_change_speed
) {
136 this->issue_change_speed
= false;
141 // fetch the data we are displaying
142 void WatchScreen::get_temp_data()
147 ok
= THEKERNEL
->public_data
->get_value( temperature_control_checksum
, bed_checksum
, current_temperature_checksum
, &returned_data
);
149 struct pad_temperature temp
= *static_cast<struct pad_temperature
*>(returned_data
);
150 this->bedtemp
= round(temp
.current_temperature
);
151 if (this->bedtemp
> 100000) this->bedtemp
= -2;
152 this->bedtarget
= round(temp
.target_temperature
);
153 //this->bedpwm= temp.pwm;
155 // temp probably disabled
157 this->bedtarget
= -1;
161 ok
= THEKERNEL
->public_data
->get_value( temperature_control_checksum
, hotend_checksum
, current_temperature_checksum
, &returned_data
);
163 struct pad_temperature temp
= *static_cast<struct pad_temperature
*>(returned_data
);
164 this->hotendtemp
= round(temp
.current_temperature
);
165 if (this->hotendtemp
> 100000) this->hotendtemp
= -2;
166 this->hotendtarget
= round(temp
.target_temperature
);
167 //this->hotendpwm= temp.pwm;
169 // temp probably disabled
170 this->hotendtemp
= -1;
171 this->hotendtarget
= -1;
175 // fetch the data we are displaying
176 double WatchScreen::get_current_speed()
180 bool ok
= THEKERNEL
->public_data
->get_value( robot_checksum
, speed_override_percent_checksum
, &returned_data
);
182 double cs
= *static_cast<double *>(returned_data
);
188 void WatchScreen::get_current_pos(double *cp
)
192 bool ok
= THEKERNEL
->public_data
->get_value( robot_checksum
, current_position_checksum
, &returned_data
);
194 double *p
= static_cast<double *>(returned_data
);
201 void WatchScreen::get_sd_play_info()
204 bool ok
= THEKERNEL
->public_data
->get_value( player_checksum
, get_progress_checksum
, &returned_data
);
206 struct pad_progress p
= *static_cast<struct pad_progress
*>(returned_data
);
207 this->elapsed_time
= p
.elapsed_secs
;
208 this->sd_pcnt_played
= p
.percent_complete
;
209 this->panel
->set_playing_file(p
.filename
);
212 this->elapsed_time
= 0;
213 this->sd_pcnt_played
= 0;
217 void WatchScreen::display_menu_line(uint16_t line
)
221 case 0: this->panel
->lcd
->printf("H%03d/%03dc B%03d/%03dc", this->hotendtemp
, this->hotendtarget
, this->bedtemp
, this->bedtarget
); break;
222 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;
223 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;
224 case 3: this->panel
->lcd
->printf("%19s", this->get_status()); break;
228 const char *WatchScreen::get_status()
230 if (panel
->hasMessage()) {
231 return panel
->getMessage().c_str();
234 if (THEKERNEL
->pauser
->paused())
237 if (panel
->is_playing())
238 return panel
->get_playing_file();
240 const char *ip
= get_network();
242 return "Smoothie ready";
248 void WatchScreen::set_speed()
250 // change pos by issuing a M220 Snnn
252 int n
= snprintf(buf
, sizeof(buf
), "M220 S%d", this->current_speed
);
257 const char *WatchScreen::get_network()
261 bool ok
= THEKERNEL
->public_data
->get_value( network_checksum
, get_ip_checksum
, &returned_data
);
263 uint8_t *ipaddr
= (uint8_t *)returned_data
;
265 int n
= snprintf(buf
, sizeof(buf
), "IP %d.%d.%d.%d", ipaddr
[0], ipaddr
[1], ipaddr
[2], ipaddr
[3]);
267 if (this->ipstr
== NULL
) {
268 this->ipstr
= (char *)malloc(n
+ 1);
270 strcpy(this->ipstr
, buf
);