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);