Arduino GRBL Companion - Автономный контроллер для GRBL

Контроллеры, драйверы, датчики, управляющие устройства.

Arduino GRBL Companion - Автономный контроллер для GRBL

Сообщение tecnocat » 16 мар 2016, 10:19

Собрал давеча автономный контроллер на базе Arduino Pro Mini для отправки данных с флешки на другую Arduino с GRBL прошивкой.
Управление сделал через энкодер, экран цветной 128*160 точек TFT_ILI9163C с кардридером для SD карт.
Делюсь тем что получилось.
Пока все на проводах:
Arduino GRBL Companion - Автономный контроллер для GRBL IMG_20160316_113237.jpg

Слева на право: Arduino Pro Mini с моей прошивкой, Arduino Pro Mini с прошивкой GRBL, Энкодер, экран с SD картой.

Вид на экран сзади:
Arduino GRBL Companion - Автономный контроллер для GRBL IMG_20160316_130007.jpg


После загрузки:
Arduino GRBL Companion - Автономный контроллер для GRBL IMG_20160316_113127.jpg

Главное меню по кнопке энкодера:
Arduino GRBL Companion - Автономный контроллер для GRBL IMG_20160316_113310.jpg

Меню ручного перемещения(MANUAL):
Arduino GRBL Companion - Автономный контроллер для GRBL IMG_20160316_125942.jpg

Меню выбора файла(FILES):
Arduino GRBL Companion - Автономный контроллер для GRBL IMG_20160316_113343.jpg

Экран исполнения выбранного файла:
Arduino GRBL Companion - Автономный контроллер для GRBL IMG_20160316_113352.jpg

Экран настроек(SETUP):
Arduino GRBL Companion - Автономный контроллер для GRBL IMG_20160316_113626.jpg


Немного видео исполнения файла(простите за вертикальное видео):


Пока все что успел накидать.

Еще по контроллеру - прошивка несмотря на мои утрамбовывания занимает 28 950 байт из 30 720 байт доступных - до бишь почти под завязку.
(Обшибся - на работе была старая версия: тот что ниже занимает 30 626 байт (99%) памяти устройства. Всего доступно 30 720 байт. )
Что есть в прошивке :
1. До вызова меню прошивка работает как обычный RS232 терминал и читает все что приходит на порт.
2. Пункт меню MANUAL - ручное перемещение по осям - работает так - выбираете энкодером ось - и крутите его вправо + влево - , чем дальше от центра шкалы тем больше подача,
при выходе за максимум подача сбрасывается до нуля для защиты, при нажатии на энкодер подача сбрасывается и вы возвращаетесь в выбор оси.
3. Пункт меню FILES - на экран выводятся все файлы которые есть на флешке без разбивки на каталоги. Энкодером выбираете нужный файл и попадаете в его выполнение - изначально выполнение на паузе.
тут 4 пункта - выполнение >, пошаговое выполнение |>, пауза || и стоп(выход). При выполнении автоматически контролируется максимальная подача (задается в настройках).
4. Пункт меню UNLOCK - разблокировка GRBL путем посылки $X.
5. Пункт меню SETUP - настройки контроллера: максимальная подача, автозапуск файла, остановка по ошибке, авторазблокировка.
6. Пункт меню RESTART GRBL - сброс GRBL путем подачи сигнала на ногу reset ведомого контроллера.
7. Пункт меню EXIT - возврат в режим терминала.

Исходники прошивки:

Код: Выделить всёРазвернуть
#include <avr/pgmspace.h>
#include <EEPROMex.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#define __160_RED_PCB__
#include <TFT_ILI9163C.h>
#include <SD.h>

// Color definitions
#define   BLACK   0x0000
#define   BLUE    0xF800
#define   RED     0x001F
#define   GREEN   0x07E0
#define CYAN    0xFFE0
#define MAGENTA 0xF81F
#define YELLOW  0x07FF
#define WHITE   0xFFFF
#define _cp437 false

#define __CS 6
#define __RST 7
#define __DC 8 //a0
#define __LED 9
#define MAX_DISPLAY_LINES 15
#define MAX_DISPLAY_FILES 14

#define __SDCS 10
#define MENU_DELAY 150
#define GRBL_RES_PIN 5

#define CHAR_HEIGHT 8
#define CHAR_WIDTH 6

#define CHAR_STOP   0x80 // 128 stop
#define CHAR_PLAY   0x81 // 129 play
#define CHAR_PAUSE   0x82 // 130 pause
#define CHAR_ARIGHT   0x83 // 131 ->
#define CHAR_ALEFT   0x84 // 132 <-
#define CHAR_AUP   0x85 // 133 up
#define CHAR_ADOWN   0x86 // 134 down
#define CHAR_MARK   0x87 // 135 mark
#define CHAR_STEP   0x88 // 136 step
#define CHAR_DRILL   0x89 // 137 drill
#define CHAR_ZLEFT   0x8A // 138 zero left
#define CHAR_ZRIGHT   0x8B // 139 zero right
#define CHAR_SLE   0x8C // 140 scale left empty
#define CHAR_SRE   0x8D // 141 scale right empty
#define CHAR_SCE   0x8E // 142 scale center empty
#define CHAR_SLF   0x8F // 143 scale left full
#define CHAR_SRF   0x90 // 144 scale right full
#define CHAR_SCF   0x91 // 145 scale center full
#define CHAR_EPLAY   0x92 // 146 play empty

#define EXEC_PAUSE  0x0 // sleep
#define EXEC_PLAY   0x1 // send commands without pause
#define EXEC_STEP   0x2 // execute step by step
#define EXEC_EXIT   0x20 // exit from execute

//#define CONFIG_MAX 30 //

#define EEPROM_FEED      0
#define EEPROM_START   8
#define EEPROM_PAUSE  9
#define EEPROM_UNLOCK   10

#define DEFAULT_FEED 1000
#define FEED_MAX 1250


TFT_ILI9163C tft = TFT_ILI9163C(__CS, __DC, __RST);

File root;

bool MENU_KEY, OK_KEY, UP_KEY, DOWN_KEY, LOOP_TEMP = false;
int8_t filecount, pp_menu, skip_page, skip, exec_mode, readcnt;
long current_line;
String fname;
char buff[80];
//String buff
long feed_dig = 0;
long feed_safe = 0;



const char acc0[] PROGMEM = "0 F0\0";
const char acc1[] PROGMEM = "0.01 F100\0";
const char acc2[] PROGMEM = "0.02 F100\0";
const char acc3[] PROGMEM = "0.03 F100\0";
const char acc4[] PROGMEM = "0.04 F100\0";
const char acc5[] PROGMEM = "0.05 F200\0";
const char acc6[] PROGMEM = "0.06 F200\0";
const char acc7[] PROGMEM = "0.07 F200\0";
const char acc8[] PROGMEM = "0.08 F200\0";
const char acc9[] PROGMEM = "0.09 F200\0";
const char acc10[] PROGMEM = "0.1 F400\0";
const char acc11[] PROGMEM = "0.2 F400\0";
const char acc12[] PROGMEM = "0.3 F400\0";
const char acc13[] PROGMEM = "0.4 F400\0";
const char acc14[] PROGMEM = "0.5 F400\0";
const char acc15[] PROGMEM = "1 F600\0";
const char acc16[] PROGMEM = "2 F600\0";
const char acc17[] PROGMEM = "3 F600\0";
const char acc18[] PROGMEM = "4 F600\0";
const char acc19[] PROGMEM = "5 F600\0";
const char acc20[] PROGMEM = "0 F1\0";
const char * const acc_table[] PROGMEM = {acc0, acc1, acc2, acc3, acc4, acc5, acc6, acc7, acc8, acc9, acc10, acc11, acc12, acc13, acc14, acc15, acc16, acc17, acc18, acc19, acc20 };

const char empty_line[] PROGMEM = "                          \n\0";
const char trail[] PROGMEM = " \0";
const char zero[] PROGMEM = " 0 \0";


const char init_error[] PROGMEM = "Init SD failed!\0";


const char text_manual[] PROGMEM = " MANUAL\0";
const char text_files[] PROGMEM = " FILES\0";
const char text_unlock[] PROGMEM = " UNLOCK\0";
const char text_setup[] PROGMEM = " SETUP\0";
const char text_reset[] PROGMEM = " RESET\0";
const char text_exit[] PROGMEM = " EXIT\0";
const char text_home[] PROGMEM = " HOME\0";

