diff --git a/Motate b/Motate index 222de41d..41e5b92a 160000 --- a/Motate +++ b/Motate @@ -1 +1 @@ -Subproject commit 222de41d509543859265bbb41e5e7fc8e0e44ff6 +Subproject commit 41e5b92a98de4b268d1804bf6eadf3333298fc75 diff --git a/g2core/Makefile b/g2core/Makefile index e4928c15..bdbb1848 100755 --- a/g2core/Makefile +++ b/g2core/Makefile @@ -52,8 +52,11 @@ NEEDS_PRINTF_FLOAT=1 # Now invoke the Motate compile system include $(MOTATE_PATH)/Motate.mk +ifeq ($(DEBUG),0) + DEVICE_DEFINES += DEBUG=0 IN_DEBUGGER=0 +endif ifeq ($(DEBUG),1) - DEVICE_DEFINES += DEBUG=1 + DEVICE_DEFINES += DEBUG=1 IN_DEBUGGER=0 endif ifeq ($(DEBUG),2) DEVICE_DEFINES += DEBUG=1 IN_DEBUGGER=1 diff --git a/g2core/board/printrboardg2/board_xio.cpp b/g2core/board/printrboardg2/board_xio.cpp index c48356be..149dbc8d 100755 --- a/g2core/board/printrboardg2/board_xio.cpp +++ b/g2core/board/printrboardg2/board_xio.cpp @@ -75,7 +75,9 @@ void board_hardware_init(void) // called 1st } -void board_xio_init(void) // called later than board_hardware_init (there are thing in between) +auto startup_file = make_xio_flash_file(""); + +void board_xio_init(void) // called later than board_hardware_init (there are things in between) { // Init SPI #if XIO_HAS_SPI @@ -86,4 +88,6 @@ void board_xio_init(void) // called later than board_hardware_init (there are th #if XIO_HAS_UART Serial.init(); #endif + + xio_send_file(startup_file); } diff --git a/g2core/board/printrboardg2/board_xio.h b/g2core/board/printrboardg2/board_xio.h index 6bc4421f..75184f19 100755 --- a/g2core/board/printrboardg2/board_xio.h +++ b/g2core/board/printrboardg2/board_xio.h @@ -30,6 +30,7 @@ #define board_xio_h #include "settings.h" +#include "xio.h" //******** USB ******** #if XIO_HAS_USB diff --git a/g2core/boards.mk b/g2core/boards.mk index f51eca4d..a5ea8d79 100644 --- a/g2core/boards.mk +++ b/g2core/boards.mk @@ -39,7 +39,8 @@ ifeq ("$(CONFIG)","Othermill") ifeq ("$(BOARD)","NONE") BOARD=g2v9k endif - SETTINGS_FILE="settings_othermill.h" +# SETTINGS_FILE="settings_othermill.h" + SETTINGS_FILE="settings_othermill_test.h" endif ifeq ("$(CONFIG)","ProbotixV90") diff --git a/g2core/canonical_machine.cpp b/g2core/canonical_machine.cpp index 706a3687..61850d6a 100644 --- a/g2core/canonical_machine.cpp +++ b/g2core/canonical_machine.cpp @@ -93,6 +93,7 @@ #include "controller.h" #include "json_parser.h" #include "text_parser.h" +#include "settings.h" #include "plan_arc.h" #include "planner.h" @@ -259,6 +260,15 @@ void cm_set_model_linenum(const uint32_t linenum) nv_add_object((const char *)"n"); // then add the line number to the nv list } +stat_t cm_check_linenum() { + if (cm.gmx.last_line_number != cm.gm.linenum) { + _debug_trap("line number out of sequence"); + return STAT_LINE_NUMBER_OUT_OF_SEQUENCE; + } + cm.gmx.last_line_number = cm.gm.linenum; + return STAT_OK; +} + /*********************************************************************************** * COORDINATE SYSTEMS AND OFFSETS * Functions to get, set and report coordinate systems and work offsets @@ -565,6 +575,31 @@ stat_t cm_get_tram(nvObj_t *nv) } +/* + * cm_set_nxt_line() - JSON command to set the next line number + * cm_get_nxt_line() - JSON query to get the next expected line number + * + * There MUST be three valid probes stored. + */ + +stat_t cm_set_nxln(nvObj_t *nv) +{ + if (nv->valuetype == TYPE_INT || nv->valuetype == TYPE_FLOAT) + { + cm.gmx.last_line_number = ((int32_t)nv->value) - 1; + return (STAT_OK); + } + return (STAT_INPUT_VALUE_RANGE_ERROR); +} + +stat_t cm_get_nxln(nvObj_t *nv) +{ + nv->value = cm.gmx.last_line_number+1; + nv->valuetype = TYPE_INT; + return (STAT_OK); +} + + /* * cm_set_model_target() - set target vector in GM model * @@ -594,7 +629,10 @@ static float _calc_ABC(const uint8_t axis, const float target[]) if ((cm.a[axis].axis_mode == AXIS_STANDARD) || (cm.a[axis].axis_mode == AXIS_INHIBITED)) { return(target[axis]); // no mm conversion - it's in degrees } - return(_to_millimeters(target[axis]) * 360 / (2 * M_PI * cm.a[axis].radius)); + + // radius mode + + return (_to_millimeters(target[axis]) * 360.0 / (2.0 * M_PI * cm.a[axis].radius)); } void cm_set_model_target(const float target[], const bool flags[]) @@ -624,15 +662,35 @@ void cm_set_model_target(const float target[], const bool flags[]) } else { tmp = _calc_ABC(axis, target); } +#if MARLIN_COMPAT_ENABLED == true + // If we are in absolute mode (generally), but the extruder is relative, + // then we adjust the extruder to a relative position + if (mst.marlin_flavor && (cm.a[axis].axis_mode == AXIS_RADIUS)) { + if ((cm.gm.distance_mode == INCREMENTAL_DISTANCE_MODE) || (mst.extruder_mode == EXTRUDER_MOVES_RELATIVE)) { + cm.gm.target[axis] += tmp; + } + else { // if (cm.gmx.extruder_mode == EXTRUDER_MOVES_NORMAL) + cm.gm.target[axis] = tmp + cm_get_active_coord_offset(axis); + } + // TODO +// else { +// cm.gm.target[axis] += tmp * cm.gmx.volume_to_filament_length[axis-3]; +// } + } + else +#endif // MARLIN_COMPAT_ENABLED if (cm.gm.distance_mode == ABSOLUTE_DISTANCE_MODE) { cm.gm.target[axis] = tmp + cm_get_active_coord_offset(axis); // sacidu93's fix to Issue #22 - } else { + } + else { cm.gm.target[axis] += tmp; } } } /* + * cm_get_soft_limits() + * cm_set_soft_limits() * cm_test_soft_limits() - return error code if soft limit is exceeded * * The target[] arg must be in absolute machine coordinates. Best done after cm_set_model_target(). @@ -643,6 +701,9 @@ void cm_set_model_target(const float target[], const bool flags[]) * This allows a single end to be tested w/the other disabled, should that requirement ever arise. */ +bool cm_get_soft_limits() { return (cm.soft_limit_enable); } +void cm_set_soft_limits(bool enable) { cm.soft_limit_enable = enable; } + static stat_t _finalize_soft_limits(const stat_t status) { cm.gm.motion_mode = MOTION_MODE_CANCEL_MOTION_MODE; // cancel motion @@ -693,9 +754,7 @@ void canonical_machine_init() // If you can assume all memory has been zeroed by a hard reset you don't need this code: // memset(&cm, 0, sizeof(cm)); // do not reset canonicalMachineSingleton once it's been initialized memset(&cm, 0, sizeof(cmSingleton_t)); // do not reset canonicalMachineSingleton once it's been initialized - memset(&cm.gm, 0, sizeof(GCodeState_t)); // clear all values, pointers and status - memset(&cm.gn, 0, sizeof(GCodeInput_t)); - memset(&cm.gf, 0, sizeof(GCodeFlags_t)); + cm.gm.reset(); // clear all values, pointers and status -- not ALL to zero, however canonical_machine_init_assertions(); // establish assertions ACTIVE_MODEL = MODEL; // setup initial Gcode model pointer @@ -719,19 +778,20 @@ void canonical_machine_reset() cm_select_plane(cm.default_select_plane); cm_set_path_control(MODEL, cm.default_path_control); cm_set_distance_mode(cm.default_distance_mode); - cm_set_arc_distance_mode(INCREMENTAL_DISTANCE_MODE); // always the default - cm_set_feed_rate_mode(UNITS_PER_MINUTE_MODE); // always the default - cm_reset_overrides(); // set overrides to initial conditions + cm_set_arc_distance_mode(INCREMENTAL_DISTANCE_MODE);// always the default + cm_set_feed_rate_mode(UNITS_PER_MINUTE_MODE); // always the default + cm_reset_overrides(); // set overrides to initial conditions // NOTE: Should unhome axes here - // reset request flags + // reset requests and flags cm.queue_flush_state = FLUSH_OFF; cm.end_hold_requested = false; cm.limit_requested = 0; // resets switch closures that occurred during initialization cm.safety_interlock_disengaged = 0; // ditto cm.safety_interlock_reengaged = 0; // ditto cm.shutdown_requested = 0; // ditto + cm.probe_report_enable = PROBE_REPORT_ENABLE; // set initial state and signal that the machine is ready for action cm.cycle_state = CYCLE_OFF; @@ -1038,10 +1098,11 @@ stat_t cm_set_arc_distance_mode(const uint8_t mode) * cm_set_work_offsets() immediately afterwards. */ -stat_t cm_set_g10_data(const uint8_t P_word, const uint8_t L_word, +stat_t cm_set_g10_data(const uint8_t P_word, const bool P_flag, + const uint8_t L_word, const bool L_flag, const float offset[], const bool flag[]) { - if (!cm.gf.L_word) { + if (!L_flag) { return (STAT_L_WORD_IS_MISSING); } @@ -1057,8 +1118,9 @@ stat_t cm_set_g10_data(const uint8_t P_word, const uint8_t L_word, cm.offset[P_word][axis] = _to_millimeters(offset[axis]); } else { // Should L20 take into account G92 offsets? - cm.offset[P_word][axis] = cm.gmx.position[axis] - - _to_millimeters(offset[axis]) - + cm.offset[P_word][axis] = + cm.gmx.position[axis] - + _to_millimeters(offset[axis]) - cm.tl_offset[axis]; } // persist offsets once machining cycle is over @@ -1078,8 +1140,8 @@ stat_t cm_set_g10_data(const uint8_t P_word, const uint8_t L_word, } else { // L10 should also take into account G92 offset cm.tt_offset[P_word][axis] = - cm.gmx.position[axis] - _to_millimeters(offset[axis]) - - cm.offset[cm.gm.coord_system][axis] - + cm.gmx.position[axis] - _to_millimeters(offset[axis]) - + cm.offset[cm.gm.coord_system][axis] - (cm.gmx.origin_offset[axis] * cm.gmx.origin_offset_enable); } // persist offsets once machining cycle is over @@ -1102,27 +1164,27 @@ stat_t cm_set_g10_data(const uint8_t P_word, const uint8_t L_word, * cm_set_coord_system() - G54-G59 * _exec_offset() - callback from planner */ -stat_t cm_set_tl_offset(const uint8_t H_word, bool apply_additional) + +stat_t cm_set_tl_offset(const uint8_t H_word, const bool H_flag, const bool apply_additional) { uint8_t tool; - if (cm.gf.H_word) - { - if (cm.gn.H_word > TOOLS) { + if (H_flag) { + if (H_word > TOOLS) { return (STAT_H_WORD_IS_INVALID); } - if (cm.gn.H_word == 0) { // interpret H0 as "current tool", just like no H at all. + if (H_word == 0) { // interpret H0 as "current tool", just like no H at all. tool = cm.gm.tool; } else { - tool = cm.gn.H_word; + tool = H_word; } - } else { + } else { tool = cm.gm.tool; } if (apply_additional) { for (uint8_t axis = AXIS_X; axis < AXES; axis++) { cm.tl_offset[axis] += cm.tt_offset[tool][axis]; } - } else { + } else { for (uint8_t axis = AXIS_X; axis < AXES; axis++) { cm.tl_offset[axis] = cm.tt_offset[tool][axis]; } @@ -1174,7 +1236,7 @@ static void _exec_offset(float *value, bool *flag) * This is useful for setting origins for homing, probing, and other operations. * * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * !!!!! DO NOT CALL THIS FUNCTION WHILE IN A MACHINING CYCLE !!!!! + * !!!!! DO NOT CALL THIS FUNCTION WHILE IN A MACHINING CYCLE !!!!! * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! * * More specifically, do not call this function if there are any moves in the planner or @@ -1309,9 +1371,8 @@ stat_t cm_straight_traverse(const float target[], const bool flags[]) cm.gm.motion_mode = MOTION_MODE_STRAIGHT_TRAVERSE; // it's legal for a G0 to have no axis words but we don't want to process it - if (!(flags[AXIS_X] || flags[AXIS_Y] || flags[AXIS_Z] || - flags[AXIS_A] || flags[AXIS_B] || flags[AXIS_C])) { - return(STAT_OK); + if (!(flags[AXIS_X] | flags[AXIS_Y] | flags[AXIS_Z] | flags[AXIS_A] | flags[AXIS_B] | flags[AXIS_C])) { + return(STAT_OK); } cm_set_model_target(target, flags); @@ -1467,10 +1528,8 @@ stat_t cm_straight_feed(const float target[], const bool flags[]) } cm.gm.motion_mode = MOTION_MODE_STRAIGHT_FEED; - // it's legal for a G1 to have no axis words but we don't want to process it - if (!(flags[AXIS_X] || flags[AXIS_Y] || flags[AXIS_Z] || - flags[AXIS_A] || flags[AXIS_B] || flags[AXIS_C])) { - return(STAT_OK); + if (!(flags[AXIS_X] | flags[AXIS_Y] | flags[AXIS_Z] | flags[AXIS_A] | flags[AXIS_B] | flags[AXIS_C])) { + return(STAT_OK); } cm_set_model_target(target, flags); @@ -1601,7 +1660,7 @@ stat_t cm_m48_enable(uint8_t enable) // M48, M49 } /* - * cm_feed_rate_override_enable() - M50 + * cm_mfo_control() - M50 manual feed rate override comtrol * * M50 enables manual feedrate override and the optional P override parameter. * P is expressed as M% to N% of programmed feedrate, typically a value from 0.05 to 2.000. @@ -1640,38 +1699,64 @@ stat_t cm_m48_enable(uint8_t enable) // M48, M49 * ENABLE ENABLE M50 Pn ENABLE start ramp w/new P value; store P value * (Note: new ramp will supercede any existing ramp) */ -stat_t cm_mfo_enable(uint8_t enable) // M50 + +stat_t cm_mfo_control(const float P_word, const bool P_flag) // M50 { bool new_enable = true; bool new_override = false; - if (cm.gf.parameter) { // if parameter is present in Gcode block - if (fp_ZERO(cm.gn.parameter)) { - new_enable = false; // P0 disables override + if (P_flag) { // if parameter is present in Gcode block + if (fp_ZERO(P_word)) { + new_enable = false; // P0 disables override } else { - if (cm.gn.parameter < FEED_OVERRIDE_MIN) { + if (P_word < FEED_OVERRIDE_MIN) { return (STAT_INPUT_LESS_THAN_MIN_VALUE); } - if (cm.gn.parameter > FEED_OVERRIDE_MAX) { + if (P_word > FEED_OVERRIDE_MAX) { return (STAT_INPUT_EXCEEDS_MAX_VALUE); } - cm.gmx.mfo_factor = cm.gn.parameter; // it validates - store it. + cm.gmx.mfo_factor = P_word; // P word is valid, store it. new_override = true; } } - if (cm.gmx.m48_enable) { // if master enable is ON + if (cm.gmx.m48_enable) { // if master enable is ON if (new_enable && (new_override || !cm.gmx.mfo_enable)) { // 3 cases to start a ramp mp_start_feed_override(FEED_OVERRIDE_RAMP_TIME, cm.gmx.mfo_factor); } else if (cm.gmx.mfo_enable && !new_enable) { // case to turn off the ramp mp_end_feed_override(FEED_OVERRIDE_RAMP_TIME); } } - cm.gmx.mfo_enable = new_enable; // always update the enable state + cm.gmx.mfo_enable = new_enable; // always update the enable state return (STAT_OK); } -// if ((new_enable && new_override) || (new_enable && !cm.gmx.mfo_enable)) { -// if (!(cm.gmx.mfo_enable && new_override)) { - +stat_t cm_mto_control(const float P_word, const bool P_flag) // M50.1 +{ + bool new_enable = true; + bool new_override = false; + if (P_flag) { // if parameter is present in Gcode block + if (fp_ZERO(P_word)) { + new_enable = false; // P0 disables override + } else { + if (P_word < TRAVERSE_OVERRIDE_MIN) { + return (STAT_INPUT_LESS_THAN_MIN_VALUE); + } + if (P_word > TRAVERSE_OVERRIDE_MAX) { + return (STAT_INPUT_EXCEEDS_MAX_VALUE); + } + cm.gmx.mto_factor = P_word; // P word is valid, store it. + new_override = true; + } + } + if (cm.gmx.m48_enable) { // if master enable is ON + if (new_enable && (new_override || !cm.gmx.mfo_enable)) { // 3 cases to start a ramp + mp_start_traverse_override(FEED_OVERRIDE_RAMP_TIME, cm.gmx.mto_factor); + } else if (cm.gmx.mto_enable && !new_enable) { // case to turn off the ramp + mp_end_traverse_override(FEED_OVERRIDE_RAMP_TIME); + } + } + cm.gmx.mto_enable = new_enable; // always update the enable state + return (STAT_OK); +} /************************************************ * Feedhold and Related Functions (no NIST ref) * @@ -2003,6 +2088,14 @@ stat_t cm_json_command(char *json_string) return mp_json_command(json_string); } +/* + * cm_json_command_immediate() - M100.1 + */ +stat_t cm_json_command_immediate(char *json_string) +{ + return mp_json_command_immediate(json_string); +} + /* * cm_json_wait() - M102 */ @@ -2588,7 +2681,9 @@ stat_t cm_run_qf(nvObj_t *nv) stat_t cm_run_home(nvObj_t *nv) { if (fp_TRUE(nv->value)) { - cm_homing_cycle_start(); + float axes[] = { 1,1,1,1,1,1 }; + bool flags[] = { 1,1,1,1,1,1 }; + cm_homing_cycle_start(axes, flags); } return (STAT_OK); } @@ -2740,7 +2835,8 @@ static const char fmt_mfoe[] = "[mfoe] manual feed override enab%3d [0=disable,1 static const char fmt_mfo[] = "[mfo] manual feedrate override%8.3f [0.05 < mfo < 2.00]\n"; static const char fmt_mtoe[] = "[mtoe] manual traverse over enab%3d [0=disable,1=enable]\n"; static const char fmt_mto[] = "[mto] manual traverse override%8.3f [0.05 < mto < 1.00]\n"; -static const char fmt_tram[] = "[tram] is coordinate space rotated to be tram %s\n"; +static const char fmt_tram[] = "[tram] is coordinate space rotated to be tram %s\n"; +static const char fmt_nxln[] = "[nxln] the next line number expected is %10d\n"; void cm_print_m48e(nvObj_t *nv) { text_print(nv, fmt_m48e);} // TYPE_INT void cm_print_mfoe(nvObj_t *nv) { text_print(nv, fmt_mfoe);} // TYPE INT @@ -2748,6 +2844,7 @@ void cm_print_mfo(nvObj_t *nv) { text_print(nv, fmt_mfo);} // TYPE FLOAT void cm_print_mtoe(nvObj_t *nv) { text_print(nv, fmt_mtoe);} // TYPE INT void cm_print_mto(nvObj_t *nv) { text_print(nv, fmt_mto);} // TYPE FLOAT void cm_print_tram(nvObj_t *nv) { text_print(nv, fmt_tram);}; // TYPE BOOL +void cm_print_nxln(nvObj_t *nv) { text_print(nv, fmt_nxln);}; // TYPE INT /* * axis print functions diff --git a/g2core/canonical_machine.h b/g2core/canonical_machine.h index 7910e703..4ff6e9cf 100644 --- a/g2core/canonical_machine.h +++ b/g2core/canonical_machine.h @@ -34,6 +34,11 @@ #include "config.h" #include "hardware.h" // Note: hardware.h is specific to the hardware target selected +#include "settings.h" + +#if MARLIN_COMPAT_ENABLED == true +#include "marlin_compatibility.h" // import Marlin definitions and enums +#endif /* Defines, Macros, and Assorted Parameters */ @@ -47,7 +52,7 @@ #define JOGGING_START_VELOCITY ((float)10.0) #define DISABLE_SOFT_LIMIT (999999) #define JERK_INPUT_MIN (0.01) // minimum allowable jerk setting in millions mm/min^3 -#define JERK_INPUT_MAX (1000000) // maximum allowable jerk setting in millions mm/min^3 +#define JERK_INPUT_MAX (1000000) // maximum allowable jerk setting in millions mm/min^3 #define PROBES_STORED 3 // we store three probes for coordinate rotation computation @@ -82,15 +87,15 @@ typedef enum { // check alignment with messages in config.c / m //### END CRITICAL REGION ### typedef enum { - MACHINE_INITIALIZING = 0, // machine is initializing - MACHINE_READY, // machine is ready for use - MACHINE_ALARM, // machine in alarm state - MACHINE_PROGRAM_STOP, // no blocks to run; like PROGRAM_END but without the M2 to reset gcode state - MACHINE_PROGRAM_END, // program end (same as MACHINE_READY, really...) - MACHINE_CYCLE, // machine is running; blocks still to run, or steppers are busy - MACHINE_INTERLOCK, // machine in interlock state - MACHINE_SHUTDOWN, // machine in shutdown state - MACHINE_PANIC // machine in panic state + MACHINE_INITIALIZING = 0, // [0] machine is initializing + MACHINE_READY, // [1] machine is ready for use + MACHINE_ALARM, // [2] machine in alarm state + MACHINE_PROGRAM_STOP, // [3] no blocks to run; like PROGRAM_END but without the M2 to reset gcode state + MACHINE_PROGRAM_END, // [4] program end (same as MACHINE_READY, really...) + MACHINE_CYCLE, // [5] machine is running; blocks still to run, or steppers are busy + MACHINE_INTERLOCK, // [6] machine in interlock state + MACHINE_SHUTDOWN, // [7] machine in shutdown state + MACHINE_PANIC // [8] machine in panic state } cmMachineState; typedef enum { @@ -139,7 +144,7 @@ typedef enum { // applies to cm.homing_state typedef enum { // applies to cm.probe_state PROBE_FAILED = 0, // probe reached endpoint without triggering PROBE_SUCCEEDED = 1, // probe was triggered, cm.probe_results has position - PROBE_WAITING // probe is waiting to be started + PROBE_WAITING = 2 // probe is waiting to be started or is running } cmProbeState; typedef enum { @@ -147,37 +152,6 @@ typedef enum { SAFETY_INTERLOCK_DISENGAGED } cmSafetyState; -/* The difference between NextAction and MotionMode is that NextAction is - * used by the current block, and may carry non-modal commands, whereas - * MotionMode persists across blocks (as G modal group 1) - */ - -typedef enum { // these are in order to optimized CASE statement - NEXT_ACTION_DEFAULT = 0, // Must be zero (invokes motion modes) - NEXT_ACTION_DWELL, // G4 - NEXT_ACTION_SET_G10_DATA, // G10 - NEXT_ACTION_GOTO_G28_POSITION, // G28 go to machine position - NEXT_ACTION_SET_G28_POSITION, // G28.1 set position in abs coordinates - NEXT_ACTION_SEARCH_HOME, // G28.2 homing cycle - NEXT_ACTION_SET_ABSOLUTE_ORIGIN, // G28.3 origin set - NEXT_ACTION_HOMING_NO_SET, // G28.4 homing cycle with no coordinate setting - NEXT_ACTION_GOTO_G30_POSITION, // G30 go to machine position - NEXT_ACTION_SET_G30_POSITION, // G30.1 set position in abs coordinates - NEXT_ACTION_STRAIGHT_PROBE_ERR, // G38.2 - NEXT_ACTION_STRAIGHT_PROBE, // G38.3 - NEXT_ACTION_STRAIGHT_PROBE_AWAY_ERR,// G38.4 - NEXT_ACTION_STRAIGHT_PROBE_AWAY, // G38.5 - NEXT_ACTION_SET_TL_OFFSET, // G43 - NEXT_ACTION_SET_ADDITIONAL_TL_OFFSET,// G43.2 - NEXT_ACTION_CANCEL_TL_OFFSET, // G49 - NEXT_ACTION_SET_ORIGIN_OFFSETS, // G92 - NEXT_ACTION_RESET_ORIGIN_OFFSETS, // G92.1 - NEXT_ACTION_SUSPEND_ORIGIN_OFFSETS, // G92.2 - NEXT_ACTION_RESUME_ORIGIN_OFFSETS, // G92.3 - NEXT_ACTION_JSON_COMMAND_SYNC, // M100 - NEXT_ACTION_JSON_WAIT // M101 -} cmNextAction; - typedef enum { // G Modal Group 1 MOTION_MODE_STRAIGHT_TRAVERSE=0, // G0 - straight traverse MOTION_MODE_STRAIGHT_FEED, // G1 - straight feed @@ -196,27 +170,6 @@ typedef enum { // G Modal Group 1 MOTION_MODE_CANNED_CYCLE_89 // G89 - boring, dwell, feed out } cmMotionMode; -typedef enum { // Used for detecting gcode errors. See NIST section 3.4 - MODAL_GROUP_G0 = 0, // {G10,G28,G28.1,G92} non-modal axis commands (note 1) - MODAL_GROUP_G1, // {G0,G1,G2,G3,G80} motion - MODAL_GROUP_G2, // {G17,G18,G19} plane selection - MODAL_GROUP_G3, // {G90,G91} distance mode - MODAL_GROUP_G5, // {G93,G94} feed rate mode - MODAL_GROUP_G6, // {G20,G21} units - MODAL_GROUP_G7, // {G40,G41,G42} cutter radius compensation - MODAL_GROUP_G8, // {G43,G49} tool length offset - MODAL_GROUP_G9, // {G98,G99} return mode in canned cycles - MODAL_GROUP_G12, // {G54,G55,G56,G57,G58,G59} coordinate system selection - MODAL_GROUP_G13, // {G61,G61.1,G64} path control mode - MODAL_GROUP_M4, // {M0,M1,M2,M30,M60} stopping - MODAL_GROUP_M6, // {M6} tool change - MODAL_GROUP_M7, // {M3,M4,M5} spindle turning - MODAL_GROUP_M8, // {M7,M8,M9} coolant (M7 & M8 may be active together) - MODAL_GROUP_M9 // {M48,M49} speed/feed override switches -} cmModalGroup; -#define MODAL_GROUP_COUNT (MODAL_GROUP_M9+1) -// Note 1: Our G0 omits G4,G30,G53,G92.1,G92.2,G92.3 as these have no axis components to error check - typedef enum { // canonical plane - translates to: // axis_0 axis_1 axis_2 CANON_PLANE_XY = 0, // G17 X Y Z @@ -372,9 +325,8 @@ typedef struct GCodeState { // Gcode model state - used by model, pl arc_distance_mode = ABSOLUTE_DISTANCE_MODE; absolute_override = ABSOLUTE_OVERRIDE_OFF; coord_system = ABSOLUTE_COORDS; - tool = 0; - tool_select = 0; - + tool = 1; + tool_select = 1; }; } GCodeState_t; @@ -401,100 +353,12 @@ typedef struct GCodeStateExtended { // Gcode dynamic state extensions - used // float cutter_radius; // D - cutter radius compensation (0 is off) // float cutter_length; // H - cutter length compensation (0 is off) + uint32_t last_line_number; // keep track of the last issued line number + uint16_t magic_end; } GCodeStateX_t; -typedef struct GCodeInput { // Gcode model inputs - meaning depends on context - - uint8_t next_action; // handles G modal group 1 moves & non-modals - cmMotionMode motion_mode; // Group1: G0, G1, G2, G3, G38.2, G80, G81, G82 - // G83, G84, G85, G86, G87, G88, G89 - - uint8_t program_flow; // used only by the gcode_parser - uint32_t linenum; // N word - float target[AXES]; // XYZABC where the move should go - - uint8_t H_word; // H word - used by G43s - uint8_t L_word; // L word - used by G10s - - float feed_rate; // F - normalized to millimeters/minute - uint8_t feed_rate_mode; // See cmFeedRateMode for settings - float parameter; // P - parameter used for dwell time in seconds, G10 coord select... - float arc_radius; // R - radius value in arc radius mode - float arc_offset[3]; // IJK - used by arc commands - - bool m48_enable; // M48/M49 input (enables for feed and spindle) - bool mfo_enable; // M50 feedrate override enable - bool mto_enable; // Mxx traverse override enable - bool sso_enable; // M51 spindle speed override enable - - uint8_t select_plane; // G17,G18,G19 - values to set plane to - uint8_t units_mode; // G20,G21 - 0=inches (G20), 1 = mm (G21) - uint8_t coord_system; // G54-G59 - select coordinate system 1-9 - uint8_t path_control; // G61... EXACT_PATH, EXACT_STOP, CONTINUOUS - uint8_t distance_mode; // G91 0=use absolute coords(G90), 1=incremental movement - uint8_t arc_distance_mode; // G90.1=use absolute IJK offsets, G91.1=incremental IJK offsets - uint8_t origin_offset_mode; // G92...TRUE=in origin offset mode - uint8_t absolute_override; // G53 TRUE = move using machine coordinates - this block only (G53) - uint8_t tool; // Tool after T and M6 (tool_select and tool_change) - uint8_t tool_select; // T value - T sets this value - uint8_t tool_change; // M6 tool change flag - moves "tool_select" to "tool" - uint8_t mist_coolant; // TRUE = mist on (M7), FALSE = off (M9) - uint8_t flood_coolant; // TRUE = flood on (M8), FALSE = off (M9) - - uint8_t spindle_control; // 0=OFF (M5), 1=CW (M3), 2=CCW (M4) - float spindle_speed; // in RPM - float spindle_override_factor; // 1.0000 x S spindle speed. Go up or down from there - uint8_t spindle_override_enable; // TRUE = override enabled - -// unimplemented gcode parameters -// float cutter_radius; // D - cutter radius compensation (0 is off) - -} GCodeInput_t; - -typedef struct GCodeFlags { // Gcode model input flags - bool next_action; - bool motion_mode; - bool modals[MODAL_GROUP_COUNT]; - bool program_flow; - bool linenum; - bool target[AXES]; - - bool H_word; - bool L_word; - bool feed_rate; - bool feed_rate_mode; - - bool m48_enable; - bool mfo_enable; - bool mto_enable; - bool sso_enable; - - bool select_plane; - bool units_mode; - bool coord_system; - bool path_control; - bool distance_mode; - bool arc_distance_mode; - bool origin_offset_mode; - bool absolute_override; - bool tool; - bool tool_select; - bool tool_change; - bool mist_coolant; - bool flood_coolant; - - bool spindle_control; - bool spindle_speed; - bool spindle_override_factor; - bool spindle_override_enable; - - bool parameter; - bool arc_radius; - bool arc_offset[3]; -} GCodeFlags_t; - /***************************************************************************** * CANONICAL MACHINE STRUCTURES */ @@ -568,6 +432,7 @@ typedef struct cmSingleton { // struct to manage cm globals and c cmHomingState homing_state; // home: homing cycle sub-state machine uint8_t homed[AXES]; // individual axis homing flags + bool probe_report_enable; // 0=disabled, 1=enabled cmProbeState probe_state[PROBES_STORED]; // probing state machine (simple) float probe_results[PROBES_STORED][AXES]; // probing results @@ -589,8 +454,6 @@ typedef struct cmSingleton { // struct to manage cm globals and c GCodeState_t *am; // active Gcode model is maintained by state management GCodeState_t gm; // core gcode model state GCodeStateX_t gmx; // extended gcode model state - GCodeInput_t gn; // gcode input values - transient - GCodeFlags_t gf; // gcode input flags - transient magic_t magic_end; } cmSingleton_t; @@ -637,6 +500,7 @@ void cm_set_motion_mode(GCodeState_t *gcode_state, const uint8_t motion_mode); void cm_set_tool_number(GCodeState_t *gcode_state, const uint8_t tool); void cm_set_absolute_override(GCodeState_t *gcode_state, const uint8_t absolute_override); void cm_set_model_linenum(const uint32_t linenum); +stat_t cm_check_linenum(); // Coordinate systems and offsets float cm_get_active_coord_offset(const uint8_t axis); @@ -650,6 +514,9 @@ void cm_update_model_position_from_runtime(void); void cm_finalize_move(void); stat_t cm_deferred_write_callback(void); void cm_set_model_target(const float target[], const bool flag[]); +bool cm_get_soft_limits(void); +void cm_set_soft_limits(bool enable); + stat_t cm_test_soft_limits(const float target[]); /*--- Canonical machining functions (loosely) defined by NIST [organized by NIST Gcode doc] ---*/ @@ -680,9 +547,11 @@ stat_t cm_select_plane(const uint8_t plane); // G stat_t cm_set_units_mode(const uint8_t mode); // G20, G21 stat_t cm_set_distance_mode(const uint8_t mode); // G90, G91 stat_t cm_set_arc_distance_mode(const uint8_t mode); // G90.1, G91.1 -stat_t cm_set_tl_offset(const uint8_t H_word, bool apply_additional); // G43, G43.2 +stat_t cm_set_tl_offset(const uint8_t H_word, const bool H_flag, // G43, G43.2 + const bool apply_additional); stat_t cm_cancel_tl_offset(void); // G49 -stat_t cm_set_g10_data(const uint8_t P_word, const uint8_t L_word, // G10 +stat_t cm_set_g10_data(const uint8_t P_word, const bool P_flag, // G10 + const uint8_t L_word, const bool L_flag, const float offset[], const bool flag[]); void cm_set_position(const uint8_t axis, const float position); // set absolute position - single axis @@ -722,17 +591,20 @@ stat_t cm_arc_feed(const float target[], const bool target_f[], // G // see spindle.h for spindle functions - which would go right here // Tool Functions (4.3.8) -stat_t cm_select_tool(const uint8_t tool); // T parameter -stat_t cm_change_tool(const uint8_t tool); // M6 +stat_t cm_select_tool(const uint8_t tool); // T parameter +stat_t cm_change_tool(const uint8_t tool); // M6 // Miscellaneous Functions (4.3.9) // see coolant.h for coolant functions - which would go right here -void cm_message(const char *message); // msg to console (e.g. Gcode comments) +void cm_message(const char *message); // msg to console (e.g. Gcode comments) void cm_reset_overrides(void); -stat_t cm_m48_enable(uint8_t enable); // M48, M49 -stat_t cm_mfo_enable(uint8_t enable); // M50 +stat_t cm_m48_enable(uint8_t enable); // M48, M49 +stat_t cm_mfo_enable(uint8_t enable); // M50 +stat_t cm_mfo_control(const float P_word, const bool P_flag); // M50 +stat_t cm_mto_control(const float P_word, const bool P_flag); // M50.1 +// See spindle.cpp for cm_sso_control() // M51 // Program Functions (4.3.10) void cm_request_feedhold(void); @@ -756,21 +628,22 @@ void cm_optional_program_stop(void); // M1 void cm_program_end(void); // M2 stat_t cm_json_command(char *json_string); // M100 +stat_t cm_json_command_immediate(char *json_string); // M100.1 stat_t cm_json_wait(char *json_string); // M102 /*--- Cycles ---*/ // Homing cycles -stat_t cm_homing_cycle_start(void); // G28.2 -stat_t cm_homing_cycle_start_no_set(void); // G28.4 +stat_t cm_homing_cycle_start(const float axes[], const bool flags[]); // G28.2 +stat_t cm_homing_cycle_start_no_set(const float axes[], const bool flags[]); // G28.4 stat_t cm_homing_cycle_callback(void); // G28.2/.4 main loop callback // Probe cycles -stat_t cm_straight_probe(float target[], - bool flags[], - bool failure_is_fatal, - bool moving_toward_switch); // G38.x +stat_t cm_straight_probe(float target[], bool flags[], // G38.x + bool trip_sense, bool alarm_flag); stat_t cm_probing_cycle_callback(void); // G38.x main loop callback +stat_t cm_get_prbr(nvObj_t *nv); // enable/disable probe report +stat_t cm_set_prbr(nvObj_t *nv); // Jogging cycle stat_t cm_jogging_cycle_callback(void); // jogging cycle main loop @@ -834,6 +707,9 @@ stat_t cm_set_mto(nvObj_t *nv); // set manual traverse override factor stat_t cm_set_tram(nvObj_t *nv); // attempt setting the rotation matrix stat_t cm_get_tram(nvObj_t *nv); // return if the rotation matrix is non-identity +stat_t cm_set_nxln(nvObj_t *nv); // set what value we expect the next line number to have +stat_t cm_get_nxln(nvObj_t *nv); // return what value we expect the next line number to have + /*--- text_mode support functions ---*/ #ifdef __TEXT_MODE @@ -884,6 +760,7 @@ stat_t cm_get_tram(nvObj_t *nv); // return if the rotation matrix is non- void cm_print_mto(nvObj_t *nv); void cm_print_tram(nvObj_t *nv); // print if the axis has been rotated + void cm_print_nxln(nvObj_t *nv); // print the value of the next line number expected void cm_print_am(nvObj_t *nv); // axis print functions void cm_print_fr(nvObj_t *nv); @@ -950,6 +827,7 @@ stat_t cm_get_tram(nvObj_t *nv); // return if the rotation matrix is non- #define cm_print_mto tx_print_stub #define cm_print_tram tx_print_stub + #define cm_print_nxln tx_print_stub #define cm_print_am tx_print_stub // axis print functions #define cm_print_fr tx_print_stub diff --git a/g2core/config.cpp b/g2core/config.cpp index d5bb4e42..00deabea 100644 --- a/g2core/config.cpp +++ b/g2core/config.cpp @@ -732,7 +732,7 @@ nvObj_t *nv_add_conditional_message(const char *string) // conditionally add void nv_print_list(stat_t status, uint8_t text_flags, uint8_t json_flags) { - if (js.json_mode == JSON_MODE) { + if ((js.json_mode == JSON_MODE) || (js.json_mode == MARLIN_COMM_MODE)) { json_print_list(status, json_flags); } else { text_print_list(status, text_flags); diff --git a/g2core/config.h b/g2core/config.h index 0dbe2607..61540726 100644 --- a/g2core/config.h +++ b/g2core/config.h @@ -195,7 +195,8 @@ typedef uint16_t index_t; // use this if there are > 255 indexed o typedef enum { TEXT_MODE = 0, // sticky text mode JSON_MODE, // sticky JSON mode - AUTO_MODE // auto-configure communications mode + AUTO_MODE, // auto-configure communications mode + MARLIN_COMM_MODE, // sticky marlin-compatibility mode (if compiled in) } commMode; typedef enum { diff --git a/g2core/config_app.cpp b/g2core/config_app.cpp index 3934cfca..db6d9230 100644 --- a/g2core/config_app.cpp +++ b/g2core/config_app.cpp @@ -165,13 +165,14 @@ const cfgItem_t cfgArray[] = { { "hom","homb",_f0, 0, cm_print_hom, get_ui8, set_01, (float *)&cm.homed[AXIS_B], false }, // B homed { "hom","homc",_f0, 0, cm_print_hom, get_ui8, set_01, (float *)&cm.homed[AXIS_C], false }, // C homed - { "prb","prbe",_f0, 0, tx_print_nul, get_ui8, set_ro, (float *)&cm.probe_state[0], 0 }, // probing state + { "prb","prbe",_f0, 0, tx_print_nul, get_ui8, set_ro, (float *)&cm.probe_state[0], 0 }, // probing state { "prb","prbx",_f0, 3, tx_print_nul, get_flt, set_ro, (float *)&cm.probe_results[0][AXIS_X], 0 }, { "prb","prby",_f0, 3, tx_print_nul, get_flt, set_ro, (float *)&cm.probe_results[0][AXIS_Y], 0 }, { "prb","prbz",_f0, 3, tx_print_nul, get_flt, set_ro, (float *)&cm.probe_results[0][AXIS_Z], 0 }, { "prb","prba",_f0, 3, tx_print_nul, get_flt, set_ro, (float *)&cm.probe_results[0][AXIS_A], 0 }, { "prb","prbb",_f0, 3, tx_print_nul, get_flt, set_ro, (float *)&cm.probe_results[0][AXIS_B], 0 }, { "prb","prbc",_f0, 3, tx_print_nul, get_flt, set_ro, (float *)&cm.probe_results[0][AXIS_C], 0 }, + { "prb","prbr",_f0, 0, tx_print_nul, cm_get_prbr, cm_get_prbr, nullptr, 0 }, // enable probe report. Init in cm_init { "jog","jogx",_f0, 0, tx_print_nul, get_nul, cm_run_jogx, (float *)&cm.jogging_dest, 0}, { "jog","jogy",_f0, 0, tx_print_nul, get_nul, cm_run_jogy, (float *)&cm.jogging_dest, 0}, @@ -603,14 +604,7 @@ const cfgItem_t cfgArray[] = { { "tof","tofa",_fip, 3, cm_print_cofs, get_flt, set_flt,(float *)&cm.tl_offset[AXIS_A], 0 }, { "tof","tofb",_fip, 3, cm_print_cofs, get_flt, set_flt,(float *)&cm.tl_offset[AXIS_B], 0 }, { "tof","tofc",_fip, 3, cm_print_cofs, get_flt, set_flt,(float *)&cm.tl_offset[AXIS_C], 0 }, -/* - { "tl","tlx",_fipc, 3, cm_print_cofs, cm_get_tof, set_flu,(float *)&cm.tl_offset[AXIS_X], 0 }, - { "tl","tly",_fipc, 3, cm_print_cofs, cm_get_tof, set_flu,(float *)&cm.tl_offset[AXIS_Y], 0 }, - { "tl","tlz",_fipc, 3, cm_print_cofs, cm_get_tof, set_flu,(float *)&cm.tl_offset[AXIS_Z], 0 }, - { "tl","tla",_fip, 3, cm_print_cofs, cm_get_tof, set_flt,(float *)&cm.tl_offset[AXIS_A], 0 }, - { "tl","tlb",_fip, 3, cm_print_cofs, cm_get_tof, set_flt,(float *)&cm.tl_offset[AXIS_B], 0 }, - { "tl","tlc",_fip, 3, cm_print_cofs, cm_get_tof, set_flt,(float *)&cm.tl_offset[AXIS_C], 0 }, -*/ + // Tool table offsets { "tt1","tt1x",_fipc, 3, cm_print_cofs, get_flt, set_flu,(float *)&cm.tt_offset[1][AXIS_X], TT1_X_OFFSET }, { "tt1","tt1y",_fipc, 3, cm_print_cofs, get_flt, set_flu,(float *)&cm.tt_offset[1][AXIS_Y], TT1_Y_OFFSET }, @@ -881,9 +875,10 @@ const cfgItem_t cfgArray[] = { #endif { "sys","ej", _fipn, 0, js_print_ej, get_ui8, json_set_ej,(float *)&cs.comm_mode, COMM_MODE }, { "sys","jv", _fipn, 0, js_print_jv, get_ui8, json_set_jv,(float *)&js.json_verbosity, JSON_VERBOSITY }, - { "sys","qv", _fipn, 0, qr_print_qv, get_ui8, set_012, (float *)&qr.queue_report_verbosity, QUEUE_REPORT_VERBOSITY }, - { "sys","sv", _fipn, 0, sr_print_sv, get_ui8, set_012, (float *)&sr.status_report_verbosity,STATUS_REPORT_VERBOSITY }, + { "sys","qv", _fipn, 0, qr_print_qv, get_ui8, set_012, (float *)&qr.queue_report_verbosity, QR_OFF}, // default to OFF, set to QUEUE_REPORT_VERBOSITY after connected + { "sys","sv", _fipn, 0, sr_print_sv, get_ui8, set_012, (float *)&sr.status_report_verbosity, SR_OFF}, // default to OFF, set to STATUS_REPORT_VERBOSITY after connectied { "sys","si", _fipn, 0, sr_print_si, get_int, sr_set_si, (float *)&sr.status_report_interval, STATUS_REPORT_INTERVAL_MS }, + { "", "nxln", _f0, 0, cm_print_nxln,cm_get_nxln,cm_set_nxln,(float *)&cs.null, 0 }, // Gcode defaults // NOTE: The ordering within the gcode defaults is important for token resolution. gc must follow gco @@ -1120,7 +1115,7 @@ const cfgItem_t cfgArray[] = { { "","g28",_f0, 0, tx_print_nul, get_grp, set_grp,(float *)&cs.null,0 }, // g28 home position { "","g30",_f0, 0, tx_print_nul, get_grp, set_grp,(float *)&cs.null,0 }, // g30 home position // +9 = 45 - { "","tof",_f0, 0, tx_print_nul, get_grp, set_grp,(float *)&cs.null,0 }, // tof offsets + { "","tof",_f0, 0, tx_print_nul, get_grp, set_grp,(float *)&cs.null,0 }, // tool offsets { "","tt1",_f0, 0, tx_print_nul, get_grp, set_grp,(float *)&cs.null,0 }, // tt offsets { "","tt2",_f0, 0, tx_print_nul, get_grp, set_grp,(float *)&cs.null,0 }, // tt offsets { "","tt3",_f0, 0, tx_print_nul, get_grp, set_grp,(float *)&cs.null,0 }, // tt offsets @@ -1130,13 +1125,13 @@ const cfgItem_t cfgArray[] = { { "","tt7",_f0, 0, tx_print_nul, get_grp, set_grp,(float *)&cs.null,0 }, // tt offsets { "","tt8",_f0, 0, tx_print_nul, get_grp, set_grp,(float *)&cs.null,0 }, // tt offsets { "","tt9",_f0, 0, tx_print_nul, get_grp, set_grp,(float *)&cs.null,0 }, // tt offsets - { "","tt10",_f0, 0, tx_print_nul, get_grp, set_grp,(float *)&cs.null,0 }, // tt offsets - { "","tt11",_f0, 0, tx_print_nul, get_grp, set_grp,(float *)&cs.null,0 }, // tt offsets - { "","tt12",_f0, 0, tx_print_nul, get_grp, set_grp,(float *)&cs.null,0 }, // tt offsets - { "","tt13",_f0, 0, tx_print_nul, get_grp, set_grp,(float *)&cs.null,0 }, // tt offsets - { "","tt14",_f0, 0, tx_print_nul, get_grp, set_grp,(float *)&cs.null,0 }, // tt offsets - { "","tt15",_f0, 0, tx_print_nul, get_grp, set_grp,(float *)&cs.null,0 }, // tt offsets - { "","tt16",_f0, 0, tx_print_nul, get_grp, set_grp,(float *)&cs.null,0 }, // tt offsets + { "","tt10",_f0, 0, tx_print_nul, get_grp, set_grp,(float *)&cs.null,0 }, // tt offsets + { "","tt11",_f0, 0, tx_print_nul, get_grp, set_grp,(float *)&cs.null,0 }, // tt offsets + { "","tt12",_f0, 0, tx_print_nul, get_grp, set_grp,(float *)&cs.null,0 }, // tt offsets + { "","tt13",_f0, 0, tx_print_nul, get_grp, set_grp,(float *)&cs.null,0 }, // tt offsets + { "","tt14",_f0, 0, tx_print_nul, get_grp, set_grp,(float *)&cs.null,0 }, // tt offsets + { "","tt15",_f0, 0, tx_print_nul, get_grp, set_grp,(float *)&cs.null,0 }, // tt offsets + { "","tt16",_f0, 0, tx_print_nul, get_grp, set_grp,(float *)&cs.null,0 }, // tt offsets // +17 = 62 { "","mpo",_f0, 0, tx_print_nul, get_grp, set_grp,(float *)&cs.null,0 }, // machine position group { "","pos",_f0, 0, tx_print_nul, get_grp, set_grp,(float *)&cs.null,0 }, // work position group @@ -1358,7 +1353,7 @@ static stat_t _do_group_list(nvObj_t *nv, char list[][TOKEN_LEN+1]) // helper to } _do_group(nv, list[i]); } - return (STAT_COMPLETE); + return (STAT_COMPLETE); // STAT_COMPLETE suppresses the normal response line } static stat_t _do_motors(nvObj_t *nv) // print parameters for all motor groups @@ -1368,7 +1363,7 @@ static stat_t _do_motors(nvObj_t *nv) // print parameters for all motor groups sprintf(group, "%d", i); _do_group(nv, group); } - return (STAT_COMPLETE); + return (STAT_COMPLETE); // STAT_COMPLETE suppresses the normal response line } static stat_t _do_axes(nvObj_t *nv) // print parameters for all axis groups @@ -1390,7 +1385,7 @@ static stat_t _do_inputs(nvObj_t *nv) // print parameters for all input groups sprintf(group, "di%d", i); _do_group(nv, group); } - return (STAT_COMPLETE); + return (STAT_COMPLETE); // STAT_COMPLETE suppresses the normal response line } static stat_t _do_outputs(nvObj_t *nv) // print parameters for all output groups @@ -1400,7 +1395,7 @@ static stat_t _do_outputs(nvObj_t *nv) // print parameters for all output group sprintf(group, "do%d", i); _do_group(nv, group); } - return (STAT_COMPLETE); + return (STAT_COMPLETE); // STAT_COMPLETE suppresses the normal response line } static stat_t _do_heaters(nvObj_t *nv) // print parameters for all heater groups @@ -1410,7 +1405,7 @@ static stat_t _do_heaters(nvObj_t *nv) // print parameters for all heater group sprintf(group, "he%d", i); _do_group(nv, group); } - return (STAT_COMPLETE); + return (STAT_COMPLETE); // STAT_COMPLETE suppresses the normal response line } static stat_t _do_all(nvObj_t *nv) // print all parameters diff --git a/g2core/controller.cpp b/g2core/controller.cpp old mode 100755 new mode 100644 index 8ba769ef..b33f128c --- a/g2core/controller.cpp +++ b/g2core/controller.cpp @@ -48,6 +48,10 @@ #include "MotatePower.h" +#if MARLIN_COMPAT_ENABLED == true +#include "marlin_compatibility.h" +#endif + /*********************************************************************************** **** STRUCTURE ALLOCATIONS ********************************************************* ***********************************************************************************/ @@ -167,6 +171,10 @@ static void _controller_HSM() DISPATCH(cm_jogging_cycle_callback()); // jog cycle operation DISPATCH(cm_deferred_write_callback()); // persist G10 changes when not in machining cycle +#if MARLIN_COMPAT_ENABLED == true + DISPATCH(marlin_callback()); // handle Marlin stuff - may return EAGAIN, must be after planner_callback! +#endif + //----- command readers and parsers --------------------------------------------------// DISPATCH(_sync_to_planner()); // ensure there is at least one free buffer in planning queue @@ -221,7 +229,17 @@ static void _dispatch_kernel(const devflags_t flags) // It's possible to let some stuff through, but that's not happening yet. return; } - + +#if MARLIN_COMPAT_ENABLED == true + // marlin_handle_fake_stk500 returns true if it responded to a stk500v2 message + if (marlin_handle_fake_stk500(cs.bufp)) { + js.json_mode = MARLIN_COMM_MODE; + sr.status_report_verbosity = SR_OFF; + qr.queue_report_verbosity = QR_OFF; + return; + } +#endif + while ((*cs.bufp == SPC) || (*cs.bufp == TAB)) { // position past any leading whitespace cs.bufp++; } @@ -263,6 +281,13 @@ static void _dispatch_kernel(const devflags_t flags) text_response(gcode_parser(cs.bufp), cs.saved_buf); } #endif + +#if MARLIN_COMPAT_ENABLED == true + else if (js.json_mode == MARLIN_COMM_MODE) { // handle marlin-specific protocol gcode + cs.comm_request_mode = MARLIN_COMM_MODE; // mode of this command + marlin_response(gcode_parser(cs.bufp), cs.saved_buf); + } +#endif else { // anything else is interpreted as Gcode cs.comm_request_mode = JSON_MODE; // mode of this command @@ -272,12 +297,37 @@ static void _dispatch_kernel(const devflags_t flags) nv_copy_string(nv, cs.bufp); // copy the Gcode line nv->valuetype = TYPE_STRING; status = gcode_parser(cs.bufp); + +#if MARLIN_COMPAT_ENABLED == true + if (js.json_mode == MARLIN_COMM_MODE) { // in case a marlin-specific M-code was found + cs.comm_request_mode = MARLIN_COMM_MODE; // mode of this command + // We are switching to marlin_comm_mode, kill status reports and queue reports + sr.status_report_verbosity = SR_OFF; + qr.queue_report_verbosity = QR_OFF; + marlin_response(status, cs.saved_buf); + return; + } +#endif + nv_print_list(status, TEXT_NO_PRINT, JSON_RESPONSE_FORMAT); sr_request_status_report(SR_REQUEST_TIMED); // generate incremental status report to show any changes } } /**** Local Functions ********************************************************/ + + +/* + * _reset_comms_mode() - reset the communications mode (and other effected settings) after connection or disconnection + */ +void _reset_comms_mode() { + // reset the communications mode + cs.comm_mode = COMM_MODE; + js.json_mode = (COMM_MODE < AUTO_MODE) ? COMM_MODE : JSON_MODE; + sr.status_report_verbosity = STATUS_REPORT_VERBOSITY; + qr.queue_report_verbosity = QUEUE_REPORT_VERBOSITY; +} + /* CONTROLLER STATE MANAGEMENT * _controller_state() - manage controller connection, startup, and other state changes */ @@ -287,9 +337,24 @@ static stat_t _controller_state() { if (cs.controller_state == CONTROLLER_CONNECTED) { // first time through after reset cs.controller_state = CONTROLLER_STARTUP; + // This is here just to put a small delay in before the startup message. +#if MARLIN_COMPAT_ENABLED == true + // For Marlin compatibility, we need this to be long enough for the UI to say something and reveal + // if it's a Marlin-compatible UI. + if (xio_connected()) { + // xio_connected will only return true for USB and other non-permanent connections + _connection_timeout.set(2000); + } else { + _connection_timeout.set(1); + } +#else _connection_timeout.set(10); +#endif } else if ((cs.controller_state == CONTROLLER_STARTUP) && (_connection_timeout.isPast())) { // first time through after reset + if (MARLIN_COMM_MODE != js.json_mode) { // MARLIN_COMM_MODE is always defined, just not always used + _reset_comms_mode(); + } cs.controller_state = CONTROLLER_READY; rpt_print_system_ready_message(); } @@ -302,9 +367,14 @@ static stat_t _controller_state() */ void controller_set_connected(bool is_connected) { + // turn off reports while no-one's listening or we determine what dialect they speak + sr.status_report_verbosity = SR_OFF; + qr.queue_report_verbosity = QR_OFF; + if (is_connected) { cs.controller_state = CONTROLLER_CONNECTED; // we JUST connected } else { // we just disconnected from the last device, we'll expect a banner again + _reset_comms_mode(); cs.controller_state = CONTROLLER_NOT_CONNECTED; } } @@ -321,7 +391,10 @@ void controller_set_muted(bool is_muted) { const bool only_to_muted = true; xio_writeline("{\"muted\":true}\n", only_to_muted); } else { - // one channel just got unmuted, announce it (except to the muted) + // we're assuming anything that can be muted speaks g2core-dialect JSON + _reset_comms_mode(); + + // something was just unmuted (usually because USB disconnected), tell it xio_writeline("{\"muted\":false}\n"); } } diff --git a/g2core/controller.h b/g2core/controller.h index 910e7822..7c6e3078 100755 --- a/g2core/controller.h +++ b/g2core/controller.h @@ -64,10 +64,12 @@ typedef struct controllerSingleton { // main TG controller struct uint32_t led_blink_rate; // used to flash indicator LED // communications state variables - // cs.comm_mode is the setting for the communications more + // cs.comm_mode is the setting for the communications mode // js.json_mode is the actual current mode (see also js.json_now) commMode comm_mode; // ej: 0=text mode sticky, 1=JSON mode sticky, 2=auto mode commMode comm_request_mode; // mode of request (may be different thatn the setting) + + bool responses_suppressed; // if true, responses are to be suppressed (for internal-file delivery) // controller serial buffers char *bufp; // pointer to primary or secondary in buffer diff --git a/g2core/cycle_homing.cpp b/g2core/cycle_homing.cpp index 8058213a..1c13a3f1 100644 --- a/g2core/cycle_homing.cpp +++ b/g2core/cycle_homing.cpp @@ -2,8 +2,8 @@ * cycle_homing.cpp - homing cycle extension to canonical_machine * This file is part of the g2core project * - * Copyright (c) 2010 - 2016 Alden S. Hart, Jr. - * Copyright (c) 2013 - 2016 Robert Giseburt + * Copyright (c) 2010 - 2017 Alden S. Hart, Jr. + * Copyright (c) 2013 - 2017 Robert Giseburt * * This file ("the software") is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2 as published by the @@ -49,6 +49,8 @@ struct hmHomingSingleton { // persistent homing runtime variables bool set_coordinates; // G28.4 flag. true = set coords to zero at the end of homing cycle stat_t (*func)(int8_t axis); // binding for callback function state machine + bool axis_flags[AXES]; // local storage for axis flags + // per-axis parameters float direction; // set to 1 for positive (max), -1 for negative (to min); float search_travel; // signed distance to travel in search @@ -152,7 +154,7 @@ static stat_t _set_homing_func(stat_t (*func)(int8_t axis)) { * to cm_get_runtime_busy() is about. */ -stat_t cm_homing_cycle_start(void) { +stat_t cm_homing_cycle_start(const float axes[], const bool flags[]) { // save relevant non-axis parameters from Gcode model hm.saved_units_mode = (cmUnitsMode)cm_get_units_mode(ACTIVE_MODEL); hm.saved_coord_system = (cmCoordSystem)cm_get_coord_system(ACTIVE_MODEL); @@ -160,6 +162,8 @@ stat_t cm_homing_cycle_start(void) { hm.saved_feed_rate_mode = (cmFeedRateMode)cm_get_feed_rate_mode(ACTIVE_MODEL); hm.saved_feed_rate = cm_get_feed_rate(ACTIVE_MODEL); + copy_vector(hm.axis_flags, flags); + // set working values cm_set_units_mode(MILLIMETERS); cm_set_distance_mode(INCREMENTAL_DISTANCE_MODE); @@ -178,8 +182,8 @@ stat_t cm_homing_cycle_start(void) { return (STAT_OK); } -stat_t cm_homing_cycle_start_no_set(void) { - cm_homing_cycle_start(); +stat_t cm_homing_cycle_start_no_set(const float axes[], const bool flags[]) { + cm_homing_cycle_start(axes, flags); hm.set_coordinates = false; // set flag to not update position variables at the end of the cycle return (STAT_OK); } @@ -407,38 +411,38 @@ static stat_t _homing_finalize_exit(int8_t axis) // third part of return to hom static int8_t _get_next_axis(int8_t axis) { #if (HOMING_AXES <= 4) if (axis == -1) { // inelegant brute force solution - if (cm.gf.target[AXIS_Z]) { + if (hm.axis_flags[AXIS_Z]) { return (AXIS_Z); } - if (cm.gf.target[AXIS_X]) { + if (hm.axis_flags[AXIS_X]) { return (AXIS_X); } - if (cm.gf.target[AXIS_Y]) { + if (hm.axis_flags[AXIS_Y]) { return (AXIS_Y); } - if (cm.gf.target[AXIS_A]) { + if (hm.axis_flags[AXIS_A]) { return (AXIS_A); } return (-2); // error } else if (axis == AXIS_Z) { - if (cm.gf.target[AXIS_X]) { + if (hm.axis_flags[AXIS_X]) { return (AXIS_X); } - if (cm.gf.target[AXIS_Y]) { + if (hm.axis_flags[AXIS_Y]) { return (AXIS_Y); } - if (cm.gf.target[AXIS_A]) { + if (hm.axis_flags[AXIS_A]) { return (AXIS_A); } } else if (axis == AXIS_X) { - if (cm.gf.target[AXIS_Y]) { + if (hm.axis_flags[AXIS_Y]) { return (AXIS_Y); } - if (cm.gf.target[AXIS_A]) { + if (hm.axis_flags[AXIS_A]) { return (AXIS_A); } } else if (axis == AXIS_Y) { - if (cm.gf.target[AXIS_A]) { + if (hm.axis_flags[AXIS_A]) { return (AXIS_A); } } @@ -446,77 +450,77 @@ static int8_t _get_next_axis(int8_t axis) { #else if (axis == -1) { - if (cm.gf.target[AXIS_Z]) { + if (hm.axis_flags[AXIS_Z]) { return (AXIS_Z); } - if (cm.gf.target[AXIS_X]) { + if (hm.axis_flags[AXIS_X]) { return (AXIS_X); } - if (cm.gf.target[AXIS_Y]) { + if (hm.axis_flags[AXIS_Y]) { return (AXIS_Y); } - if (cm.gf.target[AXIS_A]) { + if (hm.axis_flags[AXIS_A]) { return (AXIS_A); } - if (cm.gf.target[AXIS_B]) { + if (hm.axis_flags[AXIS_B]) { return (AXIS_B); } - if (cm.gf.target[AXIS_C]) { + if (hm.axis_flags[AXIS_C]) { return (AXIS_C); } return (-2); // error } else if (axis == AXIS_Z) { - if (cm.gf.target[AXIS_X]) { + if (hm.axis_flags[AXIS_X]) { return (AXIS_X); } - if (cm.gf.target[AXIS_Y]) { + if (hm.axis_flags[AXIS_Y]) { return (AXIS_Y); } - if (cm.gf.target[AXIS_A]) { + if (hm.axis_flags[AXIS_A]) { return (AXIS_A); } - if (cm.gf.target[AXIS_B]) { + if (hm.axis_flags[AXIS_B]) { return (AXIS_B); } - if (cm.gf.target[AXIS_C]) { + if (hm.axis_flags[AXIS_C]) { return (AXIS_C); } } else if (axis == AXIS_X) { - if (cm.gf.target[AXIS_Y]) { + if (hm.axis_flags[AXIS_Y]) { return (AXIS_Y); } - if (cm.gf.target[AXIS_A]) { + if (hm.axis_flags[AXIS_A]) { return (AXIS_A); } - if (cm.gf.target[AXIS_B]) { + if (hm.axis_flags[AXIS_B]) { return (AXIS_B); } - if (cm.gf.target[AXIS_C]) { + if (hm.axis_flags[AXIS_C]) { return (AXIS_C); } } else if (axis == AXIS_Y) { - if (cm.gf.target[AXIS_A]) { + if (hm.axis_flags[AXIS_A]) { return (AXIS_A); } - if (cm.gf.target[AXIS_B]) { + if (hm.axis_flags[AXIS_B]) { return (AXIS_B); } - if (cm.gf.target[AXIS_C]) { + if (hm.axis_flags[AXIS_C]) { return (AXIS_C); } } else if (axis == AXIS_A) { - if (cm.gf.target[AXIS_B]) { + if (hm.axis_flags[AXIS_B]) { return (AXIS_B); } - if (cm.gf.target[AXIS_C]) { + if (hm.axis_flags[AXIS_C]) { return (AXIS_C); } } else if (axis == AXIS_B) { - if (cm.gf.target[AXIS_C]) { + if (hm.axis_flags[AXIS_C]) { return (AXIS_C); } } return (-1); // done #endif // (HOMING_AXES <= 4) -} +} \ No newline at end of file diff --git a/g2core/cycle_probing.cpp b/g2core/cycle_probing.cpp index 3bb194e8..5438f8d3 100644 --- a/g2core/cycle_probing.cpp +++ b/g2core/cycle_probing.cpp @@ -2,7 +2,7 @@ * cycle_probing.c - probing cycle extension to canonical_machine.c * This file is part of the g2core project * - * Copyright (c) 2010 - 2016 Alden S Hart, Jr., Sarah Tappon, Tom Cauchois, Robert Giseburt + * Copyright (c) 2010 - 2017 Alden S Hart, Jr., Sarah Tappon, Tom Cauchois, Robert Giseburt * With contributions from Other Machine Company. * * This file ("the software") is free software: you can redistribute it and/or modify @@ -41,93 +41,68 @@ /**** Probe singleton structure ****/ -#define MINIMUM_PROBE_TRAVEL 0.254 +#define MINIMUM_PROBE_TRAVEL 0.254 // mm of travel below which the probe will err out -struct pbProbingSingleton { // persistent probing runtime variables - bool waiting_for_motion_end; // flag to use to now when the motion has ended - bool failure_is_fatal; // flag for G38.2 and G38.4, where failure is NOT an option - bool moving_toward_switch; // flag for G38.4 and G38.5, where we move off of the switch +struct pbProbingSingleton { // persistent probing runtime variables - stat_t (*func)(); // binding for callback function state machine - - // controls for probing cycle - uint8_t probe_input; // which input should we check? - - // state saved from gcode model - uint8_t saved_distance_mode; // G90,G91 global setting - uint8_t saved_coord_system; // G54 - G59 setting - float saved_jerk[AXES]; // saved and restored for each axis - - // probe destination + // probe target float target[AXES]; bool flags[AXES]; + + // controls for probing cycle + int8_t probe_input; // digital input to read + bool trip_sense; // true if contact CLOSURE trips probe (true for G38.2 and G38.3) + bool alarm_flag; // true if failure triggers alarm (true for G38.2 and G38.4) + bool wait_for_motion_end; // flag to know when the motion has ended + stat_t (*func)(); // binding for callback function state machine + + // saved gcode model state + cmUnitsMode saved_units_mode; // G20,G21 setting + cmDistanceMode saved_distance_mode; // G90,G91 global setting + bool saved_soft_limits; // turn off soft limits during probing + float saved_jerk[AXES]; // saved and restored for each axis }; static struct pbProbingSingleton pb; /**** NOTE: global prototypes and other .h info is located in canonical_machine.h ****/ -static stat_t _probing_init(); static stat_t _probing_start(); static stat_t _probing_backoff(); static stat_t _probing_finish(); -static stat_t _probing_finalize_exit(); -static stat_t _probing_error_exit(int8_t axis); -static stat_t _probe_axis_move(const float target[], bool exact_position); -static void _probe_axis_move_callback(float* vect, bool* flag); +static stat_t _probing_exception_exit(stat_t status); +static stat_t _probe_move(const float target[], const bool flags[]); +static void _send_probe_report(void); -/**** HELPERS *************************************************************************** - * _set_pb_func() - a convenience for setting the next dispatch vector and exiting - */ - -static stat_t _set_pb_func(uint8_t (*func)()) { - pb.func = func; - return (STAT_EAGAIN); +// helper +static void _motion_end_callback(float* vect, bool* flag) +{ + pb.wait_for_motion_end = false; } /*********************************************************************************** - **** G38.x Probing Cycle *********************************************************** + **** G38.x Probing Cycle ********************************************************** ***********************************************************************************/ -/**************************************************************************************** - * cm_probing_cycle_start() - G38.x probing cycle using limit switches - * cm_probing_cycle_callback() - main loop callback for running the probing cycle +/*********************************************************************************** + * cm_probing_cycle_start() - G38.x probing cycle using contact (digital input) * - * All cm_probe_cycle_start does is prevent any new commands from queueing to the - * planner so that the planner can move to a stop and report MACHINE_PROGRAM_STOP. - * OK, it also queues the function that's called once motion has stopped. - * - * Note: When coding a cycle (like this one) you get to perform one queued - * move per entry into the continuation, then you must exit. We put two buffer - * items into the queue: We queue a move, then we queue a "command" that simply - * sets a flag in the probing object (pb.waiting_for_motion_end) to tell us that - * the move has finished. The runtime has a special exception for probing and - * homing where if a move is interrupted it clears it out of the queue. + * cm_probe_cycle_start() is the entry point for a probe cycle. It checks for + * some errors, sets up the cycle, then prevents any new commands from queuing + * to the planner so that the planner can move to a stop and report motion stopped. * * --- Some further details --- - * Starting from the definition of G38.x from the LinuxCNC docs: - * http://linuxcnc.org/docs/2.6/html/gcode/gcode.html#sec:G38-probe * - * Once we are past the starting conditions for the probe to succeed as listed - * in the LinuxCNC documentation, we will then execute the move. After the move - * we interpret "success" as the probe value changing in the correct direction, - * and "failure" as it not changing. IOW, the move can finish and the switch not - * change, which we consider to be a failure. + * Start with the G38.x documentation, which is not repeated here. + * https://github.com/synthetos/g2/wiki/Gcode-Probes * - * Taking polarity of the switch into account to give a value of Active or - * Inactive, for G38.2 and G38.3, success requires going from Inactive to Active, - * and for G38.4 and G38.5 success requires an edge from Inactive to Active. - * - * For G38.2 and G38.4 we also put the machine into an ALARM state if the probing - * "fails". - * - * When the switch fires, the input interrupt takes a snapshot of the internal + * When the probe input fires the input interrupt takes a snapshot of the internal * encoders, then requests a "high speed" feedhold. We then run forward kinematics * on the encoder snapshot to get the reported position. We also execute a move * from the final position (after the feedhold) back to the point we report. * * Additionally, we record the last PROBES_STORED (at least 3) probe points that - * succeded. The current or most recent probe (be it sucess, failure, or - * in-progress) occupies one of those positions, wich is the one reported by the + * succeeded. The current or most recent probe (be it success, failure, or + * in-progress) occupies one of those positions, which is the one reported by the * "prb" JSON. * * Internally we store the active/most recent probe in cm.probe_results[0] and @@ -135,332 +110,279 @@ static stat_t _set_pb_func(uint8_t (*func)()) { * PROBE_SUCCEEDED, then we roll 0 to 1, and 1 to 2, up to PROBES_STORED-1. * The oldest probe is "lost." * + * Alarms and exceptions: It is *not* necessarily an error condition for the + * probe not to trigger, depending on the G38.x command received. It is an error + * for the limit or homing switches to fire, or for some other configuration error. + * These are trapped and cause Alarms. + * + * Note: Spindle and coolant are not affected during probing. Some probes require + * the spindle to be turned on. + * + * Note: When coding a cycle (like this one) you get to perform one queued + * move per entry into the continuation, then you must exit. We put two buffer + * items into the queue: We queue a move, then we queue a "command" that simply + * sets a flag in the probing object (pb.waiting_for_motion_end) to tell us that + * the move has finished. The runtime has a special exception for probing and + * homing where if a move is interrupted it clears it out of the queue. + * + * You must also wait until the last move has actually completed before declaring + * the cycle to be done. Otherwise there is a nasty race condition in the + * _controller_HSM() that may accept the next command before the position of the + * final move has been recorded in the Gcode model. That's part of what what the + * wait_for_motion_end callback is about. */ -uint8_t cm_straight_probe(float target[], bool flags[], bool failure_is_fatal, bool moving_toward_switch) { - // trap zero feed rate condition +uint8_t cm_straight_probe(float target[], bool flags[], bool trip_sense, bool alarm_flag) +{ + // error if zero feed rate if (fp_ZERO(cm.gm.feed_rate)) { - return (STAT_GCODE_FEEDRATE_NOT_SPECIFIED); + return(cm_alarm(STAT_GCODE_FEEDRATE_NOT_SPECIFIED, "Feedrate is zero")); } // error if no axes specified - if (!flags[AXIS_X] && !flags[AXIS_Y] && !flags[AXIS_Z]) { - return (STAT_GCODE_AXIS_IS_MISSING); + if (!(flags[AXIS_X] | flags[AXIS_Y] | flags[AXIS_Z] | + flags[AXIS_A] | flags[AXIS_B] | flags[AXIS_C])) { + return(cm_alarm(STAT_GCODE_AXIS_IS_MISSING, "Axis is missing")); } - pb.failure_is_fatal = failure_is_fatal; - pb.moving_toward_switch = moving_toward_switch; + // initialize the probe input; error if no probe input specified + if ((pb.probe_input = gpio_get_probing_input()) == -1) { + return(cm_alarm(STAT_NO_PROBE_INPUT_CONFIGURED, "No probe input")); + } - // set probe move endpoint - copy_vector(pb.target, target); // set probe move endpoint - copy_vector(pb.flags, flags); // set axes involved on the move + // setup + pb.alarm_flag = alarm_flag; // set true to enable probe fail alarms (all exceptions alarm regardless) + pb.trip_sense = trip_sense; // set to sense of "tripped" contact + pb.func = _probing_start; // bind probing start function + + cm_set_model_target(target, flags); // convert target to canonical form taking all offsets into account + copy_vector(pb.target, cm.gm.target); // cm_set_model_target() sets target in gm, move it to pb + copy_vector(pb.flags, flags); // set axes involved in the move // if the previous probe succeeded, roll probes to the next position if (cm.probe_state[0] == PROBE_SUCCEEDED) { for (uint8_t n = PROBES_STORED - 1; n > 0; n--) { cm.probe_state[n] = cm.probe_state[n - 1]; - for (uint8_t axis = 0; axis < AXES; axis++) { cm.probe_results[n][axis] = cm.probe_results[n - 1][axis]; } + for (uint8_t axis = 0; axis < AXES; axis++) { + cm.probe_results[n][axis] = cm.probe_results[n - 1][axis]; + } } } - - // clear the old probe position - clear_vector(cm.probe_results[0]); - - // NOTE: relying on probe_result will not detect a probe to 0,0,0. - - cm.probe_state[0] = PROBE_WAITING; // wait until planner queue empties before completing initialization - pb.waiting_for_motion_end = true; + // clear the old probe results + clear_vector(cm.probe_results[0]); // NOTE: relying on cm.probe_results will not detect a probe to 0,0,0. // queue a function to let us know when we can start probing - // the last two arguments are ignored anyway - mp_queue_command(_probe_axis_move_callback, nullptr, nullptr); - - pb.func = _probing_init; // bind probing initialization function + cm.probe_state[0] = PROBE_WAITING; // wait until planner queue empties before starting movement + pb.wait_for_motion_end = true; + mp_queue_command(_motion_end_callback, nullptr, nullptr); // note: these args are ignored return (STAT_OK); -} +} -/* +/*********************************************************************************** * cm_probing_cycle_callback() - handle probing progress * - * This is called regularly from the controller. If we report NOOP, the - * controller will continue with other tasks. Otherwise the controller will - * not execut any later tasks, including read any more "data". - * - * Note: When coding a cycle (like this one) you must wait until - * the last move has actually been queued (or has finished) before declaring - * the cycle to be done. Otherwise there is a nasty race condition in - * _controller_HSM() that may accept the next command before the position of - * the final move has been recorded in the Gcode model. That's what the call - * to cm_get_runtime_busy() is about. + * This is called regularly from the controller. If we report NOOP, the controller + * will continue with other tasks. Otherwise the controller will not execute any + * later tasks, including read any more "data". */ -uint8_t cm_probing_cycle_callback(void) { - if ((cm.cycle_state != CYCLE_PROBE) && (cm.probe_state[0] != PROBE_WAITING)) { // exit if not in a homing cycle - return (STAT_NOOP); +uint8_t cm_probing_cycle_callback(void) +{ + if ((cm.cycle_state != CYCLE_PROBE) && (cm.probe_state[0] != PROBE_WAITING)) { + return (STAT_NOOP); // exit if not in a probing cycle } - if (pb.waiting_for_motion_end) { // sync to planner move ends (using callback) + if (pb.wait_for_motion_end) { // sync to planner move ends (using callback) return (STAT_EAGAIN); } - return (pb.func()); // execute the current probing move + return (pb.func()); // execute the current probing move } -/* - * _probing_init() - G38.2 probing cycle using limit switches - * - * These initializations are required before starting the probing cycle. - * They must be done after the planner has exhausted all current CYCLE moves as - * they affect the runtime (specifically the switch modes). Side effects would - * include limit switches initiating probe actions instead of just killing movement +/*********************************************************************************** + * _probing_start() - start the probe or skip it if contact is already active */ -static uint8_t _probing_init() { - float start_position[AXES]; - +static uint8_t _probing_start() +{ // so optimistic... ;) - // NOTE: it is *not* an error condition for the probe not to trigger. - // it is an error for the limit or homing switches to fire, or for some other configuration error. + // These initializations are required before starting the probing cycle but must + // be done after the planner has exhausted all current moves as they affect the + // runtime (specifically the digital input modes). Side effects would include + // limit switches initiating probe actions instead of just killing movement + cm.probe_state[0] = PROBE_FAILED; - cm.machine_state = MACHINE_CYCLE; - cm.cycle_state = CYCLE_PROBE; + cm.machine_state = MACHINE_CYCLE; + cm.cycle_state = CYCLE_PROBE; // save relevant non-axis parameters from Gcode model - pb.saved_coord_system = cm_get_coord_system(ACTIVE_MODEL); - pb.saved_distance_mode = cm_get_distance_mode(ACTIVE_MODEL); - + pb.saved_distance_mode = (cmDistanceMode)cm_get_distance_mode(ACTIVE_MODEL); + pb.saved_units_mode = (cmUnitsMode)cm_get_units_mode(ACTIVE_MODEL); + pb.saved_soft_limits = cm_get_soft_limits(); + cm_set_soft_limits(false); + // set working values cm_set_distance_mode(ABSOLUTE_DISTANCE_MODE); - cm_set_coord_system(ABSOLUTE_COORDS); // probing is done in machine coordinates + cm_set_units_mode(MILLIMETERS); - // initialize the axes - save the jerk settings & switch to the jerk_homing settings + // Save the current jerk settings & change to the high-speed jerk settings for (uint8_t axis = 0; axis < AXES; axis++) { pb.saved_jerk[axis] = cm_get_axis_jerk(axis); // save the max jerk value cm_set_axis_jerk(axis, cm.a[axis].jerk_high); // use the high-speed jerk for probe - start_position[axis] = cm_get_absolute_position(ACTIVE_MODEL, axis); } - // error if the probe target is too close to the current position - if (get_axis_vector_length(start_position, pb.target) < MINIMUM_PROBE_TRAVEL) { - _probing_error_exit(-2); + // Error if the probe target is too close to the current position + if (get_axis_vector_length(cm.gmx.position, pb.target) < MINIMUM_PROBE_TRAVEL) { + return(_probing_exception_exit(STAT_PROBE_TRAVEL_TOO_SMALL)); } - // error if the probe target requires a move along the A/B/C axes - for (uint8_t axis = AXIS_A; axis < AXES; axis++) { - // if (fp_NE(start_position[axis], pb.target[axis])) { // old style - if (fp_TRUE(pb.flags[axis])) { - // if (pb.flags[axis]) { // will reduce to this once flags are booleans - _probing_error_exit(axis); - } - } - - // initialize the probe switch - // TODO -- for now we hard code it to z homing switch - if (fp_ZERO(cm.a[AXIS_Z].homing_input)) { - return (_probing_error_exit(-2)); - } - pb.probe_input = cm.a[AXIS_Z].homing_input; gpio_set_probing_mode(pb.probe_input, true); - // turn off spindle and start the move - cm_spindle_optional_pause(true); // pause the spindle if it's on - return (_set_pb_func(_probing_start)); // start the probe move -} - -/* - * _probing_start() - start the probe or skip it if switch is already active - */ - -static stat_t _probing_start() { - // initial probe state, don't probe if we're already contacted! - int8_t probe = gpio_read_input(pb.probe_input); - - // INPUT_INACTIVE is the right start condition for G38.2 and G38.3 - // INPUT_ACTIVE is the right start condition for G38.4 and G38.5 - // Note that we're testing for SUCCESS here - if (probe == (pb.moving_toward_switch ? INPUT_INACTIVE : INPUT_ACTIVE)) { - _probe_axis_move(pb.target, false); - return (_set_pb_func(_probing_backoff)); + // Get initial probe state, and don't probe if we're already tripped. + // If the initial input is the same as the trip_sense it's an error. + if (pb.trip_sense == gpio_read_input(pb.probe_input)) { // == is exclusive nor for booleans + return(_probing_exception_exit(STAT_PROBE_IS_ALREADY_TRIPPED)); } - cm.probe_state[0] = PROBE_FAILED; // we failed - return (_set_pb_func(_probing_finish)); + // Everything checks out. Run the probe move + _probe_move(pb.target, pb.flags); + pb.func = _probing_backoff; + return (STAT_EAGAIN); } -/* +/*********************************************************************************** * _probing_backoff() - runs after the probe move, whether it contacted or not * * Back off to the measured touch position captured by encoder snapshot */ -static stat_t _probing_backoff() { - // Test if we've contacted - int8_t probe = gpio_read_input(pb.probe_input); +static stat_t _probing_backoff() +{ + // Test if we've contacted. If so, do the backoff. Convert the contact position + // captured from the encoder in step space to steps to mm. The encoder snapshot + // was taken by input interrupt at the time of closure. - // INPUT_ACTIVE is the right end condition for G38.2 and G38.3 - // INPUT_INACTIVE is the right end condition for G38.4 and G38.5 - // Note that we're testing for SUCCESS here - if (probe == (pb.moving_toward_switch ? INPUT_ACTIVE : INPUT_INACTIVE)) { + if (pb.trip_sense == gpio_read_input(pb.probe_input)) { // exclusive or for booleans cm.probe_state[0] = PROBE_SUCCEEDED; - - // capture contact position in step space and convert from steps to mm. - // snapshot was taken by switch interrupt at the time of closure float contact_position[AXES]; kn_forward_kinematics(en_get_encoder_snapshot_vector(), contact_position); - - _probe_axis_move(contact_position, true); // NB: feed rate is the same as the probe move + _probe_move(contact_position, pb.flags); // NB: feed rate is the same as the probe move } else { cm.probe_state[0] = PROBE_FAILED; } - return (_set_pb_func(_probing_finish)); -} - -static stat_t _probe_axis_move(const float target[], bool exact_position) { - auto stored_units_mode = cm.gm.units_mode; - auto stored_distance_mode = cm.gm.distance_mode; - if (exact_position) { - cm_set_absolute_override(MODEL, ABSOLUTE_OVERRIDE_ON); // Position was stored in absolute coords - cm.gm.units_mode = MILLIMETERS; - cm.gm.distance_mode = ABSOLUTE_DISTANCE_MODE; - } - - for (uint8_t axis = AXIS_X; axis < AXES; axis++) { // set all positions - cm_set_position(axis, mp_get_runtime_absolute_position(axis)); - } - - // set this BEFORE the motion starts - pb.waiting_for_motion_end = true; - - cm_straight_feed(target, pb.flags); - - if (exact_position) { - cm.gm.units_mode = stored_units_mode; - cm.gm.distance_mode = stored_distance_mode; - } - - // the last two arguments are ignored anyway - mp_queue_command(_probe_axis_move_callback, nullptr, nullptr); - + pb.func = _probing_finish; return (STAT_EAGAIN); } -static void _probe_axis_move_callback(float* vect, bool* flag) { pb.waiting_for_motion_end = false; } - -/* - * _probing_finish() - report probe results and clean up +/*********************************************************************************** + * _probe_move() - function to execute probing moves + * + * target[] must be provided in machine canonical coordinates (absolute, mm) + * cm_set_absolute_override() also zeros work offsets, which are restored on exit. */ -static stat_t _probing_finish() { +static stat_t _probe_move(const float target[], const bool flags[]) +{ + cm_set_absolute_override(MODEL, ABSOLUTE_OVERRIDE_ON); + pb.wait_for_motion_end = true; // set this BEFORE the motion starts + cm_straight_feed(target, flags); + mp_queue_command(_motion_end_callback, nullptr, nullptr); // the last two arguments are ignored anyway + return (STAT_EAGAIN); +} + +/*********************************************************************************** + * _probe_restore_settings() - helper for both exits + * _probing_exception_exit() - exit for probes that hit an exception + * _probing_finish() - exit for successful and non-contacted (failed) probes + */ + +static void _probe_restore_settings() +{ + gpio_set_probing_mode(pb.probe_input, false); // set input back to normal operation + + for (uint8_t axis = 0; axis < AXES; axis++) { // restore axis jerks + cm.a[axis].jerk_max = pb.saved_jerk[axis]; + } + cm_set_absolute_override(MODEL, ABSOLUTE_OVERRIDE_OFF); // release abs override and restore work offsets + cm_set_distance_mode(pb.saved_distance_mode); + cm_set_units_mode(pb.saved_units_mode); + cm_set_soft_limits(pb.saved_soft_limits); + + cm_set_motion_mode(MODEL, MOTION_MODE_CANCEL_MOTION_MODE);// cancel feed modes used during probing + cm_canned_cycle_end(); + sr_request_status_report(SR_REQUEST_IMMEDIATE); // do this last +} + +static stat_t _probing_exception_exit(stat_t status) +{ + _probe_restore_settings(); // cleanup first + return (cm_alarm(status, "probe error")); +} + +static stat_t _probing_finish() +{ + _probe_restore_settings(); // cleanup first + + // set absolute position in probe results vector for (uint8_t axis = 0; axis < AXES; axis++) { cm.probe_results[0][axis] = cm_get_absolute_position(ACTIVE_MODEL, axis); } - - // If probe was successful the 'e' word == 1, otherwise e == 0 to signal an error - char buf[32]; - char* bufp = buf; - bufp += sprintf(bufp, "{\"prb\":{\"e\":%i, \"", (int)cm.probe_state[0]); - if (pb.flags[AXIS_X]) { - sprintf(bufp, "x\":%0.3f}}\n", cm.probe_results[0][AXIS_X]); + + // handle failed probes - successful probes already set the flag + if (cm.probe_state[0] == PROBE_FAILED) { + if (pb.alarm_flag) { + cm_alarm(STAT_PROBE_CYCLE_FAILED, "probing failed"); + } } - if (pb.flags[AXIS_Y]) { - sprintf(bufp, "y\":%0.3f}}\n", cm.probe_results[0][AXIS_Y]); - } - if (pb.flags[AXIS_Z]) { - sprintf(bufp, "z\":%0.3f}}\n", cm.probe_results[0][AXIS_Z]); - } - if (pb.flags[AXIS_A]) { - sprintf(bufp, "a\":%0.3f}}\n", cm.probe_results[0][AXIS_A]); - } - if (pb.flags[AXIS_B]) { - sprintf(bufp, "b\":%0.3f}}\n", cm.probe_results[0][AXIS_B]); - } - if (pb.flags[AXIS_C]) { - sprintf(bufp, "c\":%0.3f}}\n", cm.probe_results[0][AXIS_C]); - } - xio_writeline(buf); - - return (_set_pb_func(_probing_finalize_exit)); + _send_probe_report(); + return (STAT_OK); } /* - * _probe_restore_settings() - * _probing_finalize_exit() - * _probing_error_exit() + * _probe_report() - report probe results - must update results vector first */ -static void _probe_restore_settings() { - // set input back to normal operation - gpio_set_probing_mode(pb.probe_input, false); +static void _send_probe_report() { - // restore axis jerk - for (uint8_t axis = 0; axis < AXES; axis++) { cm.a[axis].jerk_max = pb.saved_jerk[axis]; } - - // restore coordinate system and distance mode - cm_set_coord_system(pb.saved_coord_system); - cm_set_distance_mode(pb.saved_distance_mode); - - // restart spindle if it was paused - cm_spindle_resume(spindle.dwell_seconds); - - // cancel the feed modes used during probing - cm_set_motion_mode(MODEL, MOTION_MODE_CANCEL_MOTION_MODE); - cm_canned_cycle_end(); + if (cm.probe_report_enable) { + // If probe was successful the 'e' word == 1, otherwise e == 0 to signal an error + char buf[32]; + char* bufp = buf; + bufp += sprintf(bufp, "{\"prb\":{\"e\":%i, \"", (int)cm.probe_state[0]); + if (pb.flags[AXIS_X]) { + sprintf(bufp, "x\":%0.3f}}\n", cm.probe_results[0][AXIS_X]); + } + if (pb.flags[AXIS_Y]) { + sprintf(bufp, "y\":%0.3f}}\n", cm.probe_results[0][AXIS_Y]); + } + if (pb.flags[AXIS_Z]) { + sprintf(bufp, "z\":%0.3f}}\n", cm.probe_results[0][AXIS_Z]); + } + if (pb.flags[AXIS_A]) { + sprintf(bufp, "a\":%0.3f}}\n", cm.probe_results[0][AXIS_A]); + } + if (pb.flags[AXIS_B]) { + sprintf(bufp, "b\":%0.3f}}\n", cm.probe_results[0][AXIS_B]); + } + if (pb.flags[AXIS_C]) { + sprintf(bufp, "c\":%0.3f}}\n", cm.probe_results[0][AXIS_C]); + } + xio_writeline(buf); + } } -static stat_t _probing_finalize_exit() { - _probe_restore_settings(); - if (cm.probe_state[0] == PROBE_SUCCEEDED) { - return (STAT_OK); - } +/* + * cm_get_prbr() - get probe report enable setting + * cm_set_prbr() - set probe report enable setting + */ - if (pb.failure_is_fatal) { - cm_alarm(STAT_PROBE_CYCLE_FAILED, "Probing error - probe failed to change."); - } - return (STAT_PROBE_CYCLE_FAILED); +stat_t cm_get_prbr(nvObj_t *nv) +{ + nv->value = (float)cm.probe_report_enable; + nv->valuetype = TYPE_INT; + return (STAT_OK); } -static stat_t _probing_error_exit(int8_t axis) { - // cleanup first - _probe_restore_settings(); - - // Generate the warning/error message. - - // Since the error exit returns via the probing callback - and not the main - // controller - it requires its own display processing. - - nv_reset_nv_list(); - if (axis == -3) { - const char* msg = "Probing error - probe switch is already active"; - if (pb.failure_is_fatal) { - cm_alarm(STAT_PROBE_CYCLE_FAILED, msg); - } else { - nv_add_conditional_message(msg); - } - } else if (axis == -4) { - const char* msg = "Probing error - probe switch is already inactive"; - if (pb.failure_is_fatal) { - cm_alarm(STAT_PROBE_CYCLE_FAILED, msg); - } else { - nv_add_conditional_message(msg); - } - } else if (axis == -2) { - const char* msg = "Probing error - invalid probe destination"; - if (pb.failure_is_fatal) { - cm_alarm(STAT_PROBE_CYCLE_FAILED, msg); - } else { - nv_add_conditional_message(msg); - } - } else { - char msg[NV_MESSAGE_LEN]; - sprintf(msg, "Probing error - %c axis cannot move during probing", cm_get_axis_char(axis)); - if (pb.failure_is_fatal) { - cm_alarm(STAT_PROBE_CYCLE_FAILED, msg); - } else { - nv_add_conditional_message(msg); - } - } - - if (!pb.failure_is_fatal) { - nv_print_list(STAT_PROBE_CYCLE_FAILED, TEXT_MULTILINE_FORMATTED, JSON_RESPONSE_FORMAT); - } - - return (STAT_PROBE_CYCLE_FAILED); +stat_t cm_set_prbr(nvObj_t *nv) +{ + cm.probe_report_enable = fp_NOT_ZERO(nv->value); + return (STAT_OK); } diff --git a/g2core/error.h b/g2core/error.h index 5ecf2cf4..c5c3e46d 100644 --- a/g2core/error.h +++ b/g2core/error.h @@ -202,10 +202,10 @@ char *get_status_message(stat_t status); #define STAT_VALUE_TYPE_ERROR 116 // JSON value does not agree with variable type #define STAT_INPUT_FROM_MUTED_CHANNEL_ERROR 117 // input from a muted channel was ignored -#define STAT_ERROR_118 118 -#define STAT_ERROR_119 119 +#define STAT_CHECKSUM_MATCH_FAILED 118 // the provided checksum didn't match +#define STAT_LINE_NUMBER_OUT_OF_SEQUENCE 119 // the provided line number was out of sequence +#define STAT_MISSING_LINE_NUMBER_WITH_CHECKSUM 120 // if a checksum is provided, a line number should be present as well -#define STAT_ERROR_120 120 #define STAT_ERROR_121 121 #define STAT_ERROR_122 122 #define STAT_ERROR_123 123 @@ -279,6 +279,7 @@ char *get_status_message(stat_t status); /* reserved for Gcode or other program errors */ +#define STAT_ERROR_182 182 #define STAT_ERROR_183 183 #define STAT_ERROR_184 184 #define STAT_ERROR_185 185 @@ -312,7 +313,7 @@ char *get_status_message(stat_t status); #define STAT_TEMPERATURE_CONTROL_ERROR 209 // temperature controls err'd out -#define STAT_ERROR_210 210 +#define STAT_G29_NOT_CONFIGURED 210 #define STAT_ERROR_211 211 #define STAT_ERROR_212 212 #define STAT_ERROR_213 213 @@ -359,9 +360,9 @@ char *get_status_message(stat_t status); #define STAT_PROBE_CYCLE_FAILED 250 // probing cycle did not complete #define STAT_PROBE_TRAVEL_TOO_SMALL 251 -#define STAT_NO_PROBE_SWITCH_CONFIGURED 252 +#define STAT_NO_PROBE_INPUT_CONFIGURED 252 #define STAT_MULTIPLE_PROBE_SWITCHES_CONFIGURED 253 -#define STAT_PROBE_SWITCH_ON_ABC_AXIS 254 +#define STAT_PROBE_IS_ALREADY_TRIPPED 254 #define STAT_ERROR_255 255 @@ -498,9 +499,9 @@ static const char stat_113[] = "JSON string too long"; static const char stat_114[] = "JSON txt fields cannot be nested"; static const char stat_115[] = "JSON maximum nesting depth exceeded"; static const char stat_116[] = "JSON value does not agree with variable type"; -static const char stat_117[] = "117"; -static const char stat_118[] = "118"; -static const char stat_119[] = "119"; +static const char stat_117[] = "Input from a muted channel was ignored"; +static const char stat_118[] = "The provided checksum didn't match"; +static const char stat_119[] = "The provided line number was out of sequence"; static const char stat_120[] = "120"; static const char stat_121[] = "121"; @@ -603,7 +604,7 @@ static const char stat_207[] = "Kill job"; static const char stat_208[] = "No GPIO for this value"; static const char stat_209[] = "209"; -static const char stat_210[] = "210"; +static const char stat_210[] = "Marlin G29 command was not configured at compile-time"; static const char stat_211[] = "211"; static const char stat_212[] = "212"; static const char stat_213[] = "213"; @@ -650,7 +651,7 @@ static const char stat_250[] = "Probe cycle failed"; static const char stat_251[] = "Probe travel is too small"; static const char stat_252[] = "No probe switch configured"; static const char stat_253[] = "Multiple probe switches configured"; -static const char stat_254[] = "Probe switch configured on ABC axis"; +static const char stat_254[] = "Probe is already tripped"; static const char stat_255[] = "255"; static const char *const stat_msg[] = { diff --git a/g2core/g2core.cppproj b/g2core/g2core.cppproj index 4e9ca4ea..5286ede4 100644 --- a/g2core/g2core.cppproj +++ b/g2core/g2core.cppproj @@ -5,7 +5,7 @@ 7.0 com.Atmel.ARMGCC.CPP {44ea8fec-55d7-4149-8a78-a574fc26bf51} - ATSAM3X8E + ATSAM3X8C none Executable CPP @@ -68,7 +68,7 @@ - 2000000 + 10000000 SWD @@ -101,8 +101,8 @@ true J41800036434 - 0x285E0A60 - 2000000 + 0x284E0A60 + 10000000 @@ -1713,6 +1713,12 @@ compile + + compile + + + compile + compile diff --git a/g2core/g2core.xcodeproj/project.pbxproj b/g2core/g2core.xcodeproj/project.pbxproj index 670d7df0..f020af3e 100644 --- a/g2core/g2core.xcodeproj/project.pbxproj +++ b/g2core/g2core.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ D44E72C11D663B0300ECD5DD /* coolant.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D44E72C01D663B0300ECD5DD /* coolant.cpp */; }; D457117C17053EFA00EA19A8 /* Makefile in Sources */ = {isa = PBXBuildFile; fileRef = D457117B17053EFA00EA19A8 /* Makefile */; }; + D4694F9E1E295B5E00F813BA /* marlin_compatibility.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D4694F9C1E295B5E00F813BA /* marlin_compatibility.cpp */; }; D48F5A56172CB1FA00D0E055 /* canonical_machine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D48F5A40172CB1F900D0E055 /* canonical_machine.cpp */; }; D48F5A57172CB1FA00D0E055 /* config_app.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D48F5A41172CB1F900D0E055 /* config_app.cpp */; }; D48F5A58172CB1FA00D0E055 /* config.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D48F5A42172CB1F900D0E055 /* config.cpp */; }; @@ -59,6 +60,8 @@ D44E72C21D663B0F00ECD5DD /* coolant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = coolant.h; sourceTree = ""; }; D4522DC91C45F41D0086AAE6 /* g2core_info.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = g2core_info.h; sourceTree = ""; }; D457117B17053EFA00EA19A8 /* Makefile */ = {isa = PBXFileReference; explicitFileType = sourcecode.make; fileEncoding = 4; path = Makefile; sourceTree = ""; usesTabs = 1; xcLanguageSpecificationIdentifier = xcode.lang.sh; }; + D4694F9C1E295B5E00F813BA /* marlin_compatibility.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = marlin_compatibility.cpp; sourceTree = ""; }; + D4694F9D1E295B5E00F813BA /* marlin_compatibility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = marlin_compatibility.h; sourceTree = ""; }; D47B744C1D4925B4004FAB53 /* device */ = {isa = PBXFileReference; lastKnownFileType = folder; path = device; sourceTree = ""; }; D48F5A40172CB1F900D0E055 /* canonical_machine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = canonical_machine.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; D48F5A41172CB1F900D0E055 /* config_app.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = config_app.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; @@ -160,6 +163,8 @@ D48F5A74172CB21100D0E055 /* json_parser.h */, D48F5A4A172CB1FA00D0E055 /* kinematics.cpp */, D48F5A75172CB21100D0E055 /* kinematics.h */, + D4694F9C1E295B5E00F813BA /* marlin_compatibility.cpp */, + D4694F9D1E295B5E00F813BA /* marlin_compatibility.h */, D48F5A4C172CB1FA00D0E055 /* persistence.cpp */, D48F5A76172CB21100D0E055 /* persistence.h */, D48F5A4D172CB1FA00D0E055 /* plan_arc.cpp */, @@ -201,7 +206,7 @@ /* Begin PBXLegacyTarget section */ D4037C091CDA42F300BC72AD /* G2 printrboardG2v3 PrintrbotPlus */ = { isa = PBXLegacyTarget; - buildArgumentsString = "$(ACTION) VERBOSE=1 COLOR=0 CONFIG=PrintrbotPlus BOARD=printrboardG2v3 DEBUG=2"; + buildArgumentsString = "$(ACTION) VERBOSE=1 COLOR=0 CONFIG=PrintrbotPlus BOARD=printrboardG2v3 DEBUG=1"; buildConfigurationList = D4037C0A1CDA42F300BC72AD /* Build configuration list for PBXLegacyTarget "G2 printrboardG2v3 PrintrbotPlus" */; buildPhases = ( ); @@ -420,6 +425,7 @@ D457117C17053EFA00EA19A8 /* Makefile in Sources */, D4B657A718B5C23C00F8616C /* pwm.cpp in Sources */, D44E72C11D663B0300ECD5DD /* coolant.cpp in Sources */, + D4694F9E1E295B5E00F813BA /* marlin_compatibility.cpp in Sources */, D4B657A318B5C21600F8616C /* cycle_probing.cpp in Sources */, D4D6453919BCAE3F0053705B /* plan_zoid.cpp in Sources */, D4B657A218B5C21600F8616C /* cycle_jogging.cpp in Sources */, diff --git a/g2core/gcode_parser.cpp b/g2core/gcode_parser.cpp index db1c3e32..2005f9e9 100644 --- a/g2core/gcode_parser.cpp +++ b/g2core/gcode_parser.cpp @@ -22,22 +22,219 @@ #include "controller.h" #include "gcode_parser.h" #include "canonical_machine.h" +#include "settings.h" #include "spindle.h" #include "coolant.h" #include "util.h" -#include "xio.h" // for char definitions +#include "xio.h" // for char definitions + +#if MARLIN_COMPAT_ENABLED == true +#include "marlin_compatibility.h" +#include "json_parser.h" // so we can switch js.comm_mode on a marlin M-code +#endif + +// Helpers + +static stat_t _execute_gcode_block_marlin(void); + +// Locally used enums + +typedef enum { // Used for detecting gcode errors. See NIST section 3.4 + MODAL_GROUP_G0 = 0, // {G10,G28,G28.1,G92} non-modal axis commands (note 1) + MODAL_GROUP_G1, // {G0,G1,G2,G3,G80} motion + MODAL_GROUP_G2, // {G17,G18,G19} plane selection + MODAL_GROUP_G3, // {G90,G91} distance mode + MODAL_GROUP_G5, // {G93,G94} feed rate mode + MODAL_GROUP_G6, // {G20,G21} units + MODAL_GROUP_G7, // {G40,G41,G42} cutter radius compensation + MODAL_GROUP_G8, // {G43,G49} tool length offset + MODAL_GROUP_G9, // {G98,G99} return mode in canned cycles + MODAL_GROUP_G12, // {G54,G55,G56,G57,G58,G59} coordinate system selection + MODAL_GROUP_G13, // {G61,G61.1,G64} path control mode + MODAL_GROUP_M4, // {M0,M1,M2,M30,M60} stopping + MODAL_GROUP_M6, // {M6} tool change + MODAL_GROUP_M7, // {M3,M4,M5} spindle turning + MODAL_GROUP_M8, // {M7,M8,M9} coolant (M7 & M8 may be active together) + MODAL_GROUP_M9 // {M48,M49} speed/feed override switches +} cmModalGroup; +#define MODAL_GROUP_COUNT (MODAL_GROUP_M9+1) +// Note 1: Our G0 omits G4,G30,G53,G92.1,G92.2,G92.3 as these have no axis components to error check + +/* The difference between NextAction and MotionMode is that NextAction is + * used by the current block, and may carry non-modal commands, whereas + * MotionMode persists across blocks (as G modal group 1) + */ + +typedef enum { // these are in order to optimized CASE statement + NEXT_ACTION_DEFAULT = 0, // Must be zero (invokes motion modes) + NEXT_ACTION_DWELL, // G4 + NEXT_ACTION_SET_G10_DATA, // G10 + NEXT_ACTION_GOTO_G28_POSITION, // G28 go to machine position + NEXT_ACTION_SET_G28_POSITION, // G28.1 set position in abs coordinates + NEXT_ACTION_SEARCH_HOME, // G28.2 homing cycle + NEXT_ACTION_SET_ABSOLUTE_ORIGIN, // G28.3 origin set + NEXT_ACTION_HOMING_NO_SET, // G28.4 homing cycle with no coordinate setting + NEXT_ACTION_GOTO_G30_POSITION, // G30 go to machine position + NEXT_ACTION_SET_G30_POSITION, // G30.1 set position in abs coordinates + NEXT_ACTION_STRAIGHT_PROBE_ERR, // G38.2 + NEXT_ACTION_STRAIGHT_PROBE, // G38.3 + NEXT_ACTION_STRAIGHT_PROBE_AWAY_ERR, // G38.4 + NEXT_ACTION_STRAIGHT_PROBE_AWAY, // G38.5 + NEXT_ACTION_SET_TL_OFFSET, // G43 + NEXT_ACTION_SET_ADDITIONAL_TL_OFFSET, // G43.2 + NEXT_ACTION_CANCEL_TL_OFFSET, // G49 + NEXT_ACTION_SET_ORIGIN_OFFSETS, // G92 + NEXT_ACTION_RESET_ORIGIN_OFFSETS, // G92.1 + NEXT_ACTION_SUSPEND_ORIGIN_OFFSETS, // G92.2 + NEXT_ACTION_RESUME_ORIGIN_OFFSETS, // G92.3 + NEXT_ACTION_JSON_COMMAND_SYNC, // M100 + NEXT_ACTION_JSON_COMMAND_ASYNC, // M100.1 + NEXT_ACTION_JSON_WAIT, // M101 + +#if MARLIN_COMPAT_ENABLED == true + NEXT_ACTION_MARLIN_TRAM_BED, // G29 + NEXT_ACTION_MARLIN_DISABLE_MOTORS, // M84 + NEXT_ACTION_MARLIN_SET_MT, // M84 (with S), M85 + NEXT_ACTION_MARLIN_SET_EXTRUDER_TEMP, // M104, M109 + NEXT_ACTION_MARLIN_PRINT_TEMPERATURES, // M105 + NEXT_ACTION_MARLIN_SET_FAN_SPEED, // M106 + NEXT_ACTION_MARLIN_STOP_FAN, // M107 + NEXT_ACTION_MARLIN_CANCEL_WAIT_TEMP, // M108 + NEXT_ACTION_MARLIN_RESET_LINE_NUMBERS, // M110 + NEXT_ACTION_MARLIN_DEBUG_STATEMENTS, // M111 (not used) + NEXT_ACTION_MARLIN_PRINT_POSITION, // M114 + NEXT_ACTION_MARLIN_REPORT_VERSION, // M115 + NEXT_ACTION_MARLIN_DISPLAY_ON_SCREEN, // M117 + NEXT_ACTION_MARLIN_SET_BED_TEMP, // M140, M190 +#endif + +} gpNextAction; + +// Structures used by Gcode parser + +typedef struct GCodeInputValue { // Gcode inputs - meaning depends on context + + gpNextAction next_action; // handles G modal group 1 moves & non-modals + cmMotionMode motion_mode; // Group1: G0, G1, G2, G3, G38.2, G80, G81, G82, G83, G84, G85, G86, G87, G88, G89 + uint8_t program_flow; // used only by the gcode_parser + uint32_t linenum; // N word + + float target[AXES]; // XYZABC where the move should go + float arc_offset[3]; // IJK - used by arc commands + float arc_radius; // R - radius value in arc radius mode + + float F_word; // F - normalized to millimeters/minute + uint8_t H_word; // H word - used by G43s + uint8_t L_word; // L word - used by G10s + float P_word; // P - parameter used for dwell time in seconds, G10 coord select... + float S_word; // S word - in RPM + + uint8_t feed_rate_mode; // See cmFeedRateMode for settings + uint8_t select_plane; // G17,G18,G19 - values to set plane to + uint8_t units_mode; // G20,G21 - 0=inches (G20), 1 = mm (G21) + uint8_t coord_system; // G54-G59 - select coordinate system 1-9 + uint8_t path_control; // G61... EXACT_PATH, EXACT_STOP, CONTINUOUS + uint8_t distance_mode; // G91 0=use absolute coords(G90), 1=incremental movement + uint8_t arc_distance_mode; // G90.1=use absolute IJK offsets, G91.1=incremental IJK offsets + uint8_t origin_offset_mode; // G92...TRUE=in origin offset mode + uint8_t absolute_override; // G53 TRUE = move using machine coordinates - this block only (G53) + uint8_t tool; // Tool after T and M6 (tool_select and tool_change) + uint8_t tool_select; // T value - T sets this value + uint8_t tool_change; // M6 tool change flag - moves "tool_select" to "tool" + uint8_t mist_coolant; // TRUE = mist on (M7), FALSE = off (M9) + uint8_t flood_coolant; // TRUE = flood on (M8), FALSE = off (M9) + uint8_t spindle_control; // 0=OFF (M5), 1=CW (M3), 2=CCW (M4) + + bool m48_enable; // M48/M49 input (enables for feed and spindle) + bool mfo_control; // M50 feedrate override control + bool mto_control; // M50.1 traverse override control + bool sso_control; // M51 spindle speed override control + +#if MARLIN_COMPAT_ENABLED == true + float E_word; // E - "extruder" - may be interpreted any number of ways + bool marlin_relative_extruder_mode; // M82, M83 (Marlin-only) +#endif + +} GCodeValue_t; + +typedef struct GCodeFlags { // Gcode input flags + + bool next_action; + bool motion_mode; + bool program_flow; + bool linenum; + + bool target[AXES]; + bool arc_offset[3]; + bool arc_radius; + + bool F_word; + bool H_word; + bool L_word; + bool P_word; + bool S_word; + + bool feed_rate_mode; + bool select_plane; + bool units_mode; + bool coord_system; + bool path_control; + bool distance_mode; + bool arc_distance_mode; + bool origin_offset_mode; + bool absolute_override; + bool tool; + bool tool_select; + bool tool_change; + bool mist_coolant; + bool flood_coolant; + bool spindle_control; + + bool m48_enable; + bool mfo_control; + bool mto_control; + bool sso_control; + + bool checksum; + +#if MARLIN_COMPAT_ENABLED == true + bool E_word; + bool marlin_wait_for_temp; // M140 or M190 - wait for temperature (Marlin-only) + bool marlin_relative_extruder_mode; +#endif + +} GCodeFlag_t; + +typedef struct GCodeParser { + bool modals[MODAL_GROUP_COUNT]; +} GCodeParser_t; + +GCodeParser_t gp; // main parser struct +GCodeValue_t gv; // gcode input values +GCodeFlag_t gf; // gcode input flags // local helper functions and macros -static void _normalize_gcode_block(char *str, char **active_comment, uint8_t *block_delete_flag); -static stat_t _get_next_gcode_word(char **pstr, char *letter, float *value); -static stat_t _point(float value); -static stat_t _validate_gcode_block(char *active_comment); -static stat_t _parse_gcode_block(char *line, char *active_comment); // Parse the block into the GN/GF structs -static stat_t _execute_gcode_block(char *active_comment); // Execute the gcode block +void _normalize_gcode_block(char *str, char **active_comment, uint8_t *block_delete_flag); +stat_t _get_next_gcode_word(char **pstr, char *letter, float *value); +stat_t _point(float value); +stat_t _verify_checksum(char *str); +stat_t _validate_gcode_block(char *active_comment); +stat_t _parse_gcode_block(char *line, char *active_comment); // Parse the block into the GN/GF structs +stat_t _execute_gcode_block(char *active_comment); // Execute the gcode block -#define SET_MODAL(m,parm,val) ({cm.gn.parm=val; cm.gf.parm=true; cm.gf.modals[m]=true; break;}) -#define SET_NON_MODAL(parm,val) ({cm.gn.parm=val; cm.gf.parm=true; break;}) -#define EXEC_FUNC(f,v) if(cm.gf.v) { status=f(cm.gn.v);} +#define SET_MODAL(m,parm,val) ({gv.parm=val; gf.parm=true; gp.modals[m]=true; break;}) +#define SET_NON_MODAL(parm,val) ({gv.parm=val; gf.parm=true; break;}) +#define EXEC_FUNC(f,v) if(gf.v) { status=f(gv.v);} + +/* + * gcode_parser_init() + */ + +void gcode_parser_init() +{ + memset(&gv, 0, sizeof(GCodeValue_t)); + memset(&gf, 0, sizeof(GCodeFlag_t)); +} /* * gcode_parser() - parse a block (line) of gcode @@ -52,6 +249,11 @@ stat_t gcode_parser(char *block) char *active_comment = &none; // gcode comment or NUL string uint8_t block_delete_flag; + stat_t check_ret = _verify_checksum(str); + if (check_ret != STAT_OK) { + return check_ret; + } + _normalize_gcode_block(str, &active_comment, &block_delete_flag); // TODO, now MSG is put in the active comment, handle that. @@ -73,6 +275,43 @@ stat_t gcode_parser(char *block) return(_parse_gcode_block(block, active_comment)); } +/* + * _verify_checksum() - ensure that, if there is a checksum, that it's valid + * + * Returns STAT_OK is it's valid. + * Returns STAT_CHECKSUM_MATCH_FAILED if the checksum doesn't match. + */ +stat_t _verify_checksum(char *str) +{ + bool has_line_number = false; // -1 means we don't have one + if (*str == 'N') { + has_line_number = true; + } + + char checksum = 0; + char c = *str++; + while (c && (c != '*') && (c != '\n') && (c != '\r')) { + checksum ^= c; + c = *str++; + } + + // c might be 0 here, in which case we didn't get a checksum and we return STAT_OK + + if (c == '*') { + *(str-1) = 0; // null terminate, the parser won't like this * here! + gf.checksum = true; + if (strtol(str, NULL, 10) != checksum) { + _debug_trap("checksum failure"); + return STAT_CHECKSUM_MATCH_FAILED; + } + if (!has_line_number) { + _debug_trap("line number missing with checksum"); + return STAT_MISSING_LINE_NUMBER_WITH_CHECKSUM; + } + } + return STAT_OK; +} + /* * _normalize_gcode_block() - normalize a block (line) of gcode in place * @@ -109,7 +348,7 @@ stat_t gcode_parser(char *block) char _normalize_scratch[RX_BUFFER_SIZE]; -static void _normalize_gcode_block(char *str, char **active_comment, uint8_t *block_delete_flag) +void _normalize_gcode_block(char *str, char **active_comment, uint8_t *block_delete_flag) { _normalize_scratch[0] = 0; @@ -337,7 +576,7 @@ static void _normalize_gcode_block(char *str, char **active_comment, uint8_t *bl * G0X... is not interpreted as hexadecimal. This is trapped. */ -static stat_t _get_next_gcode_word(char **pstr, char *letter, float *value) +stat_t _get_next_gcode_word(char **pstr, char *letter, float *value) { if (**pstr == NUL) { return (STAT_COMPLETE); } // no more words @@ -348,18 +587,31 @@ static stat_t _get_next_gcode_word(char **pstr, char *letter, float *value) *letter = **pstr; (*pstr)++; - // X-axis-becomes-a-hexadecimal-number get-value case, e.g. G0X100 --> G255 - if ((**pstr == '0') && (*(*pstr+1) == 'X')) { - *value = 0; - (*pstr)++; - return (STAT_OK); // pointer points to X - } +// // X-axis-becomes-a-hexadecimal-number get-value case, e.g. G0X100 --> G255 +// if ((**pstr == '0') && (*(*pstr+1) == 'X')) { +// *value = 0; +// (*pstr)++; +// return (STAT_OK); // pointer points to X +// } +// +// // get-value general case +// char *end = *pstr; +// *value = strtof(*pstr, &end); // get-value general case - char *end; - *value = strtof(*pstr, &end); + char *end = *pstr; + *value = c_atof(end); + if(end == *pstr) { +#if MARLIN_COMPAT_ENABLED == true + if (mst.marlin_flavor) { + *value = 0; + } else { return(STAT_BAD_NUMBER_FORMAT); + } +#else + return(STAT_BAD_NUMBER_FORMAT); +#endif } // more robust test then checking for value=0; *pstr = end; return (STAT_OK); // pointer points to next character after the word @@ -369,7 +621,7 @@ static stat_t _get_next_gcode_word(char **pstr, char *letter, float *value) * _point() - isolate the decimal point value as an integer */ -static uint8_t _point(float value) +uint8_t _point(const float value) { return((uint8_t)(value*10 - trunc(value)*10)); // isolate the decimal point as an int } @@ -378,7 +630,7 @@ static uint8_t _point(float value) * _validate_gcode_block() - check for some gross Gcode block semantic violations */ -static stat_t _validate_gcode_block(char *active_comment) +stat_t _validate_gcode_block(char *active_comment) { // Check for modal group violations. From NIST, section 3.4 "It is an error to put // a G-code from group 1 and a G-code from group 0 on the same line if both of them @@ -407,7 +659,7 @@ static stat_t _validate_gcode_block(char *active_comment) * contain only uppercase characters and signed floats (no whitespace). */ -static stat_t _parse_gcode_block(char *buf, char *active_comment) +stat_t _parse_gcode_block(char *buf, char *active_comment) { char *pstr = (char *)buf; // persistent pointer into gcode block for parsing words char letter; // parsed letter, eg.g. G or X or Y @@ -415,16 +667,16 @@ static stat_t _parse_gcode_block(char *buf, char *active_comment) stat_t status = STAT_OK; // set initial state for new move - memset(&cm.gn, 0, sizeof(GCodeInput_t)); // clear all next-state values - memset(&cm.gf, 0, sizeof(GCodeFlags_t)); // clear all next-state flags - cm.gn.motion_mode = cm_get_motion_mode(MODEL); // get motion mode from previous block + memset(&gv, 0, sizeof(GCodeValue_t)); // clear all next-state values + memset(&gf, 0, sizeof(GCodeFlag_t)); // clear all next-state flags + gv.motion_mode = cm_get_motion_mode(MODEL); // get motion mode from previous block // Causes a later exception if // (1) INVERSE_TIME_MODE is active and a feed rate is not provided or // (2) INVERSE_TIME_MODE is changed to UNITS_PER_MINUTE and a new feed rate is missing if (cm.gm.feed_rate_mode == INVERSE_TIME_MODE) {// new feed rate req'd when in INV_TIME_MODE - cm.gn.feed_rate = 0; - cm.gf.feed_rate = true; + gv.F_word = 0; + gf.F_word = true; } // extract commands and parameters @@ -454,6 +706,10 @@ static stat_t _parse_gcode_block(char *buf, char *active_comment) } break; } +#if MARLIN_COMPAT_ENABLED == true + case 29: SET_NON_MODAL (next_action, NEXT_ACTION_MARLIN_TRAM_BED); +#endif + case 30: { switch (_point(value)) { case 0: SET_MODAL (MODAL_GROUP_G0, next_action, NEXT_ACTION_GOTO_G30_POSITION); @@ -472,7 +728,7 @@ static stat_t _parse_gcode_block(char *buf, char *active_comment) } break; } - case 40: break; // ignore cancel cutter radius compensation + case 40: break; // ignore cancel cutter radius compensation. But don't fail G40s. case 43: { switch (_point(value)) { case 0: SET_NON_MODAL (next_action, NEXT_ACTION_SET_TL_OFFSET); @@ -528,6 +784,7 @@ static stat_t _parse_gcode_block(char *buf, char *active_comment) case 93: SET_MODAL (MODAL_GROUP_G5, feed_rate_mode, INVERSE_TIME_MODE); case 94: SET_MODAL (MODAL_GROUP_G5, feed_rate_mode, UNITS_PER_MINUTE_MODE); // case 95: SET_MODAL (MODAL_GROUP_G5, feed_rate_mode, UNITS_PER_REVOLUTION_MODE); + default: status = STAT_GCODE_COMMAND_UNSUPPORTED; } break; @@ -547,18 +804,63 @@ static stat_t _parse_gcode_block(char *buf, char *active_comment) case 9: SET_MODAL (MODAL_GROUP_M8, flood_coolant, false); case 48: SET_MODAL (MODAL_GROUP_M9, m48_enable, true); case 49: SET_MODAL (MODAL_GROUP_M9, m48_enable, false); - case 50: SET_MODAL (MODAL_GROUP_M9, mfo_enable, true); - case 51: SET_MODAL (MODAL_GROUP_M9, sso_enable, true); - case 100: SET_NON_MODAL (next_action, NEXT_ACTION_JSON_COMMAND_SYNC); + case 50: SET_MODAL (MODAL_GROUP_M9, mfo_control, true); + switch (_point(value)) { + case 0: SET_MODAL (MODAL_GROUP_M9, mfo_control, true); + case 1: SET_MODAL (MODAL_GROUP_M9, mto_control, true); + default: status = STAT_GCODE_COMMAND_UNSUPPORTED; + } + break; + case 51: SET_MODAL (MODAL_GROUP_M9, sso_control, true); + case 100: + switch (_point(value)) { + case 0: SET_NON_MODAL (next_action, NEXT_ACTION_JSON_COMMAND_SYNC); + case 1: SET_NON_MODAL (next_action, NEXT_ACTION_JSON_COMMAND_ASYNC); + default: status = STAT_GCODE_COMMAND_UNSUPPORTED; + } + break; case 101: SET_NON_MODAL (next_action, NEXT_ACTION_JSON_WAIT); + +#if MARLIN_COMPAT_ENABLED == true + case 20:marlin_list_sd_response(); status = STAT_COMPLETE; break; // List SD card + case 21: // Initialize SD card + case 22: status = STAT_COMPLETE; break; // Release SD card + case 23: marlin_select_sd_response(pstr); status = STAT_COMPLETE; break; // Select SD file + + case 82: SET_NON_MODAL (marlin_relative_extruder_mode, false); // set relative extruder mode off + case 83: SET_NON_MODAL (marlin_relative_extruder_mode, true); // set relative extruder mode on + + case 18: // compatibility alias for M84 + case 84: SET_NON_MODAL (next_action, NEXT_ACTION_MARLIN_DISABLE_MOTORS); // disable all motors + case 85: SET_NON_MODAL (next_action, NEXT_ACTION_MARLIN_SET_MT); // set motor timeout + + case 105: SET_NON_MODAL (next_action, NEXT_ACTION_MARLIN_PRINT_TEMPERATURES);// request temperature report + case 106: SET_NON_MODAL (next_action, NEXT_ACTION_MARLIN_SET_FAN_SPEED); // set fan speed range 0 - 255 + case 107: SET_NON_MODAL (next_action, NEXT_ACTION_MARLIN_STOP_FAN); // stop fan (speed = 0) + case 108: SET_NON_MODAL (next_action, NEXT_ACTION_MARLIN_CANCEL_WAIT_TEMP); // cancel wait for temparature + case 114: SET_NON_MODAL (next_action, NEXT_ACTION_MARLIN_PRINT_POSITION); // request position report + + case 109: gf.marlin_wait_for_temp = true; // NO break! // set wait for temp and execute M104 + case 104: SET_NON_MODAL (next_action, NEXT_ACTION_MARLIN_SET_EXTRUDER_TEMP);// set extruder temperature + + case 190: gf.marlin_wait_for_temp = true; // NO break! // set wait for temp and execute M140 + case 140: SET_NON_MODAL (next_action, NEXT_ACTION_MARLIN_SET_BED_TEMP); // set heated bed temperature + + case 110: SET_NON_MODAL (next_action, NEXT_ACTION_MARLIN_RESET_LINE_NUMBERS);// reset line numbers + case 111: status = STAT_COMPLETE; break; // ignore M111 Marlin debug statements. Don't process contents of the line further + + case 115: SET_NON_MODAL (next_action, NEXT_ACTION_MARLIN_REPORT_VERSION); // report version information + case 117: status = STAT_COMPLETE; break; //SET_NON_MODAL (next_action, NEXT_ACTION_MARLIN_DISPLAY_ON_SCREEN); +#endif // MARLIN_COMPAT_ENABLED + default: status = STAT_MCODE_COMMAND_UNSUPPORTED; } break; case 'T': SET_NON_MODAL (tool_select, (uint8_t)trunc(value)); - case 'F': SET_NON_MODAL (feed_rate, value); - case 'P': SET_NON_MODAL (parameter, value); // used for dwell time, G10 coord select - case 'S': SET_NON_MODAL (spindle_speed, value); + case 'F': SET_NON_MODAL (F_word, value); + case 'P': SET_NON_MODAL (P_word, value); // used for dwell time, G10 coord select + case 'S': SET_NON_MODAL (S_word, value); case 'X': SET_NON_MODAL (target[AXIS_X], value); case 'Y': SET_NON_MODAL (target[AXIS_Y], value); case 'Z': SET_NON_MODAL (target[AXIS_Z], value); @@ -575,6 +877,11 @@ static stat_t _parse_gcode_block(char *buf, char *active_comment) case 'L': SET_NON_MODAL (L_word, value); case 'R': SET_NON_MODAL (arc_radius, value); case 'N': SET_NON_MODAL (linenum,(uint32_t)value); // line number + +#if MARLIN_COMPAT_ENABLED == true + case 'E': SET_NON_MODAL (E_word, value); // extruder value +#endif + default: status = STAT_GCODE_COMMAND_UNSUPPORTED; } if(status != STAT_OK) break; @@ -595,8 +902,9 @@ static stat_t _parse_gcode_block(char *buf, char *active_comment) * 1. comment (includes message) [handled during block normalization] * 2. set feed rate mode (G93, G94 - inverse time or per minute) * 3. set feed rate (F) - * 3a. set feed override rate (M50.1) - * 3a. set traverse override rate (M50.2) + * 3a. Marlin functions (optional) + * 3b. set feed override rate (M50.1) + * 3c. set traverse override rate (M50.2) * 4. set spindle speed (S) * 4a. set spindle override rate (M51.1) * 5. select tool (T) @@ -624,111 +932,127 @@ static stat_t _parse_gcode_block(char *buf, char *active_comment) * to calling the canonical functions (which do the unit conversions) */ -static stat_t _execute_gcode_block(char *active_comment) +stat_t _execute_gcode_block(char *active_comment) { stat_t status = STAT_OK; - cm_set_model_linenum(cm.gn.linenum); + if (gf.linenum) { + cm_set_model_linenum(gv.linenum); + } EXEC_FUNC(cm_set_feed_rate_mode, feed_rate_mode); // G93, G94 - EXEC_FUNC(cm_set_feed_rate, feed_rate); // F - EXEC_FUNC(cm_set_spindle_speed, spindle_speed); // S -// EXEC_FUNC(cm_spindle_override_factor, spindle_override_factor); + EXEC_FUNC(cm_set_feed_rate, F_word); // F + + ritorno(_execute_gcode_block_marlin()); // execute Marlin commands if Marlin compatibility enabled + if (gf.linenum && gf.checksum) { + ritorno(cm_check_linenum()); + } + + EXEC_FUNC(cm_set_spindle_speed, S_word); // S + if (gf.sso_control) { // spindle speed override + ritorno(cm_sso_control(gv.P_word, gf.P_word)); + } + EXEC_FUNC(cm_select_tool, tool_select); // tool_select is where it's written EXEC_FUNC(cm_change_tool, tool_change); // M6 EXEC_FUNC(cm_spindle_control, spindle_control); // spindle CW, CCW, OFF -/* - EXEC_FUNC(cm_feed_rate_override_enable, feed_rate_override_enable); - EXEC_FUNC(cm_traverse_override_enable, traverse_override_enable); - EXEC_FUNC(cm_spindle_override_enable, spindle_override_enable); - EXEC_FUNC(cm_override_enables, override_enables); -*/ EXEC_FUNC(cm_mist_coolant_control, mist_coolant); // M7, M9 EXEC_FUNC(cm_flood_coolant_control, flood_coolant); // M8, M9 also disables mist coolant if OFF EXEC_FUNC(cm_m48_enable, m48_enable); - EXEC_FUNC(cm_mfo_enable, mfo_enable); -// EXEC_FUNC(cm_mfo_enable, feed_rate_override_factor); -// EXEC_FUNC(cm_sso_enable, sso_enable); - if (cm.gn.next_action == NEXT_ACTION_DWELL) { // G4 - dwell - ritorno(cm_dwell(cm.gn.parameter)); // return if error, otherwise complete the block + if (gf.mfo_control) { // manual feedrate override + ritorno(cm_mfo_control(gv.P_word, gf.P_word)); + } + if (gf.mto_control) { // manual traverse override + ritorno(cm_mto_control(gv.P_word, gf.P_word)); + } + + if (gv.next_action == NEXT_ACTION_DWELL) { // G4 - dwell + ritorno(cm_dwell(gv.P_word)); // return if error, otherwise complete the block } EXEC_FUNC(cm_select_plane, select_plane); // G17, G18, G19 EXEC_FUNC(cm_set_units_mode, units_mode); // G20, G21 //--> cutter radius compensation goes here - switch (cm.gn.next_action) { // Tool length offsets + switch (gv.next_action) { // Tool length offsets case NEXT_ACTION_SET_TL_OFFSET: { // G43 - ritorno(cm_set_tl_offset(cm.gn.H_word, false)); + ritorno(cm_set_tl_offset(gv.H_word, gf.H_word, false)); break; } case NEXT_ACTION_SET_ADDITIONAL_TL_OFFSET: { // G43.2 - ritorno(cm_set_tl_offset(cm.gn.H_word, true)); + ritorno(cm_set_tl_offset(gv.H_word, gf.H_word, true)); break; } case NEXT_ACTION_CANCEL_TL_OFFSET: { // G49 ritorno(cm_cancel_tl_offset()); break; } + default: + // quiet the compiler warning about all the things we don't handle + break; } EXEC_FUNC(cm_set_coord_system, coord_system); // G54, G55, G56, G57, G58, G59 -// EXEC_FUNC(cm_set_path_control, path_control); // G61, G61.1, G64 - if(cm.gf.path_control) { status = cm_set_path_control(MODEL, cm.gn.path_control); } + + if (gf.path_control) { // G61, G61.1, G64 + status = cm_set_path_control(MODEL, gv.path_control); + } EXEC_FUNC(cm_set_distance_mode, distance_mode); // G90, G91 EXEC_FUNC(cm_set_arc_distance_mode, arc_distance_mode); // G90.1, G91.1 //--> set retract mode goes here - switch (cm.gn.next_action) { + switch (gv.next_action) { case NEXT_ACTION_SET_G28_POSITION: { status = cm_set_g28_position(); break;} // G28.1 - case NEXT_ACTION_GOTO_G28_POSITION: { status = cm_goto_g28_position(cm.gn.target, cm.gf.target); break;} // G28 + case NEXT_ACTION_GOTO_G28_POSITION: { status = cm_goto_g28_position(gv.target, gf.target); break;} // G28 case NEXT_ACTION_SET_G30_POSITION: { status = cm_set_g30_position(); break;} // G30.1 - case NEXT_ACTION_GOTO_G30_POSITION: { status = cm_goto_g30_position(cm.gn.target, cm.gf.target); break;} // G30 + case NEXT_ACTION_GOTO_G30_POSITION: { status = cm_goto_g30_position(gv.target, gf.target); break;} // G30 - case NEXT_ACTION_SEARCH_HOME: { status = cm_homing_cycle_start(); break;} // G28.2 - case NEXT_ACTION_SET_ABSOLUTE_ORIGIN: { status = cm_set_absolute_origin(cm.gn.target, cm.gf.target); break;}// G28.3 - case NEXT_ACTION_HOMING_NO_SET: { status = cm_homing_cycle_start_no_set(); break;} // G28.4 + case NEXT_ACTION_SEARCH_HOME: { status = cm_homing_cycle_start(gv.target, gf.target); break;} // G28.2 + case NEXT_ACTION_SET_ABSOLUTE_ORIGIN: { status = cm_set_absolute_origin(gv.target, gf.target); break;} // G28.3 + case NEXT_ACTION_HOMING_NO_SET: { status = cm_homing_cycle_start_no_set(gv.target, gf.target); break;} // G28.4 - case NEXT_ACTION_STRAIGHT_PROBE_ERR: { status = cm_straight_probe(cm.gn.target, cm.gf.target, true, true); break;} // G38.2 - case NEXT_ACTION_STRAIGHT_PROBE: { status = cm_straight_probe(cm.gn.target, cm.gf.target, false, true); break;} // G38.3 - case NEXT_ACTION_STRAIGHT_PROBE_AWAY_ERR:{ status = cm_straight_probe(cm.gn.target, cm.gf.target, true, false); break;} // G38.4 - case NEXT_ACTION_STRAIGHT_PROBE_AWAY: { status = cm_straight_probe(cm.gn.target, cm.gf.target, false, false); break;} // G38.5 + case NEXT_ACTION_STRAIGHT_PROBE_ERR: { status = cm_straight_probe(gv.target, gf.target, true, true); break;} // G38.2 + case NEXT_ACTION_STRAIGHT_PROBE: { status = cm_straight_probe(gv.target, gf.target, true, false); break;} // G38.3 + case NEXT_ACTION_STRAIGHT_PROBE_AWAY_ERR:{ status = cm_straight_probe(gv.target, gf.target, false, true); break;} // G38.4 + case NEXT_ACTION_STRAIGHT_PROBE_AWAY: { status = cm_straight_probe(gv.target, gf.target, false, false); break;}// G38.5 - case NEXT_ACTION_SET_G10_DATA: { status = cm_set_g10_data(cm.gn.parameter, cm.gn.L_word, cm.gn.target, cm.gf.target); break;} - case NEXT_ACTION_SET_ORIGIN_OFFSETS: { status = cm_set_origin_offsets(cm.gn.target, cm.gf.target); break;}// G92 + case NEXT_ACTION_SET_ORIGIN_OFFSETS: { status = cm_set_origin_offsets(gv.target, gf.target); break;}// G92 case NEXT_ACTION_RESET_ORIGIN_OFFSETS: { status = cm_reset_origin_offsets(); break;} // G92.1 case NEXT_ACTION_SUSPEND_ORIGIN_OFFSETS: { status = cm_suspend_origin_offsets(); break;} // G92.2 case NEXT_ACTION_RESUME_ORIGIN_OFFSETS: { status = cm_resume_origin_offsets(); break;} // G92.3 - case NEXT_ACTION_JSON_COMMAND_SYNC: { status = cm_json_command(active_comment); break;} // M100 + case NEXT_ACTION_JSON_COMMAND_SYNC: { status = cm_json_command(active_comment); break;} // M100.0 + case NEXT_ACTION_JSON_COMMAND_ASYNC: { status = cm_json_command_immediate(active_comment); break;} // M100.1 case NEXT_ACTION_JSON_WAIT: { status = cm_json_wait(active_comment); break;} // M101 -// case NEXT_ACTION_JSON_COMMAND_IMMEDIATE: { status = mp_json_command_immediate(active_comment); break;} // M102 case NEXT_ACTION_DEFAULT: { - cm_set_absolute_override(MODEL, cm.gn.absolute_override); // apply absolute override - switch (cm.gn.motion_mode) { - case MOTION_MODE_CANCEL_MOTION_MODE: { cm.gm.motion_mode = cm.gn.motion_mode; break;} // G80 - case MOTION_MODE_STRAIGHT_TRAVERSE: { status = cm_straight_traverse(cm.gn.target, cm.gf.target); break;} // G0 - case MOTION_MODE_STRAIGHT_FEED: { status = cm_straight_feed(cm.gn.target, cm.gf.target); break;} // G1 + cm_set_absolute_override(MODEL, gv.absolute_override); // apply absolute override + switch (gv.motion_mode) { + case MOTION_MODE_CANCEL_MOTION_MODE: { cm.gm.motion_mode = gv.motion_mode; break;} // G80 + case MOTION_MODE_STRAIGHT_TRAVERSE: { status = cm_straight_traverse(gv.target, gf.target); break;} // G0 + case MOTION_MODE_STRAIGHT_FEED: { status = cm_straight_feed(gv.target, gf.target); break;} // G1 case MOTION_MODE_CW_ARC: // G2 - case MOTION_MODE_CCW_ARC: { status = cm_arc_feed(cm.gn.target, cm.gf.target, // G3 - cm.gn.arc_offset, cm.gf.arc_offset, - cm.gn.arc_radius, cm.gf.arc_radius, - cm.gn.parameter, cm.gf.parameter, - cm.gf.modals[MODAL_GROUP_G1], - cm.gn.motion_mode); + case MOTION_MODE_CCW_ARC: { status = cm_arc_feed(gv.target, gf.target, // G3 + gv.arc_offset, gf.arc_offset, + gv.arc_radius, gf.arc_radius, + gv.P_word, gf.P_word, + gp.modals[MODAL_GROUP_G1], + gv.motion_mode); break; } default: break; } cm_set_absolute_override(MODEL, ABSOLUTE_OVERRIDE_OFF); // un-set absolute override once the move is planned } + default: + // quiet the compiler warning about all the things we don't handle + break; } // do the program stops and ends : M0, M1, M2, M30, M60 - if (cm.gf.program_flow == true) { - if (cm.gn.program_flow == PROGRAM_STOP) { + if (gf.program_flow == true) { + if (gv.program_flow == PROGRAM_STOP) { cm_program_stop(); } else { cm_program_end(); @@ -737,6 +1061,180 @@ static stat_t _execute_gcode_block(char *active_comment) return (status); } +/*********************************************************************************** + * _execute_gcode_block_marlin() - collect Marlin exection functions here + */ + +static stat_t _execute_gcode_block_marlin() +{ +#if MARLIN_COMPAT_ENABLED == true + // Check for sequential line numbers + if (gf.linenum && gf.checksum) { + if (gv.next_action != NEXT_ACTION_MARLIN_RESET_LINE_NUMBERS) { + ritorno(cm_check_linenum()); + } + cm.gmx.last_line_number = cm.gm.linenum; + + // since this is handled, clear gf.checksum so it doesn't again + gf.checksum = false; + } + + // E should ONLY be seen in marlin flavor + if (gf.E_word) { + mst.marlin_flavor = true; + } + + // adjust T real quick + if (mst.marlin_flavor && gf.tool_select) { + gv.tool_select += 1; + cm.gm.tool_select = gv.tool_select; // We need to go ahead and apply to tool select, and in Marlin 0 is valid, so add 1 + cm.gm.tool = cm.gm.tool_select; // Also, in Marlin, tool changes are effective immediately :facepalm: + gf.tool_select = false; // prevent a tool_select command from being buffered (planning to zero) + } + else if (cm.gm.tool_select == 0) { + cm.gm.tool_select = 1; // We need to ensure we have a valid tool selected, often Marlin gcode won't have a T word at all + cm.gm.tool = cm.gm.tool_select; // Also, in Marlin, tool changes are effective immediately :facepalm: + } + + // Deal with E + if (gf.marlin_relative_extruder_mode) { // M82, M83 + marlin_set_extruder_mode(gv.marlin_relative_extruder_mode); + } + if (gf.E_word) { + // Ennn T0 -> Annn + if (cm.gm.tool_select == 1) { + gf.target[AXIS_A] = true; + gv.target[AXIS_A] = gv.E_word; + } + // Ennn T1 -> Bnnn + else if (cm.gm.tool_select == 2) { + gf.target[AXIS_B] = true; + gv.target[AXIS_B] = gv.E_word; + } + else { + _debug_trap("invalid tool selection"); + return (STAT_INPUT_VALUE_RANGE_ERROR); + } + } + + if ((mst.marlin_flavor || (MARLIN_COMM_MODE == js.json_mode)) && + (NEXT_ACTION_GOTO_G28_POSITION == gv.next_action)) { + gv.next_action = NEXT_ACTION_SEARCH_HOME; + } + + switch (gv.next_action) { + case NEXT_ACTION_MARLIN_PRINT_TEMPERATURES: { // M105 + js.json_mode = MARLIN_COMM_MODE; // we use M105 to know when to switch + ritorno(marlin_request_temperature_report()); + break; + } + case NEXT_ACTION_MARLIN_PRINT_POSITION: { // M114 + js.json_mode = MARLIN_COMM_MODE; // we use M105 to know when to switch + ritorno(marlin_request_position_report()); + break; + } + case NEXT_ACTION_MARLIN_SET_EXTRUDER_TEMP: // M104 or M109 + case NEXT_ACTION_MARLIN_SET_BED_TEMP: { // M140 or M190 + mst.marlin_flavor = true; // these gcodes are ONLY in marlin flavor + float temp = 0; + if (gf.S_word) { temp = gv.S_word; } + if (gf.P_word) { temp = gv.P_word; } // we treat them the same, for now + + uint8_t tool = (gv.next_action == NEXT_ACTION_MARLIN_SET_EXTRUDER_TEMP) ? cm.gm.tool_select : 3; + ritorno(marlin_set_temperature(tool, temp, gf.marlin_wait_for_temp)); + + gf.P_word = false; + gf.S_word = false; + break; + } + case NEXT_ACTION_MARLIN_CANCEL_WAIT_TEMP: { // M108 + js.json_mode = MARLIN_COMM_MODE; // we use M105 to know when to switch + cm_request_feedhold(); + cm_request_queue_flush(); + break; + } + case NEXT_ACTION_MARLIN_TRAM_BED: { // G29 + mst.marlin_flavor = true; // these gcodes are ONLY in marlin flavor + ritorno(marlin_start_tramming_bed()); + break; + } + case NEXT_ACTION_MARLIN_SET_FAN_SPEED: { // M106 + mst.marlin_flavor = true; // these gcodes are ONLY in marlin flavor + ritorno(marlin_set_fan_speed( + gf.P_word? gv.P_word : 0, + gf.S_word? gv.S_word : 0)); + gf.P_word = false; + gf.S_word = false; + break; + } + case NEXT_ACTION_MARLIN_STOP_FAN: { // M107 + mst.marlin_flavor = true; // these gcodes are ONLY in marlin flavor + ritorno(marlin_set_fan_speed(gf.P_word ? gv.P_word : 0, 0)); + gf.P_word = false; + gf.S_word = false; + break; + } + // adjust G28 (already adjusted to G28.2) + // with no X, Y, or Z, we assume all three + case NEXT_ACTION_SEARCH_HOME: { // G28 (g2core G28.2) + if (!gf.target[AXIS_X] && !gf.target[AXIS_Y] && !gf.target[AXIS_Z]) { + gv.target[AXIS_X]=0.0; gf.target[AXIS_X]=true; + gv.target[AXIS_Y]=0.0; gf.target[AXIS_Y]=true; + gv.target[AXIS_Z]=0.0; gf.target[AXIS_Z]=true; + } + break; + } + case NEXT_ACTION_MARLIN_DISABLE_MOTORS: { // M84 and M18 + if (gf.S_word) { + ritorno(marlin_set_motor_timeout(gv.S_word)); + gf.S_word = false; + } else { + ritorno(marlin_disable_motors()); + } + break; + } + case NEXT_ACTION_MARLIN_SET_MT: { // M85 + if (gf.S_word) { + ritorno(marlin_set_motor_timeout(gv.S_word)); + gf.S_word = false; + } else { + return (STAT_OK); // this means nothing, but it's not an error + } + } + case NEXT_ACTION_MARLIN_DISPLAY_ON_SCREEN: { // M117 + return (STAT_OK); // ignore for now + } + case NEXT_ACTION_MARLIN_REPORT_VERSION: { // M115 + js.json_mode = MARLIN_COMM_MODE; + ritorno(marlin_report_version()); + break; + } + case NEXT_ACTION_MARLIN_RESET_LINE_NUMBERS:{ // M110 + js.json_mode = MARLIN_COMM_MODE; // we already did this above in if (gf.linenum) {} + return (STAT_OK); + } + case NEXT_ACTION_DEFAULT: { + if (mst.marlin_flavor) { + if (gf.motion_mode) { // adjust G0 to almost always be the same as G1 + if (gf.E_word && (!gf.target[AXIS_X] && !gf.target[AXIS_Y] && !gf.target[AXIS_Z])) { + gv.motion_mode = MOTION_MODE_STRAIGHT_TRAVERSE; // G0 + } else { + gv.motion_mode = MOTION_MODE_STRAIGHT_FEED; // G1 + } + } + } + break; + } + default: + // quiet the compiler warning about all the things we don't handle + break; + } // switch (gv.next_action) + +#endif // MARLIN_COMPAT_ENABLED + + return (STAT_OK); +} + /*********************************************************************************** * CONFIGURATION AND INTERFACE FUNCTIONS diff --git a/g2core/gcode_parser.h b/g2core/gcode_parser.h old mode 100755 new mode 100644 index 25570c0c..4b61de8d --- a/g2core/gcode_parser.h +++ b/g2core/gcode_parser.h @@ -2,7 +2,7 @@ * gcode_parser.h - rs274/ngc Gcode parser * This file is part of the g2core project * - * Copyright (c) 2010 - 2016 Alden S. Hart, Jr. + * Copyright (c) 2010 - 2017 Alden S. Hart, Jr. * * This file ("the software") is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2 as published by the @@ -17,8 +17,8 @@ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#ifndef GCODE_PARSER_H_ONCE -#define GCODE_PARSER_H_ONCE +#ifndef GCODE_H_ONCE +#define GCODE_H_ONCE /* * Global Scope Functions @@ -27,4 +27,4 @@ stat_t gcode_parser(char* block); stat_t gc_get_gc(nvObj_t* nv); stat_t gc_run_gc(nvObj_t* nv); -#endif // End of include guard: GCODE_PARSER_H_ONCE +#endif // End of include guard: GCODE_H_ONCE diff --git a/g2core/gpio.cpp b/g2core/gpio.cpp old mode 100755 new mode 100644 index 4ec98fb8..edcf6a65 --- a/g2core/gpio.cpp +++ b/g2core/gpio.cpp @@ -2,8 +2,8 @@ * gpio.cpp - digital IO handling functions * This file is part of the g2core project * - * Copyright (c) 2015 - 2106 Alden S. Hart, Jr. - * Copyright (c) 2015 - 2016 Robert Giseburt + * Copyright (c) 2015 - 2107 Alden S. Hart, Jr. + * Copyright (c) 2015 - 2017 Robert Giseburt * * This file ("the software") is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2 as published by the @@ -335,11 +335,21 @@ void gpio_init(void) void outputs_reset(void) { // If the output is ACTIVE_LOW set it to 1. ACTIVE_HIGH gets set to 0. +#if D_OUT_CHANNELS >= 1 if (d_out[1-1].mode != IO_MODE_DISABLED) { (output_1_pin = (d_out[1-1].mode == IO_ACTIVE_LOW) ? 1.0 : 0.0); } +#endif +#if D_OUT_CHANNELS >= 2 if (d_out[2-1].mode != IO_MODE_DISABLED) { (output_2_pin = (d_out[2-1].mode == IO_ACTIVE_LOW) ? 1.0 : 0.0); } +#endif +#if D_OUT_CHANNELS >= 3 if (d_out[3-1].mode != IO_MODE_DISABLED) { (output_3_pin = (d_out[3-1].mode == IO_ACTIVE_LOW) ? 1.0 : 0.0); } +#endif +#if D_OUT_CHANNELS >= 4 if (d_out[4-1].mode != IO_MODE_DISABLED) { (output_4_pin = (d_out[4-1].mode == IO_ACTIVE_LOW) ? 1.0 : 0.0); } +#endif +#if D_OUT_CHANNELS >= 5 if (d_out[5-1].mode != IO_MODE_DISABLED) { (output_5_pin = (d_out[5-1].mode == IO_ACTIVE_LOW) ? 1.0 : 0.0); } +#endif #if D_OUT_CHANNELS >= 6 if (d_out[6-1].mode != IO_MODE_DISABLED) { (output_6_pin = (d_out[6-1].mode == IO_ACTIVE_LOW) ? 1.0 : 0.0); } #endif @@ -422,6 +432,7 @@ void gpio_reset(void) /* * gpio_set_homing_mode() - set/clear input to homing mode * gpio_set_probing_mode() - set/clear input to probing mode + * gpio_get_probing_input() - get probing input * gpio_read_input() - read conditioned input * (* Note: input_num_ext means EXTERNAL input number -- 1-based @@ -442,6 +453,16 @@ void gpio_set_probing_mode(const uint8_t input_num_ext, const bool is_probing) d_in[input_num_ext-1].probing_mode = is_probing; } +int8_t gpio_get_probing_input(void) +{ + for (uint8_t i = 0; i <= D_IN_CHANNELS; i++) { + if (d_in[i-1].function == INPUT_FUNCTION_PROBE) { + return (i); + } + } + return (-1); +} + bool gpio_read_input(const uint8_t input_num_ext) { if (input_num_ext == 0) { @@ -481,7 +502,6 @@ static stat_t _output_set_helper(nvObj_t *nv, const int8_t lower_bound, const in return (STAT_OK); } - stat_t io_set_mo(nvObj_t *nv) // input type or disabled { // return (_io_set_helper(nv, IO_MODE_DISABLED, IO_MODE_MAX)); @@ -668,9 +688,9 @@ stat_t io_set_output(nvObj_t *nv) #ifdef __TEXT_MODE - static const char fmt_gpio_mo[] = "[%smo] input mode%17d [0=NO,1=NC,2=disabled]\n"; + static const char fmt_gpio_mo[] = "[%smo] input mode%17d [0=active-low,1=active-hi,2=disabled]\n"; static const char fmt_gpio_ac[] = "[%sac] input action%15d [0=none,1=stop,2=fast_stop,3=halt,4=alarm,5=shutdown,6=panic,7=reset]\n"; - static const char fmt_gpio_fn[] = "[%sfn] input function%13d [0=none,1=limit,2=interlock,3=shutdown]\n"; + static const char fmt_gpio_fn[] = "[%sfn] input function%13d [0=none,1=limit,2=interlock,3=shutdown,4=probe]\n"; static const char fmt_gpio_in[] = "Input %s state: %5d\n"; static const char fmt_gpio_domode[] = "[%smo] output mode%16d [0=active low,1=active high,2=disabled]\n"; diff --git a/g2core/gpio.h b/g2core/gpio.h index 8f753171..c54d7479 100644 --- a/g2core/gpio.h +++ b/g2core/gpio.h @@ -68,9 +68,10 @@ typedef enum { // actions are initiated from within the typedef enum { // functions are requested from the ISR, run from the main loop INPUT_FUNCTION_NONE = 0, - INPUT_FUNCTION_LIMIT, // limit switch processing - INPUT_FUNCTION_INTERLOCK, // interlock processing - INPUT_FUNCTION_SHUTDOWN, // shutdown in support of external emergency stop + INPUT_FUNCTION_LIMIT = 1, // limit switch processing + INPUT_FUNCTION_INTERLOCK = 2, // interlock processing + INPUT_FUNCTION_SHUTDOWN = 3, // shutdown in support of external emergency stop + INPUT_FUNCTION_PROBE = 4, // assign input as probe input INPUT_FUNCTION_MAX // unused. Just for range checking } inputFunc; @@ -130,6 +131,7 @@ void output_reset(void); bool gpio_read_input(const uint8_t input_num); void gpio_set_homing_mode(const uint8_t input_num, const bool is_homing); void gpio_set_probing_mode(const uint8_t input_num, const bool is_probing); +int8_t gpio_get_probing_input(void); stat_t io_set_mo(nvObj_t *nv); stat_t io_set_ac(nvObj_t *nv); diff --git a/g2core/json_parser.cpp b/g2core/json_parser.cpp index 9ae5da39..3c48d084 100644 --- a/g2core/json_parser.cpp +++ b/g2core/json_parser.cpp @@ -85,7 +85,7 @@ static stat_t _get_nv_pair(nvObj_t *nv, char **pstr, int8_t *depth); * _json_parser_execute() executes sets and gets in an application agnostic way. It should work for other apps than g2core */ -void json_parser(char *str) +stat_t json_parser(char *str, bool suppress_response) // suppress_response defaults to false, see decalaration in .h { nvObj_t *nv = nv_reset_nv_list(); // get a fresh nvObj list stat_t status = _json_parser_kernal(nv, str); @@ -93,11 +93,12 @@ void json_parser(char *str) nv = nv_body; status = _json_parser_execute(nv); } - if (status == STAT_COMPLETE) { // skip the print if returning from something that already did it. - return; + if (suppress_response || (status == STAT_COMPLETE)) { // skip the print if returning from something that already did it. + return status; } nv_print_list(status, TEXT_NO_PRINT, JSON_RESPONSE_FORMAT); sr_request_status_report(SR_REQUEST_TIMED); // generate incremental status report to show any changes + return STAT_OK; } // This is almost the same as json_parser, except it doesn't *always* execute the parsed out list, and it never returns a reponse @@ -524,7 +525,7 @@ void json_print_list(stat_t status, uint8_t flags) void json_print_response(uint8_t status, const bool only_to_muted /*= false*/) { - if (js.json_verbosity == JV_SILENT) { // silent means no responses + if ((js.json_verbosity == JV_SILENT) || (cs.responses_suppressed)) { // silent means no responses return; } if (js.json_verbosity == JV_EXCEPTIONS) { // cutout for JV_EXCEPTIONS mode diff --git a/g2core/json_parser.h b/g2core/json_parser.h index c5343b74..4bef7817 100644 --- a/g2core/json_parser.h +++ b/g2core/json_parser.h @@ -82,7 +82,7 @@ extern jsSingleton_t js; /**** Function Prototypes ****/ -void json_parser(char *str); +stat_t json_parser(char *str, bool suppress_response = false); void json_parse_for_exec(char *str, bool execute); uint16_t json_serialize(nvObj_t *nv, char *out_buf, uint16_t size); void json_print_object(nvObj_t *nv); diff --git a/g2core/marlin_compatibility.cpp b/g2core/marlin_compatibility.cpp new file mode 100644 index 00000000..964df8bb --- /dev/null +++ b/g2core/marlin_compatibility.cpp @@ -0,0 +1,603 @@ +/* + * marlin_compatibility.cpp - support for marlin protocol and gcode + * This file is part of the g2core project + * + * Copyright (c) 2017 Alden S. Hart, Jr. + * Copyright (c) 2017 Rob Giseburt + * + * This file ("the software") is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2 as published by the + * Free Software Foundation. You should have received a copy of the GNU General Public + * License, version 2 along with the software. If not, see . + * + * THE SOFTWARE IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT WITHOUT ANY + * WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT + * SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "g2core.h" // #1 +#include "config.h" // #2 +#include "settings.h" + +#if MARLIN_COMPAT_ENABLED == true + +#include "controller.h" +#include "gcode_parser.h" +#include "canonical_machine.h" +#include "util.h" +#include "xio.h" // for char definitions +#include "temperature.h" // for temperature controls +#include "json_parser.h" +#include "planner.h" +#include "stepper.h" // for MOTOR_TIMEOUT_SECONDS_MIN/MOTOR_TIMEOUT_SECONDS_MAX +#include "MotateTimers.h" // for char definitions +#include "MotateUniqueID.h" // for Motate::UUID + +// Structures used +enum STK500 { + // Success + STATUS_CMD_OK = 0x00, + + // Warnings + STATUS_CMD_TOUT = 0x80, + STATUS_RDY_BSY_TOUT = 0x81, + STATUS_SET_PARAM_MISSING = 0x82, + + // Errors + STATUS_CMD_FAILED = 0xC0, + STATUS_CKSUM_ERROR = 0xC1, + STATUS_CMD_UNKNOWN = 0xC9, +}; + +// Global state variables + +MarlinStateExtended_t mst; // Marlin state object + +// Local variables + +bool temperature_requested = false; +bool position_requested = false; + +// State machine to handle marlin temperature controls +enum class MarlinSetTempState { + Idle = 0, + SettingTemperature, + StartingUpdates, + StartingWait, + StoppingUpdates, + SettingTemperatureNoWait +}; +MarlinSetTempState set_temp_state; // record the state for the temperature-control pseudo-cycle +// These next parameters for the next temperature-control pseudo-cycle are only needed until the calls are queued. +float next_temperature; // as it says +uint8_t next_temperature_tool; // 0-based, with 2 being the heat-bed + +// Information about if we are to be dumping periodic temperature updates +bool temperature_updates_requested = false; +Motate::Timeout temperature_update_timeout; + +// local helper functions and macros + +/*********************************************************************************** + * _get_specific_nv() - convenience function to get an NV object + */ +nvObj_t *_get_specific_nv(const char *key) { + nvObj_t *nv = nv_reset_nv_list(); // returns first object in the body + + strncpy(nv->token, key, TOKEN_LEN); + + // validate and post-process the token + if ((nv->index = nv_get_index((const char *)"", nv->token)) == NO_MATCH) { // get index or fail it + return nullptr; // since we JUST provided the keys, this should never happen + } + strcpy(nv->group, cfgArray[nv->index].group); // capture the group string if there is one + nv_get(nv); + return nv; +} + +/*********************************************************************************** + * _report_temperatures() - convenience function called from marlin_response() and marlin_callback() + */ +void _report_temperatures(char *(&str)) { + // Tool 0 is extruder 1 + uint8_t tool = cm.gm.tool; + + str_concat(str, " T:"); + str += floattoa(str, cm_get_temperature(tool), 2); + str_concat(str, " /"); + str += floattoa(str, cm_get_set_temperature(tool), 2); + + str_concat(str, " B:"); + str += floattoa(str, cm_get_temperature(3), 2); + str_concat(str, " /"); + str += floattoa(str, cm_get_set_temperature(3), 2); + + str_concat(str, " @:"); + str += floattoa(str, cm_get_heater_output(tool), 0); + + str_concat(str, " B@:"); + str += floattoa(str, cm_get_heater_output(3), 0); +} + +/*********************************************************************************** + * _report_position() - convenience function called from marlin_response() + */ +void _report_position(char *(&str)) { + str_concat(str, " X:"); + str += floattoa(str, cm_get_work_position(ACTIVE_MODEL, 0), 2); + str_concat(str, " Y:"); + str += floattoa(str, cm_get_work_position(ACTIVE_MODEL, 1), 2); + str_concat(str, " Z:"); + str += floattoa(str, cm_get_work_position(ACTIVE_MODEL, 2), 2); + + uint8_t tool = cm.gm.tool; + if ((tool > 0) && (tool < 3)) { + str_concat(str, " E:"); + str += floattoa(str, cm_get_work_position(ACTIVE_MODEL, 2), tool + 2); // A or B, depending on tool + } +} + +/*********************************************************************************** + *** MARLIN GCODES AND MCODES + *** Called from gcore_parser.cpp + ***********************************************************************************/ + +/*********************************************************************************** + * marlin_start_tramming_bed() - G29 called from gcode parser + * marlin G29 support - run a script to emulate a G29 homing command + */ + +#ifdef MARLIN_G29_SCRIPT +auto marlin_g29_file = make_xio_flash_file(MARLIN_G29_SCRIPT); +#endif + +stat_t marlin_start_tramming_bed() { +#ifndef MARLIN_G29_SCRIPT + return (STAT_G29_NOT_CONFIGURED); +#else + xio_send_file(marlin_g29_file); + return (STAT_OK); +#endif +} + +/*********************************************************************************** + * marlin_list_sd_response() - M20 called from gcode parser + * marlin_select_sd_response() - M23 called from gcode parser + */ + +stat_t marlin_list_sd_response() +{ + char buffer[128]; + char *str = buffer; + + str_concat(str, "Begin file list\nEnd file list\n"); + *str = 0; + xio_writeline(buffer); + + return (STAT_OK); +} + +stat_t marlin_select_sd_response(const char *file) +{ + char buffer[128]; + char *str = buffer; + + str_concat(str, "open failed, File: "); + strncpy(str, file, Motate::strlen(file)); + str += Motate::strlen(file); + str_concat(str, "\n"); + *str = 0; + xio_writeline(buffer); + + return (STAT_OK); +} + +/*********************************************************************************** + * cm_marlin_set_extruder_mode() - M82, M83 called from gcode parser (affects MODEL only) + * + * EXTRUDER_MOVES_NORMAL = 0, // M82 + * EXTRUDER_MOVES_RELATIVE, // M83 + * EXTRUDER_MOVES_VOLUMETRIC // Ultimaker2Marlin + */ + +stat_t marlin_set_extruder_mode(const uint8_t mode) +{ + mst.extruder_mode = (cmExtruderMode)mode; + return (STAT_OK); +} + +/*********************************************************************************** + * marlin_disable_motors() - M84 (without S) called from gcode parser + */ + +stat_t marlin_disable_motors() +{ + char buffer[128]; + char *str = buffer; + + // TODO: support other parameters + str_concat(str, "{md:0}"); + cm_json_command(buffer); + + return (STAT_OK); +} + +/*********************************************************************************** + * marlin_set_motor_timeout() - M84 (with S), M85 Sxxx called from gcode parser + */ + +stat_t marlin_set_motor_timeout(float s) // M18 Sxxx, M84 Sxxx, M85 Sxxx +{ + if (s < MOTOR_TIMEOUT_SECONDS_MIN) { + return (STAT_INPUT_LESS_THAN_MIN_VALUE); + } + if (s > MOTOR_TIMEOUT_SECONDS_MAX) { + return (STAT_INPUT_EXCEEDS_MAX_VALUE); + } + char buffer[128]; + char *str = buffer; + + // TODO: support other fans, or remapping output + str_concat(str, "{mt:"); + str += floattoa(str, s, 1); + str_concat(str, "}"); + + cm_json_command(buffer); + return (STAT_OK); +} + +/*********************************************************************************** + * marlin_set_temperature() - M104, M140, M109, M190 called from gcode parser + * _queue_next_temperature_comands() - returns true if it finished + * _marlin_start_temperature_updates() + * _marlin_end_temperature_updates() + */ + +void _marlin_start_temperature_updates(float* vect, bool* flag) { + temperature_updates_requested = true; + temperature_update_timeout.set(1); // immediately +} + +void _marlin_end_temperature_updates(float* vect, bool* flag) { + temperature_updates_requested = false; +} + +bool _queue_next_temperature_commands() +{ + if (MarlinSetTempState::Idle != set_temp_state) { + if (mp_planner_is_full()) { + return false; + } + + char buffer[128]; + char *str = buffer; + + if ((MarlinSetTempState::SettingTemperature == set_temp_state) || + (MarlinSetTempState::SettingTemperatureNoWait == set_temp_state)) + { + str_concat(str, "{he"); + str += inttoa(str, next_temperature_tool); + str_concat(str, "st:"); + str += floattoa(str, next_temperature, 2); + str_concat(str, "}"); + cm_json_command(buffer); + + if (MarlinSetTempState::SettingTemperatureNoWait == set_temp_state) { + set_temp_state = MarlinSetTempState::Idle; + return true; + } + + set_temp_state = MarlinSetTempState::StartingUpdates; + if (mp_planner_is_full()) { + return false; + } + } + + if (MarlinSetTempState::StartingUpdates == set_temp_state) { + mp_queue_command(_marlin_start_temperature_updates, nullptr, nullptr); + + set_temp_state = MarlinSetTempState::StartingWait; + if (mp_planner_is_full()) { + return false; + } + } + + if (MarlinSetTempState::StartingWait == set_temp_state) { + str = buffer; + str_concat(str, "{he"); + str += inttoa(str, next_temperature_tool); + str_concat(str, "at:t}"); + cm_json_wait(buffer); + + set_temp_state = MarlinSetTempState::StoppingUpdates; + if (mp_planner_is_full()) { + return false; + } + } + + if (MarlinSetTempState::StoppingUpdates == set_temp_state) { + mp_queue_command(_marlin_end_temperature_updates, nullptr, nullptr); + + set_temp_state = MarlinSetTempState::Idle; + } + } + return true; +} + +stat_t marlin_set_temperature(uint8_t tool, float temperature, bool wait) { + if (MarlinSetTempState::Idle != set_temp_state) { + return (STAT_BUFFER_FULL_FATAL); // we shouldn't be here + } + if ((tool < 1) || (tool > 3)) { + return STAT_INPUT_VALUE_RANGE_ERROR; + } + + set_temp_state = wait ? MarlinSetTempState::SettingTemperature : MarlinSetTempState::SettingTemperatureNoWait; + next_temperature = temperature; + + next_temperature_tool = tool; + + _queue_next_temperature_commands(); // we can ignore the return + return (STAT_OK); +} + +/*********************************************************************************** + * marlin_request_temperature_report() - M105 called from gcode parser + */ +stat_t marlin_request_temperature_report() // M105 +{ + uint8_t tool = cm.gm.tool; + if ((tool < 1) || (tool > 2)) { + return STAT_INPUT_VALUE_RANGE_ERROR; + } + + temperature_requested = true; + return STAT_OK; +} + +/*********************************************************************************** + * marlin_set_fan_speed() - M106, M107 called from gcode parser + */ + +stat_t marlin_set_fan_speed(const uint8_t fan, float speed) +{ + char buffer[128]; + char *str = buffer; + if ((fan != 0) || (speed < 0.0) || (speed > 255.0)) { + return STAT_INPUT_VALUE_RANGE_ERROR; + } + + // TODO: support other fans, or remapping output + str_concat(str, "{out4:"); + str += floattoa(str, (speed < 1.0) ? speed : (speed / 255.0), 4); + str_concat(str, "}"); + + cm_json_command(buffer); + + return (STAT_OK); +} + +/*********************************************************************************** + * marlin_request_position_report() - M114 called from gcode parser + */ +stat_t marlin_request_position_report() // M114 +{ + position_requested = true; + return STAT_OK; +} + +/*********************************************************************************** + * marlin_report_version() - M115 + */ + +stat_t marlin_report_version() +{ + char buffer[128]; + char *str = buffer; + + str_concat(str, "ok FIRMWARE_NAME:Marlin g2core-"); + str_concat(str, G2CORE_FIRMWARE_BUILD_STRING); + *str = 0; + xio_writeline(buffer); + str = buffer; + *str = 0; + + str_concat(str, " SOURCE_CODE_URL:https://github.com/synthetos/g2"); + *str = 0; + xio_writeline(buffer); + str = buffer; *str = 0; + + str_concat(str, " PROTOCOL_VERSION:1.0"); + *str = 0; + xio_writeline(buffer); + str = buffer; *str = 0; + + str_concat(str, " MACHINE_TYPE:"); +#ifdef SETTINGS_FILE +#define settings_file_string1(s) #s +#define settings_file_string2(s) settings_file_string1(s) + str_concat(str, settings_file_string2(SETTINGS_FILE)); +#undef settings_file_string1 +#undef settings_file_string2 +#else + str_concat(str, ""); +#endif + *str = 0; + xio_writeline(buffer); + str = buffer; *str = 0; + + // TODO: make this configurable, based on the tool table + str_concat(str, " EXTRUDER_COUNT:1"); + *str = 0; + xio_writeline(buffer); + str = buffer; *str = 0; + + str_concat(str, " UUID:"); + const char *uuid = Motate::UUID; + strncpy(str, uuid, Motate::strlen(uuid)); + str += Motate::strlen(uuid); + str_concat(str, "\n"); + *str = 0; + xio_writeline(buffer); + str = buffer; *str = 0; + + return (STAT_OK); +} + + +/*********************************************************************************** + *** MARLIN INTERNAL FUNCTIONS + ***********************************************************************************/ + +/*********************************************************************************** + * marlin_callback() - called by controller dispatcher - return STAT_EAGAIN if it failed + */ +stat_t marlin_callback() +{ + if ((js.json_mode == MARLIN_COMM_MODE) && temperature_updates_requested && (temperature_update_timeout.isPast())) { + char buffer[128]; + char *str = buffer; + + _report_temperatures(str); + + *str++ = '\n'; + *str++ = 0; + + temperature_update_timeout.set(1000); // every second + + xio_writeline(buffer); + } // temperature updates + + if (!_queue_next_temperature_commands()) { + return STAT_EAGAIN; + } + + return STAT_OK; +} + +/*********************************************************************************** + * marlin_response() - marlin mirror of text_response(), called from _dispatch_kernel() in controller.cpp + */ +void marlin_response(const stat_t status, char *buf) +{ + char buffer[128]; + char *str = buffer; + + bool request_resend = false; + + if (cs.responses_suppressed) { + return; + } + + if ((status == STAT_OK) || (status == STAT_EAGAIN) || (status == STAT_NOOP)) { + str_concat(str, "ok"); + + if (temperature_requested) { + _report_temperatures(str); + } + + if (position_requested) { + _report_position(str); + } + +// nvObj_t *nv = nv_body+1; +// +// if (nv_get_type(nv) == NV_TYPE_MESSAGE) { +// p += sprintf(p, (char *)*nv->stringp); +// } +// sprintf(p, "\n"); + + } + else if (status == STAT_CHECKSUM_MATCH_FAILED) { + str_concat(str, "Error:checksum mismatch, Last Line: "); + str += inttoa(str, cm.gmx.last_line_number); + request_resend = true; + } + else if (status == STAT_LINE_NUMBER_OUT_OF_SEQUENCE) { + str_concat(str, "Error:Line Number is not Last Line Number+1, Last Line: "); + str += inttoa(str, cm.gmx.last_line_number); + request_resend = true; + } + else { + str += sprintf(str, "Error:%s", get_status_message(status)); + } + + *str++ = '\n'; + *str++ = 0; + + // reset requests + temperature_requested = false; + position_requested = false; + + xio_writeline(buffer); + + + if (request_resend) { + str = buffer; + str_concat(str, "Resend: "); + str += inttoa(str, cm.gmx.last_line_number+1); + *str++ = '\n'; + *str++ = 0; + xio_writeline(buffer); + } +} + +/*********************************************************************************** + * marlin_handle_fake_stk500() - returns true if it handled something (IOW, don't futher process the line) + * _marlin_fake_stk500_response() - convenience function for formang responses from marlin_handle_fake_stk500() + */ + +void _marlin_fake_stk500_response(char *resp, uint16_t length) +{ + char *str = resp; + + str[2] = (length >> 8) & 0xFF; + str[3] = (length) & 0xFF; + + uint8_t crc = 0; + + for (uint16_t i = length + 5; i>0; i--) { + crc ^= *resp++; + } + + *resp = crc; + + xio_write(str, length + 6); +} + +bool marlin_handle_fake_stk500(char *str) +{ + char *resp = str; + if (*str != 0x1B) { return false; } + + // we handle only a handful of messages ... poorly + // for example: this is where we should validate the checksum, but we are going to not for now. + + str += 1 + 1 + 2 + 1; // 1 for 0x1B, 1 for sequence, 2 for length, 1 for 0x0E + + char c = *str++; + + if ((c == 0x01) || // CMD_SIGN_ON + (c == 0x10) || // CMD_ENTER_PROGMODE_ISP + (c == 0x11) // CMD_LEAVE_PROGMODE_ISP + ) + { + *str = STATUS_CMD_OK; + _marlin_fake_stk500_response(resp, 2); + + if (c == 0x11) { // CMD_LEAVE_PROGMODE_ISP + xio_exit_fake_bootloader(); + } + + return true; + } + + *str = STATUS_CMD_UNKNOWN; + _marlin_fake_stk500_response(resp, 2); + return true; +} + + +#endif // MARLIN_COMPAT_ENABLED == true diff --git a/g2core/marlin_compatibility.h b/g2core/marlin_compatibility.h new file mode 100644 index 00000000..91051392 --- /dev/null +++ b/g2core/marlin_compatibility.h @@ -0,0 +1,68 @@ +/* + * marlin_compatibility.cpp - support for marlin protocol and gcode + * This file is part of the g2core project + * + * Copyright (c) 2017 Alden S. Hart, Jr. + * Copyright (c) 2017 Rob Giseburt + * + * This file ("the software") is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2 as published by the + * Free Software Foundation. You should have received a copy of the GNU General Public + * License, version 2 along with the software. If not, see . + * + * THE SOFTWARE IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT WITHOUT ANY + * WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT + * SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef MARLIN_COMPAT_H_ONCE +#define MARLIN_COMPAT_H_ONCE + +#include "g2core.h" // #1 +#include "config.h" // #2 + +enum cmExtruderMode { + EXTRUDER_MOVES_NORMAL = 0, // M82 + EXTRUDER_MOVES_RELATIVE, // M83 + EXTRUDER_MOVES_VOLUMETRIC // Ultimaker2Marlin +}; + +typedef struct MarlinStateExtended { // Canonical machine extensions for Marlin + bool marlin_flavor; // true if we are parsing gcode as Marlin-flavor + cmExtruderMode extruder_mode; // Mode of the extruder - changes how "E" is interpreted +} MarlinStateExtended_t; + +extern MarlinStateExtended_t mst; // Marlin state object + +/* + * Global Scope Functions + */ + +// *** gcode and Mcode handling *** + +stat_t marlin_start_tramming_bed(); // G29 + +stat_t marlin_list_sd_response(); // M20 +stat_t marlin_select_sd_response(const char *file); // M23 +stat_t marlin_set_extruder_mode(const uint8_t mode); // M82, M82 +stat_t marlin_disable_motors(); // M84 +stat_t marlin_set_motor_timeout(float s); // M84 Sxxx, M85 Sxxx, M18 Sxxx + +stat_t marlin_set_temperature(uint8_t tool, float temperature, bool wait); // M104, M109, M140, M190 +stat_t marlin_request_temperature_report(); // M105 +stat_t marlin_set_fan_speed(const uint8_t fan, float speed); // M106, M107 + +stat_t marlin_request_position_report(); // M114 +stat_t marlin_report_version(); // M115 + +// *** Marlin internal functions *** + +stat_t marlin_callback(); // controller loop callback +void marlin_response(const stat_t status, char *buf); // response handler (primarily just prints "ok") +bool marlin_handle_fake_stk500(char *str); // fake stk500v2 + + +#endif // End of include guard: MARLIN_COMPAT_H_ONCE diff --git a/g2core/persistence.cpp b/g2core/persistence.cpp old mode 100755 new mode 100644 index b58d25ca..e400be8c --- a/g2core/persistence.cpp +++ b/g2core/persistence.cpp @@ -2,7 +2,7 @@ * persistence.cpp - persistence functions * This file is part of the g2core project * - * Copyright (c) 2013 - 2016 Alden S. Hart Jr. + * Copyright (c) 2013 - 2017 Alden S. Hart Jr. * * This file ("the software") is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2 as published by the @@ -71,8 +71,8 @@ stat_t read_persistent_value(nvObj_t *nv) stat_t write_persistent_value(nvObj_t *nv) { - if (cm.cycle_state != CYCLE_OFF) { // can't write when machine is moving - return(rpt_exception(STAT_FILE_NOT_OPEN, "write_persistent_value() can't write when machine is in cycle")); - } +// if (cm.cycle_state != CYCLE_OFF) { // can't write when machine is moving +// return(rpt_exception(STAT_FILE_NOT_OPEN, "write_persistent_value() can't write when machine is in cycle")); +// } return (STAT_OK); } diff --git a/g2core/planner.cpp b/g2core/planner.cpp index f8dcd146..83408aee 100644 --- a/g2core/planner.cpp +++ b/g2core/planner.cpp @@ -311,7 +311,7 @@ stat_t mp_runtime_command(mpBuf_t *bf) /************************************************************************* * mp_json_command() - queue a json command - * _exec_json_command() - execute json string + * _exec_json_command() - execute json string (from exec system) */ stat_t mp_json_command(char *json_string) @@ -334,6 +334,14 @@ static void _exec_json_command(float *value, bool *flag) jc.free_buffer(); } +/************************************************************************* + * mp_json_command_immediate() - execute a json command with response suppressed + */ + +stat_t mp_json_command_immediate(char *json_string) +{ + return json_parser(json_string); +} /************************************************************************* * mp_json_wait() - queue a json wait command @@ -597,6 +605,16 @@ void mp_end_feed_override(const float ramp_time) mp_start_feed_override (FEED_OVERRIDE_RAMP_TIME, 1.00); } +void mp_start_traverse_override(const float ramp_time, const float override_factor) +{ + return; +} + +void mp_end_traverse_override(const float ramp_time) +{ + return; +} + /* * mp_planner_time_accounting() - gather time in planner */ diff --git a/g2core/planner.h b/g2core/planner.h index 9a1da416..2289510f 100644 --- a/g2core/planner.h +++ b/g2core/planner.h @@ -311,7 +311,7 @@ struct mpBuffer_to_clear { float block_time_ms; float plannable_time_ms; // time in planner float plannable_length; // length in planner - uint8_t meet_iterations; // iterations needed in _get_meet_velocity + int8_t meet_iterations; // iterations needed in _get_meet_velocity //+++++ to here bufferState buffer_state; // used to manage queuing/dequeuing @@ -537,6 +537,7 @@ stat_t mp_runtime_command(mpBuf_t *bf); stat_t mp_json_command(char *json_string); stat_t mp_json_wait(char *json_string); +stat_t mp_json_command_immediate(char *json_string); stat_t mp_dwell(const float seconds); void mp_end_dwell(void); @@ -553,6 +554,8 @@ stat_t mp_planner_callback(); void mp_replan_queue(mpBuf_t *bf); void mp_start_feed_override(const float ramp_time, const float override); void mp_end_feed_override(const float ramp_time); +void mp_start_traverse_override(const float ramp_time, const float override); +void mp_end_traverse_override(const float ramp_time); void mp_planner_time_accounting(void); // planner buffer primitives diff --git a/g2core/report.cpp b/g2core/report.cpp index e31bbb86..cb037846 100644 --- a/g2core/report.cpp +++ b/g2core/report.cpp @@ -112,7 +112,13 @@ void rpt_print_loading_configs_message(void) void rpt_print_system_ready_message(void) { +#if MARLIN_COMPAT_ENABLED == true + if (MARLIN_COMM_MODE != js.json_mode) { + _startup_helper(STAT_OK, "SYSTEM READY"); + } +#else _startup_helper(STAT_OK, "SYSTEM READY"); +#endif if (cs.comm_mode == TEXT_MODE) { text_response(STAT_OK, (char *)"");}// prompt } diff --git a/g2core/settings/settings_Printrbot_Play.h b/g2core/settings/settings_Printrbot_Play.h index 09d996ef..3983a151 100644 --- a/g2core/settings/settings_Printrbot_Play.h +++ b/g2core/settings/settings_Printrbot_Play.h @@ -57,8 +57,10 @@ // Communications and reporting settings -#define COMM_MODE JSON_MODE // one of: TEXT_MODE, JSON_MODE -#define XIO_ENABLE_FLOW_CONTROL FLOW_CONTROL_RTS // FLOW_CONTROL_OFF, FLOW_CONTROL_RTS +#define MARLIN_COMPAT_ENABLED true // enable marlin compatibility mode +#define COMM_MODE JSON_MODE // one of: TEXT_MODE, JSON_MODE +#define XIO_ENABLE_FLOW_CONTROL FLOW_CONTROL_RTS // FLOW_CONTROL_OFF, FLOW_CONTROL_RTS +#define XIO_UART_MUTES_WHEN_USB_CONNECTED 1 // Mute the UART when USB connects #define TEXT_VERBOSITY TV_VERBOSE // one of: TV_SILENT, TV_VERBOSE #define JSON_VERBOSITY JV_LINENUM // one of: JV_SILENT, JV_FOOTER, JV_CONFIGS, JV_MESSAGES, JV_LINENUM, JV_VERBOSE @@ -103,13 +105,13 @@ #define M1_POWER_LEVEL 0.4 // 1mp // 80 steps/mm at 1/16 microstepping = 40 mm/rev -#define M3_MOTOR_MAP AXIS_Y -#define M3_STEP_ANGLE 1.8 -#define M3_TRAVEL_PER_REV 40.64 -#define M3_MICROSTEPS 32 -#define M3_POLARITY 0 -#define M3_POWER_MODE MOTOR_POWER_MODE -#define M3_POWER_LEVEL 0.4 +#define M5_MOTOR_MAP AXIS_Y +#define M5_STEP_ANGLE 1.8 +#define M5_TRAVEL_PER_REV 40.64 +#define M5_MICROSTEPS 32 +#define M5_POLARITY 0 +#define M5_POWER_MODE MOTOR_POWER_MODE +#define M5_POWER_LEVEL 0.4 #define M2_MOTOR_MAP AXIS_Z #define M2_STEP_ANGLE 1.8 @@ -129,13 +131,13 @@ #define M4_POWER_LEVEL 0.4 // 96 steps/mm at 1/16 microstepping = 33.3333 mm/rev -#define M5_MOTOR_MAP AXIS_B -#define M5_STEP_ANGLE 1.8 -#define M5_TRAVEL_PER_REV 360 // degrees moved per motor rev -#define M5_MICROSTEPS 32 -#define M5_POLARITY 0 -#define M5_POWER_MODE MOTOR_POWER_MODE -#define M5_POWER_LEVEL 0.35 +#define M3_MOTOR_MAP AXIS_B +#define M3_STEP_ANGLE 1.8 +#define M3_TRAVEL_PER_REV 360 // degrees moved per motor rev +#define M3_MICROSTEPS 32 +#define M3_POLARITY 0 +#define M3_POWER_MODE MOTOR_POWER_MODE +#define M3_POWER_LEVEL 0.35 // *** axis settings ********************************************************************************** diff --git a/g2core/settings/settings_Printrbot_Plus.h b/g2core/settings/settings_Printrbot_Plus.h index 4ca7db27..8ecb3c49 100644 --- a/g2core/settings/settings_Printrbot_Plus.h +++ b/g2core/settings/settings_Printrbot_Plus.h @@ -57,8 +57,10 @@ // Communications and reporting settings +#define MARLIN_COMPAT_ENABLED true // enable marlin compatibility mode #define COMM_MODE JSON_MODE // one of: TEXT_MODE, JSON_MODE #define XIO_ENABLE_FLOW_CONTROL FLOW_CONTROL_RTS // FLOW_CONTROL_OFF, FLOW_CONTROL_RTS +#define XIO_UART_MUTES_WHEN_USB_CONNECTED 1 // Mute the UART when USB connects #define TEXT_VERBOSITY TV_VERBOSE // one of: TV_SILENT, TV_VERBOSE #define JSON_VERBOSITY JV_MESSAGES // one of: JV_SILENT, JV_FOOTER, JV_CONFIGS, JV_MESSAGES, JV_LINENUM, JV_VERBOSE diff --git a/g2core/settings/settings_Printrbot_Simple_1403.h b/g2core/settings/settings_Printrbot_Simple_1403.h index c32c4739..a79115d1 100644 --- a/g2core/settings/settings_Printrbot_Simple_1403.h +++ b/g2core/settings/settings_Printrbot_Simple_1403.h @@ -57,8 +57,10 @@ // Communications and reporting settings +#define MARLIN_COMPAT_ENABLED true // enable marlin compatibility mode #define COMM_MODE JSON_MODE // one of: TEXT_MODE, JSON_MODE #define XIO_ENABLE_FLOW_CONTROL FLOW_CONTROL_RTS // FLOW_CONTROL_OFF, FLOW_CONTROL_RTS +#define XIO_UART_MUTES_WHEN_USB_CONNECTED 1 // Mute the UART when USB connects #define TEXT_VERBOSITY TV_VERBOSE // one of: TV_SILENT, TV_VERBOSE #define JSON_VERBOSITY JV_MESSAGES // one of: JV_SILENT, JV_FOOTER, JV_CONFIGS, JV_MESSAGES, JV_LINENUM, JV_VERBOSE diff --git a/g2core/settings/settings_Printrbot_Simple_1608.h b/g2core/settings/settings_Printrbot_Simple_1608.h index a24510b6..8fc30ccc 100644 --- a/g2core/settings/settings_Printrbot_Simple_1608.h +++ b/g2core/settings/settings_Printrbot_Simple_1608.h @@ -57,6 +57,7 @@ // Communications and reporting settings +#define MARLIN_COMPAT_ENABLED true // enable marlin compatibility mode #define COMM_MODE JSON_MODE // one of: TEXT_MODE, JSON_MODE #define XIO_ENABLE_FLOW_CONTROL FLOW_CONTROL_RTS // FLOW_CONTROL_OFF, FLOW_CONTROL_RTS #define XIO_UART_MUTES_WHEN_USB_CONNECTED 1 // Mute the UART when USB connects @@ -86,6 +87,23 @@ #define GCODE_DEFAULT_PATH_CONTROL PATH_CONTINUOUS #define GCODE_DEFAULT_DISTANCE_MODE ABSOLUTE_DISTANCE_MODE +#define MARLIN_G29_SCRIPT \ + "(MSG Tramming started)\n" \ + "M100 ({\"_leds\":3})\n" \ + "G1 X0 Y145 Z6 F20000\n" \ + "G38.2 Z-10 F200\n" \ + "G1 Z5 F20000\n" \ + "M100 ({\"_leds\":5})\n" \ + "G1 X210 Y65 F20000\n" \ + "G38.2 Z-10 F200\n" \ + "G1 Z5 F20000\n" \ + "M100 ({\"_leds\":6})\n" \ + "G1 X0 Y10 F20000\n" \ + "G38.2 Z-10 F200\n" \ + "G1 Z5 F20000\n" \ + "M100 ({\"_leds\":3})\n" \ + "M100 ({\"tram\":1})" \ + "(MSG Tramming completed)\n" // *** motor settings ************************************************************************************ @@ -282,7 +300,7 @@ // Zmin #define DI5_MODE IO_ACTIVE_LOW // Z probe #define DI5_ACTION INPUT_ACTION_NONE -#define DI5_FUNCTION INPUT_FUNCTION_NONE +#define DI5_FUNCTION INPUT_FUNCTION_PROBE // Zmax #define DI6_MODE IO_MODE_DISABLED diff --git a/g2core/settings/settings_default.h b/g2core/settings/settings_default.h index 37ad7848..02808118 100644 --- a/g2core/settings/settings_default.h +++ b/g2core/settings/settings_default.h @@ -2,7 +2,7 @@ * settings_default.h - default machine profile - Set for Shapeoko2 * This file is part of the g2core project * - * Copyright (c) 2012 - 2016 Alden S. Hart, Jr. + * Copyright (c) 2012 - 2017 Alden S. Hart, Jr. * * This file ("the software") is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2 as published by the @@ -114,6 +114,10 @@ #define COOLANT_PAUSE_ON_HOLD true // {coph: #endif +#ifndef PROBE_REPORT_ENABLE +#define PROBE_REPORT_ENABLE true // {prbr: +#endif + /* * The following is to fix an issue where feedrate override was being defined in some users * settings files but not others. This would otherwise cause an undefined compile error. @@ -171,6 +175,11 @@ //#define STATUS_REPORT_DEFAULTS "line","vel","mpox","mpoy","mpoz","mpoa","coor","ofsa","ofsx","ofsy","ofsz","dist","unit","stat","homz","homy","homx","momo" #endif + +#ifndef MARLIN_COMPAT_ENABLED +#define MARLIN_COMPAT_ENABLED false // boolean, either true or false +#endif + // *** Gcode Startup Defaults *** // #ifndef GCODE_DEFAULT_UNITS @@ -674,7 +683,7 @@ // Xmin on v9 board #ifndef DI1_MODE -#define DI1_MODE NORMALLY_OPEN +#define DI1_MODE IO_ACTIVE_LOW // Normally open #endif #ifndef DI1_ACTION #define DI1_ACTION INPUT_ACTION_NONE @@ -685,7 +694,7 @@ // Xmax #ifndef DI2_MODE -#define DI2_MODE NORMALLY_OPEN +#define DI2_MODE IO_ACTIVE_LOW // Normally open #endif #ifndef DI2_ACTION #define DI2_ACTION INPUT_ACTION_NONE @@ -696,7 +705,7 @@ // Ymin #ifndef DI3_MODE -#define DI3_MODE NORMALLY_OPEN +#define DI3_MODE IO_ACTIVE_LOW // Normally open #endif #ifndef DI3_ACTION #define DI3_ACTION INPUT_ACTION_NONE @@ -707,7 +716,7 @@ // Ymax #ifndef DI4_MODE -#define DI4_MODE NORMALLY_OPEN +#define DI4_MODE IO_ACTIVE_LOW // Normally open #endif #ifndef DI4_ACTION #define DI4_ACTION INPUT_ACTION_NONE @@ -718,18 +727,18 @@ // Zmin #ifndef DI5_MODE -#define DI5_MODE NORMALLY_OPEN +#define DI5_MODE IO_ACTIVE_LOW // Normally open #endif #ifndef DI5_ACTION #define DI5_ACTION INPUT_ACTION_NONE #endif #ifndef DI5_FUNCTION -#define DI5_FUNCTION INPUT_FUNCTION_NONE +#define DI5_FUNCTION INPUT_FUNCTION_PROBE #endif // Zmax #ifndef DI6_MODE -#define DI6_MODE NORMALLY_OPEN +#define DI6_MODE IO_ACTIVE_LOW // Normally open #endif #ifndef DI6_ACTION #define DI6_ACTION INPUT_ACTION_NONE @@ -740,7 +749,7 @@ // Amin #ifndef DI7_MODE -#define DI7_MODE NORMALLY_OPEN +#define DI7_MODE IO_ACTIVE_LOW // Normally open #endif #ifndef DI7_ACTION #define DI7_ACTION INPUT_ACTION_NONE @@ -751,7 +760,7 @@ // Amax #ifndef DI8_MODE -#define DI8_MODE NORMALLY_OPEN +#define DI8_MODE IO_ACTIVE_LOW // Normally open #endif #ifndef DI8_ACTION #define DI8_ACTION INPUT_ACTION_NONE @@ -762,7 +771,7 @@ // Safety line #ifndef DI9_MODE -#define DI9_MODE NORMALLY_CLOSED // Normally closed +#define DI9_MODE IO_ACTIVE_HIGH // Normally closed #endif #ifndef DI9_ACTION #define DI9_ACTION INPUT_ACTION_NONE diff --git a/g2core/settings/settings_makeblock.h b/g2core/settings/settings_makeblock.h index 5468b17f..ec3bf0fd 100644 --- a/g2core/settings/settings_makeblock.h +++ b/g2core/settings/settings_makeblock.h @@ -2,7 +2,7 @@ * settings_makeblock.h - makeblock engraving table * This file is part of the g2core project * - * Copyright (c) 2016 Alden S. Hart, Jr. + * Copyright (c) 2016 - 2017 Alden S. Hart, Jr. * * This file ("the software") is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2 as published by the @@ -50,26 +50,11 @@ #define COOLANT_FLOOD_POLARITY 1 // 0=active low, 1=active high #define COOLANT_PAUSE_ON_HOLD true -/* -#define JUNCTION_INTEGRATION_TIME 0.75 // cornering - between 0.10 and 2.00 (higher is faster) -#define CHORDAL_TOLERANCE 0.1 // chordal tolerance for arcs (in mm) - -#define SOFT_LIMIT_ENABLE 0 // 0=off, 1=on -#define HARD_LIMIT_ENABLE 0 // 0=off, 1=on -#define SAFETY_INTERLOCK_ENABLE 1 // 0=off, 1=on - -#define SPINDLE_ENABLE_POLARITY 1 // 0=active low, 1=active high -#define SPINDLE_DIR_POLARITY 0 // 0=clockwise is low, 1=clockwise is high -#define SPINDLE_PAUSE_ON_HOLD true -#define SPINDLE_DWELL_TIME 1.0 - -#define COOLANT_MIST_POLARITY 1 // 0=active low, 1=active high -#define COOLANT_FLOOD_POLARITY 1 // 0=active low, 1=active high -#define COOLANT_PAUSE_ON_HOLD false -*/ +#define PROBE_REPORT_ENABLE true // Communications and reporting settings +#define MARLIN_COMPAT_ENABLED true // enable marlin compatibility mode #define COMM_MODE JSON_MODE // one of: TEXT_MODE, JSON_MODE #define XIO_ENABLE_FLOW_CONTROL FLOW_CONTROL_RTS // FLOW_CONTROL_OFF, FLOW_CONTROL_RTS #define USB_SERIAL_PORTS_EXPOSED 1 @@ -83,28 +68,13 @@ #define STATUS_REPORT_MIN_MS 200 // milliseconds - enforces a viable minimum #define STATUS_REPORT_INTERVAL_MS 250 // milliseconds - set $SV=0 to disable -#define STATUS_REPORT_DEFAULTS "line","posx","posy","posz","vel","unit","stat","feed","coor","momo","plan","path","dist","mpox","mpoy","mpoz","admo","frmo","cycs","hold" +#define STATUS_REPORT_DEFAULTS "line","stat","posx","posy","posz",\ + "vel", "unit","feed","coor","momo",\ + "plan","path","dist","prbe","prbz",\ + "mpox","mpoy","mpoz",\ + "admo","frmo","cycs","hold" +// "_ts1","_cs1","_es1","_xs1","_fe1" -/* -#define COMM_MODE JSON_MODE // one of: TEXT_MODE, JSON_MODE - -#define TEXT_VERBOSITY TV_VERBOSE // one of: TV_SILENT, TV_VERBOSE -#define JSON_VERBOSITY JV_MESSAGES // one of: JV_SILENT, JV_FOOTER, JV_CONFIGS, JV_MESSAGES, JV_LINENUM, JV_VERBOSE -#define QUEUE_REPORT_VERBOSITY QR_OFF // one of: QR_OFF, QR_SINGLE, QR_TRIPLE - -#define STATUS_REPORT_VERBOSITY SR_FILTERED // one of: SR_OFF, SR_FILTERED, SR_VERBOSE -#define STATUS_REPORT_MIN_MS 100 // milliseconds - enforces a viable minimum -#define STATUS_REPORT_INTERVAL_MS 250 // milliseconds - set $SV=0 to disable - -//#define STATUS_REPORT_DEFAULTS -//"line","posx","posy","posz","posa","feed","vel","unit","coor","dist","admo","frmo","momo","stat" -#define STATUS_REPORT_DEFAULTS "line", "posx", "posy", "posz", "feed", "vel", "momo", "stat" - -// Alternate SRs that report in drawable units -//#define STATUS_REPORT_DEFAULTS -//"line","vel","mpox","mpoy","mpoz","mpoa","coor","ofsa","ofsx","ofsy","ofsz","dist","unit","stat","homz","homy","homx","momo" -//#define STATUS_REPORT_DEFAULTS "_ts1","_cs1","_es1","_xs1","_fe1","line","posx","posy","posz","vel","stat" -*/ // Gcode startup defaults #define GCODE_DEFAULT_UNITS MILLIMETERS // MILLIMETERS or INCHES #define GCODE_DEFAULT_PLANE CANON_PLANE_XY // CANON_PLANE_XY, CANON_PLANE_XZ, or CANON_PLANE_YZ @@ -120,7 +90,6 @@ // 2=MOTOR_POWERED_IN_CYCLE, // 3=MOTOR_POWERED_ONLY_WHEN_MOVING #define M1_POWER_LEVEL 0.4 // 0.0 = off, 1.0 = max -//.#define MOTOR_POWER_TIMEOUT 2.00 // motor power timeout in seconds #define MOTOR_POWER_TIMEOUT 10.00 // motor power timeout in seconds #define M1_MOTOR_MAP AXIS_X // 1ma @@ -147,6 +116,14 @@ #define M3_POWER_MODE MOTOR_POWER_MODE #define M3_POWER_LEVEL 0.4 +#define M4_MOTOR_MAP AXIS_A +#define M4_STEP_ANGLE 1.8 +#define M4_TRAVEL_PER_REV 1.25 +#define M4_MICROSTEPS 8 +#define M4_POLARITY 0 +#define M4_POWER_MODE MOTOR_POWER_MODE +#define M4_POWER_LEVEL 0.4 + // *** axis settings ********************************************************************************** #define JERK_MAX 5000 @@ -192,3 +169,87 @@ #define Z_LATCH_VELOCITY 25 #define Z_LATCH_BACKOFF 4 #define Z_ZERO_BACKOFF 2 + +#define A_AXIS_MODE AXIS_STANDARD +#define A_RADIUS 1 +#define A_VELOCITY_MAX 360000 +#define A_FEEDRATE_MAX A_VELOCITY_MAX +#define A_TRAVEL_MIN -1 +#define A_TRAVEL_MAX -1 +#define A_JERK_MAX 100000 +#define A_JERK_HIGH_SPEED A_JERK_MAX +#define A_HOMING_INPUT 0 +#define A_HOMING_DIRECTION 0 +#define A_SEARCH_VELOCITY 600 +#define A_LATCH_VELOCITY 100 +#define A_LATCH_BACKOFF 10 +#define A_ZERO_BACKOFF 2 + +//*** Input / output settings *** +/* + See gpio.h GPIO defines for options + + Homing and probing settings are independent of ACTION and FUNCTION settings + but rely on proper switch MODE setting (i.e. NC or NO) + + INPUT_MODE_DISABLED + INPUT_ACTIVE_LOW aka NORMALLY_OPEN + INPUT_ACTIVE_HIGH aka NORMALLY_CLOSED + + INPUT_ACTION_NONE + INPUT_ACTION_STOP + INPUT_ACTION_FAST_STOP + INPUT_ACTION_HALT + INPUT_ACTION_RESET + + INPUT_FUNCTION_NONE + INPUT_FUNCTION_LIMIT + INPUT_FUNCTION_INTERLOCK + INPUT_FUNCTION_SHUTDOWN + INPUT_FUNCTION_PROBE +*/ + +// Xmin on v9 board // X homing - see X axis setup +#define DI1_MODE NORMALLY_CLOSED +#define DI1_ACTION INPUT_ACTION_NONE +#define DI1_FUNCTION INPUT_FUNCTION_NONE + +// Xmax // External ESTOP +#define DI2_MODE IO_ACTIVE_HIGH +#define DI2_ACTION INPUT_ACTION_HALT +#define DI2_FUNCTION INPUT_FUNCTION_SHUTDOWN + +// Ymin // Y homing - see Y axis setup +#define DI3_MODE NORMALLY_CLOSED +#define DI3_ACTION INPUT_ACTION_NONE +#define DI3_FUNCTION INPUT_FUNCTION_NONE + +// Ymax // Safety interlock +#define DI4_MODE IO_ACTIVE_HIGH +#define DI4_ACTION INPUT_ACTION_NONE // (hold is performed by Interlock function) +#define DI4_FUNCTION INPUT_FUNCTION_INTERLOCK + +// Zmin // Z probe +#define DI5_MODE IO_ACTIVE_LOW +#define DI5_ACTION INPUT_ACTION_NONE +#define DI5_FUNCTION INPUT_FUNCTION_PROBE + +// Zmax // Z homing - see Z axis for setup +#define DI6_MODE NORMALLY_CLOSED +#define DI6_ACTION INPUT_ACTION_NONE +#define DI6_FUNCTION INPUT_FUNCTION_NONE + +// Amin // Unused +#define DI7_MODE IO_MODE_DISABLED +#define DI7_ACTION INPUT_ACTION_NONE +#define DI7_FUNCTION INPUT_FUNCTION_NONE + +// Amax // Unused +#define DI8_MODE IO_MODE_DISABLED +#define DI8_ACTION INPUT_ACTION_NONE +#define DI8_FUNCTION INPUT_FUNCTION_NONE + +// Safety line w/HW timer // Unused +#define DI9_MODE IO_MODE_DISABLED +#define DI9_ACTION INPUT_ACTION_NONE +#define DI9_FUNCTION INPUT_FUNCTION_NONE \ No newline at end of file diff --git a/g2core/settings/settings_othermill_test.h b/g2core/settings/settings_othermill_test.h index c61977f7..662f73f7 100644 --- a/g2core/settings/settings_othermill_test.h +++ b/g2core/settings/settings_othermill_test.h @@ -52,31 +52,6 @@ #define COOLANT_FLOOD_POLARITY 1 // 0=active low, 1=active high #define COOLANT_PAUSE_ON_HOLD true -constexpr float H1_DEFAULT_P = 7.0; -constexpr float H1_DEFAULT_I = 0.2; -constexpr float H1_DEFAULT_D = 100.0; - -constexpr float H2_DEFAULT_P = 7.0; -constexpr float H2_DEFAULT_I = 0.2; -constexpr float H2_DEFAULT_D = 100.0; - -constexpr float H3_DEFAULT_P = 7.0; -constexpr float H3_DEFAULT_I = 0.2; -constexpr float H3_DEFAULT_D = 100.0; - -// WARNING: Older Othermill machines use a 15deg can stack for their Z axis. -// new machines use a stepper which has the same config as the other axis. -#define HAS_CANSTACK_Z_AXIS 0 -/* -// Switch definitions for interlock & E-stop -#define ENABLE_INTERLOCK_AND_ESTOP -#define INTERLOCK_SWITCH_AXIS AXIS_Y -#define INTERLOCK_SWITCH_POSITION SW_MAX -#define ESTOP_SWITCH_AXIS AXIS_X -#define ESTOP_SWITCH_POSITION SW_MAX -#define PAUSE_DWELL_TIME 1.5 //after unpausing and turning the spindle on, dwell for 1.5s -*/ - // Communications and reporting settings #define COMM_MODE JSON_MODE // one of: TEXT_MODE, JSON_MODE @@ -89,7 +64,7 @@ constexpr float H3_DEFAULT_D = 100.0; #define STATUS_REPORT_VERBOSITY SR_FILTERED // one of: SR_OFF, SR_FILTERED, SR_VERBOSE #define STATUS_REPORT_MIN_MS 100 // milliseconds - enforces a viable minimum #define STATUS_REPORT_INTERVAL_MS 250 // milliseconds - set $SV=0 to disable -#define STATUS_REPORT_DEFAULTS \ +#define STATUS_REPORT_DEFAULTS \ "mpox", "mpoy", "mpoz", "ofsx", "ofsy", "ofsz", "g55x", "g55y", "g55z", "unit", "stat", "coor", "momo", "dist", \ "home", "mots", "plan", "line", "path", "frmo", "prbe", "safe", "spe", "spd", "hold", "macs", "cycs", "sps" @@ -132,13 +107,8 @@ constexpr float H3_DEFAULT_D = 100.0; #define M2_POWER_LEVEL_IDLE MOTOR_POWER_LEVEL_XY_IDLE #define M3_MOTOR_MAP AXIS_Z -#if HAS_CANSTACK_Z_AXIS -#define M3_STEP_ANGLE 15 -#define M3_TRAVEL_PER_REV 1.27254 -#else #define M3_STEP_ANGLE 1.8 #define M3_TRAVEL_PER_REV 4.8768 -#endif #define M3_MICROSTEPS 8 #define M3_POLARITY 0 #define M3_POWER_MODE MOTOR_POWER_MODE @@ -189,7 +159,7 @@ constexpr float H3_DEFAULT_D = 100.0; #define X_HOMING_DIRECTION 0 // xhd 0=search moves negative, 1= search moves positive #define X_SEARCH_VELOCITY (X_FEEDRATE_MAX / 3) // xsv #define X_LATCH_VELOCITY LATCH_VELOCITY // xlv mm/min -#define X_LATCH_BACKOFF 5 // xlb mm +#define X_LATCH_BACKOFF 1.5 // xlb mm #define X_ZERO_BACKOFF 0.4 // xzb mm #define Y_AXIS_MODE AXIS_STANDARD @@ -203,15 +173,11 @@ constexpr float H3_DEFAULT_D = 100.0; #define Y_HOMING_DIRECTION 0 #define Y_SEARCH_VELOCITY (Y_FEEDRATE_MAX / 3) #define Y_LATCH_VELOCITY LATCH_VELOCITY -#define Y_LATCH_BACKOFF 5 +#define Y_LATCH_BACKOFF 1.5 #define Y_ZERO_BACKOFF 0.4 #define Z_AXIS_MODE AXIS_STANDARD -#if HAS_CANSTACK_Z_AXIS -#define Z_VELOCITY_MAX 1000 -#else #define Z_VELOCITY_MAX X_VELOCITY_MAX -#endif #define Z_FEEDRATE_MAX Z_VELOCITY_MAX #define Z_TRAVEL_MIN -60.1 #define Z_TRAVEL_MAX 0 @@ -221,7 +187,7 @@ constexpr float H3_DEFAULT_D = 100.0; #define Z_HOMING_DIRECTION 1 #define Z_SEARCH_VELOCITY (Z_FEEDRATE_MAX / 3) #define Z_LATCH_VELOCITY LATCH_VELOCITY -#define Z_LATCH_BACKOFF 5 +#define Z_LATCH_BACKOFF 1.5 #define Z_ZERO_BACKOFF 0.4 // Rotary values are chosen to make the motor react the same as X for testing @@ -300,7 +266,7 @@ constexpr float H3_DEFAULT_D = 100.0; #define DI1_FUNCTION INPUT_FUNCTION_NONE // Xmax // External ESTOP -#define DI2_MODE INPUT_ACTIVE_HIGH +#define DI2_MODE IO_ACTIVE_HIGH #define DI2_ACTION INPUT_ACTION_HALT #define DI2_FUNCTION INPUT_FUNCTION_SHUTDOWN @@ -310,14 +276,14 @@ constexpr float H3_DEFAULT_D = 100.0; #define DI3_FUNCTION INPUT_FUNCTION_NONE // Ymax // Safety interlock -#define DI4_MODE INPUT_ACTIVE_HIGH +#define DI4_MODE IO_ACTIVE_HIGH #define DI4_ACTION INPUT_ACTION_NONE // (hold is performed by Interlock function) #define DI4_FUNCTION INPUT_FUNCTION_INTERLOCK // Zmin // Z probe -#define DI5_MODE INPUT_ACTIVE_LOW +#define DI5_MODE IO_ACTIVE_LOW #define DI5_ACTION INPUT_ACTION_NONE -#define DI5_FUNCTION INPUT_FUNCTION_NONE +#define DI5_FUNCTION INPUT_FUNCTION_PROBE // Zmax // Z homing - see Z axis for setup #define DI6_MODE NORMALLY_CLOSED @@ -325,17 +291,17 @@ constexpr float H3_DEFAULT_D = 100.0; #define DI6_FUNCTION INPUT_FUNCTION_NONE // Amin // Unused -#define DI7_MODE INPUT_MODE_DISABLED +#define DI7_MODE IO_MODE_DISABLED #define DI7_ACTION INPUT_ACTION_NONE #define DI7_FUNCTION INPUT_FUNCTION_NONE // Amax // Unused -#define DI8_MODE INPUT_MODE_DISABLED +#define DI8_MODE IO_MODE_DISABLED #define DI8_ACTION INPUT_ACTION_NONE #define DI8_FUNCTION INPUT_FUNCTION_NONE // Safety line w/HW timer // Unused -#define DI9_MODE INPUT_MODE_DISABLED +#define DI9_MODE IO_MODE_DISABLED #define DI9_ACTION INPUT_ACTION_NONE #define DI9_FUNCTION INPUT_FUNCTION_NONE @@ -420,3 +386,15 @@ constexpr float H3_DEFAULT_D = 100.0; #define USER_DATA_D1 0 #define USER_DATA_D2 0 #define USER_DATA_D3 0 + +constexpr float H1_DEFAULT_P = 7.0; +constexpr float H1_DEFAULT_I = 0.2; +constexpr float H1_DEFAULT_D = 100.0; + +constexpr float H2_DEFAULT_P = 7.0; +constexpr float H2_DEFAULT_I = 0.2; +constexpr float H2_DEFAULT_D = 100.0; + +constexpr float H3_DEFAULT_P = 7.0; +constexpr float H3_DEFAULT_I = 0.2; +constexpr float H3_DEFAULT_D = 100.0; diff --git a/g2core/settings/settings_shapeoko375_Dual.h b/g2core/settings/settings_shapeoko375_Dual.h index b787d58c..7a5ac7dc 100644 --- a/g2core/settings/settings_shapeoko375_Dual.h +++ b/g2core/settings/settings_shapeoko375_Dual.h @@ -86,7 +86,7 @@ #define GCODE_DEFAULT_PLANE CANON_PLANE_XY // CANON_PLANE_XY, CANON_PLANE_XZ, or CANON_PLANE_YZ #define GCODE_DEFAULT_COORD_SYSTEM G54 // G54, G55, G56, G57, G58 or G59 #define GCODE_DEFAULT_PATH_CONTROL PATH_CONTINUOUS -#define GCODE_DEFAULT_DISTANCE_MODE ABSOLUTE_MODE +#define GCODE_DEFAULT_DISTANCE_MODE ABSOLUTE_DISTANCE_MODE // *** motor settings ************************************************************************************ diff --git a/g2core/spindle.cpp b/g2core/spindle.cpp index df86f333..e61e1ee4 100644 --- a/g2core/spindle.cpp +++ b/g2core/spindle.cpp @@ -2,7 +2,7 @@ * spindle.cpp - canonical machine spindle driver * This file is part of the g2core project * - * Copyright (c) 2010 - 2016 Alden S. Hart, Jr. + * Copyright (c) 2010 - 2017 Alden S. Hart, Jr. * * This file ("the software") is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2 as published by the @@ -222,28 +222,50 @@ static float _get_spindle_pwm (cmSpindleEnable enable, cmSpindleDir direction) /* - * cm_spindle_override_enable() - * cm_spindle_override_factor() + * cm_spindle_override_control() + * cm_start_spindle_override() + * cm_end_spindle_override() */ -/* -stat_t cm_spindle_override_enable(uint8_t flag) // M51.1 + +stat_t cm_sso_control(const float P_word, const bool P_flag) // M51 { - if (fp_TRUE(cm.gf.parameter) && fp_ZERO(cm.gn.parameter)) { - spindle.override_enable = false; - } else { - spindle.override_enable = true; + bool new_enable = true; + bool new_override = false; + if (P_flag) { // if parameter is present in Gcode block + if (fp_ZERO(P_word)) { + new_enable = false; // P0 disables override + } else { + if (P_word < SPINDLE_OVERRIDE_MIN) { + return (STAT_INPUT_LESS_THAN_MIN_VALUE); + } + if (P_word > SPINDLE_OVERRIDE_MAX) { + return (STAT_INPUT_EXCEEDS_MAX_VALUE); + } + spindle.sso_factor = P_word; // P word is valid, store it. + new_override = true; + } } + if (cm.gmx.m48_enable) { // if master enable is ON + if (new_enable && (new_override || !spindle.sso_enable)) { // 3 cases to start a ramp + cm_start_spindle_override(SPINDLE_OVERRIDE_RAMP_TIME, spindle.sso_factor); + } else if (spindle.sso_enable && !new_enable) { // case to turn off the ramp + cm_end_spindle_override(SPINDLE_OVERRIDE_RAMP_TIME); + } + } + spindle.sso_enable = new_enable; // always update the enable state return (STAT_OK); } -stat_t cm_spindle_override_factor(uint8_t flag) // M50.1 +void cm_start_spindle_override(const float ramp_time, const float override_factor) { - spindle.override_enable = flag; - spindle.override_factor = cm.gn.parameter; -// change spindle speed - return (STAT_OK); + return; } -*/ + +void cm_end_spindle_override(const float ramp_time) +{ + return; +} + /**************************** * END OF SPINDLE FUNCTIONS * ****************************/ diff --git a/g2core/spindle.h b/g2core/spindle.h index 33820ce8..9e26d585 100644 --- a/g2core/spindle.h +++ b/g2core/spindle.h @@ -60,8 +60,9 @@ typedef enum { #define SPINDLE_OVERRIDE_ENABLE false #define SPINDLE_OVERRIDE_FACTOR 1.00 -#define SPINDLE_OVERRIDE_MIN 0.05 // 5% -#define SPINDLE_OVERRIDE_MAX 2.00 // 200% +#define SPINDLE_OVERRIDE_MIN 0.05 // 5% +#define SPINDLE_OVERRIDE_MAX 2.00 // 200% +#define SPINDLE_OVERRIDE_RAMP_TIME 1 // change speed in seconds /* * Spindle control structure @@ -99,8 +100,9 @@ void cm_spindle_off_immediate(void); void cm_spindle_optional_pause(bool option); // stop spindle based on system options selected void cm_spindle_resume(float dwell_seconds); // restart spindle after pause based on previous state -// stat_t cm_spindle_override_enable(uint8_t flag); // M51 -// stat_t cm_spindle_override_factor(uint8_t flag); // M51.1 +stat_t cm_sso_control(const float P_word, const bool P_flag); // M51 +void cm_start_spindle_override(const float ramp_time, const float override_factor); +void cm_end_spindle_override(const float ramp_time); stat_t cm_set_dir(nvObj_t* nv); stat_t cm_set_sso(nvObj_t* nv); diff --git a/g2core/stepper.cpp b/g2core/stepper.cpp index 40b40557..5c2a6032 100644 --- a/g2core/stepper.cpp +++ b/g2core/stepper.cpp @@ -125,11 +125,11 @@ void stepper_init() dda_timer.setInterrupts(kInterruptOnOverflow | kInterruptPriorityHighest); // setup software interrupt exec timer & initial condition - exec_timer.setInterrupts(kInterruptOnSoftwareTrigger | kInterruptPriorityMedium); + exec_timer.setInterrupts(kInterruptOnSoftwareTrigger | kInterruptPriorityHigh); st_pre.buffer_state = PREP_BUFFER_OWNED_BY_EXEC; // setup software interrupt forward plan timer & initial condition - fwd_plan_timer.setInterrupts(kInterruptOnSoftwareTrigger | kInterruptPriorityLow); + fwd_plan_timer.setInterrupts(kInterruptOnSoftwareTrigger | kInterruptPriorityMedium); // setup motor power levels and apply power level to stepper drivers for (uint8_t motor=0; motor= MOTORS) {return;} Motors[motor]->setMicrosteps(microsteps); - } +} /*********************************************************************************** diff --git a/g2core/temperature.cpp b/g2core/temperature.cpp index a3666df7..ebb3fccc 100755 --- a/g2core/temperature.cpp +++ b/g2core/temperature.cpp @@ -82,7 +82,7 @@ // TEMP_MIN_RISE_TIME milliseconds, then it's a failure (the sensor is likely // physically dislocated.) #ifndef TEMP_MIN_RISE_DEGREES_OVER_TIME -#define TEMP_MIN_RISE_DEGREES_OVER_TIME (float)5.0 +#define TEMP_MIN_RISE_DEGREES_OVER_TIME (float)10.0 #endif #ifndef TEMP_MIN_RISE_TIME #define TEMP_MIN_RISE_TIME (float)(60.0 * 1000.0) // 20 seconds @@ -365,7 +365,10 @@ struct PID { if (_rise_time_timeout.isPast()) { if (input < _rise_time_checkpoint) { // FAILURE!! - cm_alarm(STAT_TEMPERATURE_CONTROL_ERROR, "Heater temperature failed to rise fast enough."); + char buffer[128]; + char *str = buffer; + str += sprintf(str, "Heater temperature failed to rise fast enough. At: %f Set: %f", input, _set_point); + cm_alarm(STAT_TEMPERATURE_CONTROL_ERROR, buffer); _set_point = 0; _rise_time_timeout.clear(); return -1; @@ -663,7 +666,6 @@ stat_t cm_set_heater_p(nvObj_t *nv) case '2': { pid2._p_factor = nv->value / 100.0; break; } case '3': { pid3._p_factor = nv->value / 100.0; break; } - // Failsafe. We can only get here if we set it up in config_app, but not here. default: { break; } } @@ -681,7 +683,6 @@ stat_t cm_get_heater_i(nvObj_t *nv) case '2': { nv->value = pid2._i_factor * 100.0; break; } case '3': { nv->value = pid3._i_factor * 100.0; break; } - // Failsafe. We can only get here if we set it up in config_app, but not here. default: { nv->value = 0.0; break; } } @@ -697,7 +698,6 @@ stat_t cm_set_heater_i(nvObj_t *nv) case '2': { pid2._i_factor = nv->value / 100.0; break; } case '3': { pid3._i_factor = nv->value / 100.0; break; } - // Failsafe. We can only get here if we set it up in config_app, but not here. default: { break; } } @@ -714,7 +714,6 @@ stat_t cm_get_heater_d(nvObj_t *nv) case '2': { nv->value = pid2._d_factor * 100.0; break; } case '3': { nv->value = pid3._d_factor * 100.0; break; } - // Failsafe. We can only get here if we set it up in config_app, but not here. default: { nv->value = 0.0; break; } } nv->precision = GET_TABLE_WORD(precision); @@ -729,7 +728,6 @@ stat_t cm_set_heater_d(nvObj_t *nv) case '2': { pid2._d_factor = nv->value / 100.0; break; } case '3': { pid3._d_factor = nv->value / 100.0; break; } - // Failsafe. We can only get here if we set it up in config_app, but not here. default: { break; } } return (STAT_OK); @@ -737,67 +735,81 @@ stat_t cm_set_heater_d(nvObj_t *nv) /* * cm_get_set_temperature()/cm_set_set_temperature() - get/set the set value of the PID + * + * There are both the file-to-file use version, and the NV-pair form (which uses the other). */ -stat_t cm_get_set_temperature(nvObj_t *nv) +float cm_get_set_temperature(const uint8_t heater) { - switch(_get_heater_number(nv)) { - case '1': { nv->value = pid1._set_point; break; } - case '2': { nv->value = pid2._set_point; break; } - case '3': { nv->value = pid3._set_point; break; } - - // Failsafe. We can only get here if we set it up in config_app, but not here. - default: { nv->value = 0.0; break; } + switch(heater) { + case 1: { return pid1._set_point; break; } + case 2: { return pid2._set_point; break; } + case 3: { return pid3._set_point; break; } + default: { break; } } + return 0.0; +} +stat_t cm_get_set_temperature(nvObj_t *nv) +{ + nv->value = cm_get_set_temperature(_get_heater_number(nv) - '0'); nv->precision = GET_TABLE_WORD(precision); nv->valuetype = TYPE_FLOAT; return (STAT_OK); } -stat_t cm_set_set_temperature(nvObj_t *nv) +void cm_set_set_temperature(const uint8_t heater, const float value) { - switch(_get_heater_number(nv)) { - case '1': { pid1._set_point = min(TEMP_MAX_SETPOINT, nv->value); break; } - case '2': { pid2._set_point = min(TEMP_MAX_SETPOINT, nv->value); break; } - case '3': { pid3._set_point = min(TEMP_MAX_SETPOINT, nv->value); break; } + switch(heater) { + case 1: { pid1._set_point = min(TEMP_MAX_SETPOINT, value); break; } + case 2: { pid2._set_point = min(TEMP_MAX_SETPOINT, value); break; } + case 3: { pid3._set_point = min(TEMP_MAX_SETPOINT, value); break; } - // Failsafe. We can only get here if we set it up in config_app, but not here. + // default to quiet the compiler default: { break; } } - +} +stat_t cm_set_set_temperature(nvObj_t *nv) +{ + cm_set_set_temperature(_get_heater_number(nv) - '0', nv->value); return (STAT_OK); } /* * cm_get_fan_power()/cm_set_fan_power() - get/set the set high-value setting of the heater fan */ -stat_t cm_get_fan_power(nvObj_t *nv) +float cm_get_fan_power(const uint8_t heater) { - switch(_get_heater_number(nv)) { - case '1': { nv->value = min(1.0f, heater_fan1.max_value); break; } -// case '2': { nv->value = min(1.0f, heater_fan2.max_value); break; } -// case '3': { nv->value = min(1.0f, heater_fan3.max_value); break; } + switch(heater) { + case 1: { return min(1.0f, heater_fan1.max_value); } +// case 2: { return min(1.0f, heater_fan2.max_value); } +// case 3: { return min(1.0f, heater_fan3.max_value); } - // Failsafe. We can only get here if we set it up in config_app, but not here. - default: { nv->value = 0.0; break; } + default: { break; } } + return 0.0; +} +stat_t cm_get_fan_power(nvObj_t *nv) +{ + nv->value = cm_get_fan_power(_get_heater_number(nv) - '0'); nv->precision = GET_TABLE_WORD(precision); nv->valuetype = TYPE_FLOAT; return (STAT_OK); } -stat_t cm_set_fan_power(nvObj_t *nv) -{ - switch(_get_heater_number(nv)) { - case '1': { heater_fan1.max_value = max(0.0f, nv->value); break; } -// case '2': { heater_fan2.max_value = max(0.0, nv->value); break; } -// case '3': { heater_fan3.max_value = max(0.0, nv->value); break; } - // Failsafe. We can only get here if we set it up in config_app, but not here. +void cm_set_fan_power(const uint8_t heater, const float value) +{ + switch(heater) { + case 1: { heater_fan1.max_value = max(0.0f, value); break; } +// case 2: { heater_fan2.max_value = max(0.0, value); break; } +// case 3: { heater_fan3.max_value = max(0.0, value); break; } default: { break; } } - +} +stat_t cm_set_fan_power(nvObj_t *nv) +{ + cm_set_fan_power(_get_heater_number(nv) - '0', nv->value); return (STAT_OK); } @@ -811,7 +823,6 @@ stat_t cm_get_fan_min_power(nvObj_t *nv) // case '2': { nv->value = heater_fan2.min_value; break; } // case '3': { nv->value = heater_fan3.min_value; break; } - // Failsafe. We can only get here if we set it up in config_app, but not here. default: { nv->value = 0.0; break; } } @@ -827,10 +838,9 @@ stat_t cm_set_fan_min_power(nvObj_t *nv) // case '2': { heater_fan2.min_value = min(0.0, nv->value); break; } // case '3': { heater_fan3.min_value = min(0.0, nv->value); break; } - // Failsafe. We can only get here if we set it up in config_app, but not here. default: { break; } } - + return (STAT_OK); } @@ -844,7 +854,6 @@ stat_t cm_get_fan_low_temp(nvObj_t *nv) // case '2': { nv->value = heater_fan2.low_temp; break; } // case '3': { nv->value = heater_fan3.low_temp; break; } - // Failsafe. We can only get here if we set it up in config_app, but not here. default: { nv->value = 0.0; break; } } @@ -860,10 +869,9 @@ stat_t cm_set_fan_low_temp(nvObj_t *nv) // case '2': { heater_fan2.low_temp = min(0.0f, nv->value); break; } // case '3': { heater_fan3.low_temp = min(0.0f, nv->value); break; } - // Failsafe. We can only get here if we set it up in config_app, but not here. default: { break; } } - + return (STAT_OK); } @@ -877,7 +885,6 @@ stat_t cm_get_fan_high_temp(nvObj_t *nv) // case '2': { nv->value = heater_fan2.high_temp; break; } // case '3': { nv->value = heater_fan3.high_temp; break; } - // Failsafe. We can only get here if we set it up in config_app, but not here. default: { nv->value = 0.0; break; } } @@ -893,27 +900,30 @@ stat_t cm_set_fan_high_temp(nvObj_t *nv) // case '2': { heater_fan2.high_temp = min(0.0f, nv->value); break; } // case '3': { heater_fan3.high_temp = min(0.0f, nv->value); break; } - // Failsafe. We can only get here if we set it up in config_app, but not here. default: { break; } } - + return (STAT_OK); } /* * cm_get_at_temperature() - get a boolean if the heater has reaced the set value of the PID */ -stat_t cm_get_at_temperature(nvObj_t *nv) +bool cm_get_at_temperature(const uint8_t heater) { - switch(_get_heater_number(nv)) { - case '1': { nv->value = pid1._at_set_point; break; } - case '2': { nv->value = pid2._at_set_point; break; } - case '3': { nv->value = pid3._at_set_point; break; } + switch(heater) { + case 1: { return pid1._at_set_point; } + case 2: { return pid2._at_set_point; } + case 3: { return pid3._at_set_point; } - // Failsafe. We can only get here if we set it up in config_app, but not here. - default: { nv->value = 0.0; break; } + default: { break; } } + return false; +} +stat_t cm_get_at_temperature(nvObj_t *nv) +{ + nv->value = cm_get_at_temperature(_get_heater_number(nv) - '0'); nv->precision = GET_TABLE_WORD(precision); nv->valuetype = TYPE_BOOL; @@ -924,16 +934,20 @@ stat_t cm_get_at_temperature(nvObj_t *nv) /* * cm_get_heater_output() - get the output value (PWM duty cycle) of the PID */ +float cm_get_heater_output(const uint8_t heater) +{ + switch(heater) { + case 1: { return (float)fet_pin1; } + case 2: { return (float)fet_pin2; } + case 3: { return (float)fet_pin3; } + + default: { break; } + } + return 0.0; +} stat_t cm_get_heater_output(nvObj_t *nv) { - switch(_get_heater_number(nv)) { - case '1': { nv->value = (float)fet_pin1; break; } - case '2': { nv->value = (float)fet_pin2; break; } - case '3': { nv->value = (float)fet_pin3; break; } - - // Failsafe. We can only get here if we set it up in config_app, but not here. - default: { nv->value = 0.0; break; } - } + nv->value = cm_get_heater_output(_get_heater_number(nv) - '0'); nv->precision = GET_TABLE_WORD(precision); nv->valuetype = TYPE_FLOAT; @@ -950,7 +964,6 @@ stat_t cm_get_heater_adc(nvObj_t *nv) case '2': { nv->value = (float)thermistor2.raw_adc_value; break; } case '3': { nv->value = (float)thermistor3.raw_adc_value; break; } - // Failsafe. We can only get here if we set it up in config_app, but not here. default: { nv->value = 0.0; break; } } nv->precision = GET_TABLE_WORD(precision); @@ -962,17 +975,21 @@ stat_t cm_get_heater_adc(nvObj_t *nv) /* * cm_get_temperature() - get the current temperature */ + float cm_get_temperature(const uint8_t heater) + { + switch(heater) { + case 1: { return (last_reported_temp1 = thermistor1.temperature_exact()); } + case 2: { return (last_reported_temp2 = thermistor2.temperature_exact()); } + case 3: { return (last_reported_temp3 = thermistor3.temperature_exact()); } + + default: { break; } + } + + return 0.0; + } stat_t cm_get_temperature(nvObj_t *nv) { - switch(_get_heater_number(nv)) { - case '1': { nv->value = last_reported_temp1 = thermistor1.temperature_exact(); break; } - case '2': { nv->value = last_reported_temp2 = thermistor2.temperature_exact(); break; } - case '3': { nv->value = last_reported_temp3 = thermistor3.temperature_exact(); break; } - - // Failsafe. We can only get here if we set it up in config_app, but not here. - default: { nv->value = 0.0; break; } - } - + nv->value = cm_get_temperature(_get_heater_number(nv) - '0'); nv->precision = GET_TABLE_WORD(precision); nv->valuetype = TYPE_FLOAT; @@ -989,7 +1006,6 @@ stat_t cm_get_thermistor_resistance(nvObj_t *nv) case '2': { nv->value = thermistor2.get_resistance(); break; } case '3': { nv->value = thermistor3.get_resistance(); break; } - // Failsafe. We can only get here if we set it up in config_app, but not here. default: { nv->value = 0.0; break; } } nv->precision = GET_TABLE_WORD(precision); @@ -1019,7 +1035,6 @@ stat_t cm_get_pid_p(nvObj_t *nv) case '2': { nv->value = pid2._proportional; break; } case '3': { nv->value = pid3._proportional; break; } - // Failsafe. We can only get here if we set it up in config_app, but not here. default: { nv->value = 0.0; break; } } nv->precision = GET_TABLE_WORD(precision); @@ -1038,7 +1053,6 @@ stat_t cm_get_pid_i(nvObj_t *nv) case '2': { nv->value = pid2._integral; break; } case '3': { nv->value = pid3._integral; break; } - // Failsafe. We can only get here if we set it up in config_app, but not here. default: { nv->value = 0.0; break; } } nv->precision = GET_TABLE_WORD(precision); @@ -1057,12 +1071,11 @@ stat_t cm_get_pid_d(nvObj_t *nv) case '2': { nv->value = pid2._derivative; break; } case '3': { nv->value = pid3._derivative; break; } - // Failsafe. We can only get here if we set it up in config_app, but not here. default: { nv->value = 0.0; break; } } nv->precision = GET_TABLE_WORD(precision); nv->valuetype = TYPE_FLOAT; - + return (STAT_OK); } diff --git a/g2core/temperature.h b/g2core/temperature.h index 0163ce11..5c19579c 100755 --- a/g2core/temperature.h +++ b/g2core/temperature.h @@ -49,20 +49,37 @@ stat_t cm_set_heater_d(nvObj_t* nv); stat_t cm_get_pid_p(nvObj_t* nv); stat_t cm_get_pid_i(nvObj_t* nv); stat_t cm_get_pid_d(nvObj_t* nv); + +float cm_get_set_temperature(const uint8_t heater); stat_t cm_get_set_temperature(nvObj_t* nv); + +void cm_set_set_temperature(const uint8_t heater, const float value); stat_t cm_set_set_temperature(nvObj_t* nv); + +float cm_get_fan_power(const uint8_t heater); stat_t cm_get_fan_power(nvObj_t* nv); + +void cm_set_fan_power(const uint8_t heater, const float value); stat_t cm_set_fan_power(nvObj_t* nv); + stat_t cm_get_fan_min_power(nvObj_t* nv); stat_t cm_set_fan_min_power(nvObj_t* nv); stat_t cm_get_fan_low_temp(nvObj_t* nv); stat_t cm_set_fan_low_temp(nvObj_t* nv); stat_t cm_get_fan_high_temp(nvObj_t* nv); stat_t cm_set_fan_high_temp(nvObj_t* nv); + +bool cm_get_at_temperature(const uint8_t heater); stat_t cm_get_at_temperature(nvObj_t* nv); + +float cm_get_heater_output(const uint8_t heater); stat_t cm_get_heater_output(nvObj_t* nv); + stat_t cm_get_heater_adc(nvObj_t* nv); + +float cm_get_temperature(const uint8_t heater); stat_t cm_get_temperature(nvObj_t* nv); + stat_t cm_get_thermistor_resistance(nvObj_t* nv); diff --git a/g2core/util.h b/g2core/util.h index a662463c..7e6745f0 100644 --- a/g2core/util.h +++ b/g2core/util.h @@ -173,10 +173,31 @@ inline T avg(const T a,const T b) {return (a+b)/2; } #define M_SQRT3 (1.73205080756888) #endif +// Fraction part +constexpr float c_atof_frac_(char *&p_, float v_, float m_) { + return ((*p_ >= '0') && (*p_ <= '9')) ? (v_ = ((v_) + ((*p_) - '0') * m_), c_atof_frac_(++p_, v_, m_ / 10.0)) : v_; +} + +// Integer part +template +constexpr float c_atof_int_(char *&p_, int_type v_) { + return (*p_ == '.') + ? (float)(v_) + c_atof_frac_(++p_, 0, 1.0 / 10.0) + : (((*p_ >= '0') && (*p_ <= '9')) ? ((v_ = ((*p_) - '0') + (v_ * 10)), c_atof_int_(++p_, v_)) : v_); +} + +// Start portion +constexpr float c_atof(char *&p_) { return (*p_ == '-') ? (c_atof_int_(++p_, 0) * -1.0) : ( (*p_ == '+') ? c_atof_int_(++p_, 0) : (c_atof_int_(p_, 0))); } // It's assumed that the string buffer contains at lest count_ non-\0 chars //constexpr int c_strreverse(char * const t, const int count_, char hold = 0) { // return count_>1 ? (hold=*t, *t=*(t+(count_-1)), *(t+(count_-1))=hold), c_strreverse(t+1, count_-2), count_ : count_; //} +template +void str_concat(char *&dest, const char (&data)[length]) { + // length includes the \0 + strncpy(dest, data, length); dest += length-1; +}; + #endif // End of include guard: UTIL_H_ONCE diff --git a/g2core/xio.cpp b/g2core/xio.cpp old mode 100755 new mode 100644 index 8068a421..630f796e --- a/g2core/xio.cpp +++ b/g2core/xio.cpp @@ -33,7 +33,6 @@ #include "g2core.h" #include "config.h" #include "hardware.h" -#include "canonical_machine.h" // needs cm_has_hold() #include "xio.h" #include "report.h" #include "controller.h" @@ -168,6 +167,10 @@ struct xioDeviceWrapperBase { // C++ base class for device primit virtual int16_t write(const char *buffer, int16_t len) { return -1; }; virtual char *readline(devflags_t limit_flags, uint16_t &size) { return nullptr; }; + +#if MARLIN_COMPAT_ENABLED == true + virtual void exitFakeBootloaderMode() {}; +#endif }; // Here we create the xio_t class, which has convenience methods to handle cross-device actions as a whole. @@ -352,18 +355,18 @@ struct xio_t { // Always check control-capable devices FIRST for (uint8_t dev=0; dev < _dev_count; dev++) { - if (!DeviceWrappers[dev]->isActive()) + if (!DeviceWrappers[dev]->isActive()) { continue; - + } + // If this channel is a DATA only, skip it this pass - if (!DeviceWrappers[dev]->isCtrl()) + if (!DeviceWrappers[dev]->isCtrl()) { continue; - + } ret_buffer = DeviceWrappers[dev]->readline(DEV_IS_CTRL, size); if (size > 0) { flags = DeviceWrappers[dev]->flags; - return ret_buffer; } } @@ -378,18 +381,24 @@ struct xio_t { if (size > 0) { flags = DeviceWrappers[dev]->flags; - return ret_buffer; } } } - size = 0; flags = 0; return (NULL); }; +#if MARLIN_COMPAT_ENABLED == true + void exitFakeBootloaderMode() { + for (int8_t i = 0; i < _dev_count; ++i) { + DeviceWrappers[i]->exitFakeBootloaderMode(); + } + }; +#endif + uint16_t magic_end; }; @@ -442,6 +451,32 @@ struct LineRXBuffer : RXBuffer<_size, owner_type, char> { bool _last_returned_a_control = false; +#if MARLIN_COMPAT_ENABLED == true + enum class STK500V2_State { + Done, // not in the faked stk500v2 bootloader + Timeout, // timeout period, waiting for a start character + Start, // waiting for 0x1B + Sequence, // waiting for sequence byte + Length_0, // waiting for MSB of length + Length_1, // waiting for LSB of length + Header_End,// waiting for 0x0E + Data, // waiting for more data + Checksum // waiting for checksum byte + }; + STK500V2_State _stk_parser_state; + uint16_t _stk_packet_data_length; + Motate::Timeout _stk_timeout; + + void startFakeBootloaderMode() { + _stk_parser_state = STK500V2_State::Timeout; + _stk_timeout.set(2000); // two seconds + } + + void exitFakeBootloaderMode() { + _stk_parser_state = STK500V2_State::Done; + } +#endif + LineRXBuffer(owner_type owner) : parent_type{owner} {}; void init() { @@ -596,14 +631,80 @@ struct LineRXBuffer : RXBuffer<_size, owner_type, char> { while (_isMoreToScan()) { bool ends_line = false; bool is_control = false; - char c = _data[_scan_offset]; +#if MARLIN_COMPAT_ENABLED == true + // it's possible something will try to talk stk500v2 to us. + // See https://github.com/synthetos/g2/wiki/Marlin-Compatibility#stk500v2 + + if ((_stk_parser_state == STK500V2_State::Done) && (c == 0)) { + _debug_trap("scan ran into NULL (Marlin-mode)"); + flush(); // consider the connection and all data trashed + return false; + } + + if (_stk_parser_state >= STK500V2_State::Timeout) { + if (_stk_parser_state == STK500V2_State::Timeout) { + if (_stk_timeout.isPast()) { + _stk_parser_state = STK500V2_State::Done; + // start over, outside of stk500v2 mode + continue; + } + // if we got something before the timeout, then we're in stk500v2 mode + // we'll look at what we got and maybe exit anyway + _stk_parser_state = STK500V2_State::Start; + } + if (_stk_parser_state == STK500V2_State::Start) { + if (c == 0x1B) { + _stk_parser_state = STK500V2_State::Sequence; + + // this is the start of this "line" and we can "read" (skip) everything up to here. + _read_offset = _scan_offset; + _line_start_offset = _scan_offset; + } + else if ((c == '{') || (c == 'N') || (c == '\n') || (c == '\r') || (c == 'G') || (c == 'M')) { + _stk_parser_state = STK500V2_State::Done; // jump out of bootloader mode + _read_offset = _scan_offset; + continue; + } + } else if (_stk_parser_state == STK500V2_State::Sequence) { + _stk_parser_state = STK500V2_State::Length_0; // we ignore the sequence + } else if (_stk_parser_state == STK500V2_State::Length_0) { + _stk_packet_data_length = c << 8; + _stk_parser_state = STK500V2_State::Length_1; + } else if (_stk_parser_state == STK500V2_State::Length_1) { + _stk_packet_data_length |= c; + _stk_parser_state = STK500V2_State::Header_End; + } else if (_stk_parser_state == STK500V2_State::Header_End) { + if (c == 0x0E) { + _stk_parser_state = STK500V2_State::Data; + } else { // end-of-header marker was corrupt, start over + _stk_packet_data_length = 0; + _read_offset = _scan_offset; + _stk_parser_state = STK500V2_State::Start; + } + } else if (_stk_parser_state == STK500V2_State::Data) { + if (--_stk_packet_data_length == 0) { // we don't read the data here, just return it + _stk_parser_state = STK500V2_State::Checksum; + } + } else if (_stk_parser_state == STK500V2_State::Checksum) { + // We do NOT check the checksum, since if it's corrupt, we'd need to reply, and we can't reply here. + // At this point, we at least have a complete packet we will use the "control" return mechanism to + // handle this since controls don't have to be \r\n-terminated + is_control = true; + ends_line = true; + _stk_parser_state = STK500V2_State::Start; // this line is complete, reset the state engine + } + } + else +#else // not MARLIN_COMPAT_ENABLED + if (c == 0) { _debug_trap("scan ran into NULL"); flush(); // consider the connection and all data trashed return false; } +#endif // MARLIN_COMPAT_ENABLED // Look for line endings if (c == '\r' || c == '\n') { @@ -673,7 +774,6 @@ struct LineRXBuffer : RXBuffer<_size, owner_type, char> { if (_data[_line_start_offset] == '{') { is_control = true; } - // TODO --- } @@ -708,8 +808,6 @@ struct LineRXBuffer : RXBuffer<_size, owner_type, char> { // move the start of the next skip section to after this skip _line_start_offset = _scan_offset; } - - return false; // no control was found }; @@ -998,11 +1096,11 @@ struct xioDeviceWrapper : xioDeviceWrapperBase { // describes a device for re // set it as a MUTED channel, call controller_set_connected(true) // then controller_set_muted(true) + flush(); // toss anything that has been written so far. setAsConnectedAndReady(); - if (isAlwaysDataAndCtrl()) { - // Case 1 (ignoring others) + if (isAlwaysDataAndCtrl()) { // Case 1 (ignoring others) setActive(); controller_set_connected(true); @@ -1010,11 +1108,10 @@ struct xioDeviceWrapper : xioDeviceWrapperBase { // describes a device for re if (isMuteAsSecondary() && xio.othersConnected(this)) { controller_set_muted(true); // something was muted } - return; } - if(!xio.othersConnected(this)) { + if (!xio.othersConnected(this)) { // Case 1 setAsPrimaryActiveDualRole(); // report that there is now have a connection (only for the first one) @@ -1023,15 +1120,17 @@ struct xioDeviceWrapper : xioDeviceWrapperBase { // describes a device for re if (xio.checkMutedSecondaryChannels()) { controller_set_muted(true); // something was muted } +#if MARLIN_COMPAT_ENABLED == true + // start the "fake bootloader" to signal the Host that Marlin (mode) is operating + _rx_buffer.startFakeBootloaderMode(); +#endif } - else if (isMuteAsSecondary()) { - // Case 2b + else if (isMuteAsSecondary()) { // Case 2b setAsMuted(); controller_set_connected(true); // it DID just just get connected controller_set_muted(true); // but it muted it too } - else { - // Case 2a + else { // Case 2a xio.removeDataFromPrimary(); if (xio.checkMutedSecondaryChannels()) { controller_set_muted(true); // something was muted @@ -1085,6 +1184,13 @@ struct xioDeviceWrapper : xioDeviceWrapperBase { // describes a device for re } // flags & DEV_IS_CONNECTED } }; + +#if MARLIN_COMPAT_ENABLED == true + void exitFakeBootloaderMode() override { + _rx_buffer.exitFakeBootloaderMode(); + }; +#endif + }; @@ -1121,6 +1227,14 @@ struct xioFlashFileDeviceWrapper : xioDeviceWrapperBase { // describes a devi // to flush the file, just forget about it // next time it's used it'll get reset _current_file = nullptr; + cs.responses_suppressed = false; + } + + bool flushToCommand() final { + // the end of the file is the next "command" + _current_file = nullptr; + cs.responses_suppressed = false; + return false; } int16_t write(const char *buffer, int16_t len) final { @@ -1137,6 +1251,7 @@ struct xioFlashFileDeviceWrapper : xioDeviceWrapperBase { // describes a devi if ((nullptr == from) && (_current_file->isDone())) { // all done sending this file, "close" it _current_file = nullptr; + cs.responses_suppressed = false; clearActive(); return nullptr; } @@ -1151,6 +1266,7 @@ struct xioFlashFileDeviceWrapper : xioDeviceWrapperBase { // describes a devi // null-terminate the string *dst_ptr = 0; + cs.responses_suppressed = true; return _line_buffer; }; }; @@ -1287,7 +1403,15 @@ void xio_flush_to_command() { return xio.flushToCommand(); } +#if MARLIN_COMPAT_ENABLED == true +/* + * xio_end_fake_bootloader() - end the fake bootloader mode + */ +void xio_exit_fake_bootloader() { + return xio.exitFakeBootloaderMode(); +} +#endif /*********************************************************************************** * newlib-nano support functions diff --git a/g2core/xio.h b/g2core/xio.h index ec04b668..dd8322f6 100755 --- a/g2core/xio.h +++ b/g2core/xio.h @@ -51,6 +51,7 @@ //#include "g2core.h" // not required if used in g2core project #include "config.h" // required for nvObj typedef #include "canonical_machine.h" // needed for cm_has_hold() +#include "settings.h" // needed for MARLIN_COMPAT_ENABLED /**** Defines, Macros, and Assorted Parameters ****/ @@ -120,6 +121,9 @@ char *xio_readline(devflags_t &flags, uint16_t &size); int16_t xio_writeline(const char *buffer, bool only_to_muted = false); bool xio_connected(); void xio_flush_to_command(); +#if MARLIN_COMPAT_ENABLED == true +void xio_exit_fake_bootloader(); +#endif stat_t xio_set_spi(nvObj_t *nv);