Commit | Line | Data |
---|---|---|
399cb110 | 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. | |
399cb110 | 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" | |
9 | #include "Panel.h" | |
ca6effd7 JM |
10 | #include "PanelScreen.h" |
11 | ||
35089dc7 JM |
12 | #include "libs/nuts_bolts.h" |
13 | #include "libs/utils.h" | |
35089dc7 | 14 | #include "Button.h" |
ca6effd7 | 15 | |
35089dc7 | 16 | #include "modules/utils/player/PlayerPublicAccess.h" |
ca6effd7 JM |
17 | #include "screens/CustomScreen.h" |
18 | #include "screens/MainMenuScreen.h" | |
61134a65 JM |
19 | #include "SlowTicker.h" |
20 | #include "Gcode.h" | |
21 | #include "Pauser.h" | |
cee1bb2d JM |
22 | #include "TemperatureControlPublicAccess.h" |
23 | #include "ModifyValuesScreen.h" | |
24 | #include "PublicDataRequest.h" | |
61134a65 | 25 | #include "PublicData.h" |
35089dc7 JM |
26 | |
27 | #include "panels/I2CLCD.h" | |
28 | #include "panels/VikiLCD.h" | |
1c2b594a | 29 | #include "panels/Smoothiepanel.h" |
357d0eda | 30 | #include "panels/ReprapDiscountGLCD.h" |
84e7fd5a | 31 | #include "panels/ST7565.h" |
3ae0e673 JM |
32 | #include "panels/UniversalAdapter.h" |
33 | ||
f140cf3a | 34 | #include "version.h" |
7af0714f | 35 | #include "checksumm.h" |
8d54c34c | 36 | #include "ConfigValue.h" |
35089dc7 | 37 | |
ca6effd7 JM |
38 | #define panel_checksum CHECKSUM("panel") |
39 | #define enable_checksum CHECKSUM("enable") | |
40 | #define lcd_checksum CHECKSUM("lcd") | |
41 | #define i2c_lcd_checksum CHECKSUM("i2c_lcd") | |
42 | #define viki_lcd_checksum CHECKSUM("viki_lcd") | |
43 | #define smoothiepanel_checksum CHECKSUM("smoothiepanel") | |
44 | #define panelolu2_checksum CHECKSUM("panelolu2") | |
45 | #define rrd_glcd_checksum CHECKSUM("reprap_discount_glcd") | |
46 | #define st7565_glcd_checksum CHECKSUM("st7565_glcd") | |
3ae0e673 | 47 | #define universal_adapter_checksum CHECKSUM("universal_adapter") |
ca6effd7 | 48 | |
fa3f7b30 JM |
49 | #define menu_offset_checksum CHECKSUM("menu_offset") |
50 | #define encoder_resolution_checksum CHECKSUM("encoder_resolution") | |
51 | #define jog_x_feedrate_checksum CHECKSUM("alpha_jog_feedrate") | |
52 | #define jog_y_feedrate_checksum CHECKSUM("beta_jog_feedrate") | |
53 | #define jog_z_feedrate_checksum CHECKSUM("gamma_jog_feedrate") | |
f1e696a3 | 54 | #define longpress_delay_checksum CHECKSUM("longpress_delay") |
ca6effd7 JM |
55 | |
56 | #define hotend_temp_checksum CHECKSUM("hotend_temperature") | |
57 | #define bed_temp_checksum CHECKSUM("bed_temperature") | |
58 | ||
cee1bb2d JM |
59 | Panel* Panel::instance= nullptr; |
60 | ||
862fc625 JM |
61 | Panel::Panel() |
62 | { | |
cee1bb2d | 63 | instance= this; |
35089dc7 JM |
64 | this->counter_changed = false; |
65 | this->click_changed = false; | |
66 | this->refresh_flag = false; | |
67 | this->enter_menu_mode(); | |
862fc625 | 68 | this->lcd = NULL; |
58d6d841 | 69 | this->do_buttons = false; |
e456d044 | 70 | this->do_encoder = false; |
862fc625 JM |
71 | this->idle_time = 0; |
72 | this->start_up = true; | |
73 | this->current_screen = NULL; | |
7aa9fef3 | 74 | strcpy(this->playing_file, "Playing file"); |
35089dc7 JM |
75 | } |
76 | ||
862fc625 JM |
77 | Panel::~Panel() |
78 | { | |
58d6d841 | 79 | delete this->lcd; |
35089dc7 JM |
80 | } |
81 | ||
862fc625 JM |
82 | void Panel::on_module_loaded() |
83 | { | |
35089dc7 | 84 | // Exit if this module is not enabled |
314ab8f7 | 85 | if ( !THEKERNEL->config->value( panel_checksum, enable_checksum )->by_default(false)->as_bool() ) { |
58d6d841 JM |
86 | delete this; |
87 | return; | |
399cb110 | 88 | } |
35089dc7 | 89 | |
58d6d841 JM |
90 | // Initialise the LCD, see which LCD to use |
91 | if (this->lcd != NULL) delete this->lcd; | |
314ab8f7 | 92 | int lcd_cksm = get_checksum(THEKERNEL->config->value(panel_checksum, lcd_checksum)->by_default("i2c")->as_string()); |
58d6d841 JM |
93 | |
94 | // Note checksums are not const expressions when in debug mode, so don't use switch | |
862fc625 | 95 | if (lcd_cksm == i2c_lcd_checksum) { |
58d6d841 | 96 | this->lcd = new I2CLCD(); |
862fc625 | 97 | } else if (lcd_cksm == viki_lcd_checksum) { |
58d6d841 | 98 | this->lcd = new VikiLCD(); |
e0107f6b | 99 | this->lcd->set_variant(0); |
862fc625 | 100 | } else if (lcd_cksm == panelolu2_checksum) { |
e0107f6b JM |
101 | this->lcd = new VikiLCD(); |
102 | this->lcd->set_variant(1); | |
862fc625 | 103 | } else if (lcd_cksm == smoothiepanel_checksum) { |
1c2b594a | 104 | this->lcd = new Smoothiepanel(); |
862fc625 | 105 | } else if (lcd_cksm == rrd_glcd_checksum) { |
357d0eda | 106 | this->lcd = new ReprapDiscountGLCD(); |
862fc625 | 107 | } else if (lcd_cksm == st7565_glcd_checksum) { |
84e7fd5a | 108 | this->lcd = new ST7565(); |
3ae0e673 JM |
109 | } else if (lcd_cksm == universal_adapter_checksum) { |
110 | this->lcd = new UniversalAdapter(); | |
862fc625 | 111 | } else { |
58d6d841 JM |
112 | // no lcd type defined |
113 | return; | |
114 | } | |
115 | ||
cee1bb2d JM |
116 | // these need to be called here as they need the config cache loaded as they enumerate modules |
117 | this->custom_screen= new CustomScreen(); | |
118 | setup_temperature_screen(); | |
ca6effd7 | 119 | |
58d6d841 JM |
120 | // some panels may need access to this global info |
121 | this->lcd->setPanel(this); | |
122 | ||
f65ce58f | 123 | // the number of screen lines the panel supports |
862fc625 | 124 | this->screen_lines = this->lcd->get_screen_lines(); |
399cb110 | 125 | |
19fb4629 JM |
126 | // some encoders may need more clicks to move menu, this is a divisor and is in config as it is |
127 | // an end user usability issue | |
314ab8f7 | 128 | this->menu_offset = THEKERNEL->config->value( panel_checksum, menu_offset_checksum )->by_default(0)->as_number(); |
19fb4629 | 129 | |
fa3f7b30 | 130 | // override default encoder resolution if needed |
314ab8f7 | 131 | this->encoder_click_resolution = THEKERNEL->config->value( panel_checksum, encoder_resolution_checksum )->by_default(this->lcd->getEncoderResolution())->as_number(); |
fa3f7b30 | 132 | |
58d6d841 | 133 | // load jogging feedrates in mm/min |
1ad23cd3 MM |
134 | jogging_speed_mm_min[0] = THEKERNEL->config->value( panel_checksum, jog_x_feedrate_checksum )->by_default(3000.0f)->as_number(); |
135 | jogging_speed_mm_min[1] = THEKERNEL->config->value( panel_checksum, jog_y_feedrate_checksum )->by_default(3000.0f)->as_number(); | |
136 | jogging_speed_mm_min[2] = THEKERNEL->config->value( panel_checksum, jog_z_feedrate_checksum )->by_default(300.0f )->as_number(); | |
f19a841a JM |
137 | |
138 | // load the default preset temeratures | |
1ad23cd3 MM |
139 | default_hotend_temperature = THEKERNEL->config->value( panel_checksum, hotend_temp_checksum )->by_default(185.0f )->as_number(); |
140 | default_bed_temperature = THEKERNEL->config->value( panel_checksum, bed_temp_checksum )->by_default(60.0f )->as_number(); | |
f19a841a | 141 | |
399cb110 | 142 | |
95195a29 JM |
143 | this->up_button.up_attach( this, &Panel::on_up ); |
144 | this->down_button.up_attach( this, &Panel::on_down ); | |
ab4abea9 | 145 | this->click_button.up_attach( this, &Panel::on_select ); |
95195a29 | 146 | this->back_button.up_attach( this, &Panel::on_back ); |
52500e58 | 147 | this->pause_button.up_attach( this, &Panel::on_pause ); |
35089dc7 | 148 | |
355c5796 L |
149 | |
150 | //setting longpress_delay | |
151 | int longpress_delay = THEKERNEL->config->value( panel_checksum, longpress_delay_checksum )->by_default(0)->as_number(); | |
152 | this->up_button.set_longpress_delay(longpress_delay); | |
f1e696a3 | 153 | this->down_button.set_longpress_delay(longpress_delay); |
355c5796 L |
154 | // this->click_button.set_longpress_delay(longpress_delay); |
155 | // this->back_button.set_longpress_delay(longpress_delay); | |
156 | // this->pause_button.set_longpress_delay(longpress_delay); | |
61134a65 JM |
157 | |
158 | ||
c206438f | 159 | THEKERNEL->slow_ticker->attach( 50, this, &Panel::button_tick ); |
e456d044 JM |
160 | if(lcd->encoderReturnsDelta()) { |
161 | // panel handles encoder pins and returns a delta | |
10d11dfd | 162 | THEKERNEL->slow_ticker->attach( 10, this, &Panel::encoder_tick ); |
e456d044 JM |
163 | }else{ |
164 | // read encoder pins | |
165 | THEKERNEL->slow_ticker->attach( 1000, this, &Panel::encoder_check ); | |
166 | } | |
399cb110 | 167 | |
767e7c51 JM |
168 | // Register for events |
169 | this->register_for_event(ON_IDLE); | |
170 | this->register_for_event(ON_MAIN_LOOP); | |
399cb110 | 171 | this->register_for_event(ON_GCODE_RECEIVED); |
767e7c51 | 172 | |
35089dc7 | 173 | // Refresh timer |
314ab8f7 | 174 | THEKERNEL->slow_ticker->attach( 20, this, &Panel::refresh_tick ); |
35089dc7 JM |
175 | } |
176 | ||
177 | // Enter a screen, we only care about it now | |
862fc625 JM |
178 | void Panel::enter_screen(PanelScreen *screen) |
179 | { | |
35089dc7 JM |
180 | this->current_screen = screen; |
181 | this->reset_counter(); | |
182 | this->current_screen->on_enter(); | |
183 | } | |
184 | ||
185 | // Reset the counter | |
862fc625 JM |
186 | void Panel::reset_counter() |
187 | { | |
35089dc7 JM |
188 | *this->counter = 0; |
189 | this->counter_changed = false; | |
190 | } | |
191 | ||
192 | // Indicate the idle loop we want to call the refresh hook in the current screen | |
5ff3e912 | 193 | // called 20 times a second |
862fc625 JM |
194 | uint32_t Panel::refresh_tick(uint32_t dummy) |
195 | { | |
58d6d841 JM |
196 | this->refresh_flag = true; |
197 | this->idle_time++; | |
198 | return 0; | |
35089dc7 JM |
199 | } |
200 | ||
e93caece | 201 | // Encoder pins changed in interrupt or call from on_idle |
862fc625 JM |
202 | uint32_t Panel::encoder_check(uint32_t dummy) |
203 | { | |
e456d044 JM |
204 | // if encoder reads go through SPI like on universal panel adapter this needs to be |
205 | // done in idle loop, this is indicated by lcd->encoderReturnsDelta() | |
206 | // however when reading encoder directly it needs to be done | |
207 | // frequently, universal panel adapter will return an actual delta count so won't miss any if polled slowly | |
e93caece | 208 | // NOTE FIXME (WHY is it in menu only?) this code will not work if change is not 1,0,-1 anything greater (as in above case) will not work properly |
10d11dfd JM |
209 | static int encoder_counter = 0; // keeps track of absolute encoder position |
210 | static int last_encoder_click= 0; // last readfing of divided encoder count | |
35089dc7 | 211 | int change = lcd->readEncoderDelta(); |
58d6d841 | 212 | encoder_counter += change; |
10d11dfd JM |
213 | int clicks= encoder_counter/this->encoder_click_resolution; |
214 | int delta= clicks - last_encoder_click; // the number of clicks this time | |
215 | last_encoder_click= clicks; | |
b6e81799 | 216 | |
10d11dfd | 217 | if ( delta != 0 ) { |
58d6d841 | 218 | this->counter_changed = true; |
10d11dfd | 219 | (*this->counter) += delta; |
862fc625 | 220 | this->idle_time = 0; |
35089dc7 | 221 | } |
58d6d841 | 222 | return 0; |
35089dc7 JM |
223 | } |
224 | ||
225 | // Read and update each button | |
862fc625 JM |
226 | uint32_t Panel::button_tick(uint32_t dummy) |
227 | { | |
58d6d841 JM |
228 | this->do_buttons = true; |
229 | return 0; | |
35089dc7 JM |
230 | } |
231 | ||
e456d044 JM |
232 | // Read and update encoder |
233 | uint32_t Panel::encoder_tick(uint32_t dummy) | |
234 | { | |
235 | this->do_encoder = true; | |
236 | return 0; | |
237 | } | |
238 | ||
862fc625 | 239 | void Panel::on_gcode_received(void *argument) |
399cb110 | 240 | { |
862fc625 JM |
241 | Gcode *gcode = static_cast<Gcode *>(argument); |
242 | if ( gcode->has_m) { | |
243 | if ( gcode->m == 117 ) { // set LCD message | |
8090190d | 244 | this->message = get_arguments(gcode->get_command()); |
862fc625 | 245 | if (this->message.size() > 20) this->message = this->message.substr(0, 20); |
399cb110 JM |
246 | gcode->mark_as_taken(); |
247 | } | |
248 | } | |
249 | } | |
250 | ||
35089dc7 | 251 | // on main loop, we can send gcodes or do anything that waits in this loop |
862fc625 JM |
252 | void Panel::on_main_loop(void *argument) |
253 | { | |
254 | if (this->current_screen != NULL) { | |
f140cf3a JM |
255 | this->current_screen->on_main_loop(); |
256 | this->lcd->on_main_loop(); | |
257 | } | |
35089dc7 JM |
258 | } |
259 | ||
c96fd70a JM |
260 | |
261 | #define ohw_logo_antipixel_width 80 | |
262 | #define ohw_logo_antipixel_height 15 | |
161164be | 263 | static const uint8_t ohw_logo_antipixel_bits[] = { |
862fc625 JM |
264 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, |
265 | 0x00, 0x00, 0x00, 0x01, 0x80, 0x0C, 0x00, 0x33, 0x18, 0xBB, 0xFF, 0xFF, 0xFF, 0xFD, 0x80, 0x5E, | |
266 | 0x80, 0x2D, 0x6B, 0x9B, 0xFF, 0xFF, 0xFF, 0xFD, 0x80, 0xFF, 0xC0, 0x2D, 0x18, 0xAB, 0xFF, 0xFF, | |
267 | 0xFF, 0xFD, 0x80, 0xFF, 0xC0, 0x2D, 0x7B, 0xB3, 0xFF, 0xFF, 0xFF, 0xFD, 0x80, 0x7F, 0x80, 0x33, | |
268 | 0x78, 0xBB, 0xFF, 0xFF, 0xFF, 0xFD, 0x81, 0xF3, 0xE0, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, | |
269 | 0x81, 0xF3, 0xE0, 0x3F, 0xFD, 0xB3, 0x18, 0xDD, 0x98, 0xC5, 0x81, 0xF3, 0xE0, 0x3F, 0xFD, 0xAD, | |
270 | 0x6B, 0x5D, 0x6B, 0x5D, 0x80, 0x73, 0x80, 0x3F, 0xFC, 0x21, 0x1B, 0x55, 0x08, 0xC5, 0x80, 0xF3, | |
271 | 0xC0, 0x3F, 0xFD, 0xAD, 0x5B, 0x49, 0x6A, 0xDD, 0x80, 0xE1, 0xC0, 0x3F, 0xFD, 0xAD, 0x68, 0xDD, | |
272 | 0x6B, 0x45, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, | |
273 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF | |
c96fd70a JM |
274 | }; |
275 | ||
35089dc7 JM |
276 | // On idle things, we don't want to do shit in interrupts |
277 | // don't queue gcodes in this | |
862fc625 JM |
278 | void Panel::on_idle(void *argument) |
279 | { | |
280 | if (this->start_up) { | |
f140cf3a JM |
281 | this->lcd->init(); |
282 | ||
ebc64506 JM |
283 | Version v; |
284 | string build(v.get_build()); | |
285 | string date(v.get_build_date()); | |
286 | this->lcd->clear(); | |
862fc625 JM |
287 | this->lcd->setCursor(0, 0); this->lcd->printf("Welcome to Smoothie"); |
288 | this->lcd->setCursor(0, 1); this->lcd->printf("%s", build.substr(0, 20).c_str()); | |
289 | this->lcd->setCursor(0, 2); this->lcd->printf("%s", date.substr(0, 20).c_str()); | |
290 | this->lcd->setCursor(0, 3); this->lcd->printf("Please wait...."); | |
ebc64506 | 291 | |
862fc625 | 292 | if (this->lcd->hasGraphics()) { |
c96fd70a JM |
293 | this->lcd->bltGlyph(24, 40, ohw_logo_antipixel_width, ohw_logo_antipixel_height, ohw_logo_antipixel_bits); |
294 | } | |
295 | ||
f54d25cb JM |
296 | this->lcd->on_refresh(true); // tell lcd to display now |
297 | ||
f140cf3a | 298 | // Default top screen |
ca6effd7 | 299 | this->top_screen= new MainMenuScreen(); |
ca6effd7 | 300 | this->custom_screen->set_parent(this->top_screen); |
862fc625 | 301 | this->start_up = false; |
5ff3e912 | 302 | return; |
f140cf3a | 303 | } |
35089dc7 | 304 | |
ca6effd7 | 305 | MainMenuScreen *mms= static_cast<MainMenuScreen*>(this->top_screen); |
58d6d841 | 306 | // after being idle for a while switch to Watch screen |
5ff3e912 | 307 | if (this->current_screen != NULL && this->idle_time > this->current_screen->idle_timeout_secs()*20) { |
862fc625 | 308 | this->idle_time = 0; |
ca6effd7 JM |
309 | if (mms->watch_screen != this->current_screen) { |
310 | this->enter_screen(mms->watch_screen); | |
58d6d841 JM |
311 | // TODO do we need to reset any state? |
312 | } | |
399cb110 | 313 | |
58d6d841 JM |
314 | return; |
315 | } | |
399cb110 | 316 | |
5ff3e912 JM |
317 | if(current_screen == NULL && this->idle_time > 20*4) { |
318 | this->enter_screen(mms->watch_screen); | |
319 | return; | |
320 | } | |
321 | ||
e456d044 JM |
322 | if(this->do_encoder) { |
323 | this->do_encoder= false; | |
324 | encoder_check(0); | |
325 | } | |
326 | ||
862fc625 | 327 | if (this->do_buttons) { |
e456d044 | 328 | // we don't want to do SPI in interrupt mode |
58d6d841 JM |
329 | this->do_buttons = false; |
330 | ||
331 | // read the actual buttons | |
862fc625 JM |
332 | int but = lcd->readButtons(); |
333 | if (but != 0) { | |
334 | this->idle_time = 0; | |
5ff3e912 JM |
335 | if(current_screen == NULL) { |
336 | // we were in startup screen so go to watch screen | |
337 | this->enter_screen(mms->watch_screen); | |
338 | return; | |
339 | } | |
f140cf3a JM |
340 | } |
341 | ||
58d6d841 | 342 | // fire events if the buttons are active and debounce is satisfied |
862fc625 JM |
343 | this->up_button.check_signal(but & BUTTON_UP); |
344 | this->down_button.check_signal(but & BUTTON_DOWN); | |
345 | this->back_button.check_signal(but & BUTTON_LEFT); | |
346 | this->click_button.check_signal(but & BUTTON_SELECT); | |
347 | this->pause_button.check_signal(but & BUTTON_PAUSE); | |
348 | } | |
399cb110 | 349 | |
35089dc7 | 350 | // If we are in menu mode and the position has changed |
862fc625 | 351 | if ( this->mode == MENU_MODE && this->counter_change() ) { |
35089dc7 JM |
352 | this->menu_update(); |
353 | } | |
354 | ||
355 | // If we are in control mode | |
862fc625 | 356 | if ( this->mode == CONTROL_MODE && this->counter_change() ) { |
58d6d841 | 357 | this->control_value_update(); |
35089dc7 JM |
358 | } |
359 | ||
360 | // If we must refresh | |
862fc625 | 361 | if ( this->refresh_flag ) { |
35089dc7 | 362 | this->refresh_flag = false; |
862fc625 | 363 | if (this->current_screen != NULL) { |
f140cf3a JM |
364 | this->current_screen->on_refresh(); |
365 | this->lcd->on_refresh(); | |
366 | } | |
35089dc7 JM |
367 | } |
368 | } | |
369 | ||
370 | // Hooks for button clicks | |
862fc625 JM |
371 | uint32_t Panel::on_up(uint32_t dummy) |
372 | { | |
25121d12 | 373 | // this is simulating encoder clicks, but needs to be inverted to |
b6e81799 JM |
374 | // increment values on up,increment by |
375 | int inc = (this->mode == CONTROL_MODE) ? 1 : -(this->menu_offset+1); | |
25121d12 | 376 | *this->counter += inc; |
58d6d841 JM |
377 | this->counter_changed = true; |
378 | return 0; | |
35089dc7 | 379 | } |
5ff3e912 | 380 | |
862fc625 JM |
381 | uint32_t Panel::on_down(uint32_t dummy) |
382 | { | |
b6e81799 | 383 | int inc = (this->mode == CONTROL_MODE) ? -1 : (this->menu_offset+1); |
25121d12 | 384 | *this->counter += inc; |
58d6d841 JM |
385 | this->counter_changed = true; |
386 | return 0; | |
35089dc7 JM |
387 | } |
388 | ||
389 | // on most menu screens will go back to previous higher menu | |
862fc625 JM |
390 | uint32_t Panel::on_back(uint32_t dummy) |
391 | { | |
392 | if (this->mode == MENU_MODE && this->current_screen != NULL && this->current_screen->parent != NULL) { | |
58d6d841 JM |
393 | this->enter_screen(this->current_screen->parent); |
394 | } | |
395 | return 0; | |
35089dc7 JM |
396 | } |
397 | ||
862fc625 JM |
398 | uint32_t Panel::on_select(uint32_t dummy) |
399 | { | |
58d6d841 JM |
400 | // TODO make configurable, including turning off |
401 | // buzz is ignored on panels that do not support buzz | |
58d6d841 | 402 | this->click_changed = true; |
862fc625 JM |
403 | this->idle_time = 0; |
404 | lcd->buzz(60, 300); // 50ms 300Hz | |
58d6d841 | 405 | return 0; |
35089dc7 JM |
406 | } |
407 | ||
862fc625 JM |
408 | uint32_t Panel::on_pause(uint32_t dummy) |
409 | { | |
20e69746 | 410 | if (!THEKERNEL->pauser->paused()) { |
52500e58 | 411 | THEKERNEL->pauser->take(); |
862fc625 | 412 | } else { |
52500e58 | 413 | THEKERNEL->pauser->release(); |
52500e58 L |
414 | } |
415 | return 0; | |
416 | } | |
417 | ||
862fc625 JM |
418 | bool Panel::counter_change() |
419 | { | |
420 | if ( this->counter_changed ) { | |
421 | this->counter_changed = false; | |
422 | return true; | |
423 | } else { | |
424 | return false; | |
425 | } | |
426 | } | |
427 | bool Panel::click() | |
428 | { | |
429 | if ( this->click_changed ) { | |
430 | this->click_changed = false; | |
431 | return true; | |
432 | } else { | |
433 | return false; | |
434 | } | |
435 | } | |
35089dc7 JM |
436 | |
437 | ||
438 | // Enter menu mode | |
5971142d | 439 | void Panel::enter_menu_mode(bool force) |
862fc625 | 440 | { |
35089dc7 JM |
441 | this->mode = MENU_MODE; |
442 | this->counter = &this->menu_selected_line; | |
5971142d | 443 | this->menu_changed = force; |
35089dc7 JM |
444 | } |
445 | ||
862fc625 JM |
446 | void Panel::setup_menu(uint16_t rows) |
447 | { | |
f65ce58f JM |
448 | this->setup_menu(rows, min(rows, this->max_screen_lines())); |
449 | } | |
450 | ||
862fc625 JM |
451 | void Panel::setup_menu(uint16_t rows, uint16_t lines) |
452 | { | |
35089dc7 | 453 | this->menu_selected_line = 0; |
862fc625 | 454 | this->menu_current_line = 0; |
399cb110 | 455 | this->menu_start_line = 0; |
35089dc7 | 456 | this->menu_rows = rows; |
206167cf | 457 | this->panel_lines = lines; |
35089dc7 JM |
458 | } |
459 | ||
862fc625 JM |
460 | void Panel::menu_update() |
461 | { | |
399cb110 | 462 | // Limits, up and down |
446deda2 | 463 | // NOTE menu_selected_line is changed in an interrupt and can change at any time |
862fc625 | 464 | int msl = this->menu_selected_line; // hopefully this is atomic |
206167cf JM |
465 | |
466 | #if 0 | |
467 | // this allows it to wrap but with new method we dont; want to wrap | |
862fc625 JM |
468 | msl = msl % ( this->menu_rows << this->menu_offset ); |
469 | while ( msl < 0 ) { | |
470 | msl += this->menu_rows << this->menu_offset; | |
471 | } | |
206167cf JM |
472 | #else |
473 | // limit selected line to screen lines available | |
474 | if(msl >= this->menu_rows<<this->menu_offset){ | |
475 | msl= (this->menu_rows-1)<<this->menu_offset; | |
476 | }else if(msl < 0) msl= 0; | |
477 | #endif | |
446deda2 | 478 | |
206167cf | 479 | this->menu_selected_line = msl; // update atomically we hope |
b6e81799 JM |
480 | // figure out which actual line to select, if we have a menu offset it means we want to move one line per two clicks |
481 | if(msl % (this->menu_offset+1) == 0) { // only if divisible by offset | |
482 | this->menu_current_line = msl >> this->menu_offset; | |
483 | } | |
35089dc7 JM |
484 | |
485 | // What to display | |
206167cf JM |
486 | if ( this->menu_rows > this->panel_lines ) { |
487 | #if 0 | |
488 | // old way of scrolling not nice.... | |
862fc625 | 489 | if ( this->menu_current_line >= 2 ) { |
446deda2 | 490 | this->menu_start_line = this->menu_current_line - 1; |
35089dc7 | 491 | } |
206167cf JM |
492 | if ( this->menu_current_line > this->menu_rows - this->panel_lines ) { |
493 | this->menu_start_line = this->menu_rows - this->panel_lines; | |
494 | } | |
495 | #else | |
496 | // new way we only scroll the lines when the cursor hits the bottom of the screen or the top of the screen | |
497 | // do we want to scroll up? | |
498 | int sl= this->menu_current_line - this->menu_start_line; // screen line we are on | |
499 | if(sl >= this->panel_lines) { | |
500 | this->menu_start_line += ((sl+1)-this->panel_lines); // scroll up to keep it on the screen | |
501 | ||
502 | }else if(sl < 0 ) { // do we want to scroll down? | |
503 | this->menu_start_line += sl; // scroll down | |
35089dc7 | 504 | } |
206167cf JM |
505 | #endif |
506 | ||
507 | }else{ | |
508 | this->menu_start_line = 0; | |
35089dc7 JM |
509 | } |
510 | ||
511 | this->menu_changed = true; | |
512 | } | |
513 | ||
862fc625 JM |
514 | bool Panel::menu_change() |
515 | { | |
516 | if ( this->menu_changed ) { | |
517 | this->menu_changed = false; | |
518 | return true; | |
519 | } else { | |
520 | return false; | |
521 | } | |
35089dc7 JM |
522 | } |
523 | ||
862fc625 JM |
524 | bool Panel::control_value_change() |
525 | { | |
526 | if ( this->control_value_changed ) { | |
527 | this->control_value_changed = false; | |
528 | return true; | |
529 | } else { | |
530 | return false; | |
531 | } | |
35089dc7 JM |
532 | } |
533 | ||
1ad23cd3 | 534 | bool Panel::enter_control_mode(float passed_normal_increment, float passed_pressed_increment) |
862fc625 | 535 | { |
35089dc7 JM |
536 | this->mode = CONTROL_MODE; |
537 | this->normal_increment = passed_normal_increment; | |
35089dc7 JM |
538 | this->counter = &this->control_normal_counter; |
539 | this->control_normal_counter = 0; | |
58d6d841 JM |
540 | this->control_base_value = 0; |
541 | return true; | |
35089dc7 JM |
542 | } |
543 | ||
862fc625 JM |
544 | void Panel::control_value_update() |
545 | { | |
58d6d841 JM |
546 | // TODO what do we do here? |
547 | this->control_value_changed = true; | |
35089dc7 JM |
548 | } |
549 | ||
1ad23cd3 | 550 | void Panel::set_control_value(float value) |
862fc625 | 551 | { |
58d6d841 | 552 | this->control_base_value = value; |
35089dc7 JM |
553 | } |
554 | ||
1ad23cd3 | 555 | float Panel::get_control_value() |
862fc625 JM |
556 | { |
557 | return this->control_base_value + (this->control_normal_counter * this->normal_increment); | |
35089dc7 JM |
558 | } |
559 | ||
862fc625 JM |
560 | bool Panel::is_playing() const |
561 | { | |
58d6d841 JM |
562 | void *returned_data; |
563 | ||
75e6428d | 564 | bool ok = PublicData::get_value( player_checksum, is_playing_checksum, &returned_data ); |
862fc625 JM |
565 | if (ok) { |
566 | bool b = *static_cast<bool *>(returned_data); | |
58d6d841 JM |
567 | return b; |
568 | } | |
569 | return false; | |
7aa9fef3 JM |
570 | } |
571 | ||
862fc625 JM |
572 | void Panel::set_playing_file(string f) |
573 | { | |
7aa9fef3 | 574 | // just copy the first 20 characters after the first / if there |
862fc625 JM |
575 | size_t n = f.find_last_of('/'); |
576 | if (n == string::npos) n = 0; | |
577 | strncpy(playing_file, f.substr(n + 1, 19).c_str(), sizeof(playing_file)); | |
578 | playing_file[sizeof(playing_file) - 1] = 0; | |
7aa9fef3 | 579 | } |
cee1bb2d JM |
580 | |
581 | static float getTargetTemperature(uint16_t heater_cs) | |
582 | { | |
583 | void *returned_data; | |
75e6428d | 584 | bool ok = PublicData::get_value( temperature_control_checksum, heater_cs, current_temperature_checksum, &returned_data ); |
cee1bb2d JM |
585 | |
586 | if (ok) { | |
587 | struct pad_temperature temp = *static_cast<struct pad_temperature *>(returned_data); | |
588 | return temp.target_temperature; | |
589 | } | |
590 | ||
591 | return 0.0F; | |
592 | } | |
593 | ||
594 | void Panel::setup_temperature_screen() | |
595 | { | |
596 | // setup temperature screen | |
597 | auto mvs= new ModifyValuesScreen(); | |
598 | this->temperature_screen= mvs; | |
599 | ||
600 | // enumerate heaters and add a menu item for each one | |
601 | vector<uint16_t> modules; | |
602 | THEKERNEL->config->get_module_list( &modules, temperature_control_checksum ); | |
603 | ||
604 | for(auto i : modules) { | |
605 | if (!THEKERNEL->config->value(temperature_control_checksum, i, enable_checksum )->as_bool()) continue; | |
606 | void *returned_data; | |
75e6428d | 607 | bool ok = PublicData::get_value( temperature_control_checksum, i, current_temperature_checksum, &returned_data ); |
cee1bb2d JM |
608 | if (!ok) continue; |
609 | ||
610 | struct pad_temperature t = *static_cast<struct pad_temperature *>(returned_data); | |
611 | ||
612 | // rename if two of the known types | |
613 | const char *name; | |
614 | if(t.designator == "T") name= "Hotend"; | |
615 | else if(t.designator == "B") name= "Bed"; | |
616 | else name= t.designator.c_str(); | |
617 | ||
618 | mvs->addMenuItem(name, // menu name | |
619 | [i]() -> float { return getTargetTemperature(i); }, // getter | |
75e6428d | 620 | [i](float t) { PublicData::set_value( temperature_control_checksum, i, &t ); }, // setter |
cee1bb2d JM |
621 | 1.0F, // increment |
622 | 0.0F, // Min | |
623 | 500.0F // Max | |
624 | ); | |
625 | } | |
626 | } |