const char text_feedmax[] PROGMEM = " FEED Max(mm/min):\0";
const char text_autostart[] PROGMEM = " AUTO START:\0";
const char text_pauseerr[] PROGMEM = " PAUSE ON ERROR:\0";
const char text_autounlk[] PROGMEM = " AUTO UNLOCK:\0";

const char text_done[] PROGMEM = "DONE\0";
const char text_next[] PROGMEM = " >>>\0";
const char text_line[] PROGMEM = "Line:\0";
const char text_stepsize[] PROGMEM = " STEP SIZE:\0";

const char cmd_unlock[] PROGMEM = "$X\n\0";
//const char cmd_position[] PROGMEM = "?\n\0";
//const char cmd_cyclestart[] PROGMEM = "~\n\0";
//const char cmd_feedhold[] PROGMEM = "!\n\0";
const char cmd_reset[] PROGMEM = {0x18, '\n', '\0'};
const char cmd_upz[] PROGMEM = "G0Z10F500\0";
const char cmd_zeroxy[] PROGMEM = "G1X0Y0F500\0";
const char cmd_absolute[] PROGMEM = "G90\0";
const char cmd_incremental[] PROGMEM = "G91\0";
const char cmd_seek[] PROGMEM = "G0\0";
const char cmd_linear[] PROGMEM = "G1\0";


/*
   const char cfg0[] PROGMEM = "$0=\0";
   const char cfg1[] PROGMEM = "$1=\0";
   const char cfg2[] PROGMEM = "$2=\0";
   const char cfg3[] PROGMEM = "$3=\0";
   const char cfg4[] PROGMEM = "$4=\0";
   const char cfg5[] PROGMEM = "$5=\0";
   const char cfg6[] PROGMEM = "$6=\0";
   const char cfg7[] PROGMEM = "$10=\0";
   const char cfg8[] PROGMEM = "$11=\0";
   const char cfg9[] PROGMEM = "$12=\0";
   const char cfg10[] PROGMEM = "$13=\0";
   const char cfg11[] PROGMEM = "$20=\0";
   const char cfg12[] PROGMEM = "$21=\0";
   const char cfg13[] PROGMEM = "$22=\0";
   const char cfg14[] PROGMEM = "$23=\0";
   const char cfg15[] PROGMEM = "$24=\0";
   const char cfg16[] PROGMEM = "$25=\0";
   const char cfg17[] PROGMEM = "$26=\0";
   const char cfg18[] PROGMEM = "$27=\0";
   const char cfg19[] PROGMEM = "$100=\0";
   const char cfg20[] PROGMEM = "$101=\0";
   const char cfg21[] PROGMEM = "$102=\0";
   const char cfg22[] PROGMEM = "$110=\0";
   const char cfg23[] PROGMEM = "$111=\0";
   const char cfg24[] PROGMEM = "$112=\0";
   const char cfg25[] PROGMEM = "$120=\0";
   const char cfg26[] PROGMEM = "$121=\0";
   const char cfg27[] PROGMEM = "$122=\0";
   const char cfg28[] PROGMEM = "$130=\0";
   const char cfg29[] PROGMEM = "$131=\0";
   const char cfg30[] PROGMEM = "$132=\0";
   const char * const cfg_table[] PROGMEM = {cfg0, cfg1, cfg2, cfg3, cfg4, cfg5, cfg6, cfg7, cfg8, cfg9, cfg10, cfg11, cfg12, cfg13, cfg14, cfg15, cfg16, cfg17, cfg18, cfg19, cfg20, cfg21, cfg22, cfg23, cfg24, cfg25, cfg26, cfg27, cfg28, cfg29, cfg30};
*/

const char axis0[] PROGMEM = " X\0";
const char axis1[] PROGMEM = " Y\0";
const char axis2[] PROGMEM = " Z\0";
const char axis3[] PROGMEM = " F\0";
const char * const axis_label[] PROGMEM = {axis0, axis1, axis2, axis3};

/*
   //joystick defines
   #define joyPinX 1                 // X pin
   #define joyPinY 0                 // Y pin
   #define joyPinZ A3                 // Z pin
   int xmin = 000;                  // X minimum value
   int xmax = 1024;                  // X maximum value
   int ymin = 000;                  // Y minimum value
   int ymax = 1024;                  // Y maximum value
   int xzero = 0;                   // the first X value (don't touch the joystick at startup!)
   int yzero = 0;                   // the first Y value
   int segs = 10;                   // number of intervalls to consider par half direction
   double zerohold = 4;             // number of intervalls to consider as "zero position"
   int x = 0;
   int y = 0;
   static boolean joyClick = false;
*/

//encoder defines
#define encoder0PinA  2
#define encoder0PinB  3
#define encoder0PinC  4
int8_t encoder0Pos = 1;  // a counter for the dial
int8_t lastReportedPos = 1;   // change management
static boolean rotating = false;    // debounce management

// interrupt service routine vars
boolean A_set = false;
boolean B_set = false;
static boolean encClick = false;

//assistatnt procedures

void printSerialString(char *string) { //print to serial from progmem
  while (pgm_read_byte(string) != '\0') {
    Serial.write(pgm_read_byte_near(string));
    string++;
  }
}
void printTFTString(char *string) { //print to lcd from progmem
  while (pgm_read_byte(string) != '\0') {
    tft.write(pgm_read_byte_near(string));
    string++;
  }
}

void str_print(char s, byte size) //print to lcd cursor for menu choice
{
  unsigned char x;
  tft.setTextSize(size);
  for (x = 0; x <= MAX_DISPLAY_LINES; x++) {
    tft.setCursor(0, x * (size * CHAR_HEIGHT));
    tft.write(32);
  };
  tft.setCursor(0, s * (size * CHAR_HEIGHT)); //MAX_DISPLAY_LINES
  tft.setTextColor(YELLOW, BLACK);
  tft.write(CHAR_PLAY);
  tft.setTextColor(WHITE, BLACK);
  tft.setTextSize(1);
}

void gauge(int8_t x, int8_t y, int8_t width, int8_t pos, int8_t scale) { //print gauge to display
  tft.setCursor(x, y);
  pos = pos / scale;
  for (int8_t wdt = 0; wdt <= width; wdt++) {
    int8_t center = (width / 2);
    int8_t rpos = pos + center;
    if (pos < (0 - center)) {
      pos = 0 - center;
    };
    if (pos > center) {
      pos = center;
    };
    if (rpos < 0) {
      rpos = 0;
    }
    if (rpos > width) {
      rpos = width;
    }
    if ((wdt == 0) && (rpos > 0)) {
      tft.write(CHAR_SLE);
    } else if ((wdt == 0) && (rpos == 0)) {
      tft.write(CHAR_SLF);
    } else if ((wdt == width) && (rpos < width)) {
      tft.write(CHAR_SRE);
    } else if ((wdt == width) && (rpos == width)) {
      tft.write(CHAR_SRF);
    } else if (wdt == center) {
      tft.write(CHAR_SCF);
    } else {
      if (pos > 0) {
        if (wdt < center) {
          tft.write(CHAR_SCE);
        } else if ((wdt > center) && (wdt <= rpos)) {
          tft.write(CHAR_SCF);
        } else if (wdt > rpos) {
          tft.write(CHAR_SCE);
        };
      } else if (pos < 0) {
        if (wdt > center) {
          tft.write(CHAR_SCE);
        } else if ((wdt < center) && (wdt >= rpos)) {
          tft.write(CHAR_SCF);
        } else if (wdt < rpos) {
          tft.write(CHAR_SCE);
        };
      } else tft.write(CHAR_SCE);
    }
  }
}

//menu procedures
void main_screen(void) //
{
  tft.setTextSize(2);
  tft.setCursor(0, 0);
  tft.setTextColor(WHITE, BLACK);
  printTFTString((char*)text_manual);
  tft.println();
  printTFTString((char*)text_files);
  tft.println();
  printTFTString((char*)text_unlock);
  tft.println();
  printTFTString((char*)text_setup);
  tft.println();
  printTFTString((char*)text_reset);
  tft.println();
  printTFTString((char*)text_exit);
  tft.setTextSize(1);
}


