get direct step running from encoder
[clinton/Smoothieware.git] / src / modules / utils / panel / Panel.cpp
CommitLineData
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"
fb877329
JM
15#include "libs/USBDevice/USBMSD/SDCard.h"
16#include "libs/SDFAT.h"
ca6effd7 17
35089dc7 18#include "modules/utils/player/PlayerPublicAccess.h"
c150a0d3
JM
19#include "CustomScreen.h"
20#include "MainMenuScreen.h"
61134a65
JM
21#include "SlowTicker.h"
22#include "Gcode.h"
cee1bb2d
JM
23#include "TemperatureControlPublicAccess.h"
24#include "ModifyValuesScreen.h"
25#include "PublicDataRequest.h"
61134a65 26#include "PublicData.h"
fb877329
JM
27#include "StreamOutputPool.h"
28#include "platform_memory.h"
35089dc7 29
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"
7713b1ef 37#include "Config.h"
02e4b295 38#include "TemperatureControlPool.h"
35089dc7 39
fb877329
JM
40// for parse_pins in mbed
41#include "pinmap.h"
42
ca6effd7
JM
43#define panel_checksum CHECKSUM("panel")
44#define enable_checksum CHECKSUM("enable")
45#define lcd_checksum CHECKSUM("lcd")
ca6effd7
JM
46#define rrd_glcd_checksum CHECKSUM("reprap_discount_glcd")
47#define st7565_glcd_checksum CHECKSUM("st7565_glcd")
8b6bc932
JM
48#define viki2_checksum CHECKSUM("viki2")
49#define mini_viki2_checksum CHECKSUM("mini_viki2")
3ae0e673 50#define universal_adapter_checksum CHECKSUM("universal_adapter")
ca6effd7 51
fa3f7b30
JM
52#define menu_offset_checksum CHECKSUM("menu_offset")
53#define encoder_resolution_checksum CHECKSUM("encoder_resolution")
54#define jog_x_feedrate_checksum CHECKSUM("alpha_jog_feedrate")
55#define jog_y_feedrate_checksum CHECKSUM("beta_jog_feedrate")
56#define jog_z_feedrate_checksum CHECKSUM("gamma_jog_feedrate")
f1e696a3 57#define longpress_delay_checksum CHECKSUM("longpress_delay")
ca6effd7 58
fb877329
JM
59#define ext_sd_checksum CHECKSUM("external_sd")
60#define sdcd_pin_checksum CHECKSUM("sdcd_pin")
61#define spi_channel_checksum CHECKSUM("spi_channel")
62#define spi_cs_pin_checksum CHECKSUM("spi_cs_pin")
63
ca6effd7
JM
64#define hotend_temp_checksum CHECKSUM("hotend_temperature")
65#define bed_temp_checksum CHECKSUM("bed_temperature")
c89338bd 66#define panel_display_message_checksum CHECKSUM("display_message")
ca6effd7 67
cee1bb2d
JM
68Panel* Panel::instance= nullptr;
69
f9a0f86d
JM
70#define MENU_MODE 0
71#define CONTROL_MODE 1
72#define DIRECT_ENCODER_MODE 2
73
862fc625
JM
74Panel::Panel()
75{
cee1bb2d 76 instance= this;
35089dc7
JM
77 this->counter_changed = false;
78 this->click_changed = false;
79 this->refresh_flag = false;
80 this->enter_menu_mode();
862fc625 81 this->lcd = NULL;
58d6d841 82 this->do_buttons = false;
e456d044 83 this->do_encoder = false;
862fc625
JM
84 this->idle_time = 0;
85 this->start_up = true;
86 this->current_screen = NULL;
fb877329
JM
87 this->sd= nullptr;
88 this->extmounter= nullptr;
89 this->external_sd_enable= false;
7aa9fef3 90 strcpy(this->playing_file, "Playing file");
35089dc7
JM
91}
92
862fc625
JM
93Panel::~Panel()
94{
58d6d841 95 delete this->lcd;
fb877329
JM
96 delete this->extmounter;
97 delete this->sd;
35089dc7
JM
98}
99
862fc625
JM
100void Panel::on_module_loaded()
101{
35089dc7 102 // Exit if this module is not enabled
314ab8f7 103 if ( !THEKERNEL->config->value( panel_checksum, enable_checksum )->by_default(false)->as_bool() ) {
58d6d841
JM
104 delete this;
105 return;
399cb110 106 }
35089dc7 107
58d6d841
JM
108 // Initialise the LCD, see which LCD to use
109 if (this->lcd != NULL) delete this->lcd;
7713b1ef 110 int lcd_cksm = get_checksum(THEKERNEL->config->value(panel_checksum, lcd_checksum)->by_default("reprap_discount_glcd")->as_string());
58d6d841
JM
111
112 // Note checksums are not const expressions when in debug mode, so don't use switch
7713b1ef 113 if (lcd_cksm == rrd_glcd_checksum) {
357d0eda 114 this->lcd = new ReprapDiscountGLCD();
862fc625 115 } else if (lcd_cksm == st7565_glcd_checksum) {
84e7fd5a 116 this->lcd = new ST7565();
8b6bc932 117 } else if (lcd_cksm == viki2_checksum) {
8ed554f3 118 this->lcd = new ST7565(1); // variant 1
8b6bc932 119 } else if (lcd_cksm == mini_viki2_checksum) {
8ed554f3 120 this->lcd = new ST7565(2); // variant 2
3ae0e673
JM
121 } else if (lcd_cksm == universal_adapter_checksum) {
122 this->lcd = new UniversalAdapter();
862fc625 123 } else {
7713b1ef
JM
124 // no known lcd type defined
125 delete this;
58d6d841
JM
126 return;
127 }
128
fb877329
JM
129 // external sd
130 if(THEKERNEL->config->value( panel_checksum, ext_sd_checksum )->by_default(false)->as_bool()) {
131 this->external_sd_enable= true;
132 // external sdcard detect
133 this->sdcd_pin.from_string(THEKERNEL->config->value( panel_checksum, ext_sd_checksum, sdcd_pin_checksum )->by_default("nc")->as_string())->as_input();
134 this->extsd_spi_channel = THEKERNEL->config->value(panel_checksum, ext_sd_checksum, spi_channel_checksum)->by_default(0)->as_number();
135 string s= THEKERNEL->config->value( panel_checksum, ext_sd_checksum, spi_cs_pin_checksum)->by_default("2.8")->as_string();
136 s= "P" + s; // Pinnames need to be Px_x
137 this->extsd_spi_cs= parse_pins(s.c_str());
138 this->register_for_event(ON_SECOND_TICK);
139 }
140
cee1bb2d
JM
141 // these need to be called here as they need the config cache loaded as they enumerate modules
142 this->custom_screen= new CustomScreen();
ca6effd7 143
58d6d841
JM
144 // some panels may need access to this global info
145 this->lcd->setPanel(this);
146
f65ce58f 147 // the number of screen lines the panel supports
862fc625 148 this->screen_lines = this->lcd->get_screen_lines();
399cb110 149
19fb4629
JM
150 // some encoders may need more clicks to move menu, this is a divisor and is in config as it is
151 // an end user usability issue
314ab8f7 152 this->menu_offset = THEKERNEL->config->value( panel_checksum, menu_offset_checksum )->by_default(0)->as_number();
19fb4629 153
fa3f7b30 154 // override default encoder resolution if needed
314ab8f7 155 this->encoder_click_resolution = THEKERNEL->config->value( panel_checksum, encoder_resolution_checksum )->by_default(this->lcd->getEncoderResolution())->as_number();
fa3f7b30 156
58d6d841 157 // load jogging feedrates in mm/min
1ad23cd3
MM
158 jogging_speed_mm_min[0] = THEKERNEL->config->value( panel_checksum, jog_x_feedrate_checksum )->by_default(3000.0f)->as_number();
159 jogging_speed_mm_min[1] = THEKERNEL->config->value( panel_checksum, jog_y_feedrate_checksum )->by_default(3000.0f)->as_number();
160 jogging_speed_mm_min[2] = THEKERNEL->config->value( panel_checksum, jog_z_feedrate_checksum )->by_default(300.0f )->as_number();
f19a841a
JM
161
162 // load the default preset temeratures
1ad23cd3
MM
163 default_hotend_temperature = THEKERNEL->config->value( panel_checksum, hotend_temp_checksum )->by_default(185.0f )->as_number();
164 default_bed_temperature = THEKERNEL->config->value( panel_checksum, bed_temp_checksum )->by_default(60.0f )->as_number();
f19a841a 165
399cb110 166
95195a29
JM
167 this->up_button.up_attach( this, &Panel::on_up );
168 this->down_button.up_attach( this, &Panel::on_down );
ab4abea9 169 this->click_button.up_attach( this, &Panel::on_select );
95195a29 170 this->back_button.up_attach( this, &Panel::on_back );
35089dc7 171
355c5796
L
172
173 //setting longpress_delay
174 int longpress_delay = THEKERNEL->config->value( panel_checksum, longpress_delay_checksum )->by_default(0)->as_number();
175 this->up_button.set_longpress_delay(longpress_delay);
f1e696a3 176 this->down_button.set_longpress_delay(longpress_delay);
355c5796
L
177// this->click_button.set_longpress_delay(longpress_delay);
178// this->back_button.set_longpress_delay(longpress_delay);
179// this->pause_button.set_longpress_delay(longpress_delay);
61134a65
JM
180
181
c206438f 182 THEKERNEL->slow_ticker->attach( 50, this, &Panel::button_tick );
e456d044
JM
183 if(lcd->encoderReturnsDelta()) {
184 // panel handles encoder pins and returns a delta
10d11dfd 185 THEKERNEL->slow_ticker->attach( 10, this, &Panel::encoder_tick );
e456d044
JM
186 }else{
187 // read encoder pins
188 THEKERNEL->slow_ticker->attach( 1000, this, &Panel::encoder_check );
189 }
399cb110 190
767e7c51
JM
191 // Register for events
192 this->register_for_event(ON_IDLE);
193 this->register_for_event(ON_MAIN_LOOP);
c89338bd 194 this->register_for_event(ON_SET_PUBLIC_DATA);
767e7c51 195
35089dc7 196 // Refresh timer
314ab8f7 197 THEKERNEL->slow_ticker->attach( 20, this, &Panel::refresh_tick );
35089dc7
JM
198}
199
200// Enter a screen, we only care about it now
862fc625
JM
201void Panel::enter_screen(PanelScreen *screen)
202{
383c9c1c
JM
203 if(this->current_screen != nullptr)
204 this->current_screen->on_exit();
205
35089dc7
JM
206 this->current_screen = screen;
207 this->reset_counter();
208 this->current_screen->on_enter();
209}
210
211// Reset the counter
862fc625
JM
212void Panel::reset_counter()
213{
35089dc7
JM
214 *this->counter = 0;
215 this->counter_changed = false;
216}
217
218// Indicate the idle loop we want to call the refresh hook in the current screen
5ff3e912 219// called 20 times a second
862fc625
JM
220uint32_t Panel::refresh_tick(uint32_t dummy)
221{
58d6d841
JM
222 this->refresh_flag = true;
223 this->idle_time++;
224 return 0;
35089dc7
JM
225}
226
e93caece 227// Encoder pins changed in interrupt or call from on_idle
862fc625
JM
228uint32_t Panel::encoder_check(uint32_t dummy)
229{
e456d044
JM
230 // if encoder reads go through SPI like on universal panel adapter this needs to be
231 // done in idle loop, this is indicated by lcd->encoderReturnsDelta()
232 // however when reading encoder directly it needs to be done
233 // frequently, universal panel adapter will return an actual delta count so won't miss any if polled slowly
e93caece 234 // 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
235 static int encoder_counter = 0; // keeps track of absolute encoder position
236 static int last_encoder_click= 0; // last readfing of divided encoder count
f9a0f86d 237
35089dc7 238 int change = lcd->readEncoderDelta();
f9a0f86d
JM
239
240 if(mode == DIRECT_ENCODER_MODE) {
241 if(change != 0 && encoder_cb_fnc) {
242 encoder_cb_fnc(change);
243 }
244 return 0;
245 }
246
58d6d841 247 encoder_counter += change;
10d11dfd
JM
248 int clicks= encoder_counter/this->encoder_click_resolution;
249 int delta= clicks - last_encoder_click; // the number of clicks this time
250 last_encoder_click= clicks;
b6e81799 251
10d11dfd 252 if ( delta != 0 ) {
58d6d841 253 this->counter_changed = true;
10d11dfd 254 (*this->counter) += delta;
862fc625 255 this->idle_time = 0;
35089dc7 256 }
58d6d841 257 return 0;
35089dc7
JM
258}
259
260// Read and update each button
862fc625
JM
261uint32_t Panel::button_tick(uint32_t dummy)
262{
58d6d841
JM
263 this->do_buttons = true;
264 return 0;
35089dc7
JM
265}
266
f9a0f86d 267// Read and update encoder from a slow source
e456d044
JM
268uint32_t Panel::encoder_tick(uint32_t dummy)
269{
270 this->do_encoder = true;
271 return 0;
272}
273
f9a0f86d
JM
274// special mode where all encoder ticks are sent to the given function
275bool Panel::enter_direct_encoder_mode(encoder_cb_t fnc)
276{
277 encoder_cb_fnc= fnc;
278 this->mode= DIRECT_ENCODER_MODE;
279 return true;
280}
281
c89338bd 282void Panel::on_set_public_data(void *argument)
399cb110 283{
c89338bd
JM
284 PublicDataRequest *pdr = static_cast<PublicDataRequest *>(argument);
285
286 if(!pdr->starts_with(panel_checksum)) return;
287
288 if(!pdr->second_element_is(panel_display_message_checksum)) return;
289
290 string *s = static_cast<string *>(pdr->get_data_ptr());
291 if (s->size() > 20) {
292 this->message = s->substr(0, 20);
293 } else {
294 this->message= *s;
399cb110
JM
295 }
296}
297
35089dc7 298// on main loop, we can send gcodes or do anything that waits in this loop
862fc625
JM
299void Panel::on_main_loop(void *argument)
300{
301 if (this->current_screen != NULL) {
f140cf3a
JM
302 this->current_screen->on_main_loop();
303 this->lcd->on_main_loop();
304 }
35089dc7
JM
305}
306
c96fd70a
JM
307
308#define ohw_logo_antipixel_width 80
309#define ohw_logo_antipixel_height 15
161164be 310static const uint8_t ohw_logo_antipixel_bits[] = {
862fc625
JM
311 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
312 0x00, 0x00, 0x00, 0x01, 0x80, 0x0C, 0x00, 0x33, 0x18, 0xBB, 0xFF, 0xFF, 0xFF, 0xFD, 0x80, 0x5E,
313 0x80, 0x2D, 0x6B, 0x9B, 0xFF, 0xFF, 0xFF, 0xFD, 0x80, 0xFF, 0xC0, 0x2D, 0x18, 0xAB, 0xFF, 0xFF,
314 0xFF, 0xFD, 0x80, 0xFF, 0xC0, 0x2D, 0x7B, 0xB3, 0xFF, 0xFF, 0xFF, 0xFD, 0x80, 0x7F, 0x80, 0x33,
315 0x78, 0xBB, 0xFF, 0xFF, 0xFF, 0xFD, 0x81, 0xF3, 0xE0, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD,
316 0x81, 0xF3, 0xE0, 0x3F, 0xFD, 0xB3, 0x18, 0xDD, 0x98, 0xC5, 0x81, 0xF3, 0xE0, 0x3F, 0xFD, 0xAD,
317 0x6B, 0x5D, 0x6B, 0x5D, 0x80, 0x73, 0x80, 0x3F, 0xFC, 0x21, 0x1B, 0x55, 0x08, 0xC5, 0x80, 0xF3,
318 0xC0, 0x3F, 0xFD, 0xAD, 0x5B, 0x49, 0x6A, 0xDD, 0x80, 0xE1, 0xC0, 0x3F, 0xFD, 0xAD, 0x68, 0xDD,
319 0x6B, 0x45, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF,
320 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
c96fd70a
JM
321};
322
35089dc7
JM
323// On idle things, we don't want to do shit in interrupts
324// don't queue gcodes in this
862fc625
JM
325void Panel::on_idle(void *argument)
326{
327 if (this->start_up) {
f140cf3a
JM
328 this->lcd->init();
329
ebc64506
JM
330 Version v;
331 string build(v.get_build());
332 string date(v.get_build_date());
333 this->lcd->clear();
862fc625
JM
334 this->lcd->setCursor(0, 0); this->lcd->printf("Welcome to Smoothie");
335 this->lcd->setCursor(0, 1); this->lcd->printf("%s", build.substr(0, 20).c_str());
336 this->lcd->setCursor(0, 2); this->lcd->printf("%s", date.substr(0, 20).c_str());
337 this->lcd->setCursor(0, 3); this->lcd->printf("Please wait....");
ebc64506 338
862fc625 339 if (this->lcd->hasGraphics()) {
c96fd70a
JM
340 this->lcd->bltGlyph(24, 40, ohw_logo_antipixel_width, ohw_logo_antipixel_height, ohw_logo_antipixel_bits);
341 }
342
f54d25cb
JM
343 this->lcd->on_refresh(true); // tell lcd to display now
344
f140cf3a 345 // Default top screen
ca6effd7 346 this->top_screen= new MainMenuScreen();
ca6effd7 347 this->custom_screen->set_parent(this->top_screen);
862fc625 348 this->start_up = false;
5ff3e912 349 return;
f140cf3a 350 }
35089dc7 351
ca6effd7 352 MainMenuScreen *mms= static_cast<MainMenuScreen*>(this->top_screen);
58d6d841 353 // after being idle for a while switch to Watch screen
5ff3e912 354 if (this->current_screen != NULL && this->idle_time > this->current_screen->idle_timeout_secs()*20) {
862fc625 355 this->idle_time = 0;
ca6effd7
JM
356 if (mms->watch_screen != this->current_screen) {
357 this->enter_screen(mms->watch_screen);
58d6d841
JM
358 // TODO do we need to reset any state?
359 }
399cb110 360
58d6d841
JM
361 return;
362 }
399cb110 363
5ff3e912
JM
364 if(current_screen == NULL && this->idle_time > 20*4) {
365 this->enter_screen(mms->watch_screen);
366 return;
367 }
368
e456d044
JM
369 if(this->do_encoder) {
370 this->do_encoder= false;
371 encoder_check(0);
372 }
373
862fc625 374 if (this->do_buttons) {
e456d044 375 // we don't want to do SPI in interrupt mode
58d6d841
JM
376 this->do_buttons = false;
377
378 // read the actual buttons
862fc625
JM
379 int but = lcd->readButtons();
380 if (but != 0) {
381 this->idle_time = 0;
5ff3e912
JM
382 if(current_screen == NULL) {
383 // we were in startup screen so go to watch screen
384 this->enter_screen(mms->watch_screen);
385 return;
386 }
f140cf3a
JM
387 }
388
58d6d841 389 // fire events if the buttons are active and debounce is satisfied
862fc625
JM
390 this->up_button.check_signal(but & BUTTON_UP);
391 this->down_button.check_signal(but & BUTTON_DOWN);
392 this->back_button.check_signal(but & BUTTON_LEFT);
393 this->click_button.check_signal(but & BUTTON_SELECT);
862fc625 394 }
399cb110 395
f9a0f86d
JM
396 if(this->counter_change()) {
397 switch(this->mode) {
398 case MENU_MODE: // If we are in menu mode and the position has changed
399 this->menu_update();
400 break;
401 case CONTROL_MODE: // If we are in control mode
402 this->control_value_update();
403 break;
404 }
35089dc7
JM
405 }
406
407 // If we must refresh
862fc625 408 if ( this->refresh_flag ) {
35089dc7 409 this->refresh_flag = false;
862fc625 410 if (this->current_screen != NULL) {
f140cf3a
JM
411 this->current_screen->on_refresh();
412 this->lcd->on_refresh();
413 }
35089dc7
JM
414 }
415}
416
417// Hooks for button clicks
862fc625
JM
418uint32_t Panel::on_up(uint32_t dummy)
419{
25121d12 420 // this is simulating encoder clicks, but needs to be inverted to
b6e81799
JM
421 // increment values on up,increment by
422 int inc = (this->mode == CONTROL_MODE) ? 1 : -(this->menu_offset+1);
25121d12 423 *this->counter += inc;
58d6d841
JM
424 this->counter_changed = true;
425 return 0;
35089dc7 426}
5ff3e912 427
862fc625
JM
428uint32_t Panel::on_down(uint32_t dummy)
429{
b6e81799 430 int inc = (this->mode == CONTROL_MODE) ? -1 : (this->menu_offset+1);
25121d12 431 *this->counter += inc;
58d6d841
JM
432 this->counter_changed = true;
433 return 0;
35089dc7
JM
434}
435
436// on most menu screens will go back to previous higher menu
862fc625
JM
437uint32_t Panel::on_back(uint32_t dummy)
438{
439 if (this->mode == MENU_MODE && this->current_screen != NULL && this->current_screen->parent != NULL) {
58d6d841
JM
440 this->enter_screen(this->current_screen->parent);
441 }
442 return 0;
35089dc7
JM
443}
444
862fc625
JM
445uint32_t Panel::on_select(uint32_t dummy)
446{
58d6d841
JM
447 // TODO make configurable, including turning off
448 // buzz is ignored on panels that do not support buzz
58d6d841 449 this->click_changed = true;
862fc625
JM
450 this->idle_time = 0;
451 lcd->buzz(60, 300); // 50ms 300Hz
58d6d841 452 return 0;
35089dc7
JM
453}
454
862fc625
JM
455bool Panel::counter_change()
456{
457 if ( this->counter_changed ) {
458 this->counter_changed = false;
459 return true;
460 } else {
461 return false;
462 }
463}
464bool Panel::click()
465{
466 if ( this->click_changed ) {
467 this->click_changed = false;
468 return true;
469 } else {
470 return false;
471 }
472}
35089dc7 473
35089dc7 474// Enter menu mode
5971142d 475void Panel::enter_menu_mode(bool force)
862fc625 476{
35089dc7
JM
477 this->mode = MENU_MODE;
478 this->counter = &this->menu_selected_line;
5971142d 479 this->menu_changed = force;
f9a0f86d 480 encoder_cb_fnc= nullptr;
35089dc7
JM
481}
482
862fc625
JM
483void Panel::setup_menu(uint16_t rows)
484{
f65ce58f
JM
485 this->setup_menu(rows, min(rows, this->max_screen_lines()));
486}
487
862fc625
JM
488void Panel::setup_menu(uint16_t rows, uint16_t lines)
489{
35089dc7 490 this->menu_selected_line = 0;
862fc625 491 this->menu_current_line = 0;
399cb110 492 this->menu_start_line = 0;
35089dc7 493 this->menu_rows = rows;
206167cf 494 this->panel_lines = lines;
35089dc7
JM
495}
496
862fc625
JM
497void Panel::menu_update()
498{
399cb110 499 // Limits, up and down
446deda2 500 // NOTE menu_selected_line is changed in an interrupt and can change at any time
862fc625 501 int msl = this->menu_selected_line; // hopefully this is atomic
206167cf
JM
502
503 #if 0
504 // this allows it to wrap but with new method we dont; want to wrap
862fc625
JM
505 msl = msl % ( this->menu_rows << this->menu_offset );
506 while ( msl < 0 ) {
507 msl += this->menu_rows << this->menu_offset;
508 }
206167cf
JM
509 #else
510 // limit selected line to screen lines available
511 if(msl >= this->menu_rows<<this->menu_offset){
512 msl= (this->menu_rows-1)<<this->menu_offset;
513 }else if(msl < 0) msl= 0;
514 #endif
446deda2 515
206167cf 516 this->menu_selected_line = msl; // update atomically we hope
b6e81799
JM
517 // figure out which actual line to select, if we have a menu offset it means we want to move one line per two clicks
518 if(msl % (this->menu_offset+1) == 0) { // only if divisible by offset
519 this->menu_current_line = msl >> this->menu_offset;
520 }
35089dc7
JM
521
522 // What to display
206167cf
JM
523 if ( this->menu_rows > this->panel_lines ) {
524 #if 0
525 // old way of scrolling not nice....
862fc625 526 if ( this->menu_current_line >= 2 ) {
446deda2 527 this->menu_start_line = this->menu_current_line - 1;
35089dc7 528 }
206167cf
JM
529 if ( this->menu_current_line > this->menu_rows - this->panel_lines ) {
530 this->menu_start_line = this->menu_rows - this->panel_lines;
531 }
532 #else
533 // new way we only scroll the lines when the cursor hits the bottom of the screen or the top of the screen
534 // do we want to scroll up?
535 int sl= this->menu_current_line - this->menu_start_line; // screen line we are on
536 if(sl >= this->panel_lines) {
537 this->menu_start_line += ((sl+1)-this->panel_lines); // scroll up to keep it on the screen
538
539 }else if(sl < 0 ) { // do we want to scroll down?
540 this->menu_start_line += sl; // scroll down
35089dc7 541 }
206167cf
JM
542 #endif
543
544 }else{
545 this->menu_start_line = 0;
35089dc7
JM
546 }
547
548 this->menu_changed = true;
549}
550
862fc625
JM
551bool Panel::menu_change()
552{
553 if ( this->menu_changed ) {
554 this->menu_changed = false;
555 return true;
556 } else {
557 return false;
558 }
35089dc7
JM
559}
560
862fc625
JM
561bool Panel::control_value_change()
562{
563 if ( this->control_value_changed ) {
564 this->control_value_changed = false;
565 return true;
566 } else {
567 return false;
568 }
35089dc7
JM
569}
570
1ad23cd3 571bool Panel::enter_control_mode(float passed_normal_increment, float passed_pressed_increment)
862fc625 572{
35089dc7
JM
573 this->mode = CONTROL_MODE;
574 this->normal_increment = passed_normal_increment;
35089dc7
JM
575 this->counter = &this->control_normal_counter;
576 this->control_normal_counter = 0;
58d6d841 577 this->control_base_value = 0;
f9a0f86d 578 encoder_cb_fnc= nullptr;
58d6d841 579 return true;
35089dc7
JM
580}
581
862fc625
JM
582void Panel::control_value_update()
583{
58d6d841
JM
584 // TODO what do we do here?
585 this->control_value_changed = true;
35089dc7
JM
586}
587
1ad23cd3 588void Panel::set_control_value(float value)
862fc625 589{
58d6d841 590 this->control_base_value = value;
35089dc7
JM
591}
592
1ad23cd3 593float Panel::get_control_value()
862fc625
JM
594{
595 return this->control_base_value + (this->control_normal_counter * this->normal_increment);
35089dc7
JM
596}
597
862fc625
JM
598bool Panel::is_playing() const
599{
58d6d841
JM
600 void *returned_data;
601
75e6428d 602 bool ok = PublicData::get_value( player_checksum, is_playing_checksum, &returned_data );
862fc625
JM
603 if (ok) {
604 bool b = *static_cast<bool *>(returned_data);
58d6d841
JM
605 return b;
606 }
607 return false;
7aa9fef3
JM
608}
609
5f1a896b
JM
610bool Panel::is_suspended() const
611{
612 void *returned_data;
613
614 bool ok = PublicData::get_value( player_checksum, is_suspended_checksum, &returned_data );
615 if (ok) {
616 bool b = *static_cast<bool *>(returned_data);
617 return b;
618 }
619 return false;
620}
621
862fc625
JM
622void Panel::set_playing_file(string f)
623{
7aa9fef3 624 // just copy the first 20 characters after the first / if there
862fc625
JM
625 size_t n = f.find_last_of('/');
626 if (n == string::npos) n = 0;
627 strncpy(playing_file, f.substr(n + 1, 19).c_str(), sizeof(playing_file));
628 playing_file[sizeof(playing_file) - 1] = 0;
7aa9fef3 629}
cee1bb2d 630
fb877329
JM
631bool Panel::mount_external_sd(bool on)
632{
633 // now setup the external sdcard if we have one and mount it
634 if(on) {
635 if(this->sd == nullptr) {
636 PinName mosi, miso, sclk, cs= this->extsd_spi_cs;
637 if(extsd_spi_channel == 0) {
638 mosi = P0_18; miso = P0_17; sclk = P0_15;
639 } else if(extsd_spi_channel == 1) {
640 mosi = P0_9; miso = P0_8; sclk = P0_7;
641 } else{
642 this->external_sd_enable= false;
643 THEKERNEL->streams->printf("Bad SPI channel for external SDCard\n");
644 return false;
645 }
646 size_t n= sizeof(SDCard);
647 void *v = AHB0.alloc(n);
648 memset(v, 0, n); // clear the allocated memory
649 this->sd= new(v) SDCard(mosi, miso, sclk, cs); // allocate object using zeroed memory
650 }
651 delete this->extmounter; // if it was not unmounted before
652 size_t n= sizeof(SDFAT);
653 void *v = AHB0.alloc(n);
654 memset(v, 0, n); // clear the allocated memory
655 this->extmounter= new(v) SDFAT("ext", this->sd); // use cleared allocated memory
656 this->sd->disk_initialize(); // first one seems to fail, but works next time
657 THEKERNEL->streams->printf("External SDcard mounted as /ext\n");
658 }else{
659 delete this->extmounter;
660 this->extmounter= nullptr;
661 THEKERNEL->streams->printf("External SDcard unmounted\n");
662 }
663 return true;
664}
665
666void Panel::on_second_tick(void *arg)
667{
892508b8 668 if(!this->external_sd_enable || this->start_up) return;
fb877329
JM
669
670 // sd insert detect, mount sdcard if inserted, unmount if removed
671 if(this->sdcd_pin.connected()) {
672 if(this->extmounter == nullptr && this->sdcd_pin.get()) {
673 mount_external_sd(true);
674 // go to the play screen and the /ext directory
892508b8 675 // TODO we don't want to do this if we just booted and card was already in
fb877329
JM
676 THEKERNEL->current_path= "/ext";
677 MainMenuScreen *mms= static_cast<MainMenuScreen*>(this->top_screen);
678 THEPANEL->enter_screen(mms->file_screen);
679
680 }else if(this->extmounter != nullptr && !this->sdcd_pin.get()){
681 mount_external_sd(false);
682 }
683 }else{
684 // TODO for panels with no sd card detect we need to poll to see if card is inserted - or not
685 }
686}