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"
20 #include "PublicData.h"
21 #include "SwitchPublicAccess.h"
22 #include "checksumm.h"
30 static const uint8_t icons
[] = { // 115x19 - 3 bytes each: he1, he2, he3, bed, fan
31 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0,
32 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xE0,
33 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x0C, 0x60,
34 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x0E, 0x20,
35 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x0F, 0x20,
36 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x0F, 0xA0,
37 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x07, 0xA0,
38 0x7F, 0x80, 0x00, 0x3F, 0xC0, 0x00, 0x3F, 0xC0, 0x00, 0x41, 0x04, 0x00, 0x40, 0x60, 0x20,
39 0xFB, 0xC0, 0x00, 0x79, 0xE0, 0x00, 0x79, 0xE0, 0x00, 0x20, 0x82, 0x00, 0x40, 0xF0, 0x20,
40 0xF3, 0xC0, 0x00, 0x76, 0xE0, 0x00, 0x76, 0xE0, 0x00, 0x20, 0x82, 0x00, 0x40, 0xF0, 0x20,
41 0xEB, 0xC0, 0x00, 0x7E, 0xE0, 0x00, 0x7E, 0xE0, 0x00, 0x41, 0x04, 0x00, 0x40, 0x60, 0x20,
42 0x7B, 0x80, 0x00, 0x3D, 0xC0, 0x00, 0x39, 0xC0, 0x00, 0x82, 0x08, 0x00, 0x5E, 0x07, 0xA0,
43 0x7B, 0x80, 0x00, 0x3B, 0xC0, 0x00, 0x3E, 0xC0, 0x01, 0x04, 0x10, 0x00, 0x5F, 0x0F, 0xA0,
44 0xFB, 0xC0, 0x00, 0x77, 0xE0, 0x00, 0x76, 0xE0, 0x01, 0x04, 0x10, 0x00, 0x4F, 0x0F, 0x20,
45 0xFB, 0xC0, 0x00, 0x70, 0xE0, 0x00, 0x79, 0xE0, 0x00, 0x82, 0x08, 0x00, 0x47, 0x0E, 0x20,
46 0xFF, 0xC0, 0x00, 0x7F, 0xE0, 0x00, 0x7F, 0xE0, 0x00, 0x41, 0x04, 0x00, 0x63, 0x0C, 0x60,
47 0x3F, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xE0,
48 0x1E, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x0F, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xE0,
49 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00
54 WatchScreen::WatchScreen()
56 speed_changed
= false;
57 issue_change_speed
= false;
61 void WatchScreen::on_enter()
63 THEPANEL
->lcd
->clear();
64 THEPANEL
->setup_menu(4);
66 get_current_pos(this->pos
);
68 this->current_speed
= lround(get_current_speed());
69 this->refresh_screen(false);
70 THEPANEL
->enter_control_mode(1, 0.5);
71 THEPANEL
->set_control_value(this->current_speed
);
74 void WatchScreen::on_refresh()
76 // Exit if the button is clicked
77 if ( THEPANEL
->click() ) {
78 THEPANEL
->enter_screen(this->parent
);
82 // see if speed is being changed
83 if (THEPANEL
->control_value_change()) {
84 this->current_speed
= THEPANEL
->get_control_value();
85 if (this->current_speed
< 10) {
86 this->current_speed
= 10;
87 THEPANEL
->set_control_value(this->current_speed
);
88 THEPANEL
->reset_counter();
90 // flag the update to change the speed, we don't want to issue hundreds of M220s
91 // but we do want to display the change we are going to make
92 this->speed_changed
= true; // flag indicating speed changed
93 this->refresh_screen(false);
97 // Update Only every 20 refreshes, 1 a second
98 static int update_counts
= 0;
100 if ( update_counts
% 20 == 0 ) {
102 get_current_pos(this->pos
);
104 if (this->speed_changed
) {
105 this->issue_change_speed
= true; // trigger actual command to change speed
106 this->speed_changed
= false;
107 } else if (!this->issue_change_speed
) { // change still queued
108 // read it in case it was changed via M220
109 this->current_speed
= lround(get_current_speed());
110 THEPANEL
->set_control_value(this->current_speed
);
111 THEPANEL
->reset_counter();
114 this->refresh_screen(THEPANEL
->lcd
->hasGraphics() ? true : false); // graphics screens should be cleared
116 // for LCDs with leds set them according to heater status
117 // TODO should be enabled and disabled and settable from config
118 THEPANEL
->lcd
->setLed(LED_BED_ON
, this->bedtarget
> 0);
119 THEPANEL
->lcd
->setLed(LED_HOTEND_ON
, this->hotendtarget
> 0);
120 THEPANEL
->lcd
->setLed(LED_FAN_ON
, this->fan_state
);
122 if (THEPANEL
->lcd
->hasGraphics()) {
123 // display the graphical icons below the status are
124 //THEPANEL->lcd->bltGlyph(0, 34, 115, 19, icons);
125 // for (int i = 0; i < 5; ++i) {
126 // THEPANEL->lcd->bltGlyph(i*24, 38, 23, 19, icons, 15, i*24, 0);
128 if (this->hotendtarget
> 0)
129 THEPANEL
->lcd
->bltGlyph(8, 38, 20, 19, icons
, 15, 0, 0);
131 if (this->bedtarget
> 0)
132 THEPANEL
->lcd
->bltGlyph(32, 38, 23, 19, icons
, 15, 64, 0);
135 THEPANEL
->lcd
->bltGlyph(96, 38, 23, 19, icons
, 15, 96, 0);
140 // queuing gcodes needs to be done from main loop
141 void WatchScreen::on_main_loop()
143 if (this->issue_change_speed
) {
144 this->issue_change_speed
= false;
149 // fetch the data we are displaying
150 void WatchScreen::get_temp_data()
155 ok
= THEKERNEL
->public_data
->get_value( temperature_control_checksum
, bed_checksum
, current_temperature_checksum
, &returned_data
);
157 struct pad_temperature temp
= *static_cast<struct pad_temperature
*>(returned_data
);
158 this->bedtemp
= round(temp
.current_temperature
);
159 if (this->bedtemp
> 100000) this->bedtemp
= -2;
160 this->bedtarget
= round(temp
.target_temperature
);
161 //this->bedpwm= temp.pwm;
163 // temp probably disabled
165 this->bedtarget
= -1;
169 ok
= THEKERNEL
->public_data
->get_value( temperature_control_checksum
, hotend_checksum
, current_temperature_checksum
, &returned_data
);
171 struct pad_temperature temp
= *static_cast<struct pad_temperature
*>(returned_data
);
172 this->hotendtemp
= round(temp
.current_temperature
);
173 if (this->hotendtemp
> 100000) this->hotendtemp
= -2;
174 this->hotendtarget
= round(temp
.target_temperature
);
175 //this->hotendpwm= temp.pwm;
177 // temp probably disabled
178 this->hotendtemp
= -1;
179 this->hotendtarget
= -1;
183 ok
= THEKERNEL
->public_data
->get_value( switch_checksum
, fan_checksum
, 0, &returned_data
);
185 struct pad_switch s
= *static_cast<struct pad_switch
*>(returned_data
);
186 this->fan_state
= s
.state
;
188 // fan probably disabled
189 this->fan_state
= false;
193 // fetch the data we are displaying
194 float WatchScreen::get_current_speed()
198 bool ok
= THEKERNEL
->public_data
->get_value( robot_checksum
, speed_override_percent_checksum
, &returned_data
);
200 float cs
= *static_cast<float *>(returned_data
);
206 void WatchScreen::get_current_pos(float *cp
)
210 bool ok
= THEKERNEL
->public_data
->get_value( robot_checksum
, current_position_checksum
, &returned_data
);
212 float *p
= static_cast<float *>(returned_data
);
219 void WatchScreen::get_sd_play_info()
222 bool ok
= THEKERNEL
->public_data
->get_value( player_checksum
, get_progress_checksum
, &returned_data
);
224 struct pad_progress p
= *static_cast<struct pad_progress
*>(returned_data
);
225 this->elapsed_time
= p
.elapsed_secs
;
226 this->sd_pcnt_played
= p
.percent_complete
;
227 THEPANEL
->set_playing_file(p
.filename
);
230 this->elapsed_time
= 0;
231 this->sd_pcnt_played
= 0;
235 void WatchScreen::display_menu_line(uint16_t line
)
239 case 0: THEPANEL
->lcd
->printf("H%03d/%03dc B%03d/%03dc", this->hotendtemp
, this->hotendtarget
, this->bedtemp
, this->bedtarget
); break;
240 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;
241 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;
242 case 3: THEPANEL
->lcd
->printf("%19s", this->get_status()); break;
246 const char *WatchScreen::get_status()
248 if (THEPANEL
->hasMessage()) {
249 return THEPANEL
->getMessage().c_str();
252 if (THEKERNEL
->pauser
->paused())
255 if (THEPANEL
->is_playing())
256 return THEPANEL
->get_playing_file();
258 if (!THEKERNEL
->conveyor
->is_queue_empty()) {
262 const char *ip
= get_network();
264 return "Smoothie ready";
270 void WatchScreen::set_speed()
272 send_gcode("M220", 'S', this->current_speed
);
275 const char *WatchScreen::get_network()
279 bool ok
= THEKERNEL
->public_data
->get_value( network_checksum
, get_ip_checksum
, &returned_data
);
281 uint8_t *ipaddr
= (uint8_t *)returned_data
;
283 int n
= snprintf(buf
, sizeof(buf
), "IP %d.%d.%d.%d", ipaddr
[0], ipaddr
[1], ipaddr
[2], ipaddr
[3]);
285 if (this->ipstr
== NULL
) {
286 this->ipstr
= (char *)malloc(n
+ 1);
288 strcpy(this->ipstr
, buf
);