void main_menu(void) //
{
  tft.clearScreen();
  Serial.flush();
  MENU_KEY = false;
  main_screen();
  pp_menu = 0;
  lastReportedPos = encoder0Pos = 0;
  str_print(pp_menu, 2);
  do {
    read_keys();
    if (UP_KEY) {
      if (pp_menu > 0) {
        pp_menu--;
      } else {
        pp_menu = 5;
      }
      str_print(pp_menu, 2);
    };
    if (DOWN_KEY) {
      if (pp_menu < 5) {
        pp_menu++;
      } else {
        pp_menu = 0;
      }
      str_print(pp_menu, 2);
    };
    if (OK_KEY) {
      if (pp_menu == 0) manual_menu();
      if (pp_menu == 1) files_menu();
      if (pp_menu == 2) {
        current_line = 0;
        printSerialString((char*)cmd_unlock);
        break;
      }
      if (pp_menu == 3) {
        setup_menu();
      }
      if (pp_menu == 4) {
        //tft.clearScreen();
        current_line = 0;
        digitalWrite(GRBL_RES_PIN, HIGH);
        delay(200);
        digitalWrite(GRBL_RES_PIN, LOW);
        break;
      }
      if (pp_menu == 5) {
        Serial.flush();
        break;
      }
    };
    delay(MENU_DELAY);
  } while (!MENU_KEY);
  tft.clearScreen();
  MENU_KEY = false;
  current_line = 0;
};


void setup_screen(void) {
  tft.setTextSize(1);
  tft.setCursor(0, 0);
  tft.setTextColor(WHITE, BLACK);
  printTFTString((char*)text_feedmax);
  tft.println(EEPROM.readInt(EEPROM_FEED));
  printTFTString((char*)text_autostart);
  tft.println(EEPROM.read(EEPROM_START)); 
  printTFTString((char*)text_pauseerr);
  tft.println(EEPROM.read(EEPROM_PAUSE));
  printTFTString((char*)text_autounlk);
  tft.println(EEPROM.read(EEPROM_UNLOCK));
  printTFTString((char*)text_exit);
  tft.setTextSize(1);
}

/*
#define CHAR_HEIGHT 8
#define CHAR_WIDTH 6
#define EEPROM_FEED      0
#define EEPROM_START   8
#define EEPROM_UNLOCK   9
*/

void setup_param(int8_t param) {
  unsigned int temp, min, max = 0;
  tft.setTextColor(GREEN, BLACK);
  if (param == 0) {
    temp = EEPROM.readInt(EEPROM_FEED);
    min = 0; max = FEED_MAX;
  } else if (param == 1) {
    temp = EEPROM.read(EEPROM_START);
    min = 0; max = 1;
  } else if (param == 2) {
    temp = EEPROM.read(EEPROM_PAUSE);
    min = 0; max = 1;
  } else if (param == 3) {
    temp = EEPROM.read(EEPROM_UNLOCK);
    min = 0; max = 1;
  }
  do {
    if (param == 0) {
      tft.setCursor(18 * CHAR_WIDTH, 0 * CHAR_HEIGHT);
    } else if (param == 1) {
      tft.setCursor(12 * CHAR_WIDTH, 1 * CHAR_HEIGHT);
    } else if (param == 2) {
      tft.setCursor(16 * CHAR_WIDTH, 2 * CHAR_HEIGHT);
    } else if (param == 3) {
      tft.setCursor(13 * CHAR_WIDTH, 3 * CHAR_HEIGHT);
    }
    tft.print(temp);
    printTFTString((char*)trail);
    printTFTString((char*)trail);
    tft.println();
    if (param == 0) {
      if (encoder0Pos > 4)encoder0Pos = 4;
      if (encoder0Pos < -4)encoder0Pos = -4;
      if ((encoder0Pos > 1) || (encoder0Pos < 1)) {
        temp += encoder0Pos * 5;
      } else {
        temp += encoder0Pos;
      }
    } else if (param == 1) {
      if (encoder0Pos > 1)encoder0Pos = 1;
      if (encoder0Pos < 0)encoder0Pos = 0;
      temp = encoder0Pos;
    } else if (param == 2) {
      if (encoder0Pos > 1)encoder0Pos = 1;
      if (encoder0Pos < 0)encoder0Pos = 0;
      temp = encoder0Pos;
    } else if (param == 3) {
      if (encoder0Pos > 1)encoder0Pos = 1;
      if (encoder0Pos < 0)encoder0Pos = 0;
      temp = encoder0Pos;
    }
    delay(MENU_DELAY * 2);
    encRead(); if (encClick) break;
  } while (true);
  if (param == 0) {
    EEPROM.updateInt(EEPROM_FEED, temp);
  } else if (param == 1) {
    EEPROM.update(EEPROM_START, (byte)temp);
  } else if (param == 2) {
    EEPROM.update(EEPROM_PAUSE, (byte)temp);
  } else if (param == 3) {
    EEPROM.update(EEPROM_UNLOCK, (byte)temp);
  }
}


void setup_menu(void) {
  tft.clearScreen();
  Serial.flush();
  setup_screen();
  pp_menu = 0;
  lastReportedPos = encoder0Pos = 0;
  str_print(pp_menu, 1);
  do {
    read_keys();
    if (UP_KEY) {
      if (pp_menu > 0) {
        pp_menu--;
      } else {
        pp_menu = 4;
      }
      str_print(pp_menu, 1);
    };
    if (DOWN_KEY) {
      if (pp_menu < 4) {
        pp_menu++;
      } else {
        pp_menu = 0;
      }
      str_print(pp_menu, 1);
    };
    if (OK_KEY) {
      if (pp_menu == 0) setup_param(0);
      if (pp_menu == 1) setup_param(1);
      if (pp_menu == 2) setup_param(2);
      if (pp_menu == 3) setup_param(3);
      if (pp_menu == 4) {
        break;
      }
      setup_screen();
      str_print(pp_menu, 1);
    };
    delay(MENU_DELAY);
  } while (true);
  tft.clearScreen();
  main_screen();
  pp_menu = 0;
  str_print(pp_menu, 2);
}


void manual_screen() {
  tft.setCursor(0, 0 * CHAR_HEIGHT);
  tft.setTextColor(RED, BLACK);
  printTFTString((char*)pgm_read_word(&(axis_label[0])));
  gauge(12, 0 * CHAR_HEIGHT, 20, 0, 2);
  printTFTString((char*)zero);

  tft.setCursor(0, 1 * CHAR_HEIGHT);
  tft.setTextColor(GREEN, BLACK);
  printTFTString((char*)pgm_read_word(&(axis_label[1])));
  gauge(12, 1 * CHAR_HEIGHT, 20, 0, 2);
  printTFTString((char*)zero);

  tft.setCursor(0, 2 * CHAR_HEIGHT);
  tft.setTextColor(BLUE, BLACK);
  printTFTString((char*)pgm_read_word(&(axis_label[2])));
  gauge(12, 2 * CHAR_HEIGHT, 20, 0, 2);
  printTFTString((char*)zero);

  tft.setCursor(0, 3 * CHAR_HEIGHT);
  tft.setTextColor(WHITE, BLACK);
  printTFTString((char*)text_home);

  tft.setCursor(0, 4 * CHAR_HEIGHT);
  tft.setTextColor(WHITE, BLACK);
  printTFTString((char*)text_reset);
 
  tft.setCursor(0, 5 * CHAR_HEIGHT);
  tft.setTextColor(WHITE, BLACK);
  printTFTString((char*)text_exit);
}


void go_home(void){
  printSerialString((char*)cmd_absolute); 
  Serial.write(0x0A); 
  printSerialString((char*)cmd_upz);
  Serial.write(0x0A);
  printSerialString((char*)cmd_zeroxy);
  Serial.write(0x0A);
}

