// Executes user startup script, if stored. void system_execute_startup(char *line) { uint8_t n; for (n=0; n < N_STARTUP_LINE; n++) { if (!(settings_read_startup_line(n, line))) { report_status_message(STATUS_SETTING_READ_FAIL); } else { if (line[0] != 0) { printString(line); // Echo startup line to indicate execution. report_status_message(gc_execute_line(line)); } } } }
void sp_process() { char c; while((c = serialRead()) != -1) { if((char_counter > 0) && ((c == '\n') || (c == '\r'))) { // Line is complete. Then execute! line[char_counter] = 0; // treminate string status_message(gc_execute_line(line)); char_counter = 0; // reset line buffer index } else if (c <= ' ') { // Throw away whitepace and control characters } else if (c >= 'a' && c <= 'z') { // Upcase lowercase line[char_counter++] = c-'a'+'A'; } else { line[char_counter++] = c; } } }
int main(void) { FILE *fr; char fname[200]; char cmd_type; //printf("Please input the file name:"); //scanf("%s",fname); //fr=fopen(fname,"r"); while(gets(cmd)!=NULL) { gc_execute_line(cmd); //printf("%c motion= %d group=%d X=%f Y=%f Z=%f F=%d\n",gc.cmd_type,gc.motion_mode,gc.group_num,gc.p.x,gc.p.y,gc.p.z,gc.inverse_feed_rate_mode); } //fclose(fr); return 0; }
// Directs and executes one line of formatted input from protocol_process. While mostly // incoming streaming g-code blocks, this also executes Grbl internal commands, such as // settings, initiating the homing cycle, and toggling switch states. This differs from // the realtime command module by being susceptible to when Grbl is ready to execute the // next line during a cycle, so for switches like block delete, the switch only effects // the lines that are processed afterward, not necessarily real-time during a cycle, // since there are motions already stored in the buffer. However, this 'lag' should not // be an issue, since these commands are not typically used during a cycle. uint8_t system_execute_line(char *line) { uint8_t char_counter = 1; uint8_t helper_var = 0; // Helper variable float parameter, value; switch( line[char_counter] ) { case 0 : report_grbl_help(); break; case '$': case 'G': case 'C': case 'X': if ( line[(char_counter+1)] != 0 ) { return(STATUS_INVALID_STATEMENT); } switch( line[char_counter] ) { case '$' : // Prints Grbl settings if ( sys.state & (STATE_CYCLE | STATE_HOLD) ) { return(STATUS_IDLE_ERROR); } // Block during cycle. Takes too long to print. else { report_grbl_settings(); } break; case 'G' : // Prints gcode parser state // TODO: Move this to realtime commands for GUIs to request this data during suspend-state. report_gcode_modes(); break; case 'C' : // Set check g-code mode [IDLE/CHECK] // Perform reset when toggling off. Check g-code mode should only work if Grbl // is idle and ready, regardless of alarm locks. This is mainly to keep things // simple and consistent. if ( sys.state == STATE_CHECK_MODE ) { mc_reset(); report_feedback_message(MESSAGE_DISABLED); } else { if (sys.state) { return(STATUS_IDLE_ERROR); } // Requires no alarm mode. sys.state = STATE_CHECK_MODE; report_feedback_message(MESSAGE_ENABLED); } break; case 'X' : // Disable alarm lock [ALARM] if (sys.state == STATE_ALARM) { report_feedback_message(MESSAGE_ALARM_UNLOCK); sys.state = STATE_IDLE; // Don't run startup script. Prevents stored moves in startup from causing accidents. #ifndef DEFAULTS_TRINAMIC if (system_check_safety_door_ajar()) { // Check safety door switch before returning. bit_true(sys_rt_exec_state, EXEC_SAFETY_DOOR); protocol_execute_realtime(); // Enter safety door mode. } #endif } // Otherwise, no effect. break; // case 'J' : break; // Jogging methods // TODO: Here jogging can be placed for execution as a seperate subprogram. It does not need to be // susceptible to other realtime commands except for e-stop. The jogging function is intended to // be a basic toggle on/off with controlled acceleration and deceleration to prevent skipped // steps. The user would supply the desired feedrate, axis to move, and direction. Toggle on would // start motion and toggle off would initiate a deceleration to stop. One could 'feather' the // motion by repeatedly toggling to slow the motion to the desired location. Location data would // need to be updated real-time and supplied to the user through status queries. // More controlled exact motions can be taken care of by inputting G0 or G1 commands, which are // handled by the planner. It would be possible for the jog subprogram to insert blocks into the // block buffer without having the planner plan them. It would need to manage de/ac-celerations // on its own carefully. This approach could be effective and possibly size/memory efficient. // } // break; } break; default : // Block any system command that requires the state as IDLE/ALARM. (i.e. EEPROM, homing) if ( !(sys.state == STATE_IDLE || sys.state == STATE_ALARM) ) { return(STATUS_IDLE_ERROR); } switch( line[char_counter] ) { case '#' : // Print Grbl NGC parameters if ( line[++char_counter] != 0 ) { return(STATUS_INVALID_STATEMENT); } else { report_ngc_parameters(); } break; case 'H' : // Perform homing cycle [IDLE/ALARM] if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { sys.state = STATE_HOMING; // Set system state variable // Only perform homing if Grbl is idle or lost. // TODO: Likely not required. #ifndef DEFAULTS_TRINAMIC if (system_check_safety_door_ajar()) { // Check safety door switch before homing. bit_true(sys_rt_exec_state, EXEC_SAFETY_DOOR); protocol_execute_realtime(); // Enter safety door mode. } #endif mc_homing_cycle(); if (!sys.abort) { // Execute startup scripts after successful homing. sys.state = STATE_IDLE; // Set to IDLE when complete. st_go_idle(); // Set steppers to the settings idle state before returning. system_execute_startup(line); } } else { return(STATUS_SETTING_DISABLED); } break; case 'I' : // Print or store build info. [IDLE/ALARM] if ( line[++char_counter] == 0 ) { settings_read_build_info(line); report_build_info(line); } else { // Store startup line [IDLE/ALARM] if(line[char_counter++] != '=') { return(STATUS_INVALID_STATEMENT); } helper_var = char_counter; // Set helper variable as counter to start of user info line. do { line[char_counter-helper_var] = line[char_counter]; } while (line[char_counter++] != 0); settings_store_build_info(line); } break; case 'R' : // Restore defaults [IDLE/ALARM] if (line[++char_counter] != 'S') { return(STATUS_INVALID_STATEMENT); } if (line[++char_counter] != 'T') { return(STATUS_INVALID_STATEMENT); } if (line[++char_counter] != '=') { return(STATUS_INVALID_STATEMENT); } if (line[char_counter+2] != 0) { return(STATUS_INVALID_STATEMENT); } switch (line[++char_counter]) { case '$': settings_restore(SETTINGS_RESTORE_DEFAULTS); break; case '#': settings_restore(SETTINGS_RESTORE_PARAMETERS); break; case '*': settings_restore(SETTINGS_RESTORE_ALL); break; default: return(STATUS_INVALID_STATEMENT); } report_feedback_message(MESSAGE_RESTORE_DEFAULTS); mc_reset(); // Force reset to ensure settings are initialized correctly. break; case 'N' : // Startup lines. [IDLE/ALARM] if ( line[++char_counter] == 0 ) { // Print startup lines for (helper_var=0; helper_var < N_STARTUP_LINE; helper_var++) { if (!(settings_read_startup_line(helper_var, line))) { report_status_message(STATUS_SETTING_READ_FAIL); } else { report_startup_line(helper_var,line); } } break; } else { // Store startup line [IDLE Only] Prevents motion during ALARM. if (sys.state != STATE_IDLE) { return(STATUS_IDLE_ERROR); } // Store only when idle. helper_var = true; // Set helper_var to flag storing method. // No break. Continues into default: to read remaining command characters. } default : // Storing setting methods [IDLE/ALARM] if(!read_float(line, &char_counter, ¶meter)) { return(STATUS_BAD_NUMBER_FORMAT); } if(line[char_counter++] != '=') { return(STATUS_INVALID_STATEMENT); } if (helper_var) { // Store startup line // Prepare sending gcode block to gcode parser by shifting all characters helper_var = char_counter; // Set helper variable as counter to start of gcode block do { line[char_counter-helper_var] = line[char_counter]; } while (line[char_counter++] != 0); // Execute gcode block to ensure block is valid. helper_var = gc_execute_line(line); // Set helper_var to returned status code. if (helper_var) { return(helper_var); } else { helper_var = trunc(parameter); // Set helper_var to int value of parameter settings_store_startup_line(helper_var,line); } } else { // Store global setting. if(!read_float(line, &char_counter, &value)) { return(STATUS_BAD_NUMBER_FORMAT); } if((line[char_counter] != 0) || (parameter > 255)) { return(STATUS_INVALID_STATEMENT); } return(settings_store_global_setting((uint8_t)parameter, value)); } } } return(STATUS_OK); // If '$' command makes it to here, then everything's ok. }
// Directs and executes one line of formatted input from protocol_process. While mostly // incoming streaming g-code blocks, this also executes Grbl internal commands, such as // settings, initiating the homing cycle, and toggling switch states. This differs from // the runtime command module by being susceptible to when Grbl is ready to execute the // next line during a cycle, so for switches like block delete, the switch only effects // the lines that are processed afterward, not necessarily real-time during a cycle, // since there are motions already stored in the buffer. However, this 'lag' should not // be an issue, since these commands are not typically used during a cycle. uint8_t protocol_execute_line(char *line) { // Grbl internal command and parameter lines are of the form '$4=374.3' or '$' for help if(line[0] == '$') { uint8_t char_counter = 1; uint8_t helper_var = 0; // Helper variable float parameter, value; switch( line[char_counter] ) { case 0 : report_grbl_help(); break; case '$' : // Prints Grbl settings if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } else { report_grbl_settings(); } break; case '#' : // Print gcode parameters if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } else { report_gcode_parameters(); } break; case 'G' : // Prints gcode parser state if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } else { report_gcode_modes(); } break; case 'C' : // Set check g-code mode if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } // Perform reset when toggling off. Check g-code mode should only work if Grbl // is idle and ready, regardless of alarm locks. This is mainly to keep things // simple and consistent. if ( sys.state == STATE_CHECK_MODE ) { mc_reset(); report_feedback_message(MESSAGE_DISABLED); } else { if (sys.state) { return(STATUS_IDLE_ERROR); } sys.state = STATE_CHECK_MODE; report_feedback_message(MESSAGE_ENABLED); } break; case 'X' : // Disable alarm lock if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } if (sys.state == STATE_ALARM) { report_feedback_message(MESSAGE_ALARM_UNLOCK); sys.state = STATE_IDLE; // Don't run startup script. Prevents stored moves in startup from causing accidents. } break; case 'H' : // Perform homing cycle if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { // Only perform homing if Grbl is idle or lost. if ( sys.state==STATE_IDLE || sys.state==STATE_ALARM ) { mc_go_home(); if (!sys.abort) { protocol_execute_startup(); } // Execute startup scripts after successful homing. } else { return(STATUS_IDLE_ERROR); } } else { return(STATUS_SETTING_DISABLED); } break; // case 'J' : break; // Jogging methods // TODO: Here jogging can be placed for execution as a seperate subprogram. It does not need to be // susceptible to other runtime commands except for e-stop. The jogging function is intended to // be a basic toggle on/off with controlled acceleration and deceleration to prevent skipped // steps. The user would supply the desired feedrate, axis to move, and direction. Toggle on would // start motion and toggle off would initiate a deceleration to stop. One could 'feather' the // motion by repeatedly toggling to slow the motion to the desired location. Location data would // need to be updated real-time and supplied to the user through status queries. // More controlled exact motions can be taken care of by inputting G0 or G1 commands, which are // handled by the planner. It would be possible for the jog subprogram to insert blocks into the // block buffer without having the planner plan them. It would need to manage de/ac-celerations // on its own carefully. This approach could be effective and possibly size/memory efficient. case 'N' : // Startup lines. if ( line[++char_counter] == 0 ) { // Print startup lines for (helper_var=0; helper_var < N_STARTUP_LINE; helper_var++) { if (!(settings_read_startup_line(helper_var, line))) { report_status_message(STATUS_SETTING_READ_FAIL); } else { report_startup_line(helper_var,line); } } break; } else { // Store startup line helper_var = true; // Set helper_var to flag storing method. // No break. Continues into default: to read remaining command characters. } default : // Storing setting methods if(!read_float(line, &char_counter, ¶meter)) { return(STATUS_BAD_NUMBER_FORMAT); } if(line[char_counter++] != '=') { return(STATUS_UNSUPPORTED_STATEMENT); } if (helper_var) { // Store startup line // Prepare sending gcode block to gcode parser by shifting all characters helper_var = char_counter; // Set helper variable as counter to start of gcode block do { line[char_counter-helper_var] = line[char_counter]; } while (line[char_counter++] != 0); // Execute gcode block to ensure block is valid. helper_var = gc_execute_line(line); // Set helper_var to returned status code. if (helper_var) { return(helper_var); } else { helper_var = trunc(parameter); // Set helper_var to int value of parameter settings_store_startup_line(helper_var,line); } } else { // Store global setting. if(!read_float(line, &char_counter, &value)) { return(STATUS_BAD_NUMBER_FORMAT); } if(line[char_counter] != 0) { return(STATUS_UNSUPPORTED_STATEMENT); } return(settings_store_global_setting(parameter, value)); } } return(STATUS_OK); // If '$' command makes it to here, then everything's ok. } else { return(gc_execute_line(line)); // Everything else is gcode } }
void cnc_gfile(char *fileName, int mode) { #if (USE_LCD != 0) && (MAX_SHOW_GCODE_LINES > 0) int n; #endif int lineNum; uint8_t hasMoreLines; initGcodeProc(); #if (USE_SDCARD != 0) FIL fid; FRESULT res = f_open(&fid, fileName, FA_READ); if (res != FR_OK) { win_showErrorWin(); #if (USE_SDCARD == 1) scr_printf("Error open file:'%s'\nStatus:%d [%d]", fileName, (int)res, SD_errno); #elif (USE_SDCARD == 2) scr_printf("Error open file:'%s'\nStatus:%d", fileName, (int)res); #endif return; } #endif curGCodeMode = mode; #if (USE_LCD != 0) if ((curGCodeMode & GFILE_MODE_MASK_SHOW) != 0) { scr_Rectangle(crdXtoScr(0), crdYtoScr(MAX_TABLE_SIZE_Y), crdXtoScr(MAX_TABLE_SIZE_X), crdYtoScr(0), Red, false); scr_Line(prev_scrX, 30, prev_scrX, 240 - 30, Green); scr_Line(50, prev_scrY, 320 - 50, prev_scrY, Green); } #endif if ((curGCodeMode & GFILE_MODE_MASK_EXEC) != 0) { #if (USE_KEYBOARD == 1) scr_fontColor(Blue, Black); scr_gotoxy(3, 14); scr_puts("C-Cancel A-Pause 0/1-encoder"); #elif (USE_KEYBOARD == 2) SetTouchKeys(kbdGFile); #endif } lineNum = 1; hasMoreLines = true; do { char *p = cncFileBuf, *str; while (true) { *p = 0; str = p + 1; if ((cncFileBuf + sizeof(cncFileBuf) - str) < (MAX_STR_SIZE + 1)) break; #if (USE_SDCARD != 0) if (f_gets(str, MAX_STR_SIZE, &fid) == NULL) { hasMoreLines = false; break; } #endif str_trim(str); *p = (uint8_t)strlen(str) + 1; p += *p + 1; } for (p = cncFileBuf; !isGcodeStop && *p != 0; lineNum++, p += *p + 1) { uint8_t st; str = p + 1; if ((curGCodeMode & GFILE_MODE_MASK_EXEC) != 0) { GCODE_CMD *gp; #if (USE_LCD != 0) && (MAX_SHOW_GCODE_LINES > 0) int i; #endif linesBuffer.gcodePtrCur++; if (linesBuffer.gcodePtrCur > (MAX_SHOW_GCODE_LINES - 1)) linesBuffer.gcodePtrCur = 0; gp = &linesBuffer.gcode[linesBuffer.gcodePtrCur]; strcpy(gp->cmd, str); gp->lineNum = lineNum; #if (USE_LCD != 0) && (MAX_SHOW_GCODE_LINES > 0) scr_fontColor(Green, Black); // if(stepm_getRemainLines() > 1) { for (i = 0, n = linesBuffer.gcodePtrCur + 1; i < MAX_SHOW_GCODE_LINES; i++, n++) { if (n > (MAX_SHOW_GCODE_LINES - 1)) n = 0; gp = &linesBuffer.gcode[n]; scr_gotoxy(1, i); if (gp->lineNum) scr_printf("%d:%s", gp->lineNum, gp->cmd); scr_clrEndl(); } // } #endif } DBG("\n [gcode:%d] %s", lineNum, str); st = gc_execute_line(str); if (st != GCSTATUS_OK) { #if (USE_LCD != 0) scr_fontColor(Red, Black); scr_gotoxy(1, 11); switch (st) { case GCSTATUS_BAD_NUMBER_FORMAT: scr_puts("BAD_NUMBER_FORMAT"); break; case GCSTATUS_EXPECTED_COMMAND_LETTER: scr_puts("EXPECTED_COMMAND_LETTER"); break; case GCSTATUS_UNSUPPORTED_STATEMENT: scr_puts("UNSUPPORTED_STATEMENT"); break; case GCSTATUS_FLOATING_POINT_ERROR: scr_puts("FLOATING_POINT_ERROR"); break; case GCSTATUS_UNSUPPORTED_PARAM: scr_puts("UNSUPPORTED_PARAM"); break; case GCSTATUS_UNSOPORTED_FEEDRATE: scr_puts("GCSTATUS_UNSUPPORTED_FEEDRATE"); break; case GCSTATUS_TABLE_SIZE_OVER_X: scr_puts("GCSTATUS_TABLE_SIZE_OVER_X"); break; case GCSTATUS_TABLE_SIZE_OVER_Y: scr_puts("GCSTATUS_TABLE_SIZE_OVER_Y"); break; case GCSTATUS_TABLE_SIZE_OVER_Z: scr_puts("GCSTATUS_TABLE_SIZE_OVER_Z"); break; case GCSTATUS_CANCELED: scr_puts("GCSTATUS_CANCELED"); break; } scr_printf(" at line %d:\n %s", lineNum, str); #endif #if (USE_SDCARD != 0) f_close(&fid); #endif return; } } } while (!isGcodeStop && hasMoreLines); #if (USE_SDCARD != 0) f_close(&fid); #endif if ((curGCodeMode & GFILE_MODE_MASK_EXEC) == 0) { #if (USE_LCD != 0) short scrX = crdXtoScr(TABLE_CENTER_X); short scrY = crdYtoScr(TABLE_CENTER_Y); int t1 = commonTimeIdeal / 1000; int t2 = commonTimeReal / 1000; scr_Line(scrX - 8, scrY, scrX + 8, scrY, Red); scr_Line(scrX, scrY - 8, scrX, scrY + 8, Red); scr_fontColor(Green, Black); scr_gotoxy(1, 0); scr_printf("Time %02d:%02d:%02d(%02d:%02d:%02d) N.cmd:%d", t1 / 3600, (t1 / 60) % 60, t1 % 60, t2 / 3600, (t2 / 60) % 60, t2 % 60, lineNum ); scr_printf("\n X%f/%f Y%f/%f Z%f/%f", minX, maxX, minY, maxY, minZ, maxZ); #endif } else { #ifndef NO_ACCELERATION_CORRECTION cnc_line(0, 0, 0, 0, 0, 0); cnc_line(0, 0, 0, 0, 0, 0); #endif } }