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" |
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 |
68 | Panel* 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 |
74 | Panel::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 |
93 | Panel::~Panel() |
94 | { | |
58d6d841 | 95 | delete this->lcd; |
fb877329 JM |
96 | delete this->extmounter; |
97 | delete this->sd; | |
35089dc7 JM |
98 | } |
99 | ||
862fc625 JM |
100 | void 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 |
201 | void 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 |
212 | void 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 |
220 | uint32_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 |
228 | uint32_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 |
261 | uint32_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 |
268 | uint32_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 |
275 | bool 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 | 282 | void 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 |
299 | void 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 | 310 | static 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 |
325 | void 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 |
418 | uint32_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 |
428 | uint32_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 |
437 | uint32_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 |
445 | uint32_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 |
455 | bool Panel::counter_change() |
456 | { | |
457 | if ( this->counter_changed ) { | |
458 | this->counter_changed = false; | |
459 | return true; | |
460 | } else { | |
461 | return false; | |
462 | } | |
463 | } | |
464 | bool 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 | 475 | void 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 |
483 | void Panel::setup_menu(uint16_t rows) |
484 | { | |
f65ce58f JM |
485 | this->setup_menu(rows, min(rows, this->max_screen_lines())); |
486 | } | |
487 | ||
862fc625 JM |
488 | void 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 |
497 | void 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 |
551 | bool 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 |
561 | bool 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 | 571 | bool 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 |
582 | void 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 | 588 | void Panel::set_control_value(float value) |
862fc625 | 589 | { |
58d6d841 | 590 | this->control_base_value = value; |
35089dc7 JM |
591 | } |
592 | ||
1ad23cd3 | 593 | float 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 |
598 | bool 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 |
610 | bool 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 |
622 | void 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 |
631 | bool 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 | ||
666 | void 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 | } |