void move_menu(char axis) {
  printSerialString((char*)cmd_incremental);
  Serial.write(0x0A);
  do {
    tft.setCursor(0, axis * CHAR_HEIGHT);
    if (axis == 0) {
      tft.setTextColor(RED, BLACK);
    } else if (axis == 1) {
      tft.setTextColor(GREEN, BLACK);
    } else if (axis == 2) {
      tft.setTextColor(BLUE, BLACK);
    }
    printTFTString((char*)pgm_read_word(&(axis_label[axis])));
    if (encoder0Pos > 20)encoder0Pos = 20;
    if (encoder0Pos < -20)encoder0Pos = -20;
    if (lastReportedPos > 20)lastReportedPos = 20;
    if (lastReportedPos < -20)lastReportedPos = -20;
    gauge(12, axis * CHAR_HEIGHT, 20, lastReportedPos, 2);
    tft.setTextColor(YELLOW, BLACK);
    printTFTString((char*)trail);
    tft.print(abs(lastReportedPos));
    printTFTString((char*)trail);
    tft.setCursor(10, 50);
    printTFTString((char*)text_stepsize);
 
    if (lastReportedPos != 0) {
      printSerialString((char*)cmd_linear);
      printSerialString((char*)pgm_read_word(&(axis_label[axis])));
    }
    if (lastReportedPos < 0) {
      tft.write(0x2D);
      if (lastReportedPos != 0) Serial.write(0x2D);
    }
    if (lastReportedPos != 0) {
      char pos = abs(lastReportedPos);
      printSerialString((char*)pgm_read_word(&(acc_table[pos])));
      Serial.write(0x0A);
      printTFTString((char*)pgm_read_word(&(acc_table[pos])));
    }

    printTFTString((char*)empty_line);
    if (lastReportedPos != 0) {
      while (true) {
        if (Serial.available()) break;
        encRead(); if (encClick) break;
      }
      if (Serial.available()) {
        memset(buff, 0, sizeof(buff));
        readcnt = Serial.readBytesUntil(0x0A, buff, sizeof(buff));
        if (readcnt > 0) {
          for (int8_t pe = 0; pe < sizeof(buff); pe++) {
            if ((buff[pe] == 0x0D) || (buff[pe] == 0x0A)) {
              buff[pe] = 0x00;
              break;
            }
          }
          encRead(); if (encClick) break;
          //if(Serial.peek()==0x0A) Serial.read();
          //buff[readcnt] = 0;
          //tft.setTextColor(YELLOW, BLACK);
          /*if (strncmp(buff, "ok", 2)) {
             tft.setTextColor(GREEN, BLACK);
             tft.print(buff);
          }*/
          if (strncmp(buff, "error", 5) == 0) {
            tft.setCursor(0, 60);
            printTFTString((char*)empty_line);
            tft.setCursor(0, 60);
            tft.setTextColor(RED, BLACK);
            tft.print(buff);
          }
        }
      }
    }
    //delay(MENU_DELAY);
    encRead(); if (encClick) break;
  } while (true);
  encClick = false;
  encoder0Pos = lastReportedPos = 0;
  delay(MENU_DELAY);
}

void manual_menu(void) {
  tft.clearScreen();
  Serial.flush();
  manual_screen();
  pp_menu = 0;
  lastReportedPos = encoder0Pos = 0;
  str_print(pp_menu, 1);
  do {
    read_keys();
    if (UP_KEY) {
      if (pp_menu > 0) {
        pp_menu--;
      } else {
        pp_menu = 5;
      }
      str_print(pp_menu, 1);
    };
    if (DOWN_KEY) {
      if (pp_menu < 5) {
        pp_menu++;
      } else {
        pp_menu = 0;
      }
      str_print(pp_menu, 1);
    };
    if (OK_KEY) {
      if (pp_menu == 0) {
        move_menu(0);
        delay(MENU_DELAY);
      }
      if (pp_menu == 1) {
        move_menu(1);
        delay(MENU_DELAY);
      }
      if (pp_menu == 2) {
        move_menu(2);
        delay(MENU_DELAY);
      }
      if (pp_menu == 3) {
        go_home();
        delay(MENU_DELAY);
      }
      if (pp_menu == 4) {
        printSerialString((char*)cmd_reset);
        Serial.write(0x0A);
        delay(MENU_DELAY);
      }
      if (pp_menu == 5) break;
      manual_screen();
      str_print(pp_menu, 1);
    };
    delay(MENU_DELAY);
  } while (true);
  tft.clearScreen();
  main_screen();
  pp_menu = 0;
  str_print(pp_menu, 2);
}


void exec_screen(void) {
  tft.setTextSize(1);
  tft.setCursor(0, 0);
  tft.setTextColor(GREEN, BLACK);
  printTFTString((char*)text_line);
 
  tft.setCursor(30, 0);
  tft.println(current_line);
  tft.setTextColor(CYAN, BLACK);
  tft.setCursor(70, 0 * CHAR_HEIGHT);
  tft.write(0x46);
  /*tft.setCursor(70, 0 * CHAR_HEIGHT);
  tft.setTextColor(CYAN, BLACK);
  tft.write(0x58);
  tft.setCursor(110, 0 * CHAR_HEIGHT);
  tft.setTextColor(MAGENTA, BLACK);
  tft.write(0x59);
  tft.setCursor(70, 1 * CHAR_HEIGHT);
  tft.setTextColor(YELLOW, BLACK);
  tft.write(0x5A);
  tft.setCursor(110, 1 * CHAR_HEIGHT);
  tft.setTextColor(WHITE, BLACK);
  tft.write(0x46);*/
  tft.setCursor(0, 0);
}


void buttons_screen(void) {
  tft.setTextSize(1);
  tft.setCursor(30, 0);
  tft.setTextColor(GREEN, BLACK);
  tft.println(current_line);
  tft.setTextSize(2);
  tft.setTextColor(GREEN, BLACK);
  /*play button*/
  tft.setCursor(0, 1 * CHAR_HEIGHT);//MAX_DISPLAY_LINES
  if (exec_mode == EXEC_PAUSE) tft.setTextColor(BLUE, BLACK);
  if (exec_mode == EXEC_PLAY) tft.setTextColor(RED, BLACK);
  if (exec_mode == EXEC_STEP) tft.setTextColor(BLUE, BLACK);
  if ((pp_menu == 0) && (skip_page > 2)) tft.setTextColor(YELLOW, BLACK);
  tft.write(CHAR_PLAY);
  /*step button*/
  tft.setCursor(CHAR_WIDTH * 2, 1 * CHAR_HEIGHT);//MAX_DISPLAY_LINES
  if (exec_mode == EXEC_PAUSE) tft.setTextColor(BLUE, BLACK);
  if (exec_mode == EXEC_PLAY) tft.setTextColor(BLUE, BLACK);
  if (exec_mode == EXEC_STEP) tft.setTextColor(RED, BLACK);
  if ((pp_menu == 1) && (skip_page > 2)) tft.setTextColor(YELLOW, BLACK);
  tft.write(CHAR_STEP);
  /*pause button*/
  tft.setCursor(CHAR_WIDTH * 4, 1 * CHAR_HEIGHT); //MAX_DISPLAY_LINES
  if (exec_mode == EXEC_PAUSE) tft.setTextColor(RED, BLACK); else tft.setTextColor(BLUE, BLACK);
  if (exec_mode == EXEC_PLAY) tft.setTextColor(BLUE, BLACK);
  if (exec_mode == EXEC_STEP) tft.setTextColor(BLUE, BLACK);
  if ((pp_menu == 2) && (skip_page > 2)) tft.setTextColor(YELLOW, BLACK);
  tft.write(CHAR_PAUSE);
  /*stop button*/
  tft.setCursor(CHAR_WIDTH * 6, 1 * CHAR_HEIGHT); //MAX_DISPLAY_LINES
  if (exec_mode == EXEC_PAUSE) tft.setTextColor(BLUE, BLACK);
  if (exec_mode == EXEC_PLAY) tft.setTextColor(BLUE, BLACK);
  if (exec_mode == EXEC_STEP) tft.setTextColor(BLUE, BLACK);
  if ((pp_menu == 3) && (skip_page > 2)) tft.setTextColor(YELLOW, BLACK);
  tft.write(CHAR_STOP);

}
/*
const char cmd_cyclestart[] PROGMEM = "~\0x0A\0";
const char cmd_feedhold[] PROGMEM = "!\0x0A\0";

*/

