-/*
+/*
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 <http://www.gnu.org/licenses/>.
+ You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>.
*/
#include "libs/Kernel.h"
#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;
this->click_changed = false;
this->refresh_flag = false;
this->enter_menu_mode();
- this->menu_offset = 1;
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");
}
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;
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;
// 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();
+
// load jogging feedrates in mm/min
jogging_speed_mm_min[0]= this->kernel->config->value( panel_checksum, jog_x_feedrate_checksum )->by_default(3000.0)->as_number();
jogging_speed_mm_min[1]= this->kernel->config->value( panel_checksum, jog_y_feedrate_checksum )->by_default(3000.0)->as_number();
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
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
return 0;
}
+void Panel::on_gcode_received(void* argument)
+{
+ Gcode* gcode = static_cast<Gcode*>(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
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);
+ }
- // FIXME test
-// if(but&BUTTON_AUX1) lcd->buzz(10, 500);
- }
-
// If we are in menu mode and the position has changed
if( this->mode == MENU_MODE && this->counter_change() ){
this->menu_update();
// 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;
}
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<<this->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<<this->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;
}
}
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;
}
}
double Panel::get_control_value(){
- return this->control_base_value + (this->control_normal_counter*this->normal_increment/this->encoder_click_resolution);
+ return this->control_base_value + (this->control_normal_counter*this->normal_increment);
}
bool Panel::is_playing() const {