pause_button_enable true #
# Panel
-panel.enable false #
-panel.up_button_pin 0.1! #
-panel.down_button_pin 0.0! #
-panel.click_button_pin 0.18! #
-panel.encoder_a_pin 0.15! #
-panel.encoder_b_pin 0.17! #
+panel.enable false # set to true to enabel the panel code
+panel.lcd viki_lcd # set type of panel also i2c_lcd is a generic i2c panel
+panel.encoder_a_pin 0.15!^ # encoder pin if used
+panel.encoder_b_pin 0.17!^ # encoder pin if used
+#panel.button_pause_pin 0.18^ # pin used for pause button on VikiLcd
+#panel.up_button_pin 0.1! # up button if used
+#panel.down_button_pin 0.0! # down button if used
+#panel.click_button_pin 0.18! # click button if used
+
+panel.alpha_jog_feedrate 6000 # x jogging feedrate in mm/min
+panel.beta_jog_feedrate 6000 # y jogging feedrate in mm/min
+panel.gamma_jog_feedrate 200 # z jogging feedrate in mm/min
# Only needed on a smoothieboard
currentcontrol_module_enable true #
public:
Kernel();
static Kernel* instance; // the Singleton instance of Kernel usable anywhere
+
void add_module(Module* module);
void register_for_event(_EVENT_ENUM id_event, Module* module);
void call_event(_EVENT_ENUM id_event);
#include "modules/utils/player/Player.h"
#include "modules/utils/pausebutton/PauseButton.h"
#include "modules/utils/PlayLed/PlayLed.h"
+#include "modules/utils/panel/Panel.h"
+
// #include "libs/ChaNFSSD/SDFileSystem.h"
#include "libs/Config.h"
#include "libs/nuts_bolts.h"
kernel->add_module( new PlayLed() );
kernel->add_module( new Endstops() );
kernel->add_module( new Player() );
+ kernel->add_module( new Panel() );
kernel->add_module( new Touchprobe() );
// Create and initialize USB stuff
// Tell all modules about it
this->kernel->call_event(ON_BLOCK_BEGIN, this->current_block);
- // In case the module was not taken
+ // In case the module was not taken
if( this->current_block->times_taken < 1 ){
Block* temp = this->current_block;
this->current_block = NULL; // It seems this was missing and adding it fixes things, if something breaks, this may be a suspect
if(!pdr->starts_with(robot_checksum)) return;
if(pdr->second_element_is(speed_override_percent_checksum)) {
- static double return_data= 100*60/seconds_per_minute;
+ static double return_data;
+ return_data= 100*this->seconds_per_minute/60;
pdr->set_data_ptr(&return_data);
pdr->set_taken();
if(pdr->second_element_is(speed_override_percent_checksum)) {
double t= *static_cast<double*>(pdr->get_data_ptr());
- seconds_per_minute= t * 0.6;
+ this->seconds_per_minute= t * 0.6;
pdr->set_taken();
}
}
gcode->add_nl = true;
gcode->mark_as_taken();
return;
- case 114: gcode->stream->printf("C: X:%1.3f Y:%1.3f Z:%1.3f ",
+ case 114: gcode->stream->printf("C: X:%1.3f Y:%1.3f Z:%1.3f ",
from_millimeters(this->current_position[0]),
from_millimeters(this->current_position[1]),
from_millimeters(this->current_position[2]));
--- /dev/null
+#ifndef BUTTON_H
+#define BUTTON_H
+
+#include <stdlib.h>
+#include "libs/LPC17xx/sLPC17xx.h" // smoothed mbed.h lib
+#include "libs/Kernel.h"
+#include "libs/utils.h"
+#include "libs/Pin.h"
+#include <string>
+#include "libs/Hook.h"
+
+
+class Button{
+ public:
+ Button(){
+ this->counter = 0;
+ this->value = false;
+ this->up_hook = NULL;
+ this->down_hook = NULL;
+ this->button_pin = NULL;
+ }
+
+ Button* pin(Pin* passed_pin){
+ this->button_pin = passed_pin;
+ return this;
+ }
+
+ void check_signal(){
+ check_signal(this->button_pin->get()?1:0);
+ }
+
+ void check_signal(int val){
+ bool start_value = this->value;
+ if( val ){
+ if( this->counter < 5 ){ this->counter++; }
+ if( this->counter == 5 ){
+ this->value = true;
+ }
+ }else{
+ if( this->counter > 0 ){ this->counter--; }
+ if( this->counter == 0 ){
+ this->value = false;
+ }
+ }
+
+ if( start_value != this->value ){
+ if( this->value ){
+ if( this->up_hook != NULL ){
+ this->up_hook->call();
+ }
+ }else{
+ if( this->down_hook != NULL ){
+ this->down_hook->call();
+ }
+ }
+ }
+
+ }
+
+ bool get(){
+ return this->value;
+ }
+
+
+ template<typename T> Button* up_attach( T *optr, uint32_t ( T::*fptr )( uint32_t ) ){
+ this->up_hook = new Hook();
+ this->up_hook->attach(optr, fptr);
+ return this;
+ }
+
+ template<typename T> Button* down_attach( T *optr, uint32_t ( T::*fptr )( uint32_t ) ){
+ this->down_hook = new Hook();
+ this->down_hook->attach(optr, fptr);
+ return this;
+ }
+
+ private:
+ Hook* up_hook;
+ Hook* down_hook;
+ bool value;
+ char counter;
+ Pin* button_pin;
+
+};
+
+
+
+
+
+#endif
--- /dev/null
+/*
+ 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/>.
+*/
+
+#include "libs/Kernel.h"
+#include "Panel.h"
+#include "libs/nuts_bolts.h"
+#include "libs/utils.h"
+#include <string>
+using namespace std;
+#include "Button.h"
+#include "PanelScreen.h"
+#include "screens/MainMenuScreen.h"
+#include "modules/utils/player/PlayerPublicAccess.h"
+
+#include "panels/I2CLCD.h"
+#include "panels/VikiLCD.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;
+}
+
+Panel::~Panel() {
+ delete this->lcd;
+}
+
+void Panel::on_module_loaded(){
+ // Exit if this module is not enabled
+ 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;
+ int lcd_cksm = get_checksum(this->kernel->config->value(panel_checksum, lcd_checksum)->by_default("i2c")->as_string());
+
+ // Note checksums are not const expressions when in debug mode, so don't use switch
+ if(lcd_cksm == i2c_lcd_checksum) {
+ this->lcd = new I2CLCD();
+ }else if(lcd_cksm == viki_lcd_checksum) {
+ this->lcd = new VikiLCD();
+ }else{
+ // no lcd type defined
+ return;
+ }
+
+ // some panels may need access to this global info
+ this->lcd->setPanel(this);
+
+ // 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();
+ jogging_speed_mm_min[2]= this->kernel->config->value( panel_checksum, jog_z_feedrate_checksum )->by_default(300.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->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
+
+ // Refresh timer
+ this->kernel->slow_ticker->attach( 20, this, &Panel::refresh_tick );
+
+}
+
+// Enter a screen, we only care about it now
+void Panel::enter_screen(PanelScreen* screen){
+ screen->panel = this;
+ this->current_screen = screen;
+ this->reset_counter();
+ this->current_screen->on_enter();
+}
+
+// Reset the counter
+void Panel::reset_counter(){
+ *this->counter = 0;
+ this->counter_changed = false;
+}
+
+// Indicate the idle loop we want to call the refresh hook in the current screen
+uint32_t Panel::refresh_tick(uint32_t dummy){
+ this->refresh_flag = true;
+ this->idle_time++;
+ return 0;
+}
+
+// Encoder pins changed
+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
+ // frequently, smoothie panel will return an actual delta count so won't miss any if polled slowly
+ static int encoder_counter = 0;
+ int change = lcd->readEncoderDelta();
+ encoder_counter += change;
+ // TODO divisor needs to be configurable
+ if( change != 0 /*&& encoder_counter % 2 == 0*/ ){
+ this->counter_changed = true;
+ (*this->counter) += change;
+ this->idle_time= 0;
+ }
+ return 0;
+}
+
+// Read and update each button
+uint32_t Panel::button_tick(uint32_t dummy){
+ this->do_buttons = true;
+ return 0;
+}
+
+// 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();
+}
+
+// On idle things, we don't want to do shit in interrupts
+// don't queue gcodes in this
+void Panel::on_idle(void* argument){
+
+ // after being idle for a while switch to Watch screen
+ if(this->idle_time > 20*5) { // 5 seconds
+ this->idle_time= 0;
+ if(this->top_screen->watch_screen != this->current_screen) {
+ 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;
+
+ // 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);
+
+ // 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 are in control mode
+ if( this->mode == CONTROL_MODE && this->counter_change() ){
+ this->control_value_update();
+ }
+
+ // If we must refresh
+ if( this->refresh_flag ){
+ this->refresh_flag = false;
+ 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->counter_changed = true;
+ return 0;
+}
+uint32_t Panel::on_down(uint32_t dummy){
+ *this->counter += 1;
+ 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) {
+ this->enter_screen(this->current_screen->parent);
+ }
+ return 0;
+}
+
+uint32_t Panel::on_click_release(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;
+ return 0;
+}
+
+bool Panel::counter_change(){ if( this->counter_changed ){ this->counter_changed = false; return true; }else{ return false; } }
+bool Panel::click(){ if( this->click_changed ){ this->click_changed = false; return true; }else{ return false; } }
+
+
+// Enter menu mode
+void Panel::enter_menu_mode(){
+ this->mode = MENU_MODE;
+ this->counter = &this->menu_selected_line;
+ this->menu_changed = false;
+}
+
+void Panel::setup_menu(uint16_t rows, uint16_t lines){
+ this->menu_selected_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
+ 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; }
+
+ // 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() > this->menu_rows - this->menu_lines ){
+ this->menu_start_line = this->menu_rows - this->menu_lines;
+ }
+ }
+
+ this->menu_changed = true;
+}
+
+bool Panel::menu_change(){
+ if( this->menu_changed ){ this->menu_changed = false; return true; }else{ return false; }
+}
+
+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;
+}
+
+void Panel::control_value_update(){
+ // TODO what do we do here?
+ this->control_value_changed = true;
+}
+
+void Panel::set_control_value(double value){
+ this->control_base_value = value;
+}
+
+double Panel::get_control_value(){
+ return this->control_base_value + (this->control_normal_counter*this->normal_increment/this->encoder_click_resolution);
+}
+
+bool Panel::is_playing() const {
+ void *returned_data;
+
+ bool ok= THEKERNEL->public_data->get_value( player_checksum, is_playing_checksum, &returned_data );
+ if(ok) {
+ bool b= *static_cast<bool*>(returned_data);
+ return b;
+ }
+ return false;
+}
\ No newline at end of file
--- /dev/null
+/*
+ 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/>.
+*/
+
+#ifndef PANEL_H
+#define PANEL_H
+
+#include "libs/Kernel.h"
+#include "libs/nuts_bolts.h"
+#include "libs/utils.h"
+#include "libs/Pin.h"
+#include "panels/LcdBase.h"
+#include "Button.h"
+#include "PanelScreen.h"
+#include "screens/MainMenuScreen.h"
+
+#define panel_checksum CHECKSUM("panel")
+#define enable_checksum CHECKSUM("enable")
+#define lcd_checksum CHECKSUM("lcd")
+#define i2c_lcd_checksum CHECKSUM("i2c_lcd")
+#define viki_lcd_checksum CHECKSUM("viki_lcd")
+
+#define jog_x_feedrate_checksum CHECKSUM("alpha_jog_feedrate")
+#define jog_y_feedrate_checksum CHECKSUM("beta_jog_feedrate")
+#define jog_z_feedrate_checksum CHECKSUM("gamma_jog_feedrate")
+
+#define MENU_MODE 0
+#define CONTROL_MODE 1
+
+
+class Panel : public Module {
+ public:
+ Panel();
+ virtual ~Panel();
+
+ void on_module_loaded();
+ uint32_t button_tick(uint32_t dummy);
+ void on_idle(void* argument);
+ void on_main_loop(void* argument);
+ void enter_screen(PanelScreen* screen);
+ void reset_counter();
+
+ // Encoder and buttons
+ uint32_t on_up(uint32_t dummy);
+ uint32_t on_down(uint32_t dummy);
+ uint32_t on_back(uint32_t dummy);
+ uint32_t on_click_release(uint32_t dummy);
+ uint32_t refresh_tick(uint32_t dummy);
+ uint32_t encoder_check(uint32_t dummy);
+ bool counter_change();
+ bool click();
+ int get_encoder_resolution() const { return encoder_click_resolution; }
+
+ // Menu
+ void enter_menu_mode();
+ void setup_menu(uint16_t rows, uint16_t lines);
+ void menu_update();
+ bool menu_change();
+ uint16_t menu_current_line();
+
+ // Control
+ bool enter_control_mode(double passed_normal_increment, double passed_pressed_increment);
+ void set_control_value(double value);
+ double get_control_value();
+ bool control_value_change();
+ void control_value_update();
+ double get_jogging_speed(char axis) { return jogging_speed_mm_min[axis-'X']; }
+
+ // file playing from sd
+ bool is_playing() const;
+ void set_playing_file(string f) { playing_file= f; }
+ string get_playing_file() { return playing_file; }
+
+ // public as it is directly accessed by screens... not good
+ // TODO pass lcd into ctor of each sub screen
+ LcdBase* lcd;
+
+ // as panelscreen accesses private fields in Panel
+ friend class PanelScreen;
+
+ private:
+ // Menu
+ char menu_offset;
+ int menu_selected_line;
+ int menu_start_line;
+ int menu_rows;
+ int menu_lines;
+ bool menu_changed;
+ bool control_value_changed;
+
+ // Control
+ double normal_increment;
+ double pressed_increment;
+ int control_normal_counter;
+ int control_pressed_counter;
+ double control_base_value;
+
+ Button up_button;
+ Button down_button;
+ Button back_button;
+ Button click_button;
+
+ int* counter;
+ volatile bool counter_changed;
+ volatile bool click_changed;
+ volatile bool refresh_flag;
+ volatile bool do_buttons;
+ int idle_time;
+ int encoder_click_resolution;
+ char mode;
+
+ MainMenuScreen* top_screen;
+ PanelScreen* current_screen;
+
+ double jogging_speed_mm_min[3];
+
+ string playing_file;
+};
+
+#endif
--- /dev/null
+/*
+ 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/>.
+*/
+
+#include "libs/Kernel.h"
+#include "Panel.h"
+#include "PanelScreen.h"
+#include "libs/nuts_bolts.h"
+#include "libs/utils.h"
+#include <string>
+using namespace std;
+
+PanelScreen::PanelScreen(){}
+
+void PanelScreen::on_refresh(){}
+void PanelScreen::on_main_loop(){}
+
+PanelScreen* PanelScreen::set_panel(Panel* parent){
+ this->panel = parent;
+ return this;
+}
+
+void PanelScreen::on_enter(){}
+
+void PanelScreen::refresh_menu(bool clear){
+ if(clear) this->panel->lcd->clear();
+ for(uint16_t i = this->panel->menu_start_line; i < this->panel->menu_start_line + min( this->panel->menu_rows, this->panel->menu_lines ); i++ ){
+ this->panel->lcd->setCursor(2, i - this->panel->menu_start_line );
+ this->display_menu_line(i);
+ }
+ this->panel->lcd->setCursor(0, this->panel->menu_current_line() - this->panel->menu_start_line );
+ this->panel->lcd->printf(">");
+}
+
+void PanelScreen::refresh_screen(bool clear){
+ if(clear) this->panel->lcd->clear();
+ for(uint16_t i = this->panel->menu_start_line; i < this->panel->menu_start_line + min( this->panel->menu_rows, this->panel->menu_lines ); i++ ){
+ this->panel->lcd->setCursor(0, i - this->panel->menu_start_line );
+ this->display_menu_line(i);
+ }
+}
+
+void PanelScreen::display_menu_line(uint16_t line){};
+
+
+PanelScreen* PanelScreen::set_parent(PanelScreen* passed_parent){
+ this->parent = passed_parent;
+ this->set_panel( passed_parent->panel );
+ return this;
+}
+
+
+
+
--- /dev/null
+/*
+ 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/>.
+*/
+
+#ifndef PANELSCREEN_H
+#define PANELSCREEN_H
+
+#include "libs/Kernel.h"
+#include "libs/nuts_bolts.h"
+#include "libs/utils.h"
+#include "libs/Pin.h"
+#include "LcdBase.h"
+#include "Panel.h"
+
+class Panel;
+class PanelScreen {
+ public:
+ PanelScreen();
+ virtual void on_refresh();
+ virtual void on_main_loop();
+ PanelScreen* set_panel(Panel* parent);
+ PanelScreen* set_parent(PanelScreen* passed_parent);
+ virtual void on_enter();
+ // if you completely rewrite the screen do not clear it, this avoids flicker
+ void refresh_screen(bool clear);
+ void refresh_menu(bool clear);
+ void refresh_menu(void){ refresh_menu(true); };
+ virtual void display_menu_line(uint16_t line);
+
+ Panel* panel;
+ PanelScreen* parent;
+};
+
+
+
+
+
+
+#endif
--- /dev/null
+/*
+ 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/>.
+*/
+#ifndef I2CLCD_H
+#define I2CLCD_H
+
+#include "LcdBase.h"
+
+#include "I2C.h" // mbed.h lib
+#include "wait_api.h" // mbed.h lib
+#include "libs/Config.h"
+
+using namespace std;
+#include <vector>
+#include <string>
+#include <cstdio>
+#include <cstdarg>
+
+// commands
+#define LCD_CLEARDISPLAY 0x01
+#define LCD_RETURNHOME 0x02
+#define LCD_ENTRYMODESET 0x04
+#define LCD_DISPLAYCONTROL 0x08
+#define LCD_CURSORSHIFT 0x10
+#define LCD_FUNCTIONSET 0x20
+#define LCD_SETCGRAMADDR 0x40
+#define LCD_SETDDRAMADDR 0x80
+
+// flags for display entry mode
+#define LCD_ENTRYRIGHT 0x00
+#define LCD_ENTRYLEFT 0x02
+#define LCD_ENTRYSHIFTINCREMENT 0x01
+#define LCD_ENTRYSHIFTDECREMENT 0x00
+
+// flags for display on/off control
+#define LCD_DISPLAYON 0x04
+#define LCD_DISPLAYOFF 0x00
+#define LCD_CURSORON 0x02
+#define LCD_CURSOROFF 0x00
+#define LCD_BLINKON 0x01
+#define LCD_BLINKOFF 0x00
+
+// flags for display/cursor shift
+#define LCD_DISPLAYMOVE 0x08
+#define LCD_CURSORMOVE 0x00
+#define LCD_MOVERIGHT 0x04
+#define LCD_MOVELEFT 0x00
+
+// flags for function set
+#define LCD_8BITMODE 0x10
+#define LCD_4BITMODE 0x00
+#define LCD_2LINE 0x08
+#define LCD_1LINE 0x00
+#define LCD_5x10DOTS 0x04
+#define LCD_5x8DOTS 0x00
+
+// flags for backlight control
+#define LCD_BACKLIGHT 0x08
+#define LCD_NOBACKLIGHT 0x00
+
+#define En 1<<2 // Enable bit
+#define Rw 1<<1 // Read/Write bit
+#define Rs 1<<0 // Register select bit
+
+// config settings
+#define panel_checksum CHECKSUM("panel")
+#define encoder_a_pin_checksum CHECKSUM("encoder_a_pin")
+#define encoder_b_pin_checksum CHECKSUM("encoder_b_pin")
+#define up_button_pin_checksum CHECKSUM("up_button_pin")
+#define down_button_pin_checksum CHECKSUM("down_button_pin")
+#define click_button_pin_checksum CHECKSUM("click_button_pin")
+
+class I2CLCD : public LcdBase {
+ public:
+ I2CLCD() {
+ Kernel* kernel= THEKERNEL;
+ // Default values
+ this->i2c_address = 0x27;
+ this->backlightval = 0x00;
+ this->displaycontrol = 0x00;
+ this->displayfunction = 0x00;
+ this->displaymode = 0x00;
+
+ // I2C com
+ this->i2c = new mbed::I2C(P0_27, P0_28);
+
+ // configure the pins to use
+ this->encoder_a_pin.from_string(kernel->config->value( panel_checksum, encoder_a_pin_checksum)->by_default("nc")->as_string())->as_input()->pull_up();
+ this->encoder_b_pin.from_string(kernel->config->value( panel_checksum, encoder_b_pin_checksum)->by_default("nc")->as_string())->as_input()->pull_up();
+ this->click_pin.from_string(kernel->config->value( panel_checksum, click_button_pin_checksum )->by_default("nc")->as_string())->as_input()->pull_up();
+ this->up_pin.from_string(kernel->config->value( panel_checksum, up_button_pin_checksum)->by_default("nc")->as_string())->as_input()->pull_up();
+ this->down_pin.from_string(kernel->config->value( panel_checksum, down_button_pin_checksum)->by_default("nc")->as_string())->as_input()->pull_up();
+
+ }
+ virtual ~I2CLCD() {
+ delete this->i2c;
+ }
+
+ int getEncoderResolution() {
+ return 1;
+ }
+
+ uint8_t readButtons() {
+ uint8_t state= 0;
+ state |= (this->click_pin.get() ? BUTTON_SELECT : 0);
+ state |= (this->up_pin.get() ? BUTTON_UP : 0);
+ state |= (this->down_pin.get() ? BUTTON_DOWN : 0);
+ return state;
+ }
+
+ int readEncoderDelta() {
+ static int8_t enc_states[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
+ static uint8_t old_AB = 0;
+ old_AB <<= 2; //remember previous state
+ old_AB |= ( this->encoder_a_pin.get() + ( this->encoder_b_pin.get() * 2 ) ); //add current state
+ return enc_states[(old_AB&0x0f)];
+ }
+
+ void expanderWrite(char data){
+ this->i2c->start();
+ this->i2c->write(this->i2c_address<<1);
+ this->i2c->write((char)((char)data | (char)backlightval));
+ this->i2c->stop();
+ }
+
+ void pulseEnable(char data){
+ this->expanderWrite(data | En); // En high
+ wait_us(1); // enable pulse must be >450ns
+ this->expanderWrite(data & ~En); // En low
+ wait_us(50); // commands need > 37us to settle
+ }
+
+ void write4bits(char value) {
+ this->expanderWrite(value);
+ this->pulseEnable(value);
+ }
+
+ void send(char value, char mode) {
+ uint8_t highnib=value&0xf0;
+ uint8_t lownib=(value<<4)&0xf0;
+ this->write4bits((highnib)|mode);
+ this->write4bits((lownib)|mode);
+ }
+
+ void command(char value) {
+ this->send(value, 0);
+ }
+
+ void write(char value){
+ this->send(value, Rs);
+ }
+
+ void home(){
+ this->command(LCD_RETURNHOME); // set cursor position to zero
+ wait_us(2000); // this command takes a long time!
+ }
+
+ void clear(){
+ this->command(LCD_CLEARDISPLAY); // clear display, set cursor position to zero
+ wait_us(2000); // this command takes a long time!
+ }
+
+ void display() {
+ this->displaycontrol |= LCD_DISPLAYON;
+ this->command(LCD_DISPLAYCONTROL | this->displaycontrol);
+ }
+
+ void setCursor(uint8_t col, uint8_t row){
+ int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 };
+ if ( row > 4 ) {
+ row = 4-1; // we count rows starting w/0
+ }
+ this->command(LCD_SETDDRAMADDR | (col + row_offsets[row]));
+ }
+
+ void init(){
+ // Setup
+ this->displayfunction = LCD_4BITMODE | LCD_2LINE | LCD_5x8DOTS;
+ this->backlightval = LCD_NOBACKLIGHT;
+
+ // Now we pull both RS and R/W low to begin commands
+ wait_ms(50);
+ this->expanderWrite(this->backlightval);
+ wait_ms(1000);
+
+ // we start in 8bit mode, try to set 4 bit mode
+ for( char i=0;i<3;i++){
+ this->write4bits(0x03 << 4);
+ wait_us(4500);
+ }
+
+ // finally, set to 4-bit interface
+ this->write4bits(0x02 << 4);
+
+ // set # lines, font size, etc.
+ this->command(LCD_FUNCTIONSET | this->displayfunction);
+
+ // turn the display on with no cursor or blinking default
+ this->displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
+ this->display();
+
+ // clear it off
+ this->clear();
+
+ // Initialize to default text direction (for roman languages)
+ this->displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
+
+ // set the entry mode
+ this->command(LCD_ENTRYMODESET | displaymode);
+
+ this->home();
+ wait(0.1);
+
+ this->backlightval=LCD_BACKLIGHT;
+ expanderWrite(0);
+
+ }
+
+ private:
+ char displaymode;
+ char displayfunction;
+ char displaycontrol;
+ char i2c_address;
+ char backlightval;
+
+ mbed::I2C* i2c;
+
+ Pin encoder_a_pin;
+ Pin encoder_b_pin;
+ Pin click_pin;
+ Pin up_pin;
+ Pin down_pin;
+};
+
+
+#endif
--- /dev/null
+#include "LcdBase.h"
+
+LcdBase::LcdBase() {}
+LcdBase::~LcdBase() {}
+
+
+int LcdBase::printf(const std::string format, ...){
+ wait_us(10);
+ va_list args;
+ va_start(args, format);
+ int size = format.size() * 2;
+ char* buffer = new char[size];
+ while (vsprintf(buffer, format.c_str(), args) < 0){
+ delete[] buffer;
+ size *= 2;
+ buffer = new char[size];
+ }
+ string message = std::string(buffer);
+ va_end(args);
+ for(unsigned int i=0; i < message.size(); i++){
+ this->write(message.at(i));
+ }
+ delete[] buffer;
+ return 0;
+}
--- /dev/null
+#ifndef LCDBASE_H
+#define LCDBASE_H
+
+#include "I2C.h" // mbed.h lib
+#include "wait_api.h" // mbed.h lib
+
+#include "libs/Module.h"
+#include "libs/Kernel.h"
+#include "libs/nuts_bolts.h"
+#include "libs/Config.h"
+
+using namespace std;
+#include <vector>
+#include <string>
+#include <cstdio>
+#include <cstdarg>
+
+// commands
+#define LCD_CLEARDISPLAY 0x01
+#define LCD_RETURNHOME 0x02
+#define LCD_ENTRYMODESET 0x04
+#define LCD_DISPLAYCONTROL 0x08
+#define LCD_CURSORSHIFT 0x10
+#define LCD_FUNCTIONSET 0x20
+#define LCD_SETCGRAMADDR 0x40
+#define LCD_SETDDRAMADDR 0x80
+
+// flags for display entry mode
+#define LCD_ENTRYRIGHT 0x00
+#define LCD_ENTRYLEFT 0x02
+#define LCD_ENTRYSHIFTINCREMENT 0x01
+#define LCD_ENTRYSHIFTDECREMENT 0x00
+
+// flags for display on/off control
+#define LCD_DISPLAYON 0x04
+#define LCD_DISPLAYOFF 0x00
+#define LCD_CURSORON 0x02
+#define LCD_CURSOROFF 0x00
+#define LCD_BLINKON 0x01
+#define LCD_BLINKOFF 0x00
+
+// flags for display/cursor shift
+#define LCD_DISPLAYMOVE 0x08
+#define LCD_CURSORMOVE 0x00
+#define LCD_MOVERIGHT 0x04
+#define LCD_MOVELEFT 0x00
+
+// flags for function set
+#define LCD_8BITMODE 0x10
+#define LCD_4BITMODE 0x00
+#define LCD_2LINE 0x08
+#define LCD_1LINE 0x00
+#define LCD_5x10DOTS 0x04
+#define LCD_5x8DOTS 0x00
+
+// flags for backlight control
+#define LCD_BACKLIGHT 0x08
+#define LCD_NOBACKLIGHT 0x00
+
+// for setBacklight(), sets specific leds on some panels
+#define LED_OFF 0x0
+#define LED_RED 0x1
+#define LED_YELLOW 0x3
+#define LED_GREEN 0x2
+#define LED_TEAL 0x6
+#define LED_BLUE 0x4
+#define LED_VIOLET 0x5
+#define LED_WHITE 0x7
+
+// Standard directional button bits
+#define BUTTON_SELECT 0x01
+#define BUTTON_RIGHT 0x02
+#define BUTTON_DOWN 0x04
+#define BUTTON_UP 0x08
+#define BUTTON_LEFT 0x10
+#define BUTTON_AUX1 0x20
+#define BUTTON_AUX2 0x40
+#define BUTTON_AUX3 0x80
+
+class Panel;
+
+class LcdBase {
+ public:
+ LcdBase();
+ virtual ~LcdBase();
+ virtual void init()= 0;
+ int printf(const std::string format, ...);
+
+ void setPanel(Panel* p) { panel= p; }
+
+ // Required LCD functions
+ virtual void write(char value)= 0;
+ virtual void home()= 0;
+ virtual void clear()= 0;
+ virtual void display()= 0;
+ virtual void setCursor(uint8_t col, uint8_t row)= 0;
+
+ // Returns button states including the encoder select button
+ virtual uint8_t readButtons()= 0;
+
+ // returns the current encoder position
+ virtual int readEncoderDelta()= 0;
+
+ // the number of encoder clicks per detent. this is divided into
+ // accumulated clicks for control values so one detent is one
+ // increment, this varies depending on encoder type usually 1,2 or 4
+ virtual int getEncoderResolution()= 0;
+
+ // optional
+ virtual void setBacklight(uint8_t status){};
+ virtual void buzz(long,uint16_t){};
+
+ // only used on certain panels
+ virtual void on_refresh(){};
+ virtual void on_main_loop(){};
+
+ protected:
+ Panel* panel;
+
+};
+
+#endif
\ No newline at end of file
--- /dev/null
+/*
+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/>.
+*/
+#include "VikiLCD.h"
+
+// if this is defined we use the R/W poll mode instead of fixed delays
+// However at the slower I2C frequency required for Viki long cables it is slower than fixed delay
+// taken from LiquidCrystalFast.cpp and implemented for Viki LCD here by Jim Morris
+//#define USE_FASTMODE
+
+//MCP23017 - Adafruit RGB LCD Shield and VikiLCD
+// bit pattern for the burstBits function is
+//
+// B7 B6 B5 B4 B3 B2 B1 B0 A7 A6 A5 A4 A3 A2 A1 A0 - MCP23017
+// RS RW EN D4 D5 D6 D7 LB LG LR BZ B4 B3 B2 B1 B0
+// 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+#define M17_BIT_RS 0x8000
+#define M17_BIT_RW 0x4000
+#define M17_BIT_EN 0x2000
+#define M17_BIT_D4 0x1000
+#define M17_BIT_D5 0x0800
+#define M17_BIT_D6 0x0400
+#define M17_BIT_D7 0x0200
+#define M17_BIT_LB 0x0100
+#define M17_BIT_LG 0x0080
+#define M17_BIT_LR 0x0040
+#define M17_BIT_BZ 0x0020 //Added a buzzer on this pin
+#define M17_BIT_B4 0x0010
+#define M17_BIT_B3 0x0008
+#define M17_BIT_B2 0x0004
+#define M17_BIT_B1 0x0002
+#define M17_BIT_B0 0x0001
+
+VikiLCD::VikiLCD() {
+ // Default values
+ this->i2c_address = MCP23017_ADDRESS;
+ this->backlightval = 0x00;
+ this->displaycontrol = 0x00;
+ this->displayfunction = LCD_4BITMODE | LCD_2LINE | LCD_5x8DOTS; // in case they forget to call begin() at least we have somethin
+ this->displaymode = 0x00;
+ this->_numlines = 4;
+
+ // I2C com
+// this->i2c = new mbed::I2C(P0_27, P0_28);
+ this->i2c = new mbed::I2C(p9, p10); // P0_0, P0_1
+
+ i2c->frequency(60000);
+
+ // configure the pins to use
+ this->encoder_a_pin.from_string(THEKERNEL->config->value( panel_checksum, encoder_a_pin_checksum)->by_default("nc")->as_string())->as_input();
+
+ this->encoder_b_pin.from_string(THEKERNEL->config->value( panel_checksum, encoder_b_pin_checksum)->by_default("nc")->as_string())->as_input();
+
+ this->button_pause_pin.from_string(THEKERNEL->config->value( panel_checksum, button_pause_pin_checksum)->by_default("nc")->as_string())->as_input();
+
+ paused= false;
+ button_pause.pin(&this->button_pause_pin)->up_attach( this, &VikiLCD::on_pause_release);
+}
+
+VikiLCD::~VikiLCD() {
+ delete this->i2c;
+}
+
+
+void VikiLCD::init(){
+ // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
+ // according to datasheet, we need at least 40ms after power rises above 2.7V
+ // before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50
+
+ char data[2];
+
+ // Setup
+ this->displayfunction = LCD_4BITMODE | LCD_2LINE | LCD_5x8DOTS;
+ this->backlightval = LCD_NOBACKLIGHT;
+
+ wait_ms(50);
+
+ // now set up input/output pins in MCP23017
+ data[0]= MCP23017_IODIRA;
+ data[1]= 0x1F; // buttons input, all others output
+ i2c->write(this->i2c_address, data, 2);
+
+
+ // set the button pullups
+ data[0]= MCP23017_GPPUA;
+ data[1]= 0x1F;
+ i2c->write(this->i2c_address, data, 2);
+
+ data[0]= MCP23017_IODIRB;
+ data[1]= 0x00; // all pins output
+ i2c->write(this->i2c_address, data, 2);
+
+ // turn leds off
+ setBacklight(0);
+
+ //put the LCD into 4 bit mode
+ // start with a non-standard command to make it realize we're speaking 4-bit here
+ // per LCD datasheet, first command is a single 4-bit burst, 0011.
+ //-----
+ // we cannot assume that the LCD panel is powered at the same time as
+ // the arduino, so we have to perform a software reset as per page 45
+ // of the HD44780 datasheet - (kch)
+ //-----
+
+ // bit pattern for the burstBits function is
+ //
+ // B7 B6 B5 B4 B3 B2 B1 B0 A7 A6 A5 A4 A3 A2 A1 A0 - MCP23017
+ // 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+ // RS RW EN D4 D5 D6 D7 B G R B4 B3 B2 B1 B0
+ for (uint8_t i=0;i < 3;i++) {
+ burstBits8b((M17_BIT_EN|M17_BIT_D5|M17_BIT_D4) >> 8);
+ burstBits8b((M17_BIT_D5|M17_BIT_D4) >> 8);
+ }
+ burstBits8b((M17_BIT_EN|M17_BIT_D5) >> 8);
+ burstBits8b(M17_BIT_D5 >> 8);
+
+
+ wait_ms(5); // this shouldn't be necessary, but sometimes 16MHz is stupid-fast.
+
+ command(LCD_FUNCTIONSET | displayfunction); // then send 0010NF00 (N=lines, F=font)
+ wait_ms(5); // for safe keeping...
+ command(LCD_FUNCTIONSET | displayfunction); // ... twice.
+ wait_ms(5); // done!
+
+ // turn on the LCD with our defaults. since these libs seem to use personal preference, I like a cursor.
+ displaycontrol = (LCD_DISPLAYON|LCD_BACKLIGHT);
+ display();
+ // clear it off
+ clear();
+
+ displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
+ // set the entry mode
+ command(LCD_ENTRYMODESET | displaymode);
+}
+
+// we use this to burst bits to the GPIO chip whenever we need to. avoids repetitive code.
+void VikiLCD::burstBits8b(uint8_t value) {
+ char data[2];
+ data[0] = MCP23017_GPIOB;
+ data[1]= value;
+ i2c->write(this->i2c_address, data, 2);
+}
+
+// value byte order is BA
+void VikiLCD::burstBits16(uint16_t value) {
+ char data[3];
+ data[0] = MCP23017_GPIOA;
+ data[1]= value&0xFF;
+ data[2]= value>>8;
+ i2c->write(this->i2c_address, data, 3);
+}
+
+// cycle the buzzer pin at a certain frequency (hz) for a certain duration (ms)
+void VikiLCD::buzz(long duration, uint16_t freq) {
+ char data[2];
+ int currentRegister = 0;
+ // read gpio register
+ data[0] = MCP23017_GPIOA;
+ i2c->write(this->i2c_address, data, 1);
+ i2c->read(this->i2c_address, data, 1); // Read from selected Register
+ currentRegister= data[0];
+
+ duration *=1000; //convert from ms to us
+ long period = 1000000 / freq; // period in us
+ long elapsed_time = 0;
+ while (elapsed_time < duration) {
+ data[0]= MCP23017_GPIOA;
+ data[1]= currentRegister |= M17_BIT_BZ;
+ i2c->write(this->i2c_address, data, 2);
+
+ wait_us(period / 2);
+
+ data[0]= MCP23017_GPIOA;
+ data[1]= currentRegister &= ~M17_BIT_BZ;
+ i2c->write(this->i2c_address, data, 2);
+
+ wait_us(period / 2);
+ elapsed_time += (period);
+ }
+}
+
+uint8_t VikiLCD::readButtons(void) {
+ char data[2];
+ data[0] = MCP23017_GPIOA;
+ i2c->write(this->i2c_address, data, 1);
+ i2c->read(this->i2c_address, data, 1); // Read from selected Register
+
+ // check the button pause
+ button_pause.check_signal();
+
+ return (~data[0]) & ALL_BUTTON_BITS;
+}
+
+int VikiLCD::readEncoderDelta() {
+ static int8_t enc_states[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
+ static uint8_t old_AB = 0;
+ old_AB <<= 2; //remember previous state
+ old_AB |= ( this->encoder_a_pin.get() + ( this->encoder_b_pin.get() * 2 ) ); //add current state
+ return enc_states[(old_AB&0x0f)];
+}
+
+void VikiLCD::clear()
+{
+ command(LCD_CLEARDISPLAY); // clear display, set cursor position to zero
+#ifndef USE_FASTMODE
+ wait_us(2000); // this command takes a long time!
+#endif
+}
+
+void VikiLCD::home()
+{
+ command(LCD_RETURNHOME); // set cursor position to zero
+#ifndef USE_FASTMODE
+ wait_us(2000); // this command takes a long time!
+#endif
+}
+
+void VikiLCD::setCursor(uint8_t col, uint8_t row)
+{
+ int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 };
+ if ( row > _numlines ) row = _numlines - 1; // we count rows starting w/0
+ command(LCD_SETDDRAMADDR | (col + row_offsets[row]));
+}
+
+// Turn the display on/off (quickly)
+void VikiLCD::noDisplay() {
+ displaycontrol &= ~LCD_DISPLAYON;
+ command(LCD_DISPLAYCONTROL | displaycontrol);
+}
+void VikiLCD::display() {
+ displaycontrol |= LCD_DISPLAYON;
+ command(LCD_DISPLAYCONTROL | displaycontrol);
+}
+
+// Turns the underline cursor on/off
+void VikiLCD::noCursor() {
+ displaycontrol &= ~LCD_CURSORON;
+ command(LCD_DISPLAYCONTROL | displaycontrol);
+}
+void VikiLCD::cursor() {
+ displaycontrol |= LCD_CURSORON;
+ command(LCD_DISPLAYCONTROL | displaycontrol);
+}
+
+// Turn on and off the blinking cursor
+void VikiLCD::noBlink() {
+ displaycontrol &= ~LCD_BLINKON;
+ command(LCD_DISPLAYCONTROL | displaycontrol);
+}
+void VikiLCD::blink() {
+ displaycontrol |= LCD_BLINKON;
+ command(LCD_DISPLAYCONTROL | displaycontrol);
+}
+
+// These commands scroll the display without changing the RAM
+void VikiLCD::scrollDisplayLeft(void) {
+ command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
+}
+void VikiLCD::scrollDisplayRight(void) {
+ command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
+}
+
+// This is for text that flows Left to Right
+void VikiLCD::leftToRight(void) {
+ displaymode |= LCD_ENTRYLEFT;
+ command(LCD_ENTRYMODESET | displaymode);
+}
+
+// This is for text that flows Right to Left
+void VikiLCD::rightToLeft(void) {
+ displaymode &= ~LCD_ENTRYLEFT;
+ command(LCD_ENTRYMODESET | displaymode);
+}
+
+// This will 'right justify' text from the cursor
+void VikiLCD::autoscroll(void) {
+ displaymode |= LCD_ENTRYSHIFTINCREMENT;
+ command(LCD_ENTRYMODESET | displaymode);
+}
+
+// This will 'left justify' text from the cursor
+void VikiLCD::noAutoscroll(void) {
+ displaymode &= ~LCD_ENTRYSHIFTINCREMENT;
+ command(LCD_ENTRYMODESET | displaymode);
+}
+
+void VikiLCD::command(uint8_t value) {
+ send(value, 0);
+}
+
+void VikiLCD::write(char value) {
+ send(value, 1);
+}
+
+// Allows to set the backlight, if the LCD backpack is used
+void VikiLCD::setBacklight(uint8_t status) {
+ // LED turns on when bit is cleared
+ _backlightBits = M17_BIT_LB|M17_BIT_LG|M17_BIT_LR; // all off
+ if (status & LED_RED) _backlightBits &= ~M17_BIT_LR; // red on
+ if (status & LED_GREEN) _backlightBits &= ~M17_BIT_LG; // green on
+ if (status & LED_BLUE) _backlightBits &= ~M17_BIT_LB; // blue on
+
+ burstBits16(_backlightBits);
+}
+
+// write either command or data, burst it to the expander over I2C.
+void VikiLCD::send(uint8_t value, uint8_t mode) {
+#ifdef USE_FASTMODE
+ // polls for ready. not sure on I2C this is any faster
+
+ // set Data pins as input
+ char data[2];
+ data[0]= MCP23017_IODIRB;
+ data[1]= 0x1E;
+ i2c->write(this->i2c_address, data, 2);
+ uint8_t b= _backlightBits >> 8;
+ burstBits8b((M17_BIT_RW>>8)|b); // RW hi,RS lo
+ char busy;
+ data[0] = MCP23017_GPIOB;
+ do {
+ burstBits8b(((M17_BIT_RW|M17_BIT_EN)>>8)|b); // EN hi
+ i2c->write(this->i2c_address, data, 1);
+ i2c->read(this->i2c_address, &busy, 1); // Read D7
+ burstBits8b((M17_BIT_RW>>8)|b); // EN lo
+ burstBits8b(((M17_BIT_RW|M17_BIT_EN)>>8)|b); // EN hi
+ burstBits8b((M17_BIT_RW>>8)|b); // EN lo
+ } while ((busy&(M17_BIT_D7>>8)) != 0);
+
+ // reset data bits as output
+ data[0]= MCP23017_IODIRB;
+ data[1]= 0x00;
+ i2c->write(this->i2c_address, data, 2);
+ burstBits8b(b); // RW lo
+
+#else
+// wait_us(320);
+#endif
+
+ // BURST SPEED, OH MY GOD
+ // the (now High Speed!) I/O expander pinout
+ // B7 B6 B5 B4 B3 B2 B1 B0 A7 A6 A5 A4 A3 A2 A1 A0 - MCP23017
+ // 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+ // RS RW EN D4 D5 D6 D7 B G R B4 B3 B2 B1 B0
+
+ // n.b. RW bit stays LOW to write
+ uint8_t buf = _backlightBits >> 8;
+ // send high 4 bits
+ if (value & 0x10) buf |= M17_BIT_D4 >> 8;
+ if (value & 0x20) buf |= M17_BIT_D5 >> 8;
+ if (value & 0x40) buf |= M17_BIT_D6 >> 8;
+ if (value & 0x80) buf |= M17_BIT_D7 >> 8;
+
+ if (mode) buf |= (M17_BIT_RS|M17_BIT_EN) >> 8; // RS+EN
+ else buf |= M17_BIT_EN >> 8; // EN
+
+ burstBits8b(buf);
+
+ // resend w/ EN turned off
+ buf &= ~(M17_BIT_EN >> 8);
+ burstBits8b(buf);
+
+ // send low 4 bits
+ buf = _backlightBits >> 8;
+ // send high 4 bits
+ if (value & 0x01) buf |= M17_BIT_D4 >> 8;
+ if (value & 0x02) buf |= M17_BIT_D5 >> 8;
+ if (value & 0x04) buf |= M17_BIT_D6 >> 8;
+ if (value & 0x08) buf |= M17_BIT_D7 >> 8;
+
+ if (mode) buf |= (M17_BIT_RS|M17_BIT_EN) >> 8; // RS+EN
+ else buf |= M17_BIT_EN >> 8; // EN
+
+ burstBits8b(buf);
+
+ // resend w/ EN turned off
+ buf &= ~(M17_BIT_EN >> 8);
+ burstBits8b(buf);
+}
+
+// We pause the system
+uint32_t VikiLCD::on_pause_release(uint32_t dummy){
+ if(!paused) {
+ THEKERNEL->pauser->take();
+ paused= true;
+ }else{
+ THEKERNEL->pauser->release();
+ paused= false;
+ }
+ return 0;
+}
--- /dev/null
+/*
+ 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/>.
+
+Much of this was copied from LiquidTWI2
+ LiquidTWI2 High Performance i2c LCD driver for MCP23008 & MCP23017
+ hacked by Sam C. Lin / http://www.lincomatic.com
+ from
+ LiquidTWI by Matt Falcon (FalconFour) / http://falconfour.com
+ logic gleaned from Adafruit RGB LCD Shield library
+*/
+
+#ifndef VIKILCD_H
+#define VIKILCD_H
+#include "LcdBase.h"
+#include "libs/Pin.h"
+#include "Button.h"
+
+using namespace std;
+#include <vector>
+#include <string>
+#include <cstdio>
+#include <cstdarg>
+
+// VikiLcd specific settings
+
+// readButtons() will only return these bit values
+#define ALL_BUTTON_BITS (BUTTON_UP|BUTTON_DOWN|BUTTON_LEFT|BUTTON_RIGHT|BUTTON_SELECT)
+
+
+#define MCP23017_ADDRESS 0x20<<1
+
+// registers
+#define MCP23017_IODIRA 0x00
+#define MCP23017_IPOLA 0x02
+#define MCP23017_GPINTENA 0x04
+#define MCP23017_DEFVALA 0x06
+#define MCP23017_INTCONA 0x08
+#define MCP23017_IOCONA 0x0A
+#define MCP23017_GPPUA 0x0C
+#define MCP23017_INTFA 0x0E
+#define MCP23017_INTCAPA 0x10
+#define MCP23017_GPIOA 0x12
+#define MCP23017_OLATA 0x14
+
+
+#define MCP23017_IODIRB 0x01
+#define MCP23017_IPOLB 0x03
+#define MCP23017_GPINTENB 0x05
+#define MCP23017_DEFVALB 0x07
+#define MCP23017_INTCONB 0x09
+#define MCP23017_IOCONB 0x0B
+#define MCP23017_GPPUB 0x0D
+#define MCP23017_INTFB 0x0F
+#define MCP23017_INTCAPB 0x11
+#define MCP23017_GPIOB 0x13
+#define MCP23017_OLATB 0x15
+
+// config settings for Viki LCD
+#define panel_checksum CHECKSUM("panel")
+#define encoder_a_pin_checksum CHECKSUM("encoder_a_pin")
+#define encoder_b_pin_checksum CHECKSUM("encoder_b_pin")
+#define button_pause_pin_checksum CHECKSUM("button_pause_pin")
+
+class VikiLCD : public LcdBase {
+ public:
+ VikiLCD();
+ virtual ~VikiLCD();
+ void home();
+ void clear();
+ void display();
+ void setCursor(uint8_t col, uint8_t row);
+ void init();
+ void write(char value);
+
+ // added viki commands
+ void setBacklight(uint8_t status);
+
+ uint8_t readButtons();
+ int readEncoderDelta();
+ int getEncoderResolution() { return 2; }
+
+ void buzz(long,uint16_t);
+ void noCursor();
+ void cursor();
+ void noBlink();
+ void blink();
+ void scrollDisplayLeft();
+ void scrollDisplayRight();
+ void leftToRight();
+ void rightToLeft();
+ void autoscroll();
+ void noAutoscroll();
+ void noDisplay();
+
+ private:
+ void send(uint8_t, uint8_t);
+ void command(uint8_t value);
+
+ void burstBits16(uint16_t);
+ void burstBits8b(uint8_t);
+ char displaymode;
+ char displayfunction;
+ char displaycontrol;
+ char i2c_address;
+ char backlightval;
+ uint8_t _numlines,_currline;
+ uint16_t _backlightBits; // only for MCP23017
+ mbed::I2C* i2c;
+
+ Pin encoder_a_pin;
+ Pin encoder_b_pin;
+ Pin button_pause_pin;
+ Button button_pause;
+ uint32_t on_pause_release(uint32_t dummy);
+ bool paused;
+};
+
+
+#endif
--- /dev/null
+/*
+ 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/>.
+*/
+
+#include "libs/Kernel.h"
+#include "libs/SerialMessage.h"
+#include "Panel.h"
+#include "PanelScreen.h"
+#include "MainMenuScreen.h"
+#include "ControlScreen.h"
+#include "libs/nuts_bolts.h"
+#include "libs/utils.h"
+#include <string>
+#include "modules/robot/RobotPublicAccess.h"
+using namespace std;
+
+#define JOGGING_SPEED_MM_MIN 1200
+
+ControlScreen::ControlScreen(){
+ this->control_mode = NULL_CONTROL_MODE;
+}
+
+void ControlScreen::on_enter(){
+ this->panel->enter_menu_mode();
+ this->panel->setup_menu(4, 4);
+ get_current_pos(this->pos);
+ this->refresh_menu();
+ this->pos_changed= false;
+}
+
+// called in on_idle()
+void ControlScreen::on_refresh(){
+ if( this->panel->menu_change() ){
+ this->refresh_menu();
+ }
+
+ if(this->control_mode == AXIS_CONTROL_MODE) {
+
+ if( this->panel->click() ){
+ this->enter_menu_control();
+ this->refresh_menu();
+
+ }else if(this->panel->control_value_change()) {
+ this->pos[this->controlled_axis-'X'] = this->panel->get_control_value();
+ this->panel->lcd->setCursor(0,2);
+ this->display_axis_line(this->controlled_axis);
+ this->pos_changed= true; // make the gcode in main_loop
+ }
+
+ }else{
+ if( this->panel->click() ){
+ this->clicked_menu_entry(this->panel->menu_current_line());
+ }
+ }
+}
+
+// queuing gcodes needs to be done from main loop
+void ControlScreen::on_main_loop() {
+ // change actual axis value
+ if(!this->pos_changed) return;
+ this->pos_changed= false;
+
+ set_current_pos(this->controlled_axis, this->pos[this->controlled_axis-'X']);
+}
+
+void ControlScreen::display_menu_line(uint16_t line){
+ // in menu mode
+ switch( line ){
+ case 0: this->panel->lcd->printf("Back"); break;
+ case 1: this->display_axis_line('X'); break;
+ case 2: this->display_axis_line('Y'); break;
+ case 3: this->display_axis_line('Z'); break;
+ }
+}
+
+void ControlScreen::display_axis_line(char axis){
+ this->panel->lcd->printf("Move %c %8.3f", axis, this->pos[axis-'X']);
+}
+
+
+void ControlScreen::clicked_menu_entry(uint16_t line){
+ switch( line ){
+ case 0: this->panel->enter_screen(this->parent ); break;
+ case 1: this->enter_axis_control('X'); break;
+ case 2: this->enter_axis_control('Y'); break;
+ case 3: this->enter_axis_control('Z'); break;
+ }
+}
+
+void ControlScreen::enter_axis_control(char axis){
+ this->control_mode = AXIS_CONTROL_MODE;
+ this->controlled_axis = axis;
+ this->panel->enter_control_mode(this->jog_increment, this->jog_increment/10);
+ this->panel->set_control_value(this->pos[axis-'X']);
+ this->panel->lcd->clear();
+ this->panel->lcd->setCursor(0,2);
+ this->display_axis_line(this->controlled_axis);
+}
+
+void ControlScreen::enter_menu_control(){
+ this->control_mode = NULL_CONTROL_MODE;
+ this->panel->enter_menu_mode();
+}
+
+
+void ControlScreen::get_current_pos(double *cp){
+ void *returned_data;
+
+ bool ok= THEKERNEL->public_data->get_value( robot_checksum, current_position_checksum, &returned_data );
+ if(ok) {
+ double *p= static_cast<double *>(returned_data);
+ cp[0]= p[0];
+ cp[1]= p[1];
+ cp[2]= p[2];
+ }
+}
+void ControlScreen::set_current_pos(char axis, double p){
+ // change pos by issuing a G0 Xnnn
+ char buf[32];
+ int n= snprintf(buf, sizeof(buf), "G0 %c%f F%d", axis, p, (int)round(panel->get_jogging_speed(axis)));
+ string g(buf, n);
+ send_gcode(g);
+}
+
+void ControlScreen::send_gcode(std::string g) {
+ Gcode gcode(g, &(StreamOutput::NullStream));
+ THEKERNEL->call_event(ON_GCODE_RECEIVED, &gcode );
+}
+
--- /dev/null
+/*
+ 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/>.
+*/
+
+#ifndef CONTROLSCREEN_H
+#define CONTROLSCREEN_H
+
+#include "libs/Kernel.h"
+#include "libs/nuts_bolts.h"
+#include "libs/utils.h"
+#include "libs/Pin.h"
+#include "LcdBase.h"
+#include "Panel.h"
+#include "PanelScreen.h"
+#include "MainMenuScreen.h"
+
+#define NULL_CONTROL_MODE 0
+#define AXIS_CONTROL_MODE 1
+#define INCREMENT_SELECTION_MODE 2
+
+class ControlScreen : public PanelScreen {
+ public:
+ ControlScreen();
+ void on_main_loop();
+ void on_refresh();
+ void on_enter();
+ void display_menu_line(uint16_t line);
+ void set_jog_increment(double i) { jog_increment= i; }
+
+ private:
+ void clicked_menu_entry(uint16_t line);
+ void display_axis_line(char axis);
+ void send_gcode(std::string msg);
+ void enter_axis_control(char axis);
+ void enter_menu_control();
+ void get_current_pos(double *p);
+ void set_current_pos(char axis, double p);
+ char control_mode;
+ char controlled_axis;
+ double pos[3];
+ bool pos_changed;
+ double jog_increment;
+};
+
+
+
+
+
+
+#endif
--- /dev/null
+/*
+ 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/>.
+*/
+
+#include "libs/Kernel.h"
+#include "Panel.h"
+#include "PanelScreen.h"
+#include "MainMenuScreen.h"
+#include "FileScreen.h"
+#include "libs/nuts_bolts.h"
+#include "libs/utils.h"
+#include <string>
+#include "libs/SerialMessage.h"
+
+#include "DirHandle.h"
+#include "mri.h"
+
+using namespace std;
+
+FileScreen::FileScreen(){
+ this->current_folder = "";
+ this->start_play= false;
+}
+
+// When entering this screen
+void FileScreen::on_enter(){
+ this->panel->lcd->clear();
+
+ // Default folder to enter
+ if( this->current_folder.compare("") == 0 ){
+ this->enter_folder("/");
+ }else{
+ this->enter_folder(this->current_folder);
+ }
+}
+
+// For every ( potential ) refresh of the screen
+void FileScreen::on_refresh(){
+ if( this->panel->menu_change() ){
+ this->panel->lcd->clear();
+ this->refresh_menu();
+ }
+ if( this->panel->click() ){
+ this->clicked_line(this->panel->menu_current_line());
+ }
+}
+
+// Enter a new folder
+void FileScreen::enter_folder(std::string folder){
+
+ // Rembember where we are
+ this->current_folder = folder;
+
+ // We need the number of lines to setup the menu
+ uint16_t number_of_files_in_folder = this->count_folder_content(this->current_folder);
+
+ // Setup menu
+ this->panel->setup_menu(number_of_files_in_folder+1, 4); // same number of files as menu items, 4 lines
+ this->panel->enter_menu_mode();
+
+ // Display menu
+ this->panel->lcd->clear();
+ this->refresh_menu();
+
+}
+
+// Called by the panel when refreshing the menu, display .. then all files in the current dir
+void FileScreen::display_menu_line(uint16_t line){
+ if( line == 0 ){
+ this->panel->lcd->printf("..");
+ }else{
+ this->panel->lcd->printf("%s", (this->file_at(line-1)).c_str() );
+ }
+}
+
+// When a line is clicked in the menu, act
+void FileScreen::clicked_line(uint16_t line){
+ if( line == 0 ){
+ if( this->current_folder.compare("/") == 0 ){
+ // Exit file navigation
+ this->panel->enter_screen(this->parent);
+ }else{
+ // Go up one folder
+ this->current_folder = this->current_folder.substr(0,this->current_folder.find_last_of('/')+1);
+ if( this->current_folder[this->current_folder.length()-1] == '/' && this->current_folder.length() != 1 ){ this->current_folder.erase(this->current_folder.length()-1,1); }
+ this->enter_folder(this->current_folder);
+ }
+ }else{
+ //printf("enter file\r\n");
+ // Enter file
+ string path = this->current_folder;
+ if( path.compare("/") == 0 ){ path = ""; }
+ path = path + "/" + this->file_at( line-1 );
+ if( this->is_a_folder( path ) ){
+ this->enter_folder(path);
+ return;
+ }
+
+ // start printing that file...
+ this->play_path= path;
+ this->start_play= true;
+ }
+
+}
+
+// Check wether a line is a folder or a file
+bool FileScreen::is_a_folder( string path ){
+ // In the special case of /local/ ( the mbed flash chip ) we don't have sub-folders, everything is a file
+ if( path.substr(0,7).compare("/local/") == 0 ){
+ return false;
+ }
+ // Else, check if it's a folder or not
+ DIR *d;
+ d = opendir(path.c_str());
+ if(d == NULL) {
+ return false;
+ }else{
+ closedir(d);
+ return true;
+ }
+}
+
+// Find the "line"th file in the current folder
+string FileScreen::file_at(uint16_t line){
+ DIR* d;
+ struct dirent* p;
+ uint16_t count = 0;
+ d = opendir(this->current_folder.c_str());
+ if(d != NULL) {
+ while((p = readdir(d)) != NULL) {
+ if( count == line ){
+ string to_return = lc(string(p->d_name));
+ //printf("line: %u string:%s\r\n", line, to_return.c_str());
+ if( to_return[to_return.length()-1] == '.' ){ to_return[to_return.length()-1] = 0x00; }
+ closedir(d);
+ return to_return;
+ }
+ count++;
+ }
+ }
+
+ if(d != NULL) closedir(d);
+ return "";
+}
+
+// Count how many files there are in the current folder
+uint16_t FileScreen::count_folder_content(std::string folder){
+ DIR* d;
+ struct dirent* p;
+ uint16_t count = 0;
+ d = opendir(folder.c_str());
+ if(d != NULL) {
+ while((p = readdir(d)) != NULL) {
+ count++;
+ }
+ closedir(d);
+ return count;
+ } else {
+ return 0;
+ }
+}
+void FileScreen::on_main_loop(){
+ if(this->start_play){
+ this->start_play= false;
+ play(this->play_path);
+ panel->set_playing_file(this->play_path);
+ this->panel->enter_screen(this->parent);
+ return;
+ }
+}
+
+void FileScreen::play(string path) {
+ struct SerialMessage message;
+ message.message = string("play ") + path + " -q";
+ message.stream = &(StreamOutput::NullStream);
+ THEKERNEL->call_event(ON_CONSOLE_LINE_RECEIVED, &message );
+}
--- /dev/null
+/*
+ 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/>.
+*/
+
+#ifndef FILESCREEN_H
+#define FILESCREEN_H
+
+#include "libs/Kernel.h"
+#include "libs/nuts_bolts.h"
+#include "libs/utils.h"
+#include "libs/Pin.h"
+#include "LcdBase.h"
+#include "Panel.h"
+#include "PanelScreen.h"
+
+#include <string>
+using namespace std;
+
+class FileScreen : public PanelScreen {
+ public:
+ FileScreen();
+ void on_enter();
+ void on_refresh();
+ void on_main_loop();
+ void enter_folder(std::string folder);
+ uint16_t count_folder_content(std::string folder);
+ void clicked_line(uint16_t line);
+ void display_menu_line(uint16_t line);
+ bool is_a_folder( string path );
+ string file_at(uint16_t line);
+
+ std::string current_folder;
+
+ private:
+ void play(string path);
+ bool start_play;
+ string play_path;
+};
+
+
+
+
+
+
+#endif
--- /dev/null
+/*
+ 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/>.
+*/
+
+#include "libs/Kernel.h"
+#include "libs/SerialMessage.h"
+#include "Panel.h"
+#include "PanelScreen.h"
+#include "MainMenuScreen.h"
+#include "JogScreen.h"
+#include "ControlScreen.h"
+#include "libs/nuts_bolts.h"
+#include "libs/utils.h"
+#include <string>
+
+using namespace std;
+
+
+JogScreen::JogScreen(){
+ this->control_screen = new ControlScreen();
+ this->control_screen->set_parent(this);
+}
+
+void JogScreen::on_enter(){
+ this->panel->enter_menu_mode();
+ this->panel->setup_menu(4, 4); // 6 menu items, 4 lines
+ this->refresh_screen();
+}
+
+void JogScreen::on_refresh(){
+ if( this->panel->menu_change() ){
+ this->refresh_screen();
+ }
+ if( this->panel->click() ){
+ this->clicked_menu_entry(this->panel->menu_current_line());
+ }
+}
+
+void JogScreen::refresh_screen(){
+ this->refresh_menu();
+}
+
+void JogScreen::display_menu_line(uint16_t line){
+ switch( line ){
+ case 0: this->panel->lcd->printf("Back"); break;
+ case 1: this->panel->lcd->printf("Move 10.0mm \x7E"); break;
+ case 2: this->panel->lcd->printf("Move 1.0mm \x7E"); break;
+ case 3: this->panel->lcd->printf("Move 0.1mm \x7E"); break;
+ }
+}
+
+void JogScreen::clicked_menu_entry(uint16_t line){
+ switch( line ){
+ case 0: this->panel->enter_screen(this->parent); return;
+ case 1: this->control_screen->set_jog_increment(10.0); break;
+ case 2: this->control_screen->set_jog_increment(1.0); break;
+ case 3: this->control_screen->set_jog_increment(0.1); break;
+ }
+ this->panel->enter_screen(this->control_screen);
+}
--- /dev/null
+/*
+ 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/>.
+*/
+
+#ifndef JOGSCREEN_H
+#define JOGSCREEN_H
+
+#include "libs/Kernel.h"
+#include "libs/nuts_bolts.h"
+#include "libs/utils.h"
+#include "libs/Pin.h"
+#include "LcdBase.h"
+#include "Panel.h"
+#include "PanelScreen.h"
+#include "ControlScreen.h"
+
+
+class JogScreen : public PanelScreen {
+ public:
+ JogScreen();
+ void on_refresh();
+ void on_enter();
+ void refresh_screen();
+ void display_menu_line(uint16_t line);
+ void clicked_menu_entry(uint16_t line);
+
+ private:
+ ControlScreen *control_screen;
+};
+
+
+
+
+
+
+#endif
--- /dev/null
+/*
+ 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/>.
+*/
+
+#include "libs/Kernel.h"
+#include "Panel.h"
+#include "PanelScreen.h"
+#include "MainMenuScreen.h"
+#include "WatchScreen.h"
+#include "FileScreen.h"
+#include "ControlScreen.h"
+#include "PrepareScreen.h"
+#include "libs/nuts_bolts.h"
+#include "libs/utils.h"
+#include "modules/utils/player/PlayerPublicAccess.h"
+
+
+#include <string>
+using namespace std;
+
+MainMenuScreen::MainMenuScreen(){
+ // Children screens
+ this->jog_screen = (new JogScreen() )->set_parent(this);
+ this->watch_screen = (new WatchScreen() )->set_parent(this);
+ this->file_screen = (new FileScreen() )->set_parent(this);
+ this->prepare_screen = (new PrepareScreen() )->set_parent(this);
+ this->set_parent(this->watch_screen);
+}
+
+void MainMenuScreen::on_enter(){
+ this->panel->enter_menu_mode();
+ this->panel->setup_menu(6, 4); // 6 menu items, 4 lines
+ this->refresh_screen();
+}
+
+void MainMenuScreen::on_refresh(){
+ if( this->panel->menu_change() ){
+ this->refresh_screen();
+ }
+ if( this->panel->click() ){
+ this->clicked_menu_entry(this->panel->menu_current_line());
+ }
+
+
+}
+
+void MainMenuScreen::refresh_screen(){
+ this->refresh_menu();
+}
+
+void MainMenuScreen::display_menu_line(uint16_t line){
+ switch( line ){
+ case 0: this->panel->lcd->printf("Watch"); break;
+ case 1: this->panel->lcd->printf(panel->is_playing() ? "Abort" : "Play"); break;
+ case 2: this->panel->lcd->printf("Jog"); break;
+ case 3: this->panel->lcd->printf("Prepare"); break;
+ case 4: this->panel->lcd->printf("Configure"); break;
+ case 5: this->panel->lcd->printf("Tune"); break;
+ }
+
+}
+
+void MainMenuScreen::clicked_menu_entry(uint16_t line){
+ switch( line ){
+ case 0: this->panel->enter_screen(this->watch_screen ); break;
+ case 1: if(this->panel->is_playing())
+ abort_playing();
+ else
+ this->panel->enter_screen(this->file_screen);
+ break;
+ case 2: this->panel->enter_screen(this->jog_screen ); break;
+ case 3: this->panel->enter_screen(this->prepare_screen ); break;
+ }
+}
+
+
+void MainMenuScreen::abort_playing() {
+ THEKERNEL->public_data->set_value(player_checksum, abort_play_checksum, NULL);
+ this->panel->enter_screen(this->watch_screen);
+}
+
--- /dev/null
+/*
+ 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/>.
+*/
+
+#ifndef MAINMENUSCREEN_H
+#define MAINMENUSCREEN_H
+
+#include "libs/Kernel.h"
+#include "libs/nuts_bolts.h"
+#include "libs/utils.h"
+#include "libs/Pin.h"
+#include "LcdBase.h"
+#include "Panel.h"
+#include "PanelScreen.h"
+#include "WatchScreen.h"
+#include "JogScreen.h"
+
+class MainMenuScreen : public PanelScreen {
+ public:
+ MainMenuScreen();
+ void on_refresh();
+ void on_enter();
+ void refresh_screen();
+ void display_menu_line(uint16_t line);
+ void clicked_menu_entry(uint16_t line);
+
+ PanelScreen* watch_screen;
+ PanelScreen* file_screen;
+ PanelScreen* jog_screen;
+ PanelScreen* prepare_screen;
+
+ private:
+ void abort_playing();
+
+};
+
+
+
+
+
+
+#endif
--- /dev/null
+/*
+ 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/>.
+*/
+
+#include "libs/Kernel.h"
+#include "Panel.h"
+#include "PanelScreen.h"
+#include "PrepareScreen.h"
+#include "libs/nuts_bolts.h"
+#include "libs/utils.h"
+#include <string>
+#include "libs/SerialMessage.h"
+using namespace std;
+
+PrepareScreen::PrepareScreen(){
+ // Children screens
+// this->extruder_screen = (new ExtruderScreen() )->set_parent(this);
+// this->temp_screen = (new TempScreen() )->set_parent(this);
+}
+
+void PrepareScreen::on_enter(){
+ this->panel->enter_menu_mode();
+ this->panel->setup_menu(7, 4); // 7 menu items, 4 lines
+ this->refresh_screen();
+}
+
+void PrepareScreen::on_refresh(){
+ if( this->panel->menu_change() ){
+ this->refresh_screen();
+ }
+ if( this->panel->click() ){
+ this->clicked_menu_entry(this->panel->menu_current_line());
+ }
+}
+
+void PrepareScreen::refresh_screen(){
+ this->refresh_menu();
+}
+
+void PrepareScreen::display_menu_line(uint16_t line){
+ switch( line ){
+ case 0: this->panel->lcd->printf("Back" ); break;
+ case 1: this->panel->lcd->printf("Home All Axis" ); break;
+ case 2: this->panel->lcd->printf("Set Home" ); break;
+ case 3: this->panel->lcd->printf("Pre Heat" ); break;
+ case 4: this->panel->lcd->printf("Cool Down" ); break;
+ case 5: this->panel->lcd->printf("Extrude" ); break;
+ case 6: this->panel->lcd->printf("Set Temperature"); break;
+ }
+}
+
+void PrepareScreen::clicked_menu_entry(uint16_t line){
+ switch( line ){
+ case 0: this->panel->enter_screen(this->parent ); break;
+ case 1: send_gcode("G28"); break;
+ case 2: send_gcode("G92 X0 Y0 Z0"); break;
+ case 3: this->preheat(); break;
+ case 4: this->cooldown(); break;
+ case 5: this->panel->enter_screen(this->extruder_screen ); break;
+ case 6: this->panel->enter_screen(this->temp_screen ); break;
+ }
+
+
+}
+
+
+void PrepareScreen::preheat() {
+}
+
+void PrepareScreen::cooldown() {
+}
+
+void PrepareScreen::send_gcode(const char* gcstr) {
+ string gcode(gcstr);
+ struct SerialMessage message;
+ message.message = gcode;
+ message.stream = &(StreamOutput::NullStream);
+ THEKERNEL->call_event(ON_CONSOLE_LINE_RECEIVED, &message );
+}
--- /dev/null
+/*
+ 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/>.
+*/
+
+#ifndef PREPARESCREEN_H
+#define PREPARESCREEN_H
+
+#include "libs/Kernel.h"
+#include "libs/nuts_bolts.h"
+#include "libs/utils.h"
+#include "libs/Pin.h"
+#include "LcdBase.h"
+#include "Panel.h"
+#include "PanelScreen.h"
+
+class PrepareScreen : public PanelScreen {
+ public:
+ PrepareScreen();
+
+ void on_refresh();
+ void on_enter();
+ void refresh_screen();
+ void display_menu_line(uint16_t line);
+ void clicked_menu_entry(uint16_t line);
+
+ private:
+ PanelScreen* extruder_screen;
+ PanelScreen* temp_screen;
+ void preheat();
+ void cooldown();
+ void send_gcode(const char* gcstr);
+};
+
+#endif
--- /dev/null
+/*
+ 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/>.
+*/
+
+#include "libs/Kernel.h"
+#include "Panel.h"
+#include "PanelScreen.h"
+#include "MainMenuScreen.h"
+#include "WatchScreen.h"
+#include "libs/nuts_bolts.h"
+#include "libs/utils.h"
+#include "modules/tools/temperaturecontrol/TemperatureControlPublicAccess.h"
+#include "modules/robot/RobotPublicAccess.h"
+#include "modules/utils/player/PlayerPublicAccess.h"
+
+#include <string>
+using namespace std;
+
+WatchScreen::WatchScreen(){}
+
+void WatchScreen::on_enter(){
+ this->panel->lcd->clear();
+ this->panel->setup_menu(4, 4);
+ get_temp_data();
+ get_current_pos(this->pos);
+ get_sd_play_info();
+ this->current_speed= get_current_speed();
+ this->refresh_screen(false);
+ this->panel->enter_control_mode(1,0.5);
+ this->panel->set_control_value(this->current_speed);
+}
+
+void WatchScreen::on_refresh(){
+ // Exit if the button is clicked
+ if( this->panel->click() ){
+ this->panel->enter_screen(this->parent);
+ return;
+ }
+
+ // see if speed is being changed
+ if(this->panel->control_value_change()) {
+ this->current_speed= this->panel->get_control_value();
+ // change actual speed
+ set_current_speed();
+ this->refresh_screen(false);
+ }
+
+ // Update Only every 20 refreshes, 1 a second
+ static int update_counts = 0;
+ update_counts++;
+ if( update_counts % 20 == 0 ){
+ get_sd_play_info();
+ get_current_pos(this->pos);
+ get_temp_data();
+ this->current_speed= get_current_speed();
+ this->refresh_screen(false);
+ }
+}
+
+// fetch the data we are displaying
+void WatchScreen::get_temp_data() {
+ void *returned_data;
+ bool ok;
+
+ ok= THEKERNEL->public_data->get_value( temperature_control_checksum, bed_checksum, current_temperature_checksum, &returned_data );
+ if(ok) {
+ struct pad_temperature temp= *static_cast<struct pad_temperature*>(returned_data);
+ this->bedtemp= round(temp.current_temperature);
+ this->bedtarget= round(temp.target_temperature);
+ //this->bedpwm= temp.pwm;
+ }
+
+ ok= THEKERNEL->public_data->get_value( temperature_control_checksum, hotend_checksum, current_temperature_checksum, &returned_data );
+ if(ok) {
+ struct pad_temperature temp= *static_cast<struct pad_temperature*>(returned_data);
+ this->hotendtemp= round(temp.current_temperature);
+ this->hotendtarget= round(temp.target_temperature);
+ //this->hotendpwm= temp.pwm;
+ }
+}
+
+// fetch the data we are displaying
+double WatchScreen::get_current_speed() {
+ void *returned_data;
+
+ bool ok= THEKERNEL->public_data->get_value( robot_checksum, speed_override_percent_checksum, &returned_data );
+ if(ok) {
+ double cs= *static_cast<double *>(returned_data);
+ return cs;
+ }
+ return 0.0;
+}
+
+void WatchScreen::set_current_speed() {
+ bool ok= THEKERNEL->public_data->set_value( robot_checksum, speed_override_percent_checksum, &this->current_speed );
+ if(!ok) this->current_speed= 0;
+
+}
+
+void WatchScreen::get_current_pos(double *cp){
+ void *returned_data;
+
+ bool ok= THEKERNEL->public_data->get_value( robot_checksum, current_position_checksum, &returned_data );
+ if(ok) {
+ double *p= static_cast<double *>(returned_data);
+ cp[0]= p[0];
+ cp[1]= p[1];
+ cp[2]= p[2];
+ }
+}
+
+void WatchScreen::get_sd_play_info(){
+ void *returned_data;
+ bool ok= THEKERNEL->public_data->get_value( player_checksum, get_progress_checksum, &returned_data );
+ if(ok) {
+ struct pad_progress p= *static_cast<struct pad_progress*>(returned_data);
+ this->elapsed_time= p.elapsed_secs;
+ this->sd_pcnt_played= p.percent_complete;
+ }else{
+ this->elapsed_time= 0;
+ this->sd_pcnt_played= 0;
+ }
+}
+
+void WatchScreen::display_menu_line(uint16_t line){
+ // in menu mode
+ switch( line ){
+ case 0: this->panel->lcd->printf("H%03d/%03dc B%03d/%03dc", this->hotendtemp, this->hotendtarget, this->bedtemp, this->bedtarget); break;
+ case 1: this->panel->lcd->printf("X%4d Y%4d Z%7.2f", (int)round(this->pos[0]), (int)round(this->pos[1]), this->pos[2]); break;
+ case 2: this->panel->lcd->printf("%3d%% %2d:%02d %3d%% sd", (int)round(this->current_speed), this->elapsed_time/60, this->elapsed_time%60, this->sd_pcnt_played); break;
+ case 3: this->panel->lcd->printf("%19s", panel->is_playing() ? panel->get_playing_file().substr(4, 19).c_str() : "Smoothie ready"); break;
+ }
+
+}
--- /dev/null
+/*
+ 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/>.
+*/
+
+#ifndef WATCHSCREEN_H
+#define WATCHSCREEN_H
+
+#include "libs/Kernel.h"
+#include "libs/nuts_bolts.h"
+#include "libs/utils.h"
+#include "libs/Pin.h"
+#include "LcdBase.h"
+#include "Panel.h"
+#include "PanelScreen.h"
+
+class WatchScreen : public PanelScreen {
+ public:
+ WatchScreen();
+ void on_refresh();
+ void on_enter();
+ void display_menu_line(uint16_t line);
+
+ private:
+ void get_temp_data();
+ double get_current_speed();
+ void set_current_speed();
+ void get_current_pos(double *cp);
+ void get_sd_play_info();
+
+ int hotendtemp;
+ int hotendtarget;
+ int bedtemp;
+ int bedtarget;
+ double current_speed;
+ double pos[3];
+ int elapsed_time;
+ int sd_pcnt_played;
+};
+
+
+
+
+
+
+#endif
#include "libs/StreamOutput.h"
#include "modules/robot/Conveyor.h"
#include "DirHandle.h"
+#include "PublicDataRequest.h"
+#include "PlayerPublicAccess.h"
void Player::on_module_loaded(){
this->playing_file = false;
this->register_for_event(ON_CONSOLE_LINE_RECEIVED);
this->register_for_event(ON_MAIN_LOOP);
this->register_for_event(ON_SECOND_TICK);
+ this->register_for_event(ON_GET_PUBLIC_DATA);
+ this->register_for_event(ON_SET_PUBLIC_DATA);
this->on_boot_gcode = this->kernel->config->value(on_boot_gcode_checksum)->by_default("/sd/on_boot.gcode -q")->as_string();
this->on_boot_gcode_enable = this->kernel->config->value(on_boot_gcode_enable_checksum)->by_default(true)->as_bool();
}
}
+void Player::on_get_public_data(void* argument) {
+ PublicDataRequest* pdr = static_cast<PublicDataRequest*>(argument);
+
+ if(!pdr->starts_with(player_checksum)) return;
+
+ if(pdr->second_element_is(is_playing_checksum)) {
+ static bool bool_data;
+ bool_data= this->playing_file;
+ pdr->set_data_ptr(&bool_data);
+ pdr->set_taken();
+
+ }else if(pdr->second_element_is(get_progress_checksum)) {
+ static struct pad_progress p;
+ if(file_size > 0 && playing_file) {
+ p.elapsed_secs= this->elapsed_secs;
+ p.percent_complete= (this->file_size - (this->file_size - this->played_cnt)) * 100 / this->file_size;
+ pdr->set_data_ptr(&p);
+ pdr->set_taken();
+ }
+ }
+}
+
+void Player::on_set_public_data(void* argument) {
+ PublicDataRequest* pdr = static_cast<PublicDataRequest*>(argument);
+
+ if(!pdr->starts_with(player_checksum)) return;
+
+ if(pdr->second_element_is(abort_play_checksum)) {
+ if(playing_file) {
+ playing_file = false;
+ played_cnt= 0;
+ file_size= 0;
+ fclose(current_file_handler);
+ pdr->set_taken();
+ }
+ }
+}
+
+
+
+
+
/*
void Player::on_main_loop(void* argument){
if( !this->on_booted ){
void on_console_line_received( void* argument );
void on_main_loop( void* argument );
void on_second_tick(void* argument);
+ void on_get_public_data(void* argument);
+ void on_set_public_data(void* argument);
+
string absolute_from_relative( string path );
void cd_command( string parameters, StreamOutput* stream );
void play_command( string parameters, StreamOutput* stream );
--- /dev/null
+#ifndef PLAYERPUBLICACCESS_H
+#define PLAYERPUBLICACCESS_H
+
+#define player_checksum CHECKSUM("player")
+#define is_playing_checksum CHECKSUM("is_playing")
+#define abort_play_checksum CHECKSUM("abort_play")
+#define get_progress_checksum CHECKSUM("progress")
+
+struct pad_progress {
+ int percent_complete;
+ int elapsed_secs;
+};
+#endif
\ No newline at end of file