void process_keys(void) {
  read_keys();
  /*  if (exec_mode == EXEC_PLAY) {
     if (UP_KEY) {
        if (execSpeed > 0) {
     execSpeed = execSpeed - 10;
        } else {
     execSpeed = 100;
        }
     };
     if (DOWN_KEY) {
        if (execSpeed < 100 ) {
     execSpeed = execSpeed + 10;
        } else {
     execSpeed = 0;
        }
     };
  };*/
  if ((exec_mode == EXEC_PLAY) && (OK_KEY == true)) {
    exec_mode = EXEC_PAUSE;
  }
  if ((exec_mode == EXEC_PAUSE) ||  (exec_mode == EXEC_STEP)) {
    skip_page = 0;
    OK_KEY = false;
    //if (current_line > 0) printSerialString((char*)cmd_feedhold);
    do {
      read_keys();
      if (UP_KEY) {
        if (pp_menu > 0) {
          pp_menu--;
        } else {
          pp_menu = 3;
        }
      };
      if (DOWN_KEY) {
        if (pp_menu < 3) {
          pp_menu++;
        } else {
          pp_menu = 0;
        }
      };
      if (OK_KEY) {
        OK_KEY = encClick = false;
        break;
      }
      buttons_screen();
      delay(MENU_DELAY);
      skip_page++;
      if (skip_page > 3)skip_page = 0;
    } while (true);
    if (pp_menu == 0) {
      exec_mode = EXEC_PLAY;
      /*if (current_line > 0) {
        printSerialString((char*)cmd_cyclestart);
        while (Serial.available() > 0) {
          memset(buff, 0, sizeof(buff));
          readcnt = Serial.readBytesUntil(0x0A, buff, sizeof(buff));
          if (readcnt > 0) {
            buff[readcnt] = 0;
            if ((strncmp(buff, "ok", 2) == 0)) break;
          }
        }
      }*/
    }
    if (pp_menu == 1) {
      exec_mode = EXEC_STEP;
      /*if (current_line > 0) {
        /*printSerialString((char*)cmd_cyclestart);
        while (Serial.available() > 0) {
          memset(buff, 0, sizeof(buff));
          readcnt = Serial.readBytesUntil(0x0A, buff, sizeof(buff));
          if (readcnt > 0) {
            buff[readcnt] = 0;
            if ((strncmp(buff, "ok", 2) == 0)) break;
          }
        }
      }*/
    }
    if (pp_menu == 2) exec_mode = EXEC_PAUSE;
    if (pp_menu == 3) {
      //printSerialString((char*)cmd_reset);
      exec_mode = EXEC_EXIT;
    }
    delay(MENU_DELAY);
    OK_KEY = encClick = false;
    buttons_screen();
  }
  update_pos();
}

void update_pos(void) {

  tft.setTextSize(1);
  tft.setCursor(78, 0 * CHAR_HEIGHT);
  tft.setTextColor(WHITE, BLACK);
  tft.print(feed_dig);
  printTFTString((char*)trail);
  /* tft.print(execSpeed);
  tft.print(F("% "));*/
}

void execute_file(String &fname) {
  current_line = skip = skip_page = 0;
  int8_t pos_start = 0;
  int8_t pos_end = 0;
  char feed[10];
  char temp[40];
  feed_dig = feed_safe;

  if (EEPROM.read(EEPROM_START) == 1) {
    exec_mode = EXEC_PLAY;
  } else if (EEPROM.read(EEPROM_START) == 0) {
    exec_mode = EXEC_PAUSE;
  }

  /*Serial.print(F("F"));
     Serial.print(feed_dig);
     Serial.write(0x0A);
     while (Serial.available() > 0) {
     Serial.readBytesUntil(0x0A, buff, sizeof(buff));
     }
  delay(100);*/

  Serial.flush();
  pp_menu = 0;
  exec_screen();
  buttons_screen();
  process_keys();
  File text = SD.open(fname, O_READ);
  if (text) {
    while (text.available()) {
      process_keys();
      if (exec_mode == EXEC_EXIT) {
        text.close();
        exit;
      }
      current_line++;
      memset(buff, 0, sizeof(buff));
      readcnt = text.readBytesUntil(0x0A, buff, sizeof(buff));
      if (readcnt > 0) {
        buff[readcnt] = 0;
        pos_start = 0;
        for (int8_t pe = 0; pe < sizeof(buff); pe++) {
          if ((buff[pe] == 0x46)) {
            pos_start = pe;
            break;
          }
        }
        if (pos_start > 0) {
          for (int8_t pe = pos_start + 1; pe < sizeof(buff); pe++) {
            if ((buff[pe] > 0x39) || (buff[pe] == 0)) {
              pos_end = pe;
              break;
            }
          }
          strncpy(feed, buff + pos_start + 1, pos_end - pos_start);
          feed_dig = strtol(feed, 0, 0);
          if (feed_dig > feed_safe) feed_dig = feed_safe;
          //feed_dig = (feed_dig * execSpeed) / 100;
          ltoa(feed_dig, feed, 10);
          for (int8_t pe = 0; pe < 10; pe++) {
            if (feed[pe] == 0) {
              pos_end = pe - 1;
              break;
            }
          }
          strncpy(buff + pos_start + 1, feed, pos_end);
        }
        for (int8_t pe = 0; pe < sizeof(buff); pe++) {
          /*if ((buff[pe]>0x00)&&((buff[pe]!=0x0D)||(buff[pe]!=0x0A))){
             if (((buff[pe]<0x20)||(buff[pe]>0x5A))) buff[pe]=0x20;
          }*/
          if (buff[pe] == 0x0D) {
            buff[pe] = 0x0A;
            buff[pe + 1] = 0x00;
            break;
          }
          if (buff[pe] == 0x0A) {
            buff[pe + 1] = 0x00;
            break;
          }

        }
        Serial.print(buff);
        Serial.flush();
        memset(temp, 0, sizeof(temp));
        strncpy(temp, buff, sizeof(temp));
        for (int8_t pe = 0; pe < sizeof(temp); pe++) {
          if ((temp[pe] == 0x0D) || (temp[pe] == 0x0A)) {
            temp[pe] = 0x00;
            break;
          }
        }

        if (exec_mode == EXEC_STEP) {
          tft.setTextSize(1);
          tft.setCursor(0, (skip + 3)* CHAR_HEIGHT);
          printTFTString((char*)empty_line); tft.println();
          printTFTString((char*)empty_line); tft.println();
          //tft.println(F("                           "));
          //tft.println(F("                           "));
          /*tft.setCursor(0, (skip + 4)* CHAR_HEIGHT);
          tft.println(F("                           "));*/
          tft.setCursor(0, (skip + 3)* CHAR_HEIGHT);
          tft.setTextColor(YELLOW, BLACK);
          //tft.print(F("-"));
          tft.print(buff);
          skip++;
        }
        Serial.flush();
      }

      while (Serial.available() == 0) {
        encRead(); if ((encClick) && (exec_mode == EXEC_PLAY)) {
          pp_menu = 2;
          exec_mode = EXEC_PAUSE;
          process_keys();
        }
        if (exec_mode == EXEC_EXIT) break;
        //if (Serial.available() > 0) break;
      }
      while (Serial.available() > 0) {
        memset(buff, 0, sizeof(buff));
        readcnt = Serial.readBytesUntil(0x0A, buff, sizeof(buff));
        if (readcnt > 0) {
          buff[readcnt] = 0;
          if (strncmp(buff, "error", 5) == 0) {
            delay(10);
            Serial.flush();
            tft.setTextSize(1);
            tft.setCursor(0, (skip + 3)* CHAR_HEIGHT);
            printTFTString((char*)empty_line); //tft.println();
            printTFTString((char*)empty_line); //tft.println();
            printTFTString((char*)empty_line); //tft.println();
            //tft.println(F("                           "));
            //tft.println(F("                           "));
            tft.setCursor(0, (skip + 3)* CHAR_HEIGHT);
            tft.setTextColor(RED, BLACK);
            tft.print(current_line);
            printTFTString((char*)trail);
            tft.println(temp);
            tft.print(buff);
            memset(temp, 0, sizeof(temp));
            if (EEPROM.read(EEPROM_PAUSE) == 1) exec_mode = EXEC_PAUSE;
            skip++;
            //break;
          }
          /*if (exec_mode == EXEC_STEP) {
            skip++;

          }*/
          if ((strncmp(buff, "ok", 2) == 0)) {
            //tft.setTextColor(GREEN, BLACK);
            //(Serial.available()==0)
            delay(10);
            Serial.flush();
            //break;
          }
        }
        encRead();
        if ((encClick) && (exec_mode == EXEC_PLAY)) {
          pp_menu = 2;
          exec_mode = EXEC_PAUSE;
          OK_KEY = encClick = false;
          process_keys();
        }
        //if (Serial.available()==0)break;
        if (exec_mode == EXEC_EXIT) break;
      }

      /*if (skip_page == 0) {
         Serial.print(F("?"));
         Serial.write(0x0A);
         memset(buff, 0, sizeof(buff));
         while (Serial.available() > 0) {
         readcnt = Serial.readBytesUntil(0x0A, buff, sizeof(buff));
         if (readcnt > 0) {
         buff[readcnt] = 0;
         tft.setCursor(0, 3 * CHAR_HEIGHT);
         tft.println(buff);
         }
         }
      }*/
      //skip++;
      //skip_page++;
      exec_screen();
      if (skip > (MAX_DISPLAY_LINES - 5))skip = 0;
      //if (skip_page > 10)skip_page = 0;
    }
  }
  text.close();
}

