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/robot/Conveyor.h"
18 #include "modules/utils/player/PlayerPublicAccess.h"
19 #include "NetworkPublicAccess.h"
23 static const uint8_t icons
[] = { // 115x19 - 3 bytes each: he1, he2, he3, bed, fan
24 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0,
25 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xE0,
26 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x0C, 0x60,
27 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x0E, 0x20,
28 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x0F, 0x20,
29 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x0F, 0xA0,
30 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x07, 0xA0,
31 0x7F, 0x80, 0x00, 0x3F, 0xC0, 0x00, 0x3F, 0xC0, 0x00, 0x41, 0x04, 0x00, 0x40, 0x60, 0x20,
32 0xFB, 0xC0, 0x00, 0x79, 0xE0, 0x00, 0x79, 0xE0, 0x00, 0x20, 0x82, 0x00, 0x40, 0xF0, 0x20,
33 0xF3, 0xC0, 0x00, 0x76, 0xE0, 0x00, 0x76, 0xE0, 0x00, 0x20, 0x82, 0x00, 0x40, 0xF0, 0x20,
34 0xEB, 0xC0, 0x00, 0x7E, 0xE0, 0x00, 0x7E, 0xE0, 0x00, 0x41, 0x04, 0x00, 0x40, 0x60, 0x20,
35 0x7B, 0x80, 0x00, 0x3D, 0xC0, 0x00, 0x39, 0xC0, 0x00, 0x82, 0x08, 0x00, 0x5E, 0x07, 0xA0,
36 0x7B, 0x80, 0x00, 0x3B, 0xC0, 0x00, 0x3E, 0xC0, 0x01, 0x04, 0x10, 0x00, 0x5F, 0x0F, 0xA0,
37 0xFB, 0xC0, 0x00, 0x77, 0xE0, 0x00, 0x76, 0xE0, 0x01, 0x04, 0x10, 0x00, 0x4F, 0x0F, 0x20,
38 0xFB, 0xC0, 0x00, 0x70, 0xE0, 0x00, 0x79, 0xE0, 0x00, 0x82, 0x08, 0x00, 0x47, 0x0E, 0x20,
39 0xFF, 0xC0, 0x00, 0x7F, 0xE0, 0x00, 0x7F, 0xE0, 0x00, 0x41, 0x04, 0x00, 0x63, 0x0C, 0x60,
40 0x3F, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xE0,
41 0x1E, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x0F, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xE0,
42 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00
47 WatchScreen::WatchScreen()
49 speed_changed
= false;
50 issue_change_speed
= false;
54 void WatchScreen::on_enter()
56 this->panel
->lcd
->clear();
57 this->panel
->setup_menu(4);
59 get_current_pos(this->pos
);
61 this->current_speed
= lround(get_current_speed());
62 this->refresh_screen(false);
63 this->panel
->enter_control_mode(1, 0.5);
64 this->panel
->set_control_value(this->current_speed
);
67 void WatchScreen::on_refresh()
69 // Exit if the button is clicked
70 if ( this->panel
->click() ) {
71 this->panel
->enter_screen(this->parent
);
75 // see if speed is being changed
76 if (this->panel
->control_value_change()) {
77 this->current_speed
= this->panel
->get_control_value();
78 if (this->current_speed
< 10) {
79 this->current_speed
= 10;
80 this->panel
->set_control_value(this->current_speed
);
81 this->panel
->reset_counter();
83 // flag the update to change the speed, we don't want to issue hundreds of M220s
84 // but we do want to display the change we are going to make
85 this->speed_changed
= true; // flag indicating speed changed
86 this->refresh_screen(false);
90 // Update Only every 20 refreshes, 1 a second
91 static int update_counts
= 0;
93 if ( update_counts
% 20 == 0 ) {
95 get_current_pos(this->pos
);
97 if (this->speed_changed
) {
98 this->issue_change_speed
= true; // trigger actual command to change speed
99 this->speed_changed
= false;
100 } else if (!this->issue_change_speed
) { // change still queued
101 // read it in case it was changed via M220
102 this->current_speed
= lround(get_current_speed());
103 this->panel
->set_control_value(this->current_speed
);
104 this->panel
->reset_counter();
107 this->refresh_screen(this->panel
->lcd
->hasGraphics() ? true : false); // graphics screens should be cleared
109 // for LCDs with leds set them according to heater status
110 // TODO should be enabled and disabled and settable from config
111 this->panel
->lcd
->setLed(LED_BED_ON
, this->bedtarget
> 0);
112 this->panel
->lcd
->setLed(LED_HOTEND_ON
, this->hotendtarget
> 0);
113 //this->panel->lcd->setLed(LED_FAN_ON, this->fanon);
115 if (this->panel
->lcd
->hasGraphics()) {
116 // display the graphical icons below the status are
117 //this->panel->lcd->bltGlyph(0, 34, 115, 19, icons);
118 // for (int i = 0; i < 5; ++i) {
119 // this->panel->lcd->bltGlyph(i*24, 38, 23, 19, icons, 15, i*24, 0);
121 if (this->hotendtarget
> 0)
122 this->panel
->lcd
->bltGlyph(8, 38, 20, 19, icons
, 15, 0, 0);
124 if (this->bedtarget
> 0)
125 this->panel
->lcd
->bltGlyph(32, 38, 23, 19, icons
, 15, 64, 0);
127 // fan appears always on for now
128 this->panel
->lcd
->bltGlyph(96, 38, 23, 19, icons
, 15, 96, 0);
133 // queuing gcodes needs to be done from main loop
134 void WatchScreen::on_main_loop()
136 if (this->issue_change_speed
) {
137 this->issue_change_speed
= false;
142 // fetch the data we are displaying
143 void WatchScreen::get_temp_data()
148 ok
= THEKERNEL
->public_data
->get_value( temperature_control_checksum
, bed_checksum
, current_temperature_checksum
, &returned_data
);
150 struct pad_temperature temp
= *static_cast<struct pad_temperature
*>(returned_data
);
151 this->bedtemp
= round(temp
.current_temperature
);
152 if (this->bedtemp
> 100000) this->bedtemp
= -2;
153 this->bedtarget
= round(temp
.target_temperature
);
154 //this->bedpwm= temp.pwm;
156 // temp probably disabled
158 this->bedtarget
= -1;
162 ok
= THEKERNEL
->public_data
->get_value( temperature_control_checksum
, hotend_checksum
, current_temperature_checksum
, &returned_data
);
164 struct pad_temperature temp
= *static_cast<struct pad_temperature
*>(returned_data
);
165 this->hotendtemp
= round(temp
.current_temperature
);
166 if (this->hotendtemp
> 100000) this->hotendtemp
= -2;
167 this->hotendtarget
= round(temp
.target_temperature
);
168 //this->hotendpwm= temp.pwm;
170 // temp probably disabled
171 this->hotendtemp
= -1;
172 this->hotendtarget
= -1;
176 // fetch the data we are displaying
177 float WatchScreen::get_current_speed()
181 bool ok
= THEKERNEL
->public_data
->get_value( robot_checksum
, speed_override_percent_checksum
, &returned_data
);
183 float cs
= *static_cast<float *>(returned_data
);
189 void WatchScreen::get_current_pos(float *cp
)
193 bool ok
= THEKERNEL
->public_data
->get_value( robot_checksum
, current_position_checksum
, &returned_data
);
195 float *p
= static_cast<float *>(returned_data
);
202 void WatchScreen::get_sd_play_info()
205 bool ok
= THEKERNEL
->public_data
->get_value( player_checksum
, get_progress_checksum
, &returned_data
);
207 struct pad_progress p
= *static_cast<struct pad_progress
*>(returned_data
);
208 this->elapsed_time
= p
.elapsed_secs
;
209 this->sd_pcnt_played
= p
.percent_complete
;
210 this->panel
->set_playing_file(p
.filename
);
213 this->elapsed_time
= 0;
214 this->sd_pcnt_played
= 0;
218 void WatchScreen::display_menu_line(uint16_t line
)
222 case 0: this->panel
->lcd
->printf("H%03d/%03dc B%03d/%03dc", this->hotendtemp
, this->hotendtarget
, this->bedtemp
, this->bedtarget
); break;
223 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;
224 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;
225 case 3: this->panel
->lcd
->printf("%19s", this->get_status()); break;
229 const char *WatchScreen::get_status()
231 if (panel
->hasMessage()) {
232 return panel
->getMessage().c_str();
235 if (THEKERNEL
->pauser
->paused())
238 if (panel
->is_playing())
239 return panel
->get_playing_file();
241 if (!THEKERNEL
->conveyor
->queue
.is_empty()) {
245 const char *ip
= get_network();
247 return "Smoothie ready";
253 void WatchScreen::set_speed()
255 // change pos by issuing a M220 Snnn
257 int n
= snprintf(buf
, sizeof(buf
), "M220 S%d", this->current_speed
);
262 const char *WatchScreen::get_network()
266 bool ok
= THEKERNEL
->public_data
->get_value( network_checksum
, get_ip_checksum
, &returned_data
);
268 uint8_t *ipaddr
= (uint8_t *)returned_data
;
270 int n
= snprintf(buf
, sizeof(buf
), "IP %d.%d.%d.%d", ipaddr
[0], ipaddr
[1], ipaddr
[2], ipaddr
[3]);
272 if (this->ipstr
== NULL
) {
273 this->ipstr
= (char *)malloc(n
+ 1);
275 strcpy(this->ipstr
, buf
);