X-Git-Url: https://git.hcoop.net/clinton/Smoothieware.git/blobdiff_plain/fe7b1d9c640307599d31952b22ad3e6c4b312cd9..1c20d2ebff8366be6855aae8bee09eafb4f433e4:/src/modules/utils/panel/Panel.cpp diff --git a/src/modules/utils/panel/Panel.cpp b/src/modules/utils/panel/Panel.cpp index 0b720d0d..137b8e23 100644 --- a/src/modules/utils/panel/Panel.cpp +++ b/src/modules/utils/panel/Panel.cpp @@ -1,8 +1,8 @@ -/* +/* This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). 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. 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. - You should have received a copy of the GNU General Public License along with Smoothie. If not, see . + You should have received a copy of the GNU General Public License along with Smoothie. If not, see . */ #include "libs/Kernel.h" @@ -19,6 +19,9 @@ using namespace std; #include "panels/I2CLCD.h" #include "panels/VikiLCD.h" #include "panels/Smoothiepanel.h" +#include "panels/ReprapDiscountGLCD.h" +#include "panels/ST7565.h" +#include "version.h" Panel::Panel(){ this->counter_changed = false; @@ -28,6 +31,8 @@ Panel::Panel(){ this->lcd= NULL; this->do_buttons = false; this->idle_time= 0; + this->start_up= true; + this->current_screen= NULL; strcpy(this->playing_file, "Playing file"); } @@ -40,11 +45,7 @@ void Panel::on_module_loaded(){ if( !this->kernel->config->value( panel_checksum, enable_checksum )->by_default(false)->as_bool() ){ delete this; return; - } - - // Register for events - this->register_for_event(ON_IDLE); - this->register_for_event(ON_MAIN_LOOP); + } // Initialise the LCD, see which LCD to use if (this->lcd != NULL) delete this->lcd; @@ -55,8 +56,16 @@ void Panel::on_module_loaded(){ this->lcd = new I2CLCD(); }else if(lcd_cksm == viki_lcd_checksum) { this->lcd = new VikiLCD(); + this->lcd->set_variant(0); + }else if(lcd_cksm == panelolu2_checksum) { + this->lcd = new VikiLCD(); + this->lcd->set_variant(1); }else if(lcd_cksm == smoothiepanel_checksum) { this->lcd = new Smoothiepanel(); + }else if(lcd_cksm == rrd_glcd_checksum) { + this->lcd = new ReprapDiscountGLCD(); + }else if(lcd_cksm == st7565_glcd_checksum) { + this->lcd = new ST7565(); }else{ // no lcd type defined return; @@ -65,6 +74,9 @@ void Panel::on_module_loaded(){ // some panels may need access to this global info this->lcd->setPanel(this); + // the number of screen lines the panel supports + this->screen_lines= this->lcd->get_screen_lines(); + // some encoders may need more clicks to move menu, this is a divisor and is in config as it is // an end user usability issue this->menu_offset = this->kernel->config->value( panel_checksum, menu_offset_checksum )->by_default(0)->as_number(); @@ -79,25 +91,23 @@ void Panel::on_module_loaded(){ default_bed_temperature= this->kernel->config->value( panel_checksum, bed_temp_checksum )->by_default(60.0)->as_number(); this->encoder_click_resolution= this->lcd->getEncoderResolution(); - this->lcd->init(); - this->lcd->printf("Starting..."); - - this->up_button.up_attach( this, &Panel::on_up ); - this->down_button.up_attach( this, &Panel::on_down ); - this->click_button.down_attach( this, &Panel::on_click_release ); - this->back_button.up_attach( this, &Panel::on_back ); + + this->up_button.up_attach( this, &Panel::on_up ); + this->down_button.up_attach( this, &Panel::on_down ); + this->click_button.up_attach( this, &Panel::on_select ); + this->back_button.up_attach( this, &Panel::on_back ); + this->pause_button.up_attach( this, &Panel::on_pause ); this->kernel->slow_ticker->attach( 100, this, &Panel::button_tick ); this->kernel->slow_ticker->attach( 1000, this, &Panel::encoder_check ); - // Default top screen - this->top_screen = new MainMenuScreen(); - this->top_screen->set_panel(this); - this->enter_screen(this->top_screen->watch_screen); // default first screen is watch screen even though its parent is Mainmenu - + // Register for events + this->register_for_event(ON_IDLE); + this->register_for_event(ON_MAIN_LOOP); + this->register_for_event(ON_GCODE_RECEIVED); + // Refresh timer this->kernel->slow_ticker->attach( 20, this, &Panel::refresh_tick ); - } // Enter a screen, we only care about it now @@ -121,7 +131,7 @@ uint32_t Panel::refresh_tick(uint32_t dummy){ return 0; } -// Encoder pins changed +// Encoder pins changed in interrupt uint32_t Panel::encoder_check(uint32_t dummy){ // TODO if encoder reads go through i2c like on smoothie panel this needs to be // optionally done in idle loop, however when reading encoder directly it needs to be done @@ -144,15 +154,69 @@ uint32_t Panel::button_tick(uint32_t dummy){ return 0; } +void Panel::on_gcode_received(void* argument) +{ + Gcode* gcode = static_cast(argument); + if( gcode->has_m) { + if( gcode->m == 117 ) { // set LCD message + this->message= get_arguments(gcode->command); + if(this->message.size() > 20) this->message= this->message.substr(0, 20); + gcode->mark_as_taken(); + } + } +} + // on main loop, we can send gcodes or do anything that waits in this loop void Panel::on_main_loop(void* argument){ - this->current_screen->on_main_loop(); - this->lcd->on_main_loop(); + if(this->current_screen != NULL) { + this->current_screen->on_main_loop(); + this->lcd->on_main_loop(); + } } + +#define ohw_logo_antipixel_width 80 +#define ohw_logo_antipixel_height 15 +static const uint8_t ohw_logo_antipixel_bits[] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0x80,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x01,0x80,0x0C,0x00,0x33, 0x18,0xBB,0xFF,0xFF,0xFF,0xFD,0x80,0x5E, + 0x80,0x2D,0x6B,0x9B,0xFF,0xFF,0xFF,0xFD, 0x80,0xFF,0xC0,0x2D,0x18,0xAB,0xFF,0xFF, + 0xFF,0xFD,0x80,0xFF,0xC0,0x2D,0x7B,0xB3, 0xFF,0xFF,0xFF,0xFD,0x80,0x7F,0x80,0x33, + 0x78,0xBB,0xFF,0xFF,0xFF,0xFD,0x81,0xF3, 0xE0,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFD, + 0x81,0xF3,0xE0,0x3F,0xFD,0xB3,0x18,0xDD, 0x98,0xC5,0x81,0xF3,0xE0,0x3F,0xFD,0xAD, + 0x6B,0x5D,0x6B,0x5D,0x80,0x73,0x80,0x3F, 0xFC,0x21,0x1B,0x55,0x08,0xC5,0x80,0xF3, + 0xC0,0x3F,0xFD,0xAD,0x5B,0x49,0x6A,0xDD, 0x80,0xE1,0xC0,0x3F,0xFD,0xAD,0x68,0xDD, + 0x6B,0x45,0x80,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF +}; + // On idle things, we don't want to do shit in interrupts // don't queue gcodes in this void Panel::on_idle(void* argument){ + if(this->start_up) { + this->lcd->init(); + + Version v; + string build(v.get_build()); + string date(v.get_build_date()); + this->lcd->clear(); + this->lcd->setCursor(0,0); this->lcd->printf("Welcome to Smoothie"); + this->lcd->setCursor(0,1); this->lcd->printf("%s", build.substr(0, 20).c_str()); + this->lcd->setCursor(0,2); this->lcd->printf("%s", date.substr(0, 20).c_str()); + this->lcd->setCursor(0,3); this->lcd->printf("Please wait...."); + + if(this->lcd->hasGraphics()) { + this->lcd->bltGlyph(24, 40, ohw_logo_antipixel_width, ohw_logo_antipixel_height, ohw_logo_antipixel_bits); + } + + this->lcd->on_refresh(true); // tell lcd to display now + + // Default top screen + this->top_screen = new MainMenuScreen(); + this->top_screen->set_panel(this); + this->start_up= false; + //this->idle_time= 20*3; // only show for 2 seconds + } // after being idle for a while switch to Watch screen if(this->idle_time > 20*5) { // 5 seconds @@ -161,25 +225,28 @@ void Panel::on_idle(void* argument){ this->enter_screen(this->top_screen->watch_screen); // TODO do we need to reset any state? } - + return; } - + if(this->do_buttons) { // we don't want to do I2C in interrupt mode this->do_buttons = false; // read the actual buttons int but= lcd->readButtons(); - if(but != 0) this->idle_time= 0; - + if(but != 0){ + this->idle_time= 0; + } + // fire events if the buttons are active and debounce is satisfied this->up_button.check_signal(but&BUTTON_UP); this->down_button.check_signal(but&BUTTON_DOWN); this->back_button.check_signal(but&BUTTON_LEFT); this->click_button.check_signal(but&BUTTON_SELECT); - } - + this->pause_button.check_signal(but&BUTTON_PAUSE); + } + // If we are in menu mode and the position has changed if( this->mode == MENU_MODE && this->counter_change() ){ this->menu_update(); @@ -193,40 +260,54 @@ void Panel::on_idle(void* argument){ // If we must refresh if( this->refresh_flag ){ this->refresh_flag = false; - this->current_screen->on_refresh(); - this->lcd->on_refresh(); + if(this->current_screen != NULL) { + this->current_screen->on_refresh(); + this->lcd->on_refresh(); + } } } // Hooks for button clicks uint32_t Panel::on_up(uint32_t dummy){ - // this is simulating encoder clicks, but as one press should move menu one - // we need to increment twice as two clicks are needed to move the menu once - // this needs to be configurable and tied to menu increment - *this->counter -= 1; + // this is simulating encoder clicks, but needs to be inverted to + // increment values on up + int inc= (this->mode == CONTROL_MODE) ? 1 : -1; + *this->counter += inc; this->counter_changed = true; return 0; } uint32_t Panel::on_down(uint32_t dummy){ - *this->counter += 1; + int inc= (this->mode == CONTROL_MODE) ? -1 : 1; + *this->counter += inc; this->counter_changed = true; return 0; } // on most menu screens will go back to previous higher menu uint32_t Panel::on_back(uint32_t dummy){ - if(this->mode == MENU_MODE && this->current_screen->parent != NULL) { + if(this->mode == MENU_MODE && this->current_screen != NULL && this->current_screen->parent != NULL) { this->enter_screen(this->current_screen->parent); } return 0; } -uint32_t Panel::on_click_release(uint32_t dummy){ +uint32_t Panel::on_select(uint32_t dummy){ // TODO make configurable, including turning off // buzz is ignored on panels that do not support buzz - lcd->buzz(60,300); // 50ms 300Hz this->click_changed = true; this->idle_time= 0; + lcd->buzz(60,300); // 50ms 300Hz + return 0; +} + +uint32_t Panel::on_pause(uint32_t dummy){ + if(!paused) { + THEKERNEL->pauser->take(); + paused= true; + }else{ + THEKERNEL->pauser->release(); + paused= false; + } return 0; } @@ -241,30 +322,35 @@ void Panel::enter_menu_mode(){ this->menu_changed = false; } +void Panel::setup_menu(uint16_t rows){ + this->setup_menu(rows, min(rows, this->max_screen_lines())); +} + void Panel::setup_menu(uint16_t rows, uint16_t lines){ this->menu_selected_line = 0; - this->menu_start_line = 0; + this->menu_current_line= 0; + this->menu_start_line = 0; this->menu_rows = rows; this->menu_lines = lines; } -uint16_t Panel::menu_current_line(){ - return this->menu_selected_line >> this->menu_offset; -} - void Panel::menu_update(){ + // Limits, up and down + // NOTE menu_selected_line is changed in an interrupt and can change at any time + int msl= this->menu_selected_line; // hopefully this is atomic + msl = msl % ( this->menu_rows<menu_offset ); + while( msl < 0 ){ msl += this->menu_rows << this->menu_offset; } + this->menu_selected_line= msl; // update atomically we hope - // Limits, up and down - this->menu_selected_line = this->menu_selected_line % ( this->menu_rows<menu_offset ); - while( this->menu_selected_line < 0 ){ this->menu_selected_line += this->menu_rows << this->menu_offset; } + this->menu_current_line= msl >> this->menu_offset; // What to display this->menu_start_line = 0; if( this->menu_rows > this->menu_lines ){ - if( this->menu_current_line() >= 2 ){ - this->menu_start_line = this->menu_current_line() - 1; + if( this->menu_current_line >= 2 ){ + this->menu_start_line = this->menu_current_line - 1; } - if( this->menu_current_line() > this->menu_rows - this->menu_lines ){ + if( this->menu_current_line > this->menu_rows - this->menu_lines ){ this->menu_start_line = this->menu_rows - this->menu_lines; } } @@ -280,14 +366,11 @@ bool Panel::control_value_change(){ if( this->control_value_changed ){ this->control_value_changed = false; return true; }else{ return false; } } - bool Panel::enter_control_mode(double passed_normal_increment, double passed_pressed_increment){ this->mode = CONTROL_MODE; this->normal_increment = passed_normal_increment; - this->pressed_increment = passed_pressed_increment; this->counter = &this->control_normal_counter; this->control_normal_counter = 0; - this->control_pressed_counter = 0; this->control_base_value = 0; return true; }