boolean init_card(void) {
  if (root) root.close();
  if (!SD.begin(__SDCS)) {
    printTFTString((char*)init_error);
    digitalWrite(__SDCS, LOW);
    delay(MENU_DELAY * 4);
    digitalWrite(__SDCS, HIGH);
    tft.clearScreen();
    return false;
  }
  root = SD.open("/");
  if (!root) {
    printTFTString((char*)init_error);
    delay(MENU_DELAY * 4);
    tft.clearScreen();
    return false;
  }
  root.rewindDirectory();
  tft.clearScreen();
  return true;
}

void printDirectory(File dir, String &file, bool getfile) {
  while (filecount < MAX_DISPLAY_FILES) {
    File entry = dir.openNextFile(O_READ);
    if (!entry) {
      entry.close();
      break;
    }
    if (entry.isDirectory()) {
      printDirectory(entry, file, getfile);
    } else {
      if ((getfile) && (skip == 0)) {
        file = entry.name();
        break;
      }
      if (skip == 0) {
        filecount++;
        printTFTString((char*)trail);
        tft.print(entry.name());
        tft.println();
      };
      if (skip > 0) skip--;
    }
    entry.close();
  }
}

void list(void) {
  skip = skip_page * MAX_DISPLAY_FILES;
  tft.clearScreen();
  tft.setCursor(0, 0);
  filecount = 0;
  root.rewindDirectory();
  printDirectory(root, fname, false);
  tft.setTextColor(GREEN, BLACK);
  if (filecount == MAX_DISPLAY_FILES) {
    printTFTString((char*)text_next);
  } else {
    printTFTString((char*)text_exit);
  }
  tft.setTextColor(WHITE, BLACK);
  pp_menu = 0;
  str_print(pp_menu, 1);
}

void files_menu(void) {
  skip_page = skip = 0;
  if (init_card()) {
    list();
  } else {
    MENU_KEY = true;
  }
  do {
    read_keys();
    if (UP_KEY) {
      if (pp_menu > 0) {
        pp_menu--;
      } else {
        pp_menu = filecount;
      }
      str_print(pp_menu, 1);
    };
    if (DOWN_KEY) {
      if (pp_menu < filecount) {
        pp_menu++;
      } else {
        pp_menu = 0;
      }
      str_print(pp_menu, 1);
    };
    if (OK_KEY) {
      if (pp_menu < filecount) {
        root.rewindDirectory();
        skip = (skip_page * MAX_DISPLAY_FILES) + pp_menu;
        filecount = 0;
        fname = "";
        printDirectory(root, fname, true);
        tft.clearScreen();
        tft.setCursor(0, 0);
        execute_file(fname);
        tft.clearScreen();
        go_home();
        tft.setTextSize(5);
        do {
          encRead();
          tft.setCursor(22, 45);
          tft.setTextColor(GREEN, BLACK);
          printTFTString((char*)text_done);
        } while (!encClick);
        tft.setTextSize(0);
        break;
      }
      if ((filecount == MAX_DISPLAY_FILES) && (pp_menu == filecount)) {
        skip_page++;
      } else {
        break;
      }
      list();
    };
    delay(MENU_DELAY);
  } while (!MENU_KEY);
  tft.clearScreen();
  filecount = skip_page = skip = 0;
  main_screen();
  pp_menu = 1;
  str_print(pp_menu, 2);
};

void rotEncoder() {
  rotating = true;
}


void setup() {
  Serial.begin(115200);
  Serial.setTimeout(1);
  while (!Serial) {
  }
  EEPROM.setMaxAllowedWrites(2048);

  pinMode(__SDCS, OUTPUT);
  digitalWrite(__SDCS, HIGH);

  pinMode(__LED, OUTPUT);
  digitalWrite(__LED, HIGH);

  tft.begin();
  //tft.colorSpace(1);
  tft.setRotation(1);
  tft.fillScreen(BLACK);
  tft.setTextWrap(false);
  tft.setTextColor(WHITE, BLACK);
  tft.setCursor(0, 0);
  /*
     yzero = analogRead(joyPinY);
     xzero = analogRead(joyPinX);
     pinMode(joyPinZ, INPUT_PULLUP);
  */

  pinMode(encoder0PinA, INPUT);
  digitalWrite(encoder0PinA, HIGH);     // подключить подтягивающий резистор
  pinMode(encoder0PinB, INPUT);
  digitalWrite(encoder0PinB, HIGH);    // подключить подтягивающий резистор
  pinMode(encoder0PinC, INPUT_PULLUP);

  attachInterrupt(0, doEncoderA, CHANGE);  // настроить прерывание interrupt 0 РЅР° pin 2
  attachInterrupt(1, doEncoderB, CHANGE);  // настроить прерывание interrupt 0 РЅР° pin 3

  lastReportedPos = encoder0Pos = 0;
  // The internal 1.1V reference provides for better
  // resolution from the LM35, and is also more stable
  // when powered from either a battery or USB...
  //analogReference(INTERNAL);
  pinMode(GRBL_RES_PIN, OUTPUT);
  digitalWrite(GRBL_RES_PIN, HIGH);
  delay(200);
  digitalWrite(GRBL_RES_PIN, LOW);
  //tft.defineScrollArea(8,152);
  current_line = skip = 0;
  tft.setTextColor(GREEN, BLACK);
  feed_safe = EEPROM.readInt(EEPROM_FEED);
  if (feed_safe > FEED_MAX) {
    feed_safe = FEED_MAX;
    EEPROM.updateInt(EEPROM_FEED, FEED_MAX);
  }
}

/*
   void joy_read() {
   joyClick = (digitalRead(joyPinZ) == HIGH ? false : true);
   int value = 0;                   // variable to hold the actual reading
   value = analogRead(joyPinX);
   // push limits
   if (value < xmin) xmin = value;
   else if (value > xmax) xmax = value;
   // start at zero
   x = 0;
   // divide lower half
   int seg = (xzero - xmin) / (segs + zerohold);
   // find what segment we are in
   for (int i = 0; i < segs + 1; i++)
   {
    if (value >= xzero - ((segs - i) + zerohold)*seg) x = i;
   }
   // devide upper part
   seg = (xmax - xzero) / (segs + zerohold);
   // find what segment we are in
   for (int i = segs + 1; i < 2 * segs + 1; i++)
   {
    if (value >= xzero + ((i - segs - 1) + zerohold)*seg) x = i;
   }
   // make X range from [-10,10] instead of [0,20]
   x -= segs;
   // invert X
   x = -x;
   //delay (10);
   // the same as before but with "Y" :-)
   value = analogRead(joyPinY);
   if (value < ymin) ymin = value;
   else if (value > ymax) ymax = value;
   y = 0;
   seg = (yzero - ymin) / (segs + 1);
   for (int i = 0; i < segs + 1; i++)
   {
    if (value >= yzero - ((segs - i) + 1)*seg) y = i;
   }
   seg = (ymax - yzero) / (segs + zerohold);
   for (int i = segs + 1; i < 2 * segs + 1; i++)
   {
    if (value >= yzero + ((i - segs - 1) + zerohold)*seg) y = i;
   }
   y -= segs;

   }
*/

void doEncoderA() {
  // debounce
  if ( rotating ) delay (1);  // wait a little until the bouncing is done
  // Test transition, did things really change?
  if ( digitalRead(encoder0PinA) != A_set ) { // debounce once more
    A_set = !A_set;
    // adjust counter + if A leads B
    if ( A_set && !B_set ) encoder0Pos++;
    //if (encoder0Pos > encoder0Max) encoder0Pos = encoder0Min+1;
    rotating = false;  // no more debouncing until loop() hits again
  }
}
// Interrupt on B changing state
void doEncoderB() {
  if ( rotating ) delay (1);
  if ( digitalRead(encoder0PinB) != B_set ) {
    B_set = !B_set;
    //  adjust counter - 1 if B leads A
    if ( B_set && !A_set ) encoder0Pos --;
    //if (encoder0Pos<=encoder0Min) encoder0Pos = encoder0Max;
    rotating = false;
  }
}

void encRead() {
  encClick = (digitalRead(encoder0PinC) == HIGH ? false : true);
  if (encoder0Pos != lastReportedPos) lastReportedPos = encoder0Pos;
}


void read_keys(void) {
  if (encoder0Pos == 0) {
    UP_KEY = false;
    DOWN_KEY = false;
  }
  encRead();
  OK_KEY = encClick;
  if (encoder0Pos > 0) {
    DOWN_KEY = true;
    encoder0Pos = lastReportedPos = 0;
  } else if (encoder0Pos < 0) {
    UP_KEY = true;
    encoder0Pos = lastReportedPos = 0;
  }
}

void loop() {
  if (Serial.available()) {
    memset(buff, 0, sizeof(buff));
    readcnt = Serial.readBytesUntil(0x0A, buff, sizeof(buff));
    if (readcnt > 0) {
      buff[readcnt] = 0;
      //tft.setCursor(0, (MAX_DISPLAY_LINES+current_line) * CHAR_HEIGHT);
      tft.setTextColor(YELLOW, BLACK);
      tft.setCursor(0, current_line * CHAR_HEIGHT);
      printTFTString((char*)empty_line); tft.println();
      //tft.println(F("                          "));
      tft.setCursor(0, current_line * CHAR_HEIGHT);
      if (strncmp(buff, "ok", 2) == 0) {
        tft.setTextColor(GREEN, BLACK);
      }
      if (strncmp(buff, "error", 5) == 0) {
        tft.setTextColor(RED, BLACK);
      }
      tft.println(buff);
      current_line++;
      if (current_line > MAX_DISPLAY_LINES)current_line = 0;
      Serial.flush();
      if ((strncmp(buff, "['$", 3) == 0) && (EEPROM.read(EEPROM_UNLOCK) == 1)) {
        printSerialString((char*)cmd_unlock);
        delay(10);
      }
    }
  }
  encRead();
  if (encClick) main_menu();
}


Схему нарисовал:
Arduino GRBL Companion - Автономный контроллер для GRBL autocont.GIF
Схема


Чуть позднее будет новая прошивка - добавил пару пунктов

Обновил архив проекта.
Изменения:
В меню MANUAL добавилось два пункта:
HOME - идем "домой" по абсолютным координатам X0 Y0 Z10
RESET - софт сброс GRBL
В главном меню RESET - так и остался железным сбросом ардуины с GRBL
После завершения выполнения программы идем "домой" по абсолютным координатам X0 Y0 Z10



27.02.2017
Обновление проекта - собрал все в кучу
Архив с исходниками и библиотеками:
full_pack_27_02_2017.zip
(3.53 Мб) Скачиваний: 3124


Для изменения ориентации экрана в прошивку ST7735 добавил константу:
Код: Выделить всёРазвернуть
/*-----------------*/
#define DISPLAY_ROT 1 // display rotation - 1 - SD slot down, 3 - SD slot up
/*-----------------*/
Последний раз редактировалось tecnocat 27 фев 2017, 08:00, всего редактировалось 7 раз(а).
tecnocat
Опытный
 
Сообщения: 100
Зарегистрирован: 09 июн 2014, 08:07
Репутация: 39

Re: Arduino GRBL Companion - Автономный контроллер для GRBL

Сообщение tecnocat » 17 мар 2016, 06:48

Самое интересное ни у кого вопросов до сих пор не появилось. :)
tecnocat
Опытный
 
Сообщения: 100
Зарегистрирован: 09 июн 2014, 08:07
Репутация: 39

Re: Arduino GRBL Companion - Автономный контроллер для GRBL

Сообщение selenur » 17 мар 2016, 07:01

tecnocat писал(а):Самое интересное ни у кого вопросов до сих пор не появилось. :)

Так что-бы были вопросы, нужно что-бы кто-то решил повторить твоё решение, а в связи с тем что ты ещё пока только делаешь, а не используешь полноценно, никто и не пытался повторить :-)
Но наверняка уже есть форумчане, которые следят за твоей разработкой.
Мой сайт: http://selenur.ru
Исходники моих программ: https://github.com/selenur
Аватара пользователя
selenur
Почётный участник
Почётный участник
 
Сообщения: 4042
Зарегистрирован: 21 авг 2013, 19:44
Откуда: Новый Уренгой
Репутация: 1438
Медальки:
Настоящее имя: Сергей

Re: Arduino GRBL Companion - Автономный контроллер для GRBL

Сообщение tecnocat » 17 мар 2016, 07:57

selenur писал(а): а в связи с тем что ты ещё пока только делаешь

В принципе устройство уже работает - чуть позже накидаю схему и выложу прошивку, ну и поснимаю видео работы уже со станком.
Так что после технологических испытаний - милости просим. :)
tecnocat
Опытный
 
Сообщения: 100
Зарегистрирован: 09 июн 2014, 08:07
Репутация: 39

Re: Arduino GRBL Companion - Автономный контроллер для GRBL

Сообщение grakun » 17 мар 2016, 08:55

tecnocat писал(а):Самое интересное ни у кого вопросов до сих пор не появилось.

Selenur прав, тема интересная и вопросы будут. Они могут и у Вас появится после технологических испытаний.
Аватара пользователя
grakun
Мастер
 
Сообщения: 273
Зарегистрирован: 30 сен 2015, 02:47
Откуда: г.Саратов
Репутация: 53
Настоящее имя: Гракун

Re: Arduino GRBL Companion - Автономный контроллер для GRBL

Сообщение nevkon » 17 мар 2016, 10:31

Возможно возмусь повторить. Но у меня atmega128a контроллер, наверно на нем буду собирать. И экран сенсорный. Пока еще только ковыряю grbl (мой контроллер на Arduino Nano самодельный с нестандартными портами).
nevkon
Мастер
 
Сообщения: 1546
Зарегистрирован: 17 июл 2015, 10:25
Откуда: Балаково (Саратовская обл.)
Репутация: 195
Настоящее имя: Константин

Re: Arduino GRBL Companion - Автономный контроллер для GRBL

Сообщение tecnocat » 17 мар 2016, 12:21

Поменял шапку - забирайте исходники в копилку :)
tecnocat
Опытный
 
Сообщения: 100
Зарегистрирован: 09 июн 2014, 08:07
Репутация: 39

Re: Arduino GRBL Companion - Автономный контроллер для GRBL

Сообщение 3DPrinter » 20 мар 2016, 23:36

вау.
Сколько такое удовольствие обходится?
Не дешевле бы было использовать какое-то спаренное решение для 3Д принтеров? Там экраны в паре с каритридером недорогие.
https://3DPrinter.ua - купить 3D принтер или заказать 3D печать в Украине.
Аватара пользователя
3DPrinter
Мастер
 
Сообщения: 252
Зарегистрирован: 27 янв 2014, 23:51
Откуда: http://3DPrinter.ua
Репутация: 4
Настоящее имя: Мария

Re: Arduino GRBL Companion - Автономный контроллер для GRBL

Сообщение pup » 21 мар 2016, 00:03

у меня чото такое ощущение, что этот экран с кардридером и энкодером содержит же ардуину. то есть по смыслу что-то похожее (но очевидно с несколько иной реализацией). завтра если не забуду - попробую разглядеть что там на плате..
pup
Мастер
 
Сообщения: 676
Зарегистрирован: 12 фев 2015, 10:12
Репутация: 11

Re: Arduino GRBL Companion - Автономный контроллер для GRBL

Сообщение selenur » 22 мар 2016, 18:13

К стати только что с гитхаба получил уведомление,https://github.com/grbl/grbl/issues/915#issuecomment-199856554 и видео примера:

применяется arduino DUE, это 32-х битный, достаточно мощный контроллер.
Мой сайт: http://selenur.ru
Исходники моих программ: https://github.com/selenur
Аватара пользователя
selenur
Почётный участник
Почётный участник
 
Сообщения: 4042
Зарегистрирован: 21 авг 2013, 19:44
Откуда: Новый Уренгой
Репутация: 1438
Медальки:
Настоящее имя: Сергей

Re: Arduino GRBL Companion - Автономный контроллер для GRBL

Сообщение tecnocat » 24 мар 2016, 06:30

3DPrinter писал(а):вау.
Сколько такое удовольствие обходится?
Не дешевле бы было использовать какое-то спаренное решение для 3Д принтеров? Там экраны в паре с каритридером недорогие.


Ну 1,5$ ардуина, 5$ экран с кардридером, энкодер еще какие то копейки.

pup писал(а):у меня чото такое ощущение, что этот экран с кардридером и энкодером содержит же ардуину. то есть по смыслу что-то похожее (но очевидно с несколько иной реализацией). завтра если не забуду - попробую разглядеть что там на плате..


Этот экран вообще пустой - все рулится из прошивки.
http://www.aliexpress.com/item/1x-TFT-S ... 82382.html
tecnocat
Опытный
 
Сообщения: 100
Зарегистрирован: 09 июн 2014, 08:07
Репутация: 39

Re: Arduino GRBL Companion - Автономный контроллер для GRBL

Сообщение tecnocat » 24 мар 2016, 07:29

Погонял на выходных контроллер - работает нормально.
Выпилил вот такие обложки для блокнота.
Arduino GRBL Companion - Автономный контроллер для GRBL IMG_20160324_093620.jpg

Arduino GRBL Companion - Автономный контроллер для GRBL IMG_20160324_093627.jpg

Arduino GRBL Companion - Автономный контроллер для GRBL IMG_20160324_093643.jpg


Видео к сожалению не снимал, все выпилилось за два часа со сменой инструмента.
tecnocat
Опытный
 
Сообщения: 100
Зарегистрирован: 09 июн 2014, 08:07
Репутация: 39

Re: Arduino GRBL Companion - Автономный контроллер для GRBL

Сообщение tecnocat » 25 мар 2016, 21:32

Обновлен архив с исходниками и сами исходники.
tecnocat
Опытный
 
Сообщения: 100
Зарегистрирован: 09 июн 2014, 08:07
Репутация: 39

Re: Arduino GRBL Companion - Автономный контроллер для GRBL

Сообщение yurayerz » 01 апр 2016, 19:55

Извиняюсь, что влезаю в чужой топик :roll: со своим рылом, но тема автономного контроллера для grbl мне близка и понятна. Не могу не поделиться! :cheesy:
Смахнул пыль с проекта годовой давности - автономный "посылатель" г-кода для grbl. Слеплен на ардуине УНО, LCD2004 (c i2c интерфейсом), и Data Logger Shield (c SD-слотом, RTC и площадкой для прототипирования; задействован только SD-слот и припаяны кнопки управления на площадку - чтобы не городить икебану из проводов). Задумка - создать прототип из распространенных узлов. Ну и код для символьного дисплея компактнее, чем для графического
Задача девайса - дать возможность выбрать файл УП с карточки и посылать кадры по UART в grbl, реализуя протокол "агрессивной" загрузки - запихивать кадры УП в grbl, максимально наполняя ее буфер (улучшая работу предпросмотрщика) и отслеживая состояние очереди. Внятного видео снять не удалось, уж что получилось! Черная коробушка - блок управления с grbl и драйверами на DRV8825, с вторичным миниатюрным DC-DC 24-5V (питания драйверов) и принудительной вентиляцией.

На LCD: первая строка - очередной кадр УП, вторая - ответ grbl на соответствующий кадр УП, четвертая - подсказка по кнопкам управления.
Третья - имя файла УП, и самое интересное - состояние очереди (queue) Q:110/5 , т.е., отослано 5 кадров, общей длиной 110 байт. Видно, что номер кадра УП опережает номер ответа grbl.

Проект остался недоделан, т.к., иногда "терялись" ответы от grbl, соответственно, возникало взаимное ожидание - grbl ждал кадры от девайса, а девайс, видя заполненную очередь, ждал ответов от grbl. Ну и еще, как оказалось, не хватает быстродействия. Может из-за долгого обмена с LCD2004 по i2c. Лепить дисплей 10 проводами к ардуине было лениво. Ежели б LCD посадить на параллельный интерфейс, может и взлетело бы.
Аватара пользователя
yurayerz
Опытный
 
Сообщения: 126
Зарегистрирован: 04 янв 2015, 02:46
Откуда: Ярославль
Репутация: 30
Медальки:
Настоящее имя: Юрий

Re: Arduino GRBL Companion - Автономный контроллер для GRBL

Сообщение tecnocat » 03 апр 2016, 11:03

У меня все быстродействие тоже съедается выводом на экран, поэтому вывод исполняемых строк только в режиме пошагового исполнения, и ради него же не запрашиваются текущие координаты. А больше всего памяти съел обмен с SD картой, но поддерживается и FAT и FAT32.
tecnocat
Опытный
 
Сообщения: 100
Зарегистрирован: 09 июн 2014, 08:07
Репутация: 39

Re: Arduino GRBL Companion - Автономный контроллер для GRBL

Сообщение grakun » 03 апр 2016, 18:20

yurayerz писал(а):Проект остался недоделан, т.к.,

Уважаемый, а зачем он нужен, проект недоделанный? Вы доделайте потом показывайте.
Аватара пользователя
grakun
Мастер
 
Сообщения: 273
Зарегистрирован: 30 сен 2015, 02:47
Откуда: г.Саратов
Репутация: 53
Настоящее имя: Гракун

Re: Arduino GRBL Companion - Автономный контроллер для GRBL

Сообщение yurayerz » 17 апр 2016, 19:44

grakun писал(а):Вы доделайте потом показывайте.
(c) "Зачем ты такой злой?" :wik:
Форум - это место, где все делятся своими мыслями, наработками, технологиями ..., задают вопросы, глупые вопросы и очень глупые вопросы. ;)

Итак, малость переделал - посадил LCD на параллельный интерфейс. Вместо программного последовательного интерфейса (SoftwareSerial) подключил grbl к аппаратному (Serial) - любимая Freeduino Uno издохла, пришлось взять Arduino Mega V3, у которого аж три железных УАРТа. Впрочем, можно было и к UNO к железному порту прицепить, но тогда пришлось бы дергать туда-сюда USB-кабель.

И, в общем-то, взлетело! Отработало несколько простеньких УП без взаимоблокировок.Напомню, основная идея - проверка "агрессивной" загрузки УП в grbl. Видно, что когда механика опережает (очередь команд пуста, Q:0/0), станок движется рывками. Если электроника опережает и успевает напихать команд в буфер (в очередь) grbl, станок движется плавно.
Осталось плюшек в девайс добавить - ручное управление и настройки. Может какойнить энкодер присобачить. Сейчас пока даже дребезг кнопок не обрабатывал, ибо не это главное.

И да, NEMA23 управляются драйверами на DRV8825 + активное охлаждение кулером. Греется только последняя, радиатор которой заслонен от обдува первыми. Ступенькой их штоле поставить? :)
Не, у меня есть драйвера и на TB6600, а это чисто эксперимент - насколько степстиков хватит?
Аватара пользователя
yurayerz
Опытный
 
Сообщения: 126
Зарегистрирован: 04 янв 2015, 02:46
Откуда: Ярославль
Репутация: 30
Медальки:
Настоящее имя: Юрий

Re: Arduino GRBL Companion - Автономный контроллер для GRBL

Сообщение grakun » 18 апр 2016, 20:18

yurayerz писал(а):"Зачем ты такой злой?"

Совсем не злой, делал этот проект два года назад. Мелочей там (типа дребезга контактов ) нет. Когда доделаете все, не хватит памяти. Будете искать варианты. Поэтому я и написал сначала доделайте.
Аватара пользователя
grakun
Мастер
 
Сообщения: 273
Зарегистрирован: 30 сен 2015, 02:47
Откуда: г.Саратов
Репутация: 53
Настоящее имя: Гракун

Re: Arduino GRBL Companion - Автономный контроллер для GRBL

Сообщение ligor73 » 29 апр 2016, 07:51

Здравствуйте.
Не получается скомпилировать. Пишет ошибка.
Вложения
Arduino GRBL Companion - Автономный контроллер для GRBL ошибка1.jpg
ligor73
Новичок
 
Сообщения: 5
Зарегистрирован: 30 мар 2013, 09:34
Откуда: Новосибирск
Репутация: 0

Re: Arduino GRBL Companion - Автономный контроллер для GRBL

Сообщение tecnocat » 29 апр 2016, 10:44

ligor73 писал(а):Здравствуйте.
Не получается скомпилировать. Пишет ошибка.

У меня весь проект собирался в среде версии 1.6.7, а как неоднократно писалось в интернете - версии Arduino Studio друг от друга сильно отличаются.
Тут одно из двух - либо обновлять библиотеку SD, либо поставить среду посвежее.
tecnocat
Опытный
 
Сообщения: 100
Зарегистрирован: 09 июн 2014, 08:07
Репутация: 39

След.

Вернуться в Электроника

Кто сейчас на конференции

Зарегистрированные пользователи: Bing [Bot], CNCCLUB, CybSys, daemon78, Ershoff, Евжений, ferum, Gestap, gigs, Google [Bot], Majestic-12 [Bot], max73, mikehv, niksooon, paganel, putnik, Raptirius, Roots, SeReGa Sh, shalek, uralpt, X-Shadow, Yandex [bot], Тенгель

Reputation System ©'