Clean up
authorJoseph Coffland <joseph@cauldrondevelopment.com>
Wed, 6 Jul 2016 11:42:52 +0000 (04:42 -0700)
committerJoseph Coffland <joseph@cauldrondevelopment.com>
Wed, 6 Jul 2016 11:42:52 +0000 (04:42 -0700)
27 files changed:
src/axes.c
src/canonical_machine.c [deleted file]
src/canonical_machine.h [deleted file]
src/config.h
src/gcode_parser.c
src/homing.c
src/huanyang.h
src/machine.c [new file with mode: 0644]
src/machine.h [new file with mode: 0644]
src/main.c
src/plan/arc.c
src/plan/buffer.h
src/plan/calibrate.c
src/plan/command.c
src/plan/dwell.c
src/plan/exec.c
src/plan/feedhold.h
src/plan/jog.c
src/plan/line.c
src/plan/planner.c
src/plan/planner.h
src/probing.c
src/pwm_spindle.h
src/spindle.h
src/stepper.c
src/switch.c
src/tmc2660.c

index 93684d172dead45d628a0f5c5effbe554b953bf8..fb727ba1f1a7b5fd0692cb5d54f67b73422c8488 100644 (file)
@@ -26,7 +26,7 @@
 \******************************************************************************/
 
 
-#include "canonical_machine.h"
+#include "machine.h"
 
 
 uint8_t get_axis_mode(int axis) {
diff --git a/src/canonical_machine.c b/src/canonical_machine.c
deleted file mode 100644 (file)
index 9f06ac8..0000000
+++ /dev/null
@@ -1,1289 +0,0 @@
-/******************************************************************************\
-
-                This file is part of the Buildbotics firmware.
-
-                  Copyright (c) 2015 - 2016 Buildbotics LLC
-                  Copyright (c) 2010 - 2015 Alden S. Hart, Jr.
-                  Copyright (c) 2012 - 2015 Rob Giseburt
-                            All rights reserved.
-
-     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 <http://www.gnu.org/licenses/>.
-
-     The software is distributed in the hope that it will be useful, but
-          WITHOUT ANY WARRANTY; without even the implied warranty of
-      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-               Lesser General Public License for more details.
-
-       You should have received a copy of the GNU Lesser General Public
-                License along with the software.  If not, see
-                       <http://www.gnu.org/licenses/>.
-
-                For information regarding this software email:
-                  "Joseph Coffland" <joseph@buildbotics.com>
-
-\******************************************************************************/
-
-/* This code is a loose implementation of Kramer, Proctor and Messina's
- * canonical machining functions as described in the NIST RS274/NGC v3
- *
- * The canonical machine is the layer between the Gcode parser and
- * the motion control code for a specific robot. It keeps state and
- * executes commands - passing the stateless commands to the motion
- * planning layer.
- *
- * Synchronizing command execution
- *
- *   "Synchronous commands" are commands that affect the runtime need
- *   to be synchronized with movement. Examples include G4 dwells,
- *   program stops and ends, and most M commands.  These are queued
- *   into the planner queue and execute from the queue. Synchronous
- *   commands work like this:
- *
- *     - Call the cm_xxx_xxx() function which will do any input
- *       validation and return an error if it detects one.
- *
- *     - The cm_ function calls mp_queue_command(). Arguments are a
- *       callback to the _exec_...() function, which is the runtime
- *       execution routine, and any arguments that are needed by the
- *       runtime. See typedef for *exec in planner.h for details
- *
- *     - mp_queue_command() stores the callback and the args in a
-          planner buffer.
- *
- *     - When planner execution reaches the buffer it executes the
- *       callback w/ the args.  Take careful note that the callback
- *       executes under an interrupt, so beware of variables that may
- *       need to be volatile.
- *
- *   Note: - The synchronous command execution mechanism uses 2
- *   vectors in the bf buffer to store and return values for the
- *   callback. It's obvious, but impractical to pass the entire bf
- *   buffer to the callback as some of these commands are actually
- *   executed locally and have no buffer.
- */
-
-#include "canonical_machine.h"
-
-#include "config.h"
-#include "stepper.h"
-#include "spindle.h"
-#include "coolant.h"
-#include "switch.h"
-#include "hardware.h"
-#include "util.h"
-#include "usart.h"            // for serial queue flush
-#include "estop.h"
-
-#include "plan/planner.h"
-#include "plan/buffer.h"
-#include "plan/feedhold.h"
-#include "plan/dwell.h"
-#include "plan/command.h"
-#include "plan/arc.h"
-#include "plan/line.h"
-
-#include <stdbool.h>
-#include <string.h>
-#include <math.h>
-#include <stdio.h>
-
-
-cmSingleton_t cm = {
-  // Offsets
-  .offset = {
-    {}, // ABSOLUTE_COORDS
-
-    {0, 0, 0, 0, 0, 0}, // G54
-    {X_TRAVEL_MAX / 2, Y_TRAVEL_MAX / 2, 0, 0, 0, 0}, // G55
-    {0, 0, 0, 0, 0, 0}, // G56
-    {0, 0, 0, 0, 0, 0}, // G57
-    {0, 0, 0, 0, 0, 0}, // G58
-    {0, 0, 0, 0, 0, 0}, // G59
-  },
-
-  // Axes
-  .a = {
-    {
-      .axis_mode =         X_AXIS_MODE,
-      .velocity_max =      X_VELOCITY_MAX,
-      .feedrate_max =      X_FEEDRATE_MAX,
-      .travel_min =        X_TRAVEL_MIN,
-      .travel_max =        X_TRAVEL_MAX,
-      .jerk_max =          X_JERK_MAX,
-      .jerk_homing =       X_JERK_HOMING,
-      .junction_dev =      X_JUNCTION_DEVIATION,
-      .search_velocity =   X_SEARCH_VELOCITY,
-      .latch_velocity =    X_LATCH_VELOCITY,
-      .latch_backoff =     X_LATCH_BACKOFF,
-      .zero_backoff =      X_ZERO_BACKOFF,
-    }, {
-      .axis_mode =         Y_AXIS_MODE,
-      .velocity_max =      Y_VELOCITY_MAX,
-      .feedrate_max =      Y_FEEDRATE_MAX,
-      .travel_min =        Y_TRAVEL_MIN,
-      .travel_max =        Y_TRAVEL_MAX,
-      .jerk_max =          Y_JERK_MAX,
-      .jerk_homing =       Y_JERK_HOMING,
-      .junction_dev =      Y_JUNCTION_DEVIATION,
-      .search_velocity =   Y_SEARCH_VELOCITY,
-      .latch_velocity =    Y_LATCH_VELOCITY,
-      .latch_backoff =     Y_LATCH_BACKOFF,
-      .zero_backoff =      Y_ZERO_BACKOFF,
-    }, {
-      .axis_mode =         Z_AXIS_MODE,
-      .velocity_max =      Z_VELOCITY_MAX,
-      .feedrate_max =      Z_FEEDRATE_MAX,
-      .travel_min =        Z_TRAVEL_MIN,
-      .travel_max =        Z_TRAVEL_MAX,
-      .jerk_max =          Z_JERK_MAX,
-      .jerk_homing =       Z_JERK_HOMING,
-      .junction_dev =      Z_JUNCTION_DEVIATION,
-      .search_velocity =   Z_SEARCH_VELOCITY,
-      .latch_velocity =    Z_LATCH_VELOCITY,
-      .latch_backoff =     Z_LATCH_BACKOFF,
-      .zero_backoff =      Z_ZERO_BACKOFF,
-    }, {
-      .axis_mode =         A_AXIS_MODE,
-      .velocity_max =      A_VELOCITY_MAX,
-      .feedrate_max =      A_FEEDRATE_MAX,
-      .travel_min =        A_TRAVEL_MIN,
-      .travel_max =        A_TRAVEL_MAX,
-      .jerk_max =          A_JERK_MAX,
-      .jerk_homing =       A_JERK_HOMING,
-      .junction_dev =      A_JUNCTION_DEVIATION,
-      .radius =            A_RADIUS,
-      .search_velocity =   A_SEARCH_VELOCITY,
-      .latch_velocity =    A_LATCH_VELOCITY,
-      .latch_backoff =     A_LATCH_BACKOFF,
-      .zero_backoff =      A_ZERO_BACKOFF,
-    }, {
-      .axis_mode =         B_AXIS_MODE,
-      .velocity_max =      B_VELOCITY_MAX,
-      .feedrate_max =      B_FEEDRATE_MAX,
-      .travel_min =        B_TRAVEL_MIN,
-      .travel_max =        B_TRAVEL_MAX,
-      .jerk_max =          B_JERK_MAX,
-      .junction_dev =      B_JUNCTION_DEVIATION,
-      .radius =            B_RADIUS,
-    }, {
-      .axis_mode =         C_AXIS_MODE,
-      .velocity_max =      C_VELOCITY_MAX,
-      .feedrate_max =      C_FEEDRATE_MAX,
-      .travel_min =        C_TRAVEL_MIN,
-      .travel_max =        C_TRAVEL_MAX,
-      .jerk_max =          C_JERK_MAX,
-      .junction_dev =      C_JUNCTION_DEVIATION,
-      .radius =            C_RADIUS,
-    }
-  },
-
-  .combined_state = COMBINED_READY,
-  .machine_state = MACHINE_READY,
-
-  // State
-  .gm = {.motion_mode = MOTION_MODE_CANCEL_MOTION_MODE},
-  .gn = {0},
-  .gf = {0},
-};
-
-
-// Command execution callbacks from planner queue
-static void _exec_offset(float *value, float *flag);
-static void _exec_change_tool(float *value, float *flag);
-static void _exec_select_tool(float *value, float *flag);
-static void _exec_mist_coolant_control(float *value, float *flag);
-static void _exec_flood_coolant_control(float *value, float *flag);
-static void _exec_absolute_origin(float *value, float *flag);
-static void _exec_program_finalize(float *value, float *flag);
-
-// Canonical Machine State functions
-
-/// Combines raw states into something a user might want to see
-cmCombinedState_t cm_get_combined_state() {
-  if (cm.cycle_state == CYCLE_OFF) cm.combined_state = cm.machine_state;
-  else if (cm.cycle_state == CYCLE_PROBE) cm.combined_state = COMBINED_PROBE;
-  else if (cm.cycle_state == CYCLE_HOMING) cm.combined_state = COMBINED_HOMING;
-  else {
-    if (cm.motion_state == MOTION_RUN) cm.combined_state = COMBINED_RUN;
-    if (cm.motion_state == MOTION_HOLD) cm.combined_state = COMBINED_HOLD;
-  }
-
-  if (cm.machine_state == MACHINE_SHUTDOWN)
-    cm.combined_state = COMBINED_SHUTDOWN;
-
-  return cm.combined_state;
-}
-
-uint32_t cm_get_line() {return cm.gm.line;}
-cmMachineState_t cm_get_machine_state() {return cm.machine_state;}
-cmCycleState_t cm_get_cycle_state() {return cm.cycle_state;}
-cmMotionState_t cm_get_motion_state() {return cm.motion_state;}
-cmFeedholdState_t cm_get_hold_state() {return cm.hold_state;}
-cmHomingState_t cm_get_homing_state() {return cm.homing_state;}
-cmMotionMode_t cm_get_motion_mode() {return cm.gm.motion_mode;}
-cmCoordSystem_t cm_get_coord_system() {return cm.gm.coord_system;}
-cmUnitsMode_t cm_get_units_mode() {return cm.gm.units_mode;}
-cmCanonicalPlane_t cm_get_select_plane() {return cm.gm.select_plane;}
-cmPathControlMode_t cm_get_path_control() {return cm.gm.path_control;}
-cmDistanceMode_t cm_get_distance_mode() {return cm.gm.distance_mode;}
-cmFeedRateMode_t cm_get_feed_rate_mode() {return cm.gm.feed_rate_mode;}
-uint8_t cm_get_tool() {return cm.gm.tool;}
-cmSpindleMode_t cm_get_spindle_mode() {return cm.gm.spindle_mode;}
-bool cm_get_runtime_busy() {return mp_get_runtime_busy();}
-float cm_get_feed_rate() {return cm.gm.feed_rate;}
-
-
-void cm_set_machine_state(cmMachineState_t machine_state) {
-  cm.machine_state = machine_state;
-}
-
-
-void cm_set_motion_state(cmMotionState_t motion_state) {
-  cm.motion_state = motion_state;
-}
-
-
-void cm_set_motion_mode(cmMotionMode_t motion_mode) {
-  cm.gm.motion_mode = motion_mode;
-}
-
-
-void cm_set_spindle_mode(cmSpindleMode_t spindle_mode) {
-  cm.gm.spindle_mode = spindle_mode;
-}
-
-
-void cm_set_spindle_speed_parameter(float speed) {cm.gm.spindle_speed = speed;}
-void cm_set_tool_number(uint8_t tool) {cm.gm.tool = tool;}
-
-
-void cm_set_absolute_override(bool absolute_override) {
-  cm.gm.absolute_override = absolute_override;
-  // must reset offsets if you change absolute override
-  cm_set_work_offsets();
-}
-
-
-void cm_set_model_line(uint32_t line) {cm.gm.line = line;}
-
-
-/* Jerk functions
- *
- * Jerk values can be rather large, often in the billions. This makes
- * for some pretty big numbers for people to deal with. Jerk values
- * are stored in the system in truncated format; values are divided by
- * 1,000,000 then reconstituted before use.
- *
- * The axis_jerk() functions expect the jerk in divided-by 1,000,000 form
- */
-
-/// returns jerk for an axis
-float cm_get_axis_jerk(uint8_t axis) {
-  return cm.a[axis].jerk_max;
-}
-
-
-/// sets the jerk for an axis, including recirpcal and cached values
-void cm_set_axis_jerk(uint8_t axis, float jerk) {
-  cm.a[axis].jerk_max = jerk;
-  cm.a[axis].recip_jerk = 1 / (jerk * JERK_MULTIPLIER);
-}
-
-
-/* Coordinate systems and offsets
- *
- * Functions to get, set and report coordinate systems and work offsets
- * These functions are not part of the NIST defined functions
- */
-/*
- * Notes on Coordinate System and Offset functions
- *
- * All positional information in the canonical machine is kept as
- * absolute coords and in canonical units (mm). The offsets are only
- * used to translate in and out of canonical form during
- * interpretation and response.
- *
- * Managing the coordinate systems & offsets is somewhat complicated.
- * The following affect offsets:
- *    - coordinate system selected. 1-9 correspond to G54-G59
- *    - absolute override: forces current move to be interpreted in machine
- *      coordinates: G53 (system 0)
- *    - G92 offsets are added "on top of" the coord system offsets --
- *      if origin_offset_enable
- *    - G28 and G30 moves; these are run in absolute coordinates
- *
- * The offsets themselves are considered static, are kept in cm, and are
- * supposed to be persistent.
- *
- * To reduce complexity and data load the following is done:
- *    - Full data for coordinates/offsets is only accessible by the canonical
- *      machine, not the downstream
- *    - Resolved set of coord and G92 offsets, with per-move exceptions can
- *      be captured as "work_offsets"
- *    - The core gcode context (gm) only knows about the active coord system
- *      and the work offsets
- */
-
-/* Return the currently active coordinate offset for an axis
- *
- * Takes G5x, G92 and absolute override into account to return the
- * active offset for this move
- *
- * This function is typically used to evaluate and set offsets, as
- * opposed to cm_get_work_offset() which merely returns what's in the
- * work_offset[] array.
- */
-float cm_get_active_coord_offset(uint8_t axis) {
-  if (cm.gm.absolute_override) return 0; // no offset in absolute override mode
-  float offset = cm.offset[cm.gm.coord_system][axis];
-
-  if (cm.origin_offset_enable)
-    offset += cm.origin_offset[axis]; // includes G5x and G92 components
-
-  return offset;
-}
-
-
-/// Return a coord offset
-float cm_get_work_offset(uint8_t axis) {
-  return cm.ms.work_offset[axis];
-}
-
-
-// Capture coord offsets from the model into absolute values
-void cm_set_work_offsets() {
-  for (int axis = 0; axis < AXES; axis++)
-    cm.ms.work_offset[axis] = cm_get_active_coord_offset(axis);
-}
-
-
-/* Get position of axis in absolute coordinates
- *
- * NOTE: Machine position is always returned in mm mode. No units conversion
- * is performed
- */
-float cm_get_absolute_position(uint8_t axis) {
-  return cm.position[axis];
-}
-
-
-/* Return work position in prevailing units (mm/inch) and with all offsets
- * applied.
- *
- * NOTE: This function only works after the gcode_state struct has had the
- * work_offsets setup by calling cm_get_model_coord_offset_vector() first.
- */
-float cm_get_work_position(uint8_t axis) {
-  float position = cm.position[axis] - cm_get_active_coord_offset(axis);
-
-  if (cm.gm.units_mode == INCHES) position /= MM_PER_INCH;
-
-  return position;
-}
-
-
-/* Critical helpers
- *
- * Core functions supporting the canonical machining functions
- * These functions are not part of the NIST defined functions
- */
-
-/* Perform final operations for a traverse or feed
- *
- * These routines set the point position in the gcode model.
- *
- * Note: As far as the canonical machine is concerned the final
- * position of a Gcode block (move) is achieved as soon as the move is
- * planned and the move target becomes the new model position.  In
- * reality the planner will (in all likelihood) have only just queued
- * the move for later execution, and the real tool position is still
- * close to the starting point.
- */
-void cm_finalize_move() {
-  copy_vector(cm.position, cm.ms.target);        // update model position
-
-  // if in ivnerse time mode reset feed rate so next block requires an
-  // explicit feed rate setting
-  if (cm.gm.feed_rate_mode == INVERSE_TIME_MODE &&
-      cm.gm.motion_mode == MOTION_MODE_STRAIGHT_FEED)
-    cm.gm.feed_rate = 0;
-}
-
-
-/* Compute optimal and minimum move times into the gcode_state
- *
- * "Minimum time" is the fastest the move can be performed given
- * the velocity constraints on each participating axis - regardless
- * of the feed rate requested. The minimum time is the time limited
- * by the rate-limiting axis. The minimum time is needed to compute
- * the optimal time and is recorded for possible feed override
- * computation.
- *
- * "Optimal time" is either the time resulting from the requested
- * feed rate or the minimum time if the requested feed rate is not
- * achievable. Optimal times for traverses are always the minimum
- * time.
- *
- * The gcode state must have targets set prior by having
- * cm_set_target(). Axis modes are taken into account by this.
- *
- * The following times are compared and the longest is returned:
- *   - G93 inverse time (if G93 is active)
- *   - time for coordinated move at requested feed rate
- *   - time that the slowest axis would require for the move
- *
- * Sets the following variables in the gcode_state struct
- *   - move_time is set to optimal time
- *
- * NIST RS274NGC_v3 Guidance
- *
- * The following is verbatim text from NIST RS274NGC_v3. As I
- * interpret A for moves that combine both linear and rotational
- * movement, the feed rate should apply to the XYZ movement, with
- * the rotational axis (or axes) timed to start and end at the same
- * time the linear move is performed. It is possible under this
- * case for the rotational move to rate-limit the linear move.
- *
- *  2.1.2.5 Feed Rate
- *
- * The rate at which the controlled point or the axes move is
- * nominally a steady rate which may be set by the user. In the
- * Interpreter, the interpretation of the feed rate is as follows
- * unless inverse time feed rate mode is being used in the
- * RS274/NGC view (see Section 3.5.19). The canonical machining
- * functions view of feed rate, as described in Section 4.3.5.1,
- * has conditions under which the set feed rate is applied
- * differently, but none of these is used in the Interpreter.
- *
- * A.  For motion involving one or more of the X, Y, and Z axes
- *     (with or without simultaneous rotational axis motion), the
- *     feed rate means length units per minute along the programmed
- *     XYZ path, as if the rotational axes were not moving.
- *
- * B.  For motion of one rotational axis with X, Y, and Z axes not
- *     moving, the feed rate means degrees per minute rotation of
- *     the rotational axis.
- *
- * C.  For motion of two or three rotational axes with X, Y, and Z
- *     axes not moving, the rate is applied as follows. Let dA, dB,
- *     and dC be the angles in degrees through which the A, B, and
- *     C axes, respectively, must move.  Let D = sqrt(dA^2 + dB^2 +
- *     dC^2). Conceptually, D is a measure of total angular motion,
- *     using the usual Euclidean metric. Let T be the amount of
- *     time required to move through D degrees at the current feed
- *     rate in degrees per minute. The rotational axes should be
- *     moved in coordinated linear motion so that the elapsed time
- *     from the start to the end of the motion is T plus any time
- *     required for acceleration or deceleration.
- */
-void cm_calc_move_time(const float axis_length[], const float axis_square[]) {
-  float inv_time = 0;           // inverse time if doing a feed in G93 mode
-  float xyz_time = 0;           // linear coordinated move at requested feed
-  float abc_time = 0;           // rotary coordinated move at requested feed
-  float max_time = 0;           // time required for the rate-limiting axis
-  float tmp_time = 0;           // used in computation
-
-  // compute times for feed motion
-  if (cm.gm.motion_mode != MOTION_MODE_STRAIGHT_TRAVERSE) {
-    if (cm.gm.feed_rate_mode == INVERSE_TIME_MODE) {
-      // feed rate was un-inverted to minutes by cm_set_feed_rate()
-      inv_time = cm.gm.feed_rate;
-      cm.gm.feed_rate_mode = UNITS_PER_MINUTE_MODE;
-
-    } else {
-      // compute length of linear move in millimeters. Feed rate is provided as
-      // mm/min
-      xyz_time = sqrt(axis_square[AXIS_X] + axis_square[AXIS_Y] +
-                      axis_square[AXIS_Z]) / cm.gm.feed_rate;
-
-      // if no linear axes, compute length of multi-axis rotary move in degrees.
-      // Feed rate is provided as degrees/min
-      if (fp_ZERO(xyz_time))
-        abc_time = sqrt(axis_square[AXIS_A] + axis_square[AXIS_B] +
-                        axis_square[AXIS_C]) / cm.gm.feed_rate;
-    }
-  }
-
-  for (uint8_t axis = 0; axis < AXES; axis++) {
-    if (cm.gm.motion_mode == MOTION_MODE_STRAIGHT_TRAVERSE)
-      tmp_time = fabs(axis_length[axis]) / cm.a[axis].velocity_max;
-
-    else // MOTION_MODE_STRAIGHT_FEED
-      tmp_time = fabs(axis_length[axis]) / cm.a[axis].feedrate_max;
-
-    max_time = max(max_time, tmp_time);
-  }
-
-  cm.ms.move_time = max4(inv_time, max_time, xyz_time, abc_time);
-}
-
-
-/// Set endpoint position from final runtime position
-void cm_update_model_position_from_runtime() {
-  copy_vector(cm.position, mr.ms.target);
-}
-
-
-/* Set target vector in GM model
- *
- * This is a core routine. It handles:
- *    - conversion of linear units to internal canonical form (mm)
- *    - conversion of relative mode to absolute (internal canonical form)
- *    - translation of work coordinates to machine coordinates (internal
- *      canonical form)
- *    - computation and application of axis modes as so:
- *
- *    DISABLED  - Incoming value is ignored. Target value is not changed
- *    ENABLED   - Convert axis values to canonical format and store as target
- *    INHIBITED - Same processing as ENABLED, but axis will not actually be run
- *    RADIUS    - ABC axis value is provided in Gcode block in linear units
- *              - Target is set to degrees based on axis' Radius value
- *              - Radius mode is only processed for ABC axes. Application to
- *                XYZ is ignored.
- *
- *    Target coordinates are provided in target[]
- *    Axes that need processing are signaled in flag[]
- */
-
-// ESTEE: _calc_ABC is a fix to workaround a gcc compiler bug wherein it runs
-// out of spill registers we moved this block into its own function so that we
-// get a fresh stack push
-// ALDEN: This shows up in avr-gcc 4.7.0 and avr-libc 1.8.0
-static float _calc_ABC(uint8_t axis, float target[], float flag[]) {
-  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);
-}
-
-
-void cm_set_model_target(float target[], float flag[]) {
-  float tmp = 0;
-
-  // process XYZABC for lower modes
-  for (int axis = AXIS_X; axis <= AXIS_Z; axis++) {
-    if (fp_FALSE(flag[axis]) || cm.a[axis].axis_mode == AXIS_DISABLED)
-      continue; // skip axis if not flagged for update or its disabled
-
-    else if (cm.a[axis].axis_mode == AXIS_STANDARD ||
-             cm.a[axis].axis_mode == AXIS_INHIBITED) {
-      if (cm.gm.distance_mode == ABSOLUTE_MODE)
-        cm.ms.target[axis] =
-          cm_get_active_coord_offset(axis) + TO_MILLIMETERS(target[axis]);
-      else cm.ms.target[axis] += TO_MILLIMETERS(target[axis]);
-    }
-  }
-
-  // FYI: The ABC loop below relies on the XYZ loop having been run first
-  for (int axis = AXIS_A; axis <= AXIS_C; axis++) {
-    if (fp_FALSE(flag[axis]) || cm.a[axis].axis_mode == AXIS_DISABLED)
-      continue; // skip axis if not flagged for update or its disabled
-    else tmp = _calc_ABC(axis, target, flag);
-
-    if (cm.gm.distance_mode == ABSOLUTE_MODE)
-      // sacidu93's fix to Issue #22
-      cm.ms.target[axis] = tmp + cm_get_active_coord_offset(axis);
-    else cm.ms.target[axis] += tmp;
-  }
-}
-
-
-/* Return error code if soft limit is exceeded
- *
- * Must be called with target properly set in GM struct. Best done
- * after cm_set_model_target().
- *
- * Tests for soft limit for any homed axis if min and max are
- * different values. You can set min and max to 0,0 to disable soft
- * limits for an axis. Also will not test a min or a max if the value
- * is < -1000000 (negative one million). This allows a single end to
- * be tested w/the other disabled, should that requirement ever arise.
- */
-stat_t cm_test_soft_limits(float target[]) {
-#ifdef SOFT_LIMIT_ENABLE
-    for (int axis = 0; axis < AXES; axis++) {
-      if (!cm.homed[axis]) continue; // don't test axes that are not homed
-
-      if (fp_EQ(cm.a[axis].travel_min, cm.a[axis].travel_max)) continue;
-
-      if (cm.a[axis].travel_min > DISABLE_SOFT_LIMIT &&
-          target[axis] < cm.a[axis].travel_min)
-        return STAT_SOFT_LIMIT_EXCEEDED;
-
-      if (cm.a[axis].travel_max > DISABLE_SOFT_LIMIT &&
-          target[axis] > cm.a[axis].travel_max)
-        return STAT_SOFT_LIMIT_EXCEEDED;
-    }
-#endif
-
-  return STAT_OK;
-}
-
-
-/* Canonical machining functions
- *    Values are passed in pre-unit_converted state (from gn structure)
- *    All operations occur on gm (current model state)
- *
- * These are organized by section number (x.x.x) in the order they are
- * found in NIST RS274 NGCv3
- */
-
-// Initialization and Termination (4.3.2)
-
-void canonical_machine_init() {
-  // Init 1/jerk
-  for (uint8_t axis = 0; axis < AXES; axis++)
-    cm.a[axis].recip_jerk = 1 / (cm.a[axis].jerk_max * JERK_MULTIPLIER);
-
-  // Set gcode defaults
-  cm_set_units_mode(GCODE_DEFAULT_UNITS);
-  cm_set_coord_system(GCODE_DEFAULT_COORD_SYSTEM);
-  cm_set_plane(GCODE_DEFAULT_PLANE);
-  cm_set_path_control(GCODE_DEFAULT_PATH_CONTROL);
-  cm_set_distance_mode(GCODE_DEFAULT_DISTANCE_MODE);
-  cm_set_feed_rate_mode(UNITS_PER_MINUTE_MODE); // always the default
-
-  // Sub-system inits
-  cm_spindle_init();
-  coolant_init();
-}
-
-
-/// Alarm state; send an exception report and stop processing input
-stat_t cm_alarm(const char *location, stat_t code) {
-  status_message_P(location, STAT_LEVEL_ERROR, code, "ALARM");
-  estop_trigger();
-  return code;
-}
-
-
-/// Clear soft alarm
-stat_t cm_clear() {
-  if (cm.cycle_state == CYCLE_OFF)
-    cm.machine_state = MACHINE_PROGRAM_STOP;
-  else cm.machine_state = MACHINE_CYCLE;
-
-  return STAT_OK;
-}
-
-
-// Representation (4.3.3)
-//
-// Affect the Gcode model only (asynchronous)
-// These functions assume input validation occurred upstream.
-
-/// G17, G18, G19 select axis plane
-void cm_set_plane(cmCanonicalPlane_t plane) {cm.gm.select_plane = plane;}
-
-
-/// G20, G21
-void cm_set_units_mode(cmUnitsMode_t mode) {cm.gm.units_mode = mode;}
-
-
-/// G90, G91
-void cm_set_distance_mode(cmDistanceMode_t mode) {cm.gm.distance_mode = mode;}
-
-
-/* G10 L2 Pn, delayed persistence
- *
- * This function applies the offset to the GM model. You can also
- * use $g54x - $g59c config functions to change offsets.
- *
- * It also does not reset the work_offsets which may be
- * accomplished by calling cm_set_work_offsets() immediately
- * afterwards.
- */
-void cm_set_coord_offsets(cmCoordSystem_t coord_system, float offset[],
-                            float flag[]) {
-  if (coord_system < G54 || coord_system > COORD_SYSTEM_MAX)
-    return; // you can't set G53
-
-  for (int axis = 0; axis < AXES; axis++)
-    if (fp_TRUE(flag[axis]))
-      cm.offset[coord_system][axis] = TO_MILLIMETERS(offset[axis]);
-}
-
-
-// Representation functions that affect gcode model and are queued to planner
-// (synchronous)
-
-/// G54-G59
-void cm_set_coord_system(cmCoordSystem_t coord_system) {
-  cm.gm.coord_system = coord_system;
-
-  // pass coordinate system in value[0] element
-  float value[AXES] = {coord_system};
-  // second vector (flags) is not used, so fake it
-  mp_queue_command(_exec_offset, value, value);
-}
-
-
-static void _exec_offset(float *value, float *flag) {
-  // coordinate system is passed in value[0] element
-  uint8_t coord_system = value[0];
-  float offsets[AXES];
-
-  for (int axis = 0; axis < AXES; axis++)
-    offsets[axis] = cm.offset[coord_system][axis] +
-      cm.origin_offset[axis] * (cm.origin_offset_enable ? 1 : 0);
-
-  mp_set_runtime_work_offset(offsets);
-  cm_set_work_offsets(); // set work offsets in the Gcode model
-}
-
-
-/* Set the position of a single axis in the model, planner and runtime
- *
- * This command sets an axis/axes to a position provided as an argument.
- * This is useful for setting origins for homing, probing, and other operations.
- *
- *  !!!!! 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 if the runtime is moving. The system must be
- * quiescent or you will introduce positional errors. This is true
- * because the planned / running moves have a different reference
- * frame than the one you are now going to set. These functions should
- * only be called during initialization sequences and during cycles
- * (such as homing cycles) when you know there are no more moves in
- * the planner and that all motion has stopped.  Use
- * cm_get_runtime_busy() to be sure the system is quiescent.
- */
-void cm_set_position(int axis, float position) {
-  // TODO: Interlock involving runtime_busy test
-  cm.position[axis] = position;
-  cm.ms.target[axis] = position;
-  mp_set_planner_position(axis, position);
-  mp_set_runtime_position(axis, position);
-  mp_set_steps_to_runtime_position();
-}
-
-
-// G28.3 functions and support
-
-/* G28.3 - model, planner and queue to runtime
- *
- * Takes a vector of origins (presumably 0's, but not necessarily) and
- * applies them to all axes where the corresponding position in the
- * flag vector is true (1).
- *
- * This is a 2 step process. The model and planner contexts are set
- * immediately, the runtime command is queued and synchronized with
- * the planner queue. This includes the runtime position and the step
- * recording done by the encoders. At that point any axis that is set
- * is also marked as homed.
- */
-void cm_set_absolute_origin(float origin[], float flag[]) {
-  float value[AXES];
-
-  for (int axis = 0; axis < AXES; axis++)
-    if (fp_TRUE(flag[axis])) {
-      value[axis] = TO_MILLIMETERS(origin[axis]);
-      cm.position[axis] = value[axis];         // set model position
-      cm.ms.target[axis] = value[axis];            // reset model target
-      mp_set_planner_position(axis, value[axis]);  // set mm position
-    }
-
-  mp_queue_command(_exec_absolute_origin, value, flag);
-}
-
-
-static void _exec_absolute_origin(float *value, float *flag) {
-  for (int axis = 0; axis < AXES; axis++)
-    if (fp_TRUE(flag[axis])) {
-      mp_set_runtime_position(axis, value[axis]);
-      cm.homed[axis] = true;    // G28.3 is not considered homed until here
-    }
-
-  mp_set_steps_to_runtime_position();
-}
-
-
-/* G92's behave according to NIST 3.5.18 & LinuxCNC G92
- * http://linuxcnc.org/docs/html/gcode/gcode.html#sec:G92-G92.1-G92.2-G92.3
- */
-
-/// G92
-void cm_set_origin_offsets(float offset[], float flag[]) {
-  // set offsets in the Gcode model extended context
-  cm.origin_offset_enable = true;
-  for (int axis = 0; axis < AXES; axis++)
-    if (fp_TRUE(flag[axis]))
-      cm.origin_offset[axis] = cm.position[axis] -
-        cm.offset[cm.gm.coord_system][axis] - TO_MILLIMETERS(offset[axis]);
-
-  // now pass the offset to the callback - setting the coordinate system also
-  // applies the offsets
-  // pass coordinate system in value[0] element
-  float value[AXES] = {cm.gm.coord_system};
-  mp_queue_command(_exec_offset, value, value); // second vector is not used
-}
-
-
-/// G92.1
-void cm_reset_origin_offsets() {
-  cm.origin_offset_enable = false;
-  for (int axis = 0; axis < AXES; axis++)
-    cm.origin_offset[axis] = 0;
-
-  float value[AXES] = {cm.gm.coord_system};
-  mp_queue_command(_exec_offset, value, value);
-}
-
-
-/// G92.2
-void cm_suspend_origin_offsets() {
-  cm.origin_offset_enable = false;
-  float value[AXES] = {cm.gm.coord_system};
-  mp_queue_command(_exec_offset, value, value);
-}
-
-
-/// G92.3
-void cm_resume_origin_offsets() {
-  cm.origin_offset_enable = true;
-  float value[AXES] = {cm.gm.coord_system};
-  mp_queue_command(_exec_offset, value, value);
-}
-
-
-// Free Space Motion (4.3.4)
-
-/// G0 linear rapid
-stat_t cm_straight_traverse(float target[], float flags[]) {
-  cm.gm.motion_mode = MOTION_MODE_STRAIGHT_TRAVERSE;
-  cm_set_model_target(target, flags);
-
-  // test soft limits
-  stat_t status = cm_test_soft_limits(cm.ms.target);
-  if (status != STAT_OK) return CM_ALARM(status);
-
-  // prep and plan the move
-  cm_set_work_offsets(&cm.gm); // capture fully resolved offsets to the state
-  cm_cycle_start();            // required for homing & other cycles
-  cm.ms.line = cm.gm.line;     // copy line number
-  mp_aline(&cm.ms);            // send the move to the planner
-  cm_finalize_move();
-
-  return STAT_OK;
-}
-
-
-/// G28.1
-void cm_set_g28_position() {copy_vector(cm.g28_position, cm.position);}
-
-
-/// G28
-stat_t cm_goto_g28_position(float target[], float flags[]) {
-  cm_set_absolute_override(true);
-
-  // move through intermediate point, or skip
-  cm_straight_traverse(target, flags);
-
-  // make sure you have an available buffer
-  mp_wait_for_buffer();
-
-  // execute actual stored move
-  float f[] = {1, 1, 1, 1, 1, 1};
-  return cm_straight_traverse(cm.g28_position, f);
-}
-
-
-/// G30.1
-void cm_set_g30_position() {copy_vector(cm.g30_position, cm.position);}
-
-
-/// G30
-stat_t cm_goto_g30_position(float target[], float flags[]) {
-  cm_set_absolute_override(true);
-
-  // move through intermediate point, or skip
-  cm_straight_traverse(target, flags);
-
-  // make sure you have an available buffer
-  mp_wait_for_buffer();
-
-  // execute actual stored move
-  float f[] = {1, 1, 1, 1, 1, 1};
-  return cm_straight_traverse(cm.g30_position, f);
-}
-
-
-// Machining Attributes (4.3.5)
-
-/// F parameter
-/// Normalize feed rate to mm/min or to minutes if in inverse time mode
-void cm_set_feed_rate(float feed_rate) {
-  if (cm.gm.feed_rate_mode == INVERSE_TIME_MODE)
-    // normalize to minutes (active for this gcode block only)
-    cm.gm.feed_rate = 1 / feed_rate;
-
-  else cm.gm.feed_rate = TO_MILLIMETERS(feed_rate);
-}
-
-
-/// G93, G94 See cmFeedRateMode
-void cm_set_feed_rate_mode(cmFeedRateMode_t mode) {cm.gm.feed_rate_mode = mode;}
-
-
-/// G61, G61.1, G64
-void cm_set_path_control(cmPathControlMode_t mode) {cm.gm.path_control = mode;}
-
-
-// Machining Functions (4.3.6) See arc.c
-
-/// G4, P parameter (seconds)
-stat_t cm_dwell(float seconds) {
-  return mp_dwell(seconds);
-}
-
-
-/// G1
-stat_t cm_straight_feed(float target[], float flags[]) {
-  // trap zero feed rate condition
-  if (cm.gm.feed_rate_mode != INVERSE_TIME_MODE && fp_ZERO(cm.gm.feed_rate))
-    return STAT_GCODE_FEEDRATE_NOT_SPECIFIED;
-
-  cm.gm.motion_mode = MOTION_MODE_STRAIGHT_FEED;
-  cm_set_model_target(target, flags);
-
-  // test soft limits
-  stat_t status = cm_test_soft_limits(cm.ms.target);
-  if (status != STAT_OK) return CM_ALARM(status);
-
-  // prep and plan the move
-  cm_set_work_offsets(&cm.gm); // capture the fully resolved offsets to state
-  cm_cycle_start();            // required for homing & other cycles
-  cm.ms.line = cm.gm.line;     // copy line number
-  status = mp_aline(&cm.ms);   // send the move to the planner
-  cm_finalize_move();
-
-  return status;
-}
-
-
-// Spindle Functions (4.3.7) see spindle.c, spindle.h
-
-/* Tool Functions (4.3.8)
- *
- * Note: These functions don't actually do anything for now, and there's a bug
- *       where T and M in different blocks don't work correctly
- */
-
-/// T parameter
-void cm_select_tool(uint8_t tool_select) {
-  float value[AXES] = {tool_select};
-  mp_queue_command(_exec_select_tool, value, value);
-}
-
-
-static void _exec_select_tool(float *value, float *flag) {
-  cm.gm.tool_select = value[0];
-}
-
-
-/// M6 This might become a complete tool change cycle
-void cm_change_tool(uint8_t tool_change) {
-  float value[AXES] = {cm.gm.tool_select};
-  mp_queue_command(_exec_change_tool, value, value);
-}
-
-
-static void _exec_change_tool(float *value, float *flag) {
-  cm.gm.tool = (uint8_t)value[0];
-}
-
-
-// Miscellaneous Functions (4.3.9)
-/// M7
-void cm_mist_coolant_control(bool mist_coolant) {
-  float value[AXES] = {mist_coolant};
-  mp_queue_command(_exec_mist_coolant_control, value, value);
-}
-
-
-static void _exec_mist_coolant_control(float *value, float *flag) {
-  coolant_set_mist(cm.gm.mist_coolant = value[0]);
-}
-
-
-/// M8, M9
-void cm_flood_coolant_control(bool flood_coolant) {
-  float value[AXES] = {flood_coolant};
-  mp_queue_command(_exec_flood_coolant_control, value, value);
-}
-
-
-static void _exec_flood_coolant_control(float *value, float *flag) {
-  cm.gm.flood_coolant = value[0];
-
-  coolant_set_flood(value[0]);
-  if (!value[0]) coolant_set_mist(false); // M9 special function
-}
-
-
-/* Override enables are kind of a mess in Gcode. This is an attempt to sort
- * them out.  See
- * http://www.linuxcnc.org/docs/2.4/html/gcode_main.html#sec:M50:-Feed-Override
- */
-
-/// M48, M49
-void cm_override_enables(bool flag) {
-  cm.gm.feed_rate_override_enable = flag;
-  cm.gm.traverse_override_enable = flag;
-  cm.gm.spindle_override_enable = flag;
-}
-
-
-/// M50
-void cm_feed_rate_override_enable(bool flag) {
-  if (fp_TRUE(cm.gf.parameter) && fp_ZERO(cm.gn.parameter))
-    cm.gm.feed_rate_override_enable = false;
-  else cm.gm.feed_rate_override_enable = true;
-}
-
-
-/// M50.1
-void cm_feed_rate_override_factor(bool flag) {
-  cm.gm.feed_rate_override_enable = flag;
-  cm.gm.feed_rate_override_factor = cm.gn.parameter;
-}
-
-
-/// M50.2
-void cm_traverse_override_enable(bool flag) {
-  if (fp_TRUE(cm.gf.parameter) && fp_ZERO(cm.gn.parameter))
-    cm.gm.traverse_override_enable = false;
-  else cm.gm.traverse_override_enable = true;
-}
-
-
-/// M51
-void cm_traverse_override_factor(bool flag) {
-  cm.gm.traverse_override_enable = flag;
-  cm.gm.traverse_override_factor = cm.gn.parameter;
-}
-
-
-/// M51.1
-void cm_spindle_override_enable(bool flag) {
-  if (fp_TRUE(cm.gf.parameter) && fp_ZERO(cm.gn.parameter))
-    cm.gm.spindle_override_enable = false;
-  else cm.gm.spindle_override_enable = true;
-}
-
-
-/// M50.1
-void cm_spindle_override_factor(bool flag) {
-  cm.gm.spindle_override_enable = flag;
-  cm.gm.spindle_override_factor = cm.gn.parameter;
-}
-
-
-void cm_message(const char *message) {
-  status_message_P(0, STAT_LEVEL_INFO, STAT_OK, PSTR("%s"), message);
-}
-
-
-/* Program Functions (4.3.10)
- *
- * This group implements stop, start, end, and hold.
- * It is extended beyond the NIST spec to handle various situations.
- *
- * cm_program_stop and cm_optional_program_stop are synchronous Gcode
- * commands that are received through the interpreter. They cause all motion
- * to stop at the end of the current command, including spindle motion.
- *
- * Note that the stop occurs at the end of the immediately preceding command
- * (i.e. the stop is queued behind the last command).
- *
- * cm_program_end is a stop that also resets the machine to initial state
- */
-
-/* Feedholds, queue flushes and cycles starts are all related. The request
- * functions set flags for these. The sequencing callback interprets the flags
- * according to the following rules:
- *
- *   A feedhold request received during motion should be honored
- *   A feedhold request received during a feedhold should be ignored and reset
- *   A feedhold request received during a motion stop should be ignored and
- *     reset
- *
- *   A queue flush request received during motion should be ignored but not
- *     reset
- *   A queue flush request received during a feedhold should be deferred until
- *     the feedhold enters a HOLD state (i.e. until deceleration is complete)
- *   A queue flush request received during a motion stop should be honored
- *
- *   A cycle start request received during motion should be ignored and reset
- *   A cycle start request received during a feedhold should be deferred until
- *     the feedhold enters a HOLD state (i.e. until deceleration is complete)
- *     If a queue flush request is also present the queue flush should be done
- *     first
- *   A cycle start request received during a motion stop should be honored and
- *     should start to run anything in the planner queue
- */
-
-
-/// Initiate a feedhold right now
-void cm_request_feedhold() {cm.feedhold_requested = true;}
-void cm_request_queue_flush() {cm.queue_flush_requested = true;}
-void cm_request_cycle_start() {cm.cycle_start_requested = true;}
-
-
-/// Process feedholds, cycle starts & queue flushes
-void cm_feedhold_sequencing_callback() {
-  if (cm.feedhold_requested) {
-    if (cm.motion_state == MOTION_RUN && cm.hold_state == FEEDHOLD_OFF) {
-      cm_set_motion_state(MOTION_HOLD);
-      cm.hold_state = FEEDHOLD_SYNC;     // invokes hold from aline execution
-    }
-
-    cm.feedhold_requested = false;
-  }
-
-  if (cm.queue_flush_requested) {
-    if ((cm.motion_state == MOTION_STOP ||
-         (cm.motion_state == MOTION_HOLD && cm.hold_state == FEEDHOLD_HOLD)) &&
-        !cm_get_runtime_busy()) {
-      cm.queue_flush_requested = false;
-      cm_queue_flush();
-    }
-  }
-
-  bool processing =
-    cm.hold_state == FEEDHOLD_SYNC ||
-    cm.hold_state == FEEDHOLD_PLAN ||
-    cm.hold_state == FEEDHOLD_DECEL;
-
-  if (cm.cycle_start_requested && !cm.queue_flush_requested && !processing) {
-    cm.cycle_start_requested = false;
-    cm.hold_state = FEEDHOLD_END_HOLD;
-    cm_cycle_start();
-    mp_end_hold();
-  }
-}
-
-
-stat_t cm_queue_flush() {
-  if (cm_get_runtime_busy()) return STAT_COMMAND_NOT_ACCEPTED;
-
-  mp_flush_planner();                      // flush planner queue
-
-  // Note: The following uses low-level mp calls for absolute position.
-  for (int axis = 0; axis < AXES; axis++)
-    // set mm from mr
-    cm_set_position(axis, mp_get_runtime_absolute_position(axis));
-
-  float value[AXES] = {MACHINE_PROGRAM_STOP};
-  _exec_program_finalize(value, value);    // finalize now, not later
-
-  return STAT_OK;
-}
-
-
-/* Program and cycle state functions
- *
- * cm_program_end() implements M2 and M30
- * The END behaviors are defined by NIST 3.6.1 are:
- *    1. Axis offsets are set to zero (like G92.2) and origin offsets are set
- *       to the default (like G54)
- *    2. Selected plane is set to CANON_PLANE_XY (like G17)
- *    3. Distance mode is set to MODE_ABSOLUTE (like G90)
- *    4. Feed rate mode is set to UNITS_PER_MINUTE (like G94)
- *    5. Feed and speed overrides are set to ON (like M48)
- *    6. Cutter compensation is turned off (like G40)
- *    7. The spindle is stopped (like M5)
- *    8. The current motion mode is set to G_1 (like G1)
- *    9. Coolant is turned off (like M9)
- *
- * cm_program_end() implments things slightly differently:
- *    1. Axis offsets are set to G92.1 CANCEL offsets
- *       (instead of using G92.2 SUSPEND Offsets)
- *       Set default coordinate system (uses $gco, not G54)
- *    2. Selected plane is set to default plane ($gpl)
- *       (instead of setting it to G54)
- *    3. Distance mode is set to MODE_ABSOLUTE (like G90)
- *    4. Feed rate mode is set to UNITS_PER_MINUTE (like G94)
- *    5. Not implemented
- *    6. Not implemented
- *    7. The spindle is stopped (like M5)
- *    8. Motion mode is canceled like G80 (not set to G1)
- *    9. Coolant is turned off (like M9)
- *    +  Default INCHES or MM units mode is restored ($gun)
- */
-static void _exec_program_finalize(float *value, float *flag) {
-  cm.machine_state = (uint8_t)value[0];
-  cm_set_motion_state(MOTION_STOP);
-  if (cm.cycle_state == CYCLE_MACHINING)
-    cm.cycle_state = CYCLE_OFF;     // don't end cycle if homing, probing, etc.
-
-  cm.hold_state = FEEDHOLD_OFF;     // end feedhold (if in feed hold)
-  cm.cycle_start_requested = false; // cancel any pending cycle start request
-  mp_zero_segment_velocity();       // for reporting purposes
-
-  // perform the following resets if it's a program END
-  if (cm.machine_state == MACHINE_PROGRAM_END) {
-    cm_reset_origin_offsets();           // G92.1 - we do G91.1 instead of G92.2
-    cm_set_coord_system(GCODE_DEFAULT_COORD_SYSTEM);
-    cm_set_plane(GCODE_DEFAULT_PLANE);
-    cm_set_distance_mode(GCODE_DEFAULT_DISTANCE_MODE);
-    cm_spindle_control(SPINDLE_OFF);                 // M5
-    cm_flood_coolant_control(false);                 // M9
-    cm_set_feed_rate_mode(UNITS_PER_MINUTE_MODE);    // G94
-    cm_set_motion_mode(MOTION_MODE_CANCEL_MOTION_MODE);
-  }
-}
-
-
-/// Do a cycle start right now
-void cm_cycle_start() {
-  cm.machine_state = MACHINE_CYCLE;
-
-  // don't (re)start homing, probe or other canned cycles
-  if (cm.cycle_state == CYCLE_OFF) cm.cycle_state = CYCLE_MACHINING;
-}
-
-
-/// Do a cycle end right now
-void cm_cycle_end() {
-  if (cm.cycle_state != CYCLE_OFF) {
-    float value[AXES] = {MACHINE_PROGRAM_STOP};
-    _exec_program_finalize(value, value);
-  }
-}
-
-
-
-/// M0  Queue a program stop
-void cm_program_stop() {
-  float value[AXES] = {MACHINE_PROGRAM_STOP};
-  mp_queue_command(_exec_program_finalize, value, value);
-}
-
-
-/// M1
-void cm_optional_program_stop() {
-  float value[AXES] = {MACHINE_PROGRAM_STOP};
-  mp_queue_command(_exec_program_finalize, value, value);
-}
-
-
-/// M2, M30
-void cm_program_end() {
-  float value[AXES] = {MACHINE_PROGRAM_END};
-  mp_queue_command(_exec_program_finalize, value, value);
-}
-
-
-/// return ASCII char for axis given the axis number
-char cm_get_axis_char(int8_t axis) {
-  char axis_char[] = "XYZABC";
-  if (axis < 0 || axis > AXES) return ' ';
-  return axis_char[axis];
-}
diff --git a/src/canonical_machine.h b/src/canonical_machine.h
deleted file mode 100644 (file)
index dc23e38..0000000
+++ /dev/null
@@ -1,568 +0,0 @@
-/******************************************************************************\
-
-                This file is part of the Buildbotics firmware.
-
-                  Copyright (c) 2015 - 2016 Buildbotics LLC
-                  Copyright (c) 2010 - 2015 Alden S. Hart, Jr.
-                            All rights reserved.
-
-     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 <http://www.gnu.org/licenses/>.
-
-     The software is distributed in the hope that it will be useful, but
-          WITHOUT ANY WARRANTY; without even the implied warranty of
-      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-               Lesser General Public License for more details.
-
-       You should have received a copy of the GNU Lesser General Public
-                License along with the software.  If not, see
-                       <http://www.gnu.org/licenses/>.
-
-                For information regarding this software email:
-                  "Joseph Coffland" <joseph@buildbotics.com>
-
-\******************************************************************************/
-
-#pragma once
-
-
-#include "config.h"
-#include "status.h"
-
-#include <stdint.h>
-#include <stdbool.h>
-
-
-#define TO_MILLIMETERS(a) (cm.gm.units_mode == INCHES ? (a) * MM_PER_INCH : a)
-
-#define DISABLE_SOFT_LIMIT -1000000
-
-
-
-/* Machine state model
- *
- * The following main variables track canonical machine state and state
- * transitions.
- *        - cm.machine_state    - overall state of machine and program execution
- *        - cm.cycle_state    - what cycle the machine is executing (or none)
- *        - cm.motion_state    - state of movement
- *
- * Allowed states and combined states:
- *
- *   MACHINE STATE     CYCLE STATE   MOTION_STATE    COMBINED_STATE (FYI)
- *   -------------     ------------  -------------   --------------------
- *   MACHINE_UNINIT    na            na              (U)
- *   MACHINE_READY     CYCLE_OFF     MOTION_STOP     (ROS) RESET-OFF-STOP
- *   MACHINE_PROG_STOP CYCLE_OFF     MOTION_STOP     (SOS) STOP-OFF-STOP
- *   MACHINE_PROG_END  CYCLE_OFF     MOTION_STOP     (EOS) END-OFF-STOP
- *
- *   MACHINE_CYCLE     CYCLE_STARTED MOTION_STOP     (CSS) CYCLE-START-STOP
- *   MACHINE_CYCLE     CYCLE_STARTED MOTION_RUN      (CSR) CYCLE-START-RUN
- *   MACHINE_CYCLE     CYCLE_STARTED MOTION_HOLD     (CSH) CYCLE-START-HOLD
- *   MACHINE_CYCLE     CYCLE_STARTED MOTION_END_HOLD (CSE) CYCLE-START-END_HOLD
- *
- *   MACHINE_CYCLE     CYCLE_HOMING  MOTION_STOP     (CHS) CYCLE-HOMING-STOP
- *   MACHINE_CYCLE     CYCLE_HOMING  MOTION_RUN      (CHR) CYCLE-HOMING-RUN
- *   MACHINE_CYCLE     CYCLE_HOMING  MOTION_HOLD     (CHH) CYCLE-HOMING-HOLD
- *   MACHINE_CYCLE     CYCLE_HOMING  MOTION_END_HOLD (CHE) CYCLE-HOMING-END_HOLD
- */
-
-/// check alignment with messages in config.c / msg_stat strings
-typedef enum {
-  COMBINED_INITIALIZING, // machine is initializing
-  COMBINED_READY,        // machine is ready for use. Also null move STOP state
-  COMBINED_ALARM,        // machine in soft alarm state
-  COMBINED_PROGRAM_STOP, // program stop or no more blocks
-  COMBINED_PROGRAM_END,  // program end
-  COMBINED_RUN,          // motion is running
-  COMBINED_HOLD,         // motion is holding
-  COMBINED_PROBE,        // probe cycle active
-  COMBINED_CYCLE,        // machine is running (cycling)
-  COMBINED_HOMING,       // homing is treated as a cycle
-  COMBINED_SHUTDOWN,     // machine in hard alarm state (shutdown)
-} cmCombinedState_t;
-
-
-typedef enum {
-  MACHINE_INITIALIZING, // machine is initializing
-  MACHINE_READY,        // machine is ready for use
-  MACHINE_ALARM,        // machine in soft alarm state
-  MACHINE_PROGRAM_STOP, // program stop or no more blocks
-  MACHINE_PROGRAM_END,  // program end
-  MACHINE_CYCLE,        // machine is running (cycling)
-  MACHINE_SHUTDOWN,     // machine in hard alarm state (shutdown)
-} cmMachineState_t;
-
-
-typedef enum {
-  CYCLE_OFF,            // machine is idle
-  CYCLE_MACHINING,      // in normal machining cycle
-  CYCLE_PROBE,          // in probe cycle
-  CYCLE_HOMING,         // homing is treated as a specialized cycle
-} cmCycleState_t;
-
-
-typedef enum {
-  MOTION_STOP,          // motion has stopped
-  MOTION_RUN,           // machine is in motion
-  MOTION_HOLD           // feedhold in progress
-} cmMotionState_t;
-
-
-typedef enum {          // feedhold_state machine
-  FEEDHOLD_OFF,         // no feedhold in effect
-  FEEDHOLD_SYNC,        // start hold - sync to latest aline segment
-  FEEDHOLD_PLAN,        // replan blocks for feedhold
-  FEEDHOLD_DECEL,       // decelerate to hold point
-  FEEDHOLD_HOLD,        // holding
-  FEEDHOLD_END_HOLD     // end hold (transient state to OFF)
-} cmFeedholdState_t;
-
-
-typedef enum {          // applies to cm.homing_state
-  HOMING_NOT_HOMED,     // machine is not homed (0=false)
-  HOMING_HOMED,         // machine is homed (1=true)
-  HOMING_WAITING        // machine waiting to be homed
-} cmHomingState_t;
-
-
-typedef enum {          // applies to cm.probe_state
-  PROBE_FAILED,         // probe reached endpoint without triggering
-  PROBE_SUCCEEDED,      // probe was triggered, cm.probe_results has position
-  PROBE_WAITING,        // probe is waiting to be started
-} cmProbeState_t;
-
-
-/* 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)
- */
-
-/// these are in order to optimized CASE statement
-typedef enum {
-  NEXT_ACTION_DEFAULT,                // Must be zero (invokes motion modes)
-  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 no coord setting
-  NEXT_ACTION_SET_G28_POSITION,       // G28.1 set position in abs coordinates
-  NEXT_ACTION_GOTO_G28_POSITION,      // G28 go to machine position
-  NEXT_ACTION_SET_G30_POSITION,       // G30.1
-  NEXT_ACTION_GOTO_G30_POSITION,      // G30
-  NEXT_ACTION_SET_COORD_DATA,         // G10
-  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_DWELL,                  // G4
-  NEXT_ACTION_STRAIGHT_PROBE          // G38.2
-} cmNextAction_t;
-
-
-typedef enum {                        // G Modal Group 1
-  MOTION_MODE_STRAIGHT_TRAVERSE,      // G0 - straight traverse
-  MOTION_MODE_STRAIGHT_FEED,          // G1 - straight feed
-  MOTION_MODE_CW_ARC,                 // G2 - clockwise arc feed
-  MOTION_MODE_CCW_ARC,                // G3 - counter-clockwise arc feed
-  MOTION_MODE_CANCEL_MOTION_MODE,     // G80
-  MOTION_MODE_STRAIGHT_PROBE,         // G38.2
-  MOTION_MODE_CANNED_CYCLE_81,        // G81 - drilling
-  MOTION_MODE_CANNED_CYCLE_82,        // G82 - drilling with dwell
-  MOTION_MODE_CANNED_CYCLE_83,        // G83 - peck drilling
-  MOTION_MODE_CANNED_CYCLE_84,        // G84 - right hand tapping
-  MOTION_MODE_CANNED_CYCLE_85,        // G85 - boring, no dwell, feed out
-  MOTION_MODE_CANNED_CYCLE_86,        // G86 - boring, spindle stop, rapid out
-  MOTION_MODE_CANNED_CYCLE_87,        // G87 - back boring
-  MOTION_MODE_CANNED_CYCLE_88,        // G88 - boring, spindle stop, manual out
-  MOTION_MODE_CANNED_CYCLE_89,        // G89 - boring, dwell, feed out
-} cmMotionMode_t;
-
-
-typedef enum {   // Used for detecting gcode errors. See NIST section 3.4
-  MODAL_GROUP_G0,     // {G10,G28,G28.1,G92}       non-modal axis commands
-  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
-  MODAL_GROUP_M9,     // {M48,M49}                 speed/feed override switches
-} cmModalGroup_t;
-
-#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,     // G17    X          Y          Z
-  CANON_PLANE_XZ,     // G18    X          Z          Y
-  CANON_PLANE_YZ      // G19    Y          Z          X
-} cmCanonicalPlane_t;
-
-
-typedef enum {
-  INCHES,        // G20
-  MILLIMETERS,   // G21
-  DEGREES        // ABC axes (this value used for displays only)
-} cmUnitsMode_t;
-
-
-typedef enum {
-  ABSOLUTE_COORDS,                // machine coordinate system
-  G54,                            // G54 coordinate system
-  G55,                            // G55 coordinate system
-  G56,                            // G56 coordinate system
-  G57,                            // G57 coordinate system
-  G58,                            // G58 coordinate system
-  G59                             // G59 coordinate system
-} cmCoordSystem_t;
-
-#define COORD_SYSTEM_MAX G59      // set this manually to the last one
-
-/// G Modal Group 13
-typedef enum {
-  /// G61 - hits corners but does not stop if it does not need to.
-  PATH_EXACT_PATH,
-  PATH_EXACT_STOP,                // G61.1 - stops at all corners
-  PATH_CONTINUOUS                 // G64 and typically the default mode
-} cmPathControlMode_t;
-
-
-typedef enum {
-  ABSOLUTE_MODE,                  // G90
-  INCREMENTAL_MODE                // G91
-} cmDistanceMode_t;
-
-
-typedef enum {
-  INVERSE_TIME_MODE,              // G93
-  UNITS_PER_MINUTE_MODE,          // G94
-  UNITS_PER_REVOLUTION_MODE       // G95 (unimplemented)
-} cmFeedRateMode_t;
-
-
-typedef enum {
-  ORIGIN_OFFSET_SET,      // G92 - set origin offsets
-  ORIGIN_OFFSET_CANCEL,   // G92.1 - zero out origin offsets
-  ORIGIN_OFFSET_SUSPEND,  // G92.2 - do not apply offsets, but preserve values
-  ORIGIN_OFFSET_RESUME    // G92.3 - resume application of the suspended offsets
-} cmOriginOffset_t;
-
-
-typedef enum {
-  PROGRAM_STOP,
-  PROGRAM_END
-} cmProgramFlow_t;
-
-
-/// spindle state settings
-typedef enum {
-  SPINDLE_OFF,
-  SPINDLE_CW,
-  SPINDLE_CCW
-} cmSpindleMode_t;
-
-
-/// mist and flood coolant states
-typedef enum {
-  COOLANT_OFF,        // all coolant off
-  COOLANT_ON,         // request coolant on or indicate both coolants are on
-  COOLANT_MIST,       // indicates mist coolant on
-  COOLANT_FLOOD       // indicates flood coolant on
-} cmCoolantState_t;
-
-
-/// used for spindle and arc dir
-typedef enum {
-  DIRECTION_CW,
-  DIRECTION_CCW
-} cmDirection_t;
-
-
-/// axis modes (ordered: see _cm_get_feed_time())
-typedef enum {
-  AXIS_DISABLED,              // kill axis
-  AXIS_STANDARD,              // axis in coordinated motion w/standard behaviors
-  AXIS_INHIBITED,             // axis is computed but not activated
-  AXIS_RADIUS,                // rotary axis calibrated to circumference
-  AXIS_MODE_MAX
-} cmAxisMode_t; // ordering must be preserved.
-
-
-/* Gcode model - The following GCodeModel/GCodeInput structs are used:
- *
- * - gm is the core Gcode model state. It keeps the internal gcode
- *     state model in normalized, canonical form. All values are unit
- *     converted (to mm) and in the machine coordinate system
- *     (absolute coordinate system). Gm is owned by the canonical
- *     machine layer and should be accessed only through cm_ routines.
- *
- * - gn is used by the gcode interpreter and is re-initialized for
- *     each gcode block.It accepts data in the new gcode block in the
- *     formats present in the block (pre-normalized forms). During
- *     initialization some state elements are necessarily restored
- *     from gm.
- *
- * - gf is used by the gcode parser interpreter to hold flags for any
- *     data that has changed in gn during the parse. cm.gf.target[]
- *     values are also used by the canonical machine during
- *     set_target().
- */
-
-
-typedef struct {
-  int32_t line;              // gcode block line number
-  float target[AXES];        // XYZABC where the move should go
-  float work_offset[AXES];   // offset from work coordinate system
-  float move_time;           // optimal time for move given axis constraints
-} MoveState_t;
-
-
-/// Gcode model state
-typedef struct GCodeState {
-  uint32_t line;                      // Gcode block line number
-
-  uint8_t tool;                       // Tool after T and M6
-  uint8_t tool_select;                // T - sets this value
-
-  float feed_rate;                    // F - in mm/min or inverse time mode
-  cmFeedRateMode_t feed_rate_mode;
-  float feed_rate_override_factor;    // 1.0000 x F feed rate.
-  bool feed_rate_override_enable;     // M48, M49
-  float traverse_override_factor;     // 1.0000 x traverse rate
-  bool traverse_override_enable;
-
-  float spindle_speed;                // in RPM
-  cmSpindleMode_t spindle_mode;
-  float spindle_override_factor;      // 1.0000 x S spindle speed
-  bool spindle_override_enable;       // true = override enabled
-
-  cmMotionMode_t motion_mode;         // Group 1 modal motion
-  cmCanonicalPlane_t select_plane;    // G17, G18, G19
-  cmUnitsMode_t units_mode;           // G20, G21
-  cmCoordSystem_t coord_system;       // G54-G59 - select coordinate system 1-9
-  bool absolute_override;             // G53 true = move in machine coordinates
-  cmPathControlMode_t path_control;   // G61
-  cmDistanceMode_t distance_mode;     // G91
-  cmDistanceMode_t arc_distance_mode; // G91.1
-
-  bool mist_coolant;                  // true = mist on (M7), false = off (M9)
-  bool flood_coolant;                 // true = flood on (M8), false = off (M9)
-
-  cmNextAction_t next_action;         // handles G group 1 moves & non-modals
-  cmProgramFlow_t program_flow;       // used only by the gcode_parser
-
-  // unimplemented gcode parameters
-  // float cutter_radius;           // D - cutter radius compensation (0 is off)
-  // float cutter_length;           // H - cutter length compensation (0 is off)
-
-  // Used for input only
-  float target[AXES];                 // XYZABC where the move should go
-  bool override_enables;              // feed and spindle enable
-  bool tool_change;                   // M6 tool change flag
-
-  float parameter;                    // P - dwell time in sec, G10 coord select
-
-  float arc_radius;                   // R - radius value in arc radius mode
-  float arc_offset[3];                // IJK - used by arc commands
-} GCodeState_t;
-
-
-typedef struct cmAxis {
-  cmAxisMode_t axis_mode;
-  float feedrate_max;    // max velocity in mm/min or deg/min
-  float velocity_max;    // max velocity in mm/min or deg/min
-  float travel_max;      // max work envelope for soft limits
-  float travel_min;      // min work envelope for soft limits
-  float jerk_max;        // max jerk (Jm) in mm/min^3 divided by 1 million
-  float jerk_homing;     // homing jerk (Jh) in mm/min^3 divided by 1 million
-  float recip_jerk;      // reciprocal of current jerk value - with million
-  float junction_dev;    // aka cornering delta
-  float radius;          // radius in mm for rotary axis modes
-  float search_velocity; // homing search velocity
-  float latch_velocity;  // homing latch velocity
-  float latch_backoff;   // backoff from switches prior to homing latch movement
-  float zero_backoff;    // backoff from switches for machine zero
-} AxisConfig_t;
-
-
-typedef struct cmSingleton {          // struct to manage cm globals and cycles
-  // coordinate systems and offsets absolute (G53) + G54,G55,G56,G57,G58,G59
-  float offset[COORDS + 1][AXES];
-  float origin_offset[AXES];          // G92 offsets
-  bool origin_offset_enable;          // G92 offsets enabled/disabled
-
-  float position[AXES];               // model position (not used in gn or gf)
-  float g28_position[AXES];           // stored machine position for G28
-  float g30_position[AXES];           // stored machine position for G30
-
-  // settings for axes X,Y,Z,A B,C
-  AxisConfig_t a[AXES];
-
-  cmCombinedState_t combined_state;    // combination of states for display
-  cmMachineState_t machine_state;
-  cmCycleState_t cycle_state;
-  cmMotionState_t motion_state;
-  cmFeedholdState_t hold_state;        // hold: feedhold sub-state machine
-  cmHomingState_t homing_state;        // home: homing cycle sub-state machine
-  bool homed[AXES];                    // individual axis homing flags
-
-  cmProbeState_t probe_state;
-  float probe_results[AXES];           // probing results
-
-  bool feedhold_requested;             // feedhold character received
-  bool queue_flush_requested;          // queue flush character received
-  bool cycle_start_requested;          // cycle start character received
-
-  // Model states
-  MoveState_t ms;
-  GCodeState_t gm;                     // core gcode model state
-  GCodeState_t gn;                     // gcode input values
-  GCodeState_t gf;                     // gcode input flags
-} cmSingleton_t;
-
-
-extern cmSingleton_t cm;               // canonical machine controller singleton
-
-
-// Model state getters and setters
-uint32_t cm_get_line();
-cmCombinedState_t cm_get_combined_state();
-cmMachineState_t cm_get_machine_state();
-cmCycleState_t cm_get_cycle_state();
-cmMotionState_t cm_get_motion_state();
-cmFeedholdState_t cm_get_hold_state();
-cmHomingState_t cm_get_homing_state();
-cmMotionMode_t cm_get_motion_mode();
-cmCoordSystem_t cm_get_coord_system();
-cmUnitsMode_t cm_get_units_mode();
-cmCanonicalPlane_t cm_get_select_plane();
-cmPathControlMode_t cm_get_path_control();
-cmDistanceMode_t cm_get_distance_mode();
-cmFeedRateMode_t cm_get_feed_rate_mode();
-uint8_t cm_get_tool();
-cmSpindleMode_t cm_get_spindle_mode();
-bool cm_get_runtime_busy();
-float cm_get_feed_rate();
-
-void cm_set_machine_state(cmMachineState_t machine_state);
-void cm_set_motion_state(cmMotionState_t motion_state);
-void cm_set_motion_mode(cmMotionMode_t motion_mode);
-void cm_set_spindle_mode(cmSpindleMode_t spindle_mode);
-void cm_set_spindle_speed_parameter(float speed);
-void cm_set_tool_number(uint8_t tool);
-void cm_set_absolute_override(bool absolute_override);
-void cm_set_model_line(uint32_t line);
-
-float cm_get_axis_jerk(uint8_t axis);
-void cm_set_axis_jerk(uint8_t axis, float jerk);
-
-// Coordinate systems and offsets
-float cm_get_active_coord_offset(uint8_t axis);
-float cm_get_work_offset(uint8_t axis);
-void cm_set_work_offsets();
-float cm_get_absolute_position(uint8_t axis);
-float cm_get_work_position(uint8_t axis);
-
-// Critical helpers
-void cm_calc_move_time(const float axis_length[], const float axis_square[]);
-void cm_update_model_position_from_runtime();
-void cm_finalize_move();
-stat_t cm_deferred_write_callback();
-void cm_set_model_target(float target[], float flag[]);
-stat_t cm_test_soft_limits(float target[]);
-
-// Canonical machining functions defined by NIST [organized by NIST Gcode doc]
-
-// Initialization and termination (4.3.2)
-void canonical_machine_init();
-/// enter alarm state. returns same status code
-stat_t cm_alarm(const char *location, stat_t status);
-stat_t cm_clear();
-
-#define CM_ALARM(CODE) cm_alarm(STATUS_LOCATION, CODE)
-
-// Representation (4.3.3)
-void cm_set_plane(cmCanonicalPlane_t plane);
-void cm_set_units_mode(cmUnitsMode_t mode);
-void cm_set_distance_mode(cmDistanceMode_t mode);
-void cm_set_coord_offsets(cmCoordSystem_t coord_system, float offset[],
-                          float flag[]);
-
-void cm_set_position(int axis, float position);
-void cm_set_absolute_origin(float origin[], float flag[]);
-
-void cm_set_coord_system(cmCoordSystem_t coord_system);
-void cm_set_origin_offsets(float offset[], float flag[]);
-void cm_reset_origin_offsets();
-void cm_suspend_origin_offsets();
-void cm_resume_origin_offsets();
-
-// Free Space Motion (4.3.4)
-stat_t cm_straight_traverse(float target[], float flags[]);
-void cm_set_g28_position();
-stat_t cm_goto_g28_position(float target[], float flags[]);
-void cm_set_g30_position();
-stat_t cm_goto_g30_position(float target[], float flags[]);
-
-// Machining Attributes (4.3.5)
-void cm_set_feed_rate(float feed_rate);
-void cm_set_feed_rate_mode(cmFeedRateMode_t mode);
-void cm_set_path_control(cmPathControlMode_t mode);
-
-// Machining Functions (4.3.6)
-stat_t cm_straight_feed(float target[], float flags[]);
-stat_t cm_arc_feed(float target[], float flags[],
-                   float i, float j, float k,
-                   float radius, uint8_t motion_mode);
-stat_t cm_dwell(float seconds);
-
-// Spindle Functions (4.3.7) see spindle.h
-
-// Tool Functions (4.3.8)
-void cm_select_tool(uint8_t tool);
-void cm_change_tool(uint8_t tool);
-
-// Miscellaneous Functions (4.3.9)
-void cm_mist_coolant_control(bool mist_coolant);
-void cm_flood_coolant_control(bool flood_coolant);
-
-void cm_override_enables(bool flag);
-void cm_feed_rate_override_enable(bool flag);
-void cm_feed_rate_override_factor(bool flag);
-void cm_traverse_override_enable(bool flag);
-void cm_traverse_override_factor(bool flag);
-void cm_spindle_override_enable(bool flag);
-void cm_spindle_override_factor(bool flag);
-
-void cm_message(const char *message);
-
-// Program Functions (4.3.10)
-void cm_request_feedhold();
-void cm_request_queue_flush();
-void cm_request_cycle_start();
-
-void cm_feedhold_sequencing_callback();
-stat_t cm_queue_flush();
-
-void cm_cycle_start();
-void cm_cycle_end();
-void cm_feedhold();
-void cm_program_stop();
-void cm_optional_program_stop();
-void cm_program_end();
-
-// Cycles
-char cm_get_axis_char(int8_t axis);
index 124c4c937bab2ad7e44b8bfb3637118b12cddc8a..e737bd7ad86e5e7f15ba294dca8027aab7416299 100644 (file)
@@ -169,7 +169,7 @@ typedef enum {
 #define VELOCITY_MAX             13000  // mm/min
 #define FEEDRATE_MAX             VELOCITY_MAX
 
-#define X_AXIS_MODE              AXIS_STANDARD // See canonical_machine.h
+#define X_AXIS_MODE              AXIS_STANDARD // See machine.h
 #define X_VELOCITY_MAX           VELOCITY_MAX  // G0 max velocity in mm/min
 #define X_FEEDRATE_MAX           FEEDRATE_MAX  // G1 max feed rate in mm/min
 #define X_TRAVEL_MIN             0             // minimum travel for soft limits
@@ -265,7 +265,7 @@ typedef enum {
 
 // Gcode defaults
 #define GCODE_DEFAULT_UNITS         MILLIMETERS // MILLIMETERS or INCHES
-#define GCODE_DEFAULT_PLANE         CANON_PLANE_XY // See canonical_machine.h
+#define GCODE_DEFAULT_PLANE         PLANE_XY // See machine.h
 #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
index 340ed0b73bb0cf70a299297ed277c7f9c0156f69..d00e2dd7ea6e5dafe128cea724cce8c76a53a163 100644 (file)
@@ -28,7 +28,7 @@
 
 #include "gcode_parser.h"
 
-#include "canonical_machine.h"
+#include "machine.h"
 #include "spindle.h"
 #include "probing.h"
 #include "homing.h"
@@ -195,7 +195,7 @@ static stat_t _validate_gcode_block() {
 
 /* Execute parsed block
  *
- * Conditionally (based on whether a flag is set in gf) call the canonical
+ * Conditionally (based on whether a flag is set in gf) call the
  * machining functions in order of execution as per RS274NGC_3 table 8
  * (below, with modifications):
  *
@@ -228,7 +228,7 @@ static stat_t _validate_gcode_block() {
  *   21. stop and end (M0, M1, M2, M30, M60)
  *
  * Values in gn are in original units and should not be unit converted prior
- * to calling the canonical functions (which do the unit conversions)
+ * to calling the machine functions (which do the unit conversions)
  */
 static stat_t _execute_gcode_block() {
   stat_t status = STAT_OK;
@@ -378,9 +378,9 @@ static stat_t _parse_gcode_block(char *buf) {
       case 4: SET_NON_MODAL(next_action, NEXT_ACTION_DWELL);
       case 10:
         SET_MODAL(MODAL_GROUP_G0, next_action, NEXT_ACTION_SET_COORD_DATA);
-      case 17: SET_MODAL(MODAL_GROUP_G2, select_plane, CANON_PLANE_XY);
-      case 18: SET_MODAL(MODAL_GROUP_G2, select_plane, CANON_PLANE_XZ);
-      case 19: SET_MODAL(MODAL_GROUP_G2, select_plane, CANON_PLANE_YZ);
+      case 17: SET_MODAL(MODAL_GROUP_G2, select_plane, PLANE_XY);
+      case 18: SET_MODAL(MODAL_GROUP_G2, select_plane, PLANE_XZ);
+      case 19: SET_MODAL(MODAL_GROUP_G2, select_plane, PLANE_YZ);
       case 20: SET_MODAL(MODAL_GROUP_G6, units_mode, INCHES);
       case 21: SET_MODAL(MODAL_GROUP_G6, units_mode, MILLIMETERS);
       case 28:
index acded39f9d5c93f04c5c24b884e22cd9f424c878..a9efd443ef9ae588fe5346a2fdc3901297a7568e 100644 (file)
@@ -26,7 +26,7 @@
 
 \******************************************************************************/
 
-#include "canonical_machine.h"
+#include "machine.h"
 #include "switch.h"
 #include "util.h"
 #include "report.h"
index 60d5bbfc9739fdbeb2f9c11e3d8488ecdba3be27..fce7aa00826022d903fac9fb6c60b014c948cb53 100644 (file)
@@ -27,7 +27,7 @@
 
 #pragma once
 
-#include "canonical_machine.h"
+#include "machine.h"
 
 
 void huanyang_init();
diff --git a/src/machine.c b/src/machine.c
new file mode 100644 (file)
index 0000000..cdbe35b
--- /dev/null
@@ -0,0 +1,1291 @@
+/******************************************************************************\
+
+                This file is part of the Buildbotics firmware.
+
+                  Copyright (c) 2015 - 2016 Buildbotics LLC
+                  Copyright (c) 2010 - 2015 Alden S. Hart, Jr.
+                  Copyright (c) 2012 - 2015 Rob Giseburt
+                            All rights reserved.
+
+     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 <http://www.gnu.org/licenses/>.
+
+     The software is distributed in the hope that it will be useful, but
+          WITHOUT ANY WARRANTY; without even the implied warranty of
+      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+               Lesser General Public License for more details.
+
+       You should have received a copy of the GNU Lesser General Public
+                License along with the software.  If not, see
+                       <http://www.gnu.org/licenses/>.
+
+                For information regarding this software email:
+                  "Joseph Coffland" <joseph@buildbotics.com>
+
+\******************************************************************************/
+
+/* This code is a loose implementation of Kramer, Proctor and Messina's
+ * machining functions as described in the NIST RS274/NGC v3
+ *
+ * The machine is the layer between the Gcode parser and
+ * the motion control code for a specific robot. It keeps state and
+ * executes commands - passing the stateless commands to the motion
+ * planning layer.
+ *
+ * Synchronizing command execution
+ *
+ *   "Synchronous commands" are commands that affect the runtime need
+ *   to be synchronized with movement. Examples include G4 dwells,
+ *   program stops and ends, and most M commands.  These are queued
+ *   into the planner queue and execute from the queue. Synchronous
+ *   commands work like this:
+ *
+ *     - Call the cm_xxx_xxx() function which will do any input
+ *       validation and return an error if it detects one.
+ *
+ *     - The cm_ function calls mp_queue_command(). Arguments are a
+ *       callback to the _exec_...() function, which is the runtime
+ *       execution routine, and any arguments that are needed by the
+ *       runtime. See typedef for *exec in planner.h for details
+ *
+ *     - mp_queue_command() stores the callback and the args in a
+          planner buffer.
+ *
+ *     - When planner execution reaches the buffer it executes the
+ *       callback w/ the args.  Take careful note that the callback
+ *       executes under an interrupt, so beware of variables that may
+ *       need to be volatile.
+ *
+ *   Note: - The synchronous command execution mechanism uses 2
+ *   vectors in the bf buffer to store and return values for the
+ *   callback. It's obvious, but impractical to pass the entire bf
+ *   buffer to the callback as some of these commands are actually
+ *   executed locally and have no buffer.
+ */
+
+#include "machine.h"
+
+#include "config.h"
+#include "stepper.h"
+#include "spindle.h"
+#include "coolant.h"
+#include "switch.h"
+#include "hardware.h"
+#include "util.h"
+#include "usart.h"            // for serial queue flush
+#include "estop.h"
+
+#include "plan/planner.h"
+#include "plan/buffer.h"
+#include "plan/feedhold.h"
+#include "plan/dwell.h"
+#include "plan/command.h"
+#include "plan/arc.h"
+#include "plan/line.h"
+
+#include <stdbool.h>
+#include <string.h>
+#include <math.h>
+#include <stdio.h>
+
+
+cmSingleton_t cm = {
+  // Offsets
+  .offset = {
+    {}, // ABSOLUTE_COORDS
+
+    {0, 0, 0, 0, 0, 0}, // G54
+    {X_TRAVEL_MAX / 2, Y_TRAVEL_MAX / 2, 0, 0, 0, 0}, // G55
+    {0, 0, 0, 0, 0, 0}, // G56
+    {0, 0, 0, 0, 0, 0}, // G57
+    {0, 0, 0, 0, 0, 0}, // G58
+    {0, 0, 0, 0, 0, 0}, // G59
+  },
+
+  // Axes
+  .a = {
+    {
+      .axis_mode =         X_AXIS_MODE,
+      .velocity_max =      X_VELOCITY_MAX,
+      .feedrate_max =      X_FEEDRATE_MAX,
+      .travel_min =        X_TRAVEL_MIN,
+      .travel_max =        X_TRAVEL_MAX,
+      .jerk_max =          X_JERK_MAX,
+      .jerk_homing =       X_JERK_HOMING,
+      .junction_dev =      X_JUNCTION_DEVIATION,
+      .search_velocity =   X_SEARCH_VELOCITY,
+      .latch_velocity =    X_LATCH_VELOCITY,
+      .latch_backoff =     X_LATCH_BACKOFF,
+      .zero_backoff =      X_ZERO_BACKOFF,
+    }, {
+      .axis_mode =         Y_AXIS_MODE,
+      .velocity_max =      Y_VELOCITY_MAX,
+      .feedrate_max =      Y_FEEDRATE_MAX,
+      .travel_min =        Y_TRAVEL_MIN,
+      .travel_max =        Y_TRAVEL_MAX,
+      .jerk_max =          Y_JERK_MAX,
+      .jerk_homing =       Y_JERK_HOMING,
+      .junction_dev =      Y_JUNCTION_DEVIATION,
+      .search_velocity =   Y_SEARCH_VELOCITY,
+      .latch_velocity =    Y_LATCH_VELOCITY,
+      .latch_backoff =     Y_LATCH_BACKOFF,
+      .zero_backoff =      Y_ZERO_BACKOFF,
+    }, {
+      .axis_mode =         Z_AXIS_MODE,
+      .velocity_max =      Z_VELOCITY_MAX,
+      .feedrate_max =      Z_FEEDRATE_MAX,
+      .travel_min =        Z_TRAVEL_MIN,
+      .travel_max =        Z_TRAVEL_MAX,
+      .jerk_max =          Z_JERK_MAX,
+      .jerk_homing =       Z_JERK_HOMING,
+      .junction_dev =      Z_JUNCTION_DEVIATION,
+      .search_velocity =   Z_SEARCH_VELOCITY,
+      .latch_velocity =    Z_LATCH_VELOCITY,
+      .latch_backoff =     Z_LATCH_BACKOFF,
+      .zero_backoff =      Z_ZERO_BACKOFF,
+    }, {
+      .axis_mode =         A_AXIS_MODE,
+      .velocity_max =      A_VELOCITY_MAX,
+      .feedrate_max =      A_FEEDRATE_MAX,
+      .travel_min =        A_TRAVEL_MIN,
+      .travel_max =        A_TRAVEL_MAX,
+      .jerk_max =          A_JERK_MAX,
+      .jerk_homing =       A_JERK_HOMING,
+      .junction_dev =      A_JUNCTION_DEVIATION,
+      .radius =            A_RADIUS,
+      .search_velocity =   A_SEARCH_VELOCITY,
+      .latch_velocity =    A_LATCH_VELOCITY,
+      .latch_backoff =     A_LATCH_BACKOFF,
+      .zero_backoff =      A_ZERO_BACKOFF,
+    }, {
+      .axis_mode =         B_AXIS_MODE,
+      .velocity_max =      B_VELOCITY_MAX,
+      .feedrate_max =      B_FEEDRATE_MAX,
+      .travel_min =        B_TRAVEL_MIN,
+      .travel_max =        B_TRAVEL_MAX,
+      .jerk_max =          B_JERK_MAX,
+      .junction_dev =      B_JUNCTION_DEVIATION,
+      .radius =            B_RADIUS,
+    }, {
+      .axis_mode =         C_AXIS_MODE,
+      .velocity_max =      C_VELOCITY_MAX,
+      .feedrate_max =      C_FEEDRATE_MAX,
+      .travel_min =        C_TRAVEL_MIN,
+      .travel_max =        C_TRAVEL_MAX,
+      .jerk_max =          C_JERK_MAX,
+      .junction_dev =      C_JUNCTION_DEVIATION,
+      .radius =            C_RADIUS,
+    }
+  },
+
+  .combined_state = COMBINED_READY,
+  .machine_state = MACHINE_READY,
+
+  // State
+  .gm = {.motion_mode = MOTION_MODE_CANCEL_MOTION_MODE},
+  .gn = {0},
+  .gf = {0},
+};
+
+
+// Command execution callbacks from planner queue
+static void _exec_offset(float *value, float *flag);
+static void _exec_change_tool(float *value, float *flag);
+static void _exec_select_tool(float *value, float *flag);
+static void _exec_mist_coolant_control(float *value, float *flag);
+static void _exec_flood_coolant_control(float *value, float *flag);
+static void _exec_absolute_origin(float *value, float *flag);
+static void _exec_program_finalize(float *value, float *flag);
+
+// Machine State functions
+
+/// Combines raw states into something a user might want to see
+cmCombinedState_t cm_get_combined_state() {
+  if (cm.cycle_state == CYCLE_OFF) cm.combined_state = cm.machine_state;
+  else if (cm.cycle_state == CYCLE_PROBE) cm.combined_state = COMBINED_PROBE;
+  else if (cm.cycle_state == CYCLE_HOMING) cm.combined_state = COMBINED_HOMING;
+  else {
+    if (cm.motion_state == MOTION_RUN) cm.combined_state = COMBINED_RUN;
+    if (cm.motion_state == MOTION_HOLD) cm.combined_state = COMBINED_HOLD;
+  }
+
+  if (cm.machine_state == MACHINE_SHUTDOWN)
+    cm.combined_state = COMBINED_SHUTDOWN;
+
+  return cm.combined_state;
+}
+
+uint32_t cm_get_line() {return cm.gm.line;}
+cmMachineState_t cm_get_machine_state() {return cm.machine_state;}
+cmCycleState_t cm_get_cycle_state() {return cm.cycle_state;}
+cmMotionState_t cm_get_motion_state() {return cm.motion_state;}
+cmFeedholdState_t cm_get_hold_state() {return cm.hold_state;}
+cmHomingState_t cm_get_homing_state() {return cm.homing_state;}
+cmMotionMode_t cm_get_motion_mode() {return cm.gm.motion_mode;}
+cmCoordSystem_t cm_get_coord_system() {return cm.gm.coord_system;}
+cmUnitsMode_t cm_get_units_mode() {return cm.gm.units_mode;}
+cmPlane_t cm_get_select_plane() {return cm.gm.select_plane;}
+cmPathControlMode_t cm_get_path_control() {return cm.gm.path_control;}
+cmDistanceMode_t cm_get_distance_mode() {return cm.gm.distance_mode;}
+cmFeedRateMode_t cm_get_feed_rate_mode() {return cm.gm.feed_rate_mode;}
+uint8_t cm_get_tool() {return cm.gm.tool;}
+cmSpindleMode_t cm_get_spindle_mode() {return cm.gm.spindle_mode;}
+bool cm_get_runtime_busy() {return mp_get_runtime_busy();}
+float cm_get_feed_rate() {return cm.gm.feed_rate;}
+
+
+void cm_set_machine_state(cmMachineState_t machine_state) {
+  cm.machine_state = machine_state;
+}
+
+
+void cm_set_motion_state(cmMotionState_t motion_state) {
+  cm.motion_state = motion_state;
+}
+
+
+void cm_set_motion_mode(cmMotionMode_t motion_mode) {
+  cm.gm.motion_mode = motion_mode;
+}
+
+
+void cm_set_spindle_mode(cmSpindleMode_t spindle_mode) {
+  cm.gm.spindle_mode = spindle_mode;
+}
+
+
+void cm_set_spindle_speed_parameter(float speed) {cm.gm.spindle_speed = speed;}
+void cm_set_tool_number(uint8_t tool) {cm.gm.tool = tool;}
+
+
+void cm_set_absolute_override(bool absolute_override) {
+  cm.gm.absolute_override = absolute_override;
+  // must reset offsets if you change absolute override
+  cm_set_work_offsets();
+}
+
+
+void cm_set_model_line(uint32_t line) {cm.gm.line = line;}
+
+
+/* Jerk functions
+ *
+ * Jerk values can be rather large, often in the billions. This makes
+ * for some pretty big numbers for people to deal with. Jerk values
+ * are stored in the system in truncated format; values are divided by
+ * 1,000,000 then reconstituted before use.
+ *
+ * The axis_jerk() functions expect the jerk in divided-by 1,000,000 form
+ */
+
+/// returns jerk for an axis
+float cm_get_axis_jerk(uint8_t axis) {
+  return cm.a[axis].jerk_max;
+}
+
+
+/// sets the jerk for an axis, including recirpcal and cached values
+void cm_set_axis_jerk(uint8_t axis, float jerk) {
+  cm.a[axis].jerk_max = jerk;
+  cm.a[axis].recip_jerk = 1 / (jerk * JERK_MULTIPLIER);
+}
+
+
+/* Coordinate systems and offsets
+ *
+ * Functions to get, set and report coordinate systems and work offsets
+ * These functions are not part of the NIST defined functions
+ */
+/*
+ * Notes on Coordinate System and Offset functions
+ *
+ * All positional information in the machine is kept as
+ * absolute coords and in canonical units (mm). The offsets are only
+ * used to translate in and out of canonical form during
+ * interpretation and response.
+ *
+ * Managing the coordinate systems & offsets is somewhat complicated.
+ * The following affect offsets:
+ *    - coordinate system selected. 1-9 correspond to G54-G59
+ *    - absolute override: forces current move to be interpreted in machine
+ *      coordinates: G53 (system 0)
+ *    - G92 offsets are added "on top of" the coord system offsets --
+ *      if origin_offset_enable
+ *    - G28 and G30 moves; these are run in absolute coordinates
+ *
+ * The offsets themselves are considered static, are kept in cm, and are
+ * supposed to be persistent.
+ *
+ * To reduce complexity and data load the following is done:
+ *    - Full data for coordinates/offsets is only accessible by the
+ *      machine, not the downstream
+ *    - Resolved set of coord and G92 offsets, with per-move exceptions can
+ *      be captured as "work_offsets"
+ *    - The core gcode context (gm) only knows about the active coord system
+ *      and the work offsets
+ */
+
+/* Return the currently active coordinate offset for an axis
+ *
+ * Takes G5x, G92 and absolute override into account to return the
+ * active offset for this move
+ *
+ * This function is typically used to evaluate and set offsets, as
+ * opposed to cm_get_work_offset() which merely returns what's in the
+ * work_offset[] array.
+ */
+float cm_get_active_coord_offset(uint8_t axis) {
+  if (cm.gm.absolute_override) return 0; // no offset in absolute override mode
+  float offset = cm.offset[cm.gm.coord_system][axis];
+
+  if (cm.origin_offset_enable)
+    offset += cm.origin_offset[axis]; // includes G5x and G92 components
+
+  return offset;
+}
+
+
+/// Return a coord offset
+float cm_get_work_offset(uint8_t axis) {
+  return cm.ms.work_offset[axis];
+}
+
+
+// Capture coord offsets from the model into absolute values
+void cm_set_work_offsets() {
+  for (int axis = 0; axis < AXES; axis++)
+    cm.ms.work_offset[axis] = cm_get_active_coord_offset(axis);
+}
+
+
+/* Get position of axis in absolute coordinates
+ *
+ * NOTE: Machine position is always returned in mm mode. No units conversion
+ * is performed
+ */
+float cm_get_absolute_position(uint8_t axis) {
+  return cm.position[axis];
+}
+
+
+/* Return work position in prevailing units (mm/inch) and with all offsets
+ * applied.
+ *
+ * NOTE: This function only works after the gcode_state struct has had the
+ * work_offsets setup by calling cm_get_model_coord_offset_vector() first.
+ */
+float cm_get_work_position(uint8_t axis) {
+  float position = cm.position[axis] - cm_get_active_coord_offset(axis);
+
+  if (cm.gm.units_mode == INCHES) position /= MM_PER_INCH;
+
+  return position;
+}
+
+
+/* Critical helpers
+ *
+ * Core functions supporting the machining functions
+ * These functions are not part of the NIST defined functions
+ */
+
+/* Perform final operations for a traverse or feed
+ *
+ * These routines set the point position in the gcode model.
+ *
+ * Note: As far as the machine is concerned the final
+ * position of a Gcode block (move) is achieved as soon as the move is
+ * planned and the move target becomes the new model position.  In
+ * reality the planner will (in all likelihood) have only just queued
+ * the move for later execution, and the real tool position is still
+ * close to the starting point.
+ */
+void cm_finalize_move() {
+  copy_vector(cm.position, cm.ms.target);        // update model position
+
+  // if in ivnerse time mode reset feed rate so next block requires an
+  // explicit feed rate setting
+  if (cm.gm.feed_rate_mode == INVERSE_TIME_MODE &&
+      cm.gm.motion_mode == MOTION_MODE_STRAIGHT_FEED)
+    cm.gm.feed_rate = 0;
+}
+
+
+/* Compute optimal and minimum move times into the gcode_state
+ *
+ * "Minimum time" is the fastest the move can be performed given
+ * the velocity constraints on each participating axis - regardless
+ * of the feed rate requested. The minimum time is the time limited
+ * by the rate-limiting axis. The minimum time is needed to compute
+ * the optimal time and is recorded for possible feed override
+ * computation.
+ *
+ * "Optimal time" is either the time resulting from the requested
+ * feed rate or the minimum time if the requested feed rate is not
+ * achievable. Optimal times for traverses are always the minimum
+ * time.
+ *
+ * The gcode state must have targets set prior by having
+ * cm_set_target(). Axis modes are taken into account by this.
+ *
+ * The following times are compared and the longest is returned:
+ *   - G93 inverse time (if G93 is active)
+ *   - time for coordinated move at requested feed rate
+ *   - time that the slowest axis would require for the move
+ *
+ * Sets the following variables in the gcode_state struct
+ *   - move_time is set to optimal time
+ *
+ * NIST RS274NGC_v3 Guidance
+ *
+ * The following is verbatim text from NIST RS274NGC_v3. As I
+ * interpret A for moves that combine both linear and rotational
+ * movement, the feed rate should apply to the XYZ movement, with
+ * the rotational axis (or axes) timed to start and end at the same
+ * time the linear move is performed. It is possible under this
+ * case for the rotational move to rate-limit the linear move.
+ *
+ *  2.1.2.5 Feed Rate
+ *
+ * The rate at which the controlled point or the axes move is
+ * nominally a steady rate which may be set by the user. In the
+ * Interpreter, the interpretation of the feed rate is as follows
+ * unless inverse time feed rate mode is being used in the
+ * RS274/NGC view (see Section 3.5.19). The machining
+ * functions view of feed rate, as described in Section 4.3.5.1,
+ * has conditions under which the set feed rate is applied
+ * differently, but none of these is used in the Interpreter.
+ *
+ * A.  For motion involving one or more of the X, Y, and Z axes
+ *     (with or without simultaneous rotational axis motion), the
+ *     feed rate means length units per minute along the programmed
+ *     XYZ path, as if the rotational axes were not moving.
+ *
+ * B.  For motion of one rotational axis with X, Y, and Z axes not
+ *     moving, the feed rate means degrees per minute rotation of
+ *     the rotational axis.
+ *
+ * C.  For motion of two or three rotational axes with X, Y, and Z
+ *     axes not moving, the rate is applied as follows. Let dA, dB,
+ *     and dC be the angles in degrees through which the A, B, and
+ *     C axes, respectively, must move.  Let D = sqrt(dA^2 + dB^2 +
+ *     dC^2). Conceptually, D is a measure of total angular motion,
+ *     using the usual Euclidean metric. Let T be the amount of
+ *     time required to move through D degrees at the current feed
+ *     rate in degrees per minute. The rotational axes should be
+ *     moved in coordinated linear motion so that the elapsed time
+ *     from the start to the end of the motion is T plus any time
+ *     required for acceleration or deceleration.
+ */
+void cm_calc_move_time(const float axis_length[], const float axis_square[]) {
+  float inv_time = 0;           // inverse time if doing a feed in G93 mode
+  float xyz_time = 0;           // linear coordinated move at requested feed
+  float abc_time = 0;           // rotary coordinated move at requested feed
+  float max_time = 0;           // time required for the rate-limiting axis
+  float tmp_time = 0;           // used in computation
+
+  // compute times for feed motion
+  if (cm.gm.motion_mode != MOTION_MODE_STRAIGHT_TRAVERSE) {
+    if (cm.gm.feed_rate_mode == INVERSE_TIME_MODE) {
+      // feed rate was un-inverted to minutes by cm_set_feed_rate()
+      inv_time = cm.gm.feed_rate;
+      cm.gm.feed_rate_mode = UNITS_PER_MINUTE_MODE;
+
+    } else {
+      // compute length of linear move in millimeters. Feed rate is provided as
+      // mm/min
+      xyz_time = sqrt(axis_square[AXIS_X] + axis_square[AXIS_Y] +
+                      axis_square[AXIS_Z]) / cm.gm.feed_rate;
+
+      // if no linear axes, compute length of multi-axis rotary move in degrees.
+      // Feed rate is provided as degrees/min
+      if (fp_ZERO(xyz_time))
+        abc_time = sqrt(axis_square[AXIS_A] + axis_square[AXIS_B] +
+                        axis_square[AXIS_C]) / cm.gm.feed_rate;
+    }
+  }
+
+  for (uint8_t axis = 0; axis < AXES; axis++) {
+    if (cm.gm.motion_mode == MOTION_MODE_STRAIGHT_TRAVERSE)
+      tmp_time = fabs(axis_length[axis]) / cm.a[axis].velocity_max;
+
+    else // MOTION_MODE_STRAIGHT_FEED
+      tmp_time = fabs(axis_length[axis]) / cm.a[axis].feedrate_max;
+
+    max_time = max(max_time, tmp_time);
+  }
+
+  cm.ms.move_time = max4(inv_time, max_time, xyz_time, abc_time);
+}
+
+
+/// Set endpoint position from final runtime position
+void cm_update_model_position_from_runtime() {
+  copy_vector(cm.position, mr.ms.target);
+}
+
+
+/* Set target vector in GM model
+ *
+ * This is a core routine. It handles:
+ *    - conversion of linear units to internal canonical form (mm)
+ *    - conversion of relative mode to absolute (internal canonical form)
+ *    - translation of work coordinates to machine coordinates (internal
+ *      canonical form)
+ *    - computation and application of axis modes as so:
+ *
+ *    DISABLED  - Incoming value is ignored. Target value is not changed
+ *    ENABLED   - Convert axis values to canonical format and store as target
+ *    INHIBITED - Same processing as ENABLED, but axis will not actually be run
+ *    RADIUS    - ABC axis value is provided in Gcode block in linear units
+ *              - Target is set to degrees based on axis' Radius value
+ *              - Radius mode is only processed for ABC axes. Application to
+ *                XYZ is ignored.
+ *
+ *    Target coordinates are provided in target[]
+ *    Axes that need processing are signaled in flag[]
+ */
+
+// ESTEE: _calc_ABC is a fix to workaround a gcc compiler bug wherein it runs
+// out of spill registers we moved this block into its own function so that we
+// get a fresh stack push
+// ALDEN: This shows up in avr-gcc 4.7.0 and avr-libc 1.8.0
+static float _calc_ABC(uint8_t axis, float target[], float flag[]) {
+  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);
+}
+
+
+void cm_set_model_target(float target[], float flag[]) {
+  float tmp = 0;
+
+  // process XYZABC for lower modes
+  for (int axis = AXIS_X; axis <= AXIS_Z; axis++) {
+    if (fp_FALSE(flag[axis]) || cm.a[axis].axis_mode == AXIS_DISABLED)
+      continue; // skip axis if not flagged for update or its disabled
+
+    else if (cm.a[axis].axis_mode == AXIS_STANDARD ||
+             cm.a[axis].axis_mode == AXIS_INHIBITED) {
+      if (cm.gm.distance_mode == ABSOLUTE_MODE)
+        cm.ms.target[axis] =
+          cm_get_active_coord_offset(axis) + TO_MILLIMETERS(target[axis]);
+      else cm.ms.target[axis] += TO_MILLIMETERS(target[axis]);
+    }
+  }
+
+  // FYI: The ABC loop below relies on the XYZ loop having been run first
+  for (int axis = AXIS_A; axis <= AXIS_C; axis++) {
+    if (fp_FALSE(flag[axis]) || cm.a[axis].axis_mode == AXIS_DISABLED)
+      continue; // skip axis if not flagged for update or its disabled
+    else tmp = _calc_ABC(axis, target, flag);
+
+    if (cm.gm.distance_mode == ABSOLUTE_MODE)
+      // sacidu93's fix to Issue #22
+      cm.ms.target[axis] = tmp + cm_get_active_coord_offset(axis);
+    else cm.ms.target[axis] += tmp;
+  }
+}
+
+
+/* Return error code if soft limit is exceeded
+ *
+ * Must be called with target properly set in GM struct. Best done
+ * after cm_set_model_target().
+ *
+ * Tests for soft limit for any homed axis if min and max are
+ * different values. You can set min and max to 0,0 to disable soft
+ * limits for an axis. Also will not test a min or a max if the value
+ * is < -1000000 (negative one million). This allows a single end to
+ * be tested w/the other disabled, should that requirement ever arise.
+ */
+stat_t cm_test_soft_limits(float target[]) {
+#ifdef SOFT_LIMIT_ENABLE
+    for (int axis = 0; axis < AXES; axis++) {
+      if (!cm.homed[axis]) continue; // don't test axes that are not homed
+
+      if (fp_EQ(cm.a[axis].travel_min, cm.a[axis].travel_max)) continue;
+
+      if (cm.a[axis].travel_min > DISABLE_SOFT_LIMIT &&
+          target[axis] < cm.a[axis].travel_min)
+        return STAT_SOFT_LIMIT_EXCEEDED;
+
+      if (cm.a[axis].travel_max > DISABLE_SOFT_LIMIT &&
+          target[axis] > cm.a[axis].travel_max)
+        return STAT_SOFT_LIMIT_EXCEEDED;
+    }
+#endif
+
+  return STAT_OK;
+}
+
+
+/* machining functions
+ *    Values are passed in pre-unit_converted state (from gn structure)
+ *    All operations occur on gm (current model state)
+ *
+ * These are organized by section number (x.x.x) in the order they are
+ * found in NIST RS274 NGCv3
+ */
+
+// Initialization and Termination (4.3.2)
+
+void machine_init() {
+  // Init 1/jerk
+  for (uint8_t axis = 0; axis < AXES; axis++)
+    cm.a[axis].recip_jerk = 1 / (cm.a[axis].jerk_max * JERK_MULTIPLIER);
+
+  // Set gcode defaults
+  cm_set_units_mode(GCODE_DEFAULT_UNITS);
+  cm_set_coord_system(GCODE_DEFAULT_COORD_SYSTEM);
+  cm_set_plane(GCODE_DEFAULT_PLANE);
+  cm_set_path_control(GCODE_DEFAULT_PATH_CONTROL);
+  cm_set_distance_mode(GCODE_DEFAULT_DISTANCE_MODE);
+  cm_set_feed_rate_mode(UNITS_PER_MINUTE_MODE); // always the default
+
+  // Sub-system inits
+  cm_spindle_init();
+  coolant_init();
+}
+
+
+/// Alarm state; send an exception report and stop processing input
+stat_t cm_alarm(const char *location, stat_t code) {
+  status_message_P(location, STAT_LEVEL_ERROR, code, "ALARM");
+  estop_trigger();
+  return code;
+}
+
+
+/// Clear soft alarm
+stat_t cm_clear() {
+  if (cm.cycle_state == CYCLE_OFF)
+    cm.machine_state = MACHINE_PROGRAM_STOP;
+  else cm.machine_state = MACHINE_CYCLE;
+
+  return STAT_OK;
+}
+
+
+// Representation (4.3.3)
+//
+// Affect the Gcode model only (asynchronous)
+// These functions assume input validation occurred upstream.
+
+/// G17, G18, G19 select axis plane
+void cm_set_plane(cmPlane_t plane) {cm.gm.select_plane = plane;}
+
+
+/// G20, G21
+void cm_set_units_mode(cmUnitsMode_t mode) {cm.gm.units_mode = mode;}
+
+
+/// G90, G91
+void cm_set_distance_mode(cmDistanceMode_t mode) {cm.gm.distance_mode = mode;}
+
+
+/* G10 L2 Pn, delayed persistence
+ *
+ * This function applies the offset to the GM model. You can also
+ * use $g54x - $g59c config functions to change offsets.
+ *
+ * It also does not reset the work_offsets which may be
+ * accomplished by calling cm_set_work_offsets() immediately
+ * afterwards.
+ */
+void cm_set_coord_offsets(cmCoordSystem_t coord_system, float offset[],
+                            float flag[]) {
+  if (coord_system < G54 || coord_system > COORD_SYSTEM_MAX)
+    return; // you can't set G53
+
+  for (int axis = 0; axis < AXES; axis++)
+    if (fp_TRUE(flag[axis]))
+      cm.offset[coord_system][axis] = TO_MILLIMETERS(offset[axis]);
+}
+
+
+// Representation functions that affect gcode model and are queued to planner
+// (synchronous)
+
+/// G54-G59
+void cm_set_coord_system(cmCoordSystem_t coord_system) {
+  cm.gm.coord_system = coord_system;
+
+  // pass coordinate system in value[0] element
+  float value[AXES] = {coord_system};
+  // second vector (flags) is not used, so fake it
+  mp_queue_command(_exec_offset, value, value);
+}
+
+
+static void _exec_offset(float *value, float *flag) {
+  // coordinate system is passed in value[0] element
+  uint8_t coord_system = value[0];
+  float offsets[AXES];
+
+  for (int axis = 0; axis < AXES; axis++)
+    offsets[axis] = cm.offset[coord_system][axis] +
+      cm.origin_offset[axis] * (cm.origin_offset_enable ? 1 : 0);
+
+  mp_set_runtime_work_offset(offsets);
+  cm_set_work_offsets(); // set work offsets in the Gcode model
+}
+
+
+/* Set the position of a single axis in the model, planner and runtime
+ *
+ * This command sets an axis/axes to a position provided as an argument.
+ * This is useful for setting origins for homing, probing, and other operations.
+ *
+ *  !!!!! 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 if the runtime is moving. The system must be
+ * quiescent or you will introduce positional errors. This is true
+ * because the planned / running moves have a different reference
+ * frame than the one you are now going to set. These functions should
+ * only be called during initialization sequences and during cycles
+ * (such as homing cycles) when you know there are no more moves in
+ * the planner and that all motion has stopped.  Use
+ * cm_get_runtime_busy() to be sure the system is quiescent.
+ */
+void cm_set_position(int axis, float position) {
+  // TODO: Interlock involving runtime_busy test
+  cm.position[axis] = position;
+  cm.ms.target[axis] = position;
+  mp_set_planner_position(axis, position);
+  mp_set_runtime_position(axis, position);
+  mp_set_steps_to_runtime_position();
+}
+
+
+// G28.3 functions and support
+
+/* G28.3 - model, planner and queue to runtime
+ *
+ * Takes a vector of origins (presumably 0's, but not necessarily) and
+ * applies them to all axes where the corresponding position in the
+ * flag vector is true (1).
+ *
+ * This is a 2 step process. The model and planner contexts are set
+ * immediately, the runtime command is queued and synchronized with
+ * the planner queue. This includes the runtime position and the step
+ * recording done by the encoders. At that point any axis that is set
+ * is also marked as homed.
+ */
+void cm_set_absolute_origin(float origin[], float flag[]) {
+  float value[AXES];
+
+  for (int axis = 0; axis < AXES; axis++)
+    if (fp_TRUE(flag[axis])) {
+      value[axis] = TO_MILLIMETERS(origin[axis]);
+      cm.position[axis] = value[axis];         // set model position
+      cm.ms.target[axis] = value[axis];            // reset model target
+      mp_set_planner_position(axis, value[axis]);  // set mm position
+    }
+
+  mp_queue_command(_exec_absolute_origin, value, flag);
+}
+
+
+static void _exec_absolute_origin(float *value, float *flag) {
+  for (int axis = 0; axis < AXES; axis++)
+    if (fp_TRUE(flag[axis])) {
+      mp_set_runtime_position(axis, value[axis]);
+      cm.homed[axis] = true;    // G28.3 is not considered homed until here
+    }
+
+  mp_set_steps_to_runtime_position();
+}
+
+
+/* G92's behave according to NIST 3.5.18 & LinuxCNC G92
+ * http://linuxcnc.org/docs/html/gcode/gcode.html#sec:G92-G92.1-G92.2-G92.3
+ */
+
+/// G92
+void cm_set_origin_offsets(float offset[], float flag[]) {
+  // set offsets in the Gcode model extended context
+  cm.origin_offset_enable = true;
+  for (int axis = 0; axis < AXES; axis++)
+    if (fp_TRUE(flag[axis]))
+      cm.origin_offset[axis] = cm.position[axis] -
+        cm.offset[cm.gm.coord_system][axis] - TO_MILLIMETERS(offset[axis]);
+
+  // now pass the offset to the callback - setting the coordinate system also
+  // applies the offsets
+  // pass coordinate system in value[0] element
+  float value[AXES] = {cm.gm.coord_system};
+  mp_queue_command(_exec_offset, value, value); // second vector is not used
+}
+
+
+/// G92.1
+void cm_reset_origin_offsets() {
+  cm.origin_offset_enable = false;
+  for (int axis = 0; axis < AXES; axis++)
+    cm.origin_offset[axis] = 0;
+
+  float value[AXES] = {cm.gm.coord_system};
+  mp_queue_command(_exec_offset, value, value);
+}
+
+
+/// G92.2
+void cm_suspend_origin_offsets() {
+  cm.origin_offset_enable = false;
+  float value[AXES] = {cm.gm.coord_system};
+  mp_queue_command(_exec_offset, value, value);
+}
+
+
+/// G92.3
+void cm_resume_origin_offsets() {
+  cm.origin_offset_enable = true;
+  float value[AXES] = {cm.gm.coord_system};
+  mp_queue_command(_exec_offset, value, value);
+}
+
+
+// Free Space Motion (4.3.4)
+
+/// G0 linear rapid
+stat_t cm_straight_traverse(float target[], float flags[]) {
+  cm.gm.motion_mode = MOTION_MODE_STRAIGHT_TRAVERSE;
+  cm_set_model_target(target, flags);
+
+  // test soft limits
+  stat_t status = cm_test_soft_limits(cm.ms.target);
+  if (status != STAT_OK) return CM_ALARM(status);
+
+  // prep and plan the move
+  cm_set_work_offsets(&cm.gm); // capture fully resolved offsets to the state
+  cm_cycle_start();            // required for homing & other cycles
+  cm.ms.line = cm.gm.line;     // copy line number
+  mp_aline(&cm.ms);            // send the move to the planner
+  cm_finalize_move();
+
+  return STAT_OK;
+}
+
+
+/// G28.1
+void cm_set_g28_position() {copy_vector(cm.g28_position, cm.position);}
+
+
+/// G28
+stat_t cm_goto_g28_position(float target[], float flags[]) {
+  cm_set_absolute_override(true);
+
+  // move through intermediate point, or skip
+  cm_straight_traverse(target, flags);
+
+  // make sure you have an available buffer
+  mp_wait_for_buffer();
+
+  // execute actual stored move
+  float f[] = {1, 1, 1, 1, 1, 1};
+  return cm_straight_traverse(cm.g28_position, f);
+}
+
+
+/// G30.1
+void cm_set_g30_position() {copy_vector(cm.g30_position, cm.position);}
+
+
+/// G30
+stat_t cm_goto_g30_position(float target[], float flags[]) {
+  cm_set_absolute_override(true);
+
+  // move through intermediate point, or skip
+  cm_straight_traverse(target, flags);
+
+  // make sure you have an available buffer
+  mp_wait_for_buffer();
+
+  // execute actual stored move
+  float f[] = {1, 1, 1, 1, 1, 1};
+  return cm_straight_traverse(cm.g30_position, f);
+}
+
+
+// Machining Attributes (4.3.5)
+
+/// F parameter
+/// Normalize feed rate to mm/min or to minutes if in inverse time mode
+void cm_set_feed_rate(float feed_rate) {
+  if (cm.gm.feed_rate_mode == INVERSE_TIME_MODE)
+    // normalize to minutes (active for this gcode block only)
+    cm.gm.feed_rate = 1 / feed_rate;
+
+  else cm.gm.feed_rate = TO_MILLIMETERS(feed_rate);
+}
+
+
+/// G93, G94 See cmFeedRateMode
+void cm_set_feed_rate_mode(cmFeedRateMode_t mode) {cm.gm.feed_rate_mode = mode;}
+
+
+/// G61, G61.1, G64
+void cm_set_path_control(cmPathControlMode_t mode) {cm.gm.path_control = mode;}
+
+
+// Machining Functions (4.3.6) See arc.c
+
+/// G4, P parameter (seconds)
+stat_t cm_dwell(float seconds) {
+  return mp_dwell(seconds);
+}
+
+
+/// G1
+stat_t cm_straight_feed(float target[], float flags[]) {
+  // trap zero feed rate condition
+  if (cm.gm.feed_rate_mode != INVERSE_TIME_MODE && fp_ZERO(cm.gm.feed_rate))
+    return STAT_GCODE_FEEDRATE_NOT_SPECIFIED;
+
+  cm.gm.motion_mode = MOTION_MODE_STRAIGHT_FEED;
+  cm_set_model_target(target, flags);
+
+  // test soft limits
+  stat_t status = cm_test_soft_limits(cm.ms.target);
+  if (status != STAT_OK) return CM_ALARM(status);
+
+  // prep and plan the move
+  cm_set_work_offsets(&cm.gm); // capture the fully resolved offsets to state
+  cm_cycle_start();            // required for homing & other cycles
+  cm.ms.line = cm.gm.line;     // copy line number
+  status = mp_aline(&cm.ms);   // send the move to the planner
+  cm_finalize_move();
+
+  return status;
+}
+
+
+// Spindle Functions (4.3.7) see spindle.c, spindle.h
+
+/* Tool Functions (4.3.8)
+ *
+ * Note: These functions don't actually do anything for now, and there's a bug
+ *       where T and M in different blocks don't work correctly
+ */
+
+/// T parameter
+void cm_select_tool(uint8_t tool_select) {
+  float value[AXES] = {tool_select};
+  mp_queue_command(_exec_select_tool, value, value);
+}
+
+
+static void _exec_select_tool(float *value, float *flag) {
+  cm.gm.tool_select = value[0];
+}
+
+
+/// M6 This might become a complete tool change cycle
+void cm_change_tool(uint8_t tool_change) {
+  float value[AXES] = {cm.gm.tool_select};
+  mp_queue_command(_exec_change_tool, value, value);
+}
+
+
+static void _exec_change_tool(float *value, float *flag) {
+  cm.gm.tool = (uint8_t)value[0];
+}
+
+
+// Miscellaneous Functions (4.3.9)
+/// M7
+void cm_mist_coolant_control(bool mist_coolant) {
+  float value[AXES] = {mist_coolant};
+  mp_queue_command(_exec_mist_coolant_control, value, value);
+}
+
+
+static void _exec_mist_coolant_control(float *value, float *flag) {
+  coolant_set_mist(cm.gm.mist_coolant = value[0]);
+}
+
+
+/// M8, M9
+void cm_flood_coolant_control(bool flood_coolant) {
+  float value[AXES] = {flood_coolant};
+  mp_queue_command(_exec_flood_coolant_control, value, value);
+}
+
+
+static void _exec_flood_coolant_control(float *value, float *flag) {
+  cm.gm.flood_coolant = value[0];
+
+  coolant_set_flood(value[0]);
+  if (!value[0]) coolant_set_mist(false); // M9 special function
+}
+
+
+/* Override enables are kind of a mess in Gcode. This is an attempt to sort
+ * them out.  See
+ * http://www.linuxcnc.org/docs/2.4/html/gcode_main.html#sec:M50:-Feed-Override
+ */
+
+/// M48, M49
+void cm_override_enables(bool flag) {
+  cm.gm.feed_rate_override_enable = flag;
+  cm.gm.traverse_override_enable = flag;
+  cm.gm.spindle_override_enable = flag;
+}
+
+
+/// M50
+void cm_feed_rate_override_enable(bool flag) {
+  if (fp_TRUE(cm.gf.parameter) && fp_ZERO(cm.gn.parameter))
+    cm.gm.feed_rate_override_enable = false;
+  else cm.gm.feed_rate_override_enable = true;
+}
+
+
+/// M50.1
+void cm_feed_rate_override_factor(bool flag) {
+  cm.gm.feed_rate_override_enable = flag;
+  cm.gm.feed_rate_override_factor = cm.gn.parameter;
+}
+
+
+/// M50.2
+void cm_traverse_override_enable(bool flag) {
+  if (fp_TRUE(cm.gf.parameter) && fp_ZERO(cm.gn.parameter))
+    cm.gm.traverse_override_enable = false;
+  else cm.gm.traverse_override_enable = true;
+}
+
+
+/// M51
+void cm_traverse_override_factor(bool flag) {
+  cm.gm.traverse_override_enable = flag;
+  cm.gm.traverse_override_factor = cm.gn.parameter;
+}
+
+
+/// M51.1
+void cm_spindle_override_enable(bool flag) {
+  if (fp_TRUE(cm.gf.parameter) && fp_ZERO(cm.gn.parameter))
+    cm.gm.spindle_override_enable = false;
+  else cm.gm.spindle_override_enable = true;
+}
+
+
+/// M50.1
+void cm_spindle_override_factor(bool flag) {
+  cm.gm.spindle_override_enable = flag;
+  cm.gm.spindle_override_factor = cm.gn.parameter;
+}
+
+
+void cm_message(const char *message) {
+  status_message_P(0, STAT_LEVEL_INFO, STAT_OK, PSTR("%s"), message);
+}
+
+
+/* Program Functions (4.3.10)
+ *
+ * This group implements stop, start, end, and hold.
+ * It is extended beyond the NIST spec to handle various situations.
+ *
+ * cm_program_stop and cm_optional_program_stop are synchronous Gcode
+ * commands that are received through the interpreter. They cause all motion
+ * to stop at the end of the current command, including spindle motion.
+ *
+ * Note that the stop occurs at the end of the immediately preceding command
+ * (i.e. the stop is queued behind the last command).
+ *
+ * cm_program_end is a stop that also resets the machine to initial state
+ */
+
+/* Feedholds, queue flushes and cycles starts are all related. The request
+ * functions set flags for these. The sequencing callback interprets the flags
+ * according to the following rules:
+ *
+ *   A feedhold request received during motion should be honored
+ *   A feedhold request received during a feedhold should be ignored and reset
+ *   A feedhold request received during a motion stop should be ignored and
+ *     reset
+ *
+ *   A queue flush request received during motion should be ignored but not
+ *     reset
+ *   A queue flush request received during a feedhold should be deferred until
+ *     the feedhold enters a HOLD state (i.e. until deceleration is complete)
+ *   A queue flush request received during a motion stop should be honored
+ *
+ *   A cycle start request received during motion should be ignored and reset
+ *   A cycle start request received during a feedhold should be deferred until
+ *     the feedhold enters a HOLD state (i.e. until deceleration is complete)
+ *     If a queue flush request is also present the queue flush should be done
+ *     first
+ *   A cycle start request received during a motion stop should be honored and
+ *     should start to run anything in the planner queue
+ */
+
+
+/// Initiate a feedhold right now
+void cm_request_feedhold() {cm.feedhold_requested = true;}
+void cm_request_queue_flush() {cm.queue_flush_requested = true;}
+void cm_request_cycle_start() {cm.cycle_start_requested = true;}
+
+
+/// Process feedholds, cycle starts & queue flushes
+void cm_feedhold_callback() {
+  if (cm.feedhold_requested) {
+    if (cm.motion_state == MOTION_RUN && cm.hold_state == FEEDHOLD_OFF) {
+      cm_set_motion_state(MOTION_HOLD);
+      cm.hold_state = FEEDHOLD_SYNC;     // invokes hold from aline execution
+    }
+
+    cm.feedhold_requested = false;
+  }
+
+  if (cm.queue_flush_requested) {
+    if ((cm.motion_state == MOTION_STOP ||
+         (cm.motion_state == MOTION_HOLD && cm.hold_state == FEEDHOLD_HOLD)) &&
+        !cm_get_runtime_busy()) {
+      cm.queue_flush_requested = false;
+      cm_queue_flush();
+    }
+  }
+
+  bool processing =
+    cm.hold_state == FEEDHOLD_SYNC ||
+    cm.hold_state == FEEDHOLD_PLAN ||
+    cm.hold_state == FEEDHOLD_DECEL;
+
+  if (cm.cycle_start_requested && !cm.queue_flush_requested && !processing) {
+    cm.cycle_start_requested = false;
+    cm.hold_state = FEEDHOLD_END_HOLD;
+    cm_cycle_start();
+    mp_end_hold();
+  }
+
+  mp_plan_hold_callback();
+}
+
+
+stat_t cm_queue_flush() {
+  if (cm_get_runtime_busy()) return STAT_COMMAND_NOT_ACCEPTED;
+
+  mp_flush_planner();                      // flush planner queue
+
+  // Note: The following uses low-level mp calls for absolute position.
+  for (int axis = 0; axis < AXES; axis++)
+    // set mm from mr
+    cm_set_position(axis, mp_get_runtime_absolute_position(axis));
+
+  float value[AXES] = {MACHINE_PROGRAM_STOP};
+  _exec_program_finalize(value, value);    // finalize now, not later
+
+  return STAT_OK;
+}
+
+
+/* Program and cycle state functions
+ *
+ * cm_program_end() implements M2 and M30
+ * The END behaviors are defined by NIST 3.6.1 are:
+ *    1. Axis offsets are set to zero (like G92.2) and origin offsets are set
+ *       to the default (like G54)
+ *    2. Selected plane is set to PLANE_XY (like G17)
+ *    3. Distance mode is set to MODE_ABSOLUTE (like G90)
+ *    4. Feed rate mode is set to UNITS_PER_MINUTE (like G94)
+ *    5. Feed and speed overrides are set to ON (like M48)
+ *    6. Cutter compensation is turned off (like G40)
+ *    7. The spindle is stopped (like M5)
+ *    8. The current motion mode is set to G_1 (like G1)
+ *    9. Coolant is turned off (like M9)
+ *
+ * cm_program_end() implments things slightly differently:
+ *    1. Axis offsets are set to G92.1 CANCEL offsets
+ *       (instead of using G92.2 SUSPEND Offsets)
+ *       Set default coordinate system (uses $gco, not G54)
+ *    2. Selected plane is set to default plane ($gpl)
+ *       (instead of setting it to G54)
+ *    3. Distance mode is set to MODE_ABSOLUTE (like G90)
+ *    4. Feed rate mode is set to UNITS_PER_MINUTE (like G94)
+ *    5. Not implemented
+ *    6. Not implemented
+ *    7. The spindle is stopped (like M5)
+ *    8. Motion mode is canceled like G80 (not set to G1)
+ *    9. Coolant is turned off (like M9)
+ *    +  Default INCHES or MM units mode is restored ($gun)
+ */
+static void _exec_program_finalize(float *value, float *flag) {
+  cm.machine_state = (uint8_t)value[0];
+  cm_set_motion_state(MOTION_STOP);
+  if (cm.cycle_state == CYCLE_MACHINING)
+    cm.cycle_state = CYCLE_OFF;     // don't end cycle if homing, probing, etc.
+
+  cm.hold_state = FEEDHOLD_OFF;     // end feedhold (if in feed hold)
+  cm.cycle_start_requested = false; // cancel any pending cycle start request
+  mp_zero_segment_velocity();       // for reporting purposes
+
+  // perform the following resets if it's a program END
+  if (cm.machine_state == MACHINE_PROGRAM_END) {
+    cm_reset_origin_offsets();           // G92.1 - we do G91.1 instead of G92.2
+    cm_set_coord_system(GCODE_DEFAULT_COORD_SYSTEM);
+    cm_set_plane(GCODE_DEFAULT_PLANE);
+    cm_set_distance_mode(GCODE_DEFAULT_DISTANCE_MODE);
+    cm_spindle_control(SPINDLE_OFF);                 // M5
+    cm_flood_coolant_control(false);                 // M9
+    cm_set_feed_rate_mode(UNITS_PER_MINUTE_MODE);    // G94
+    cm_set_motion_mode(MOTION_MODE_CANCEL_MOTION_MODE);
+  }
+}
+
+
+/// Do a cycle start right now
+void cm_cycle_start() {
+  cm.machine_state = MACHINE_CYCLE;
+
+  // don't (re)start homing, probe or other canned cycles
+  if (cm.cycle_state == CYCLE_OFF) cm.cycle_state = CYCLE_MACHINING;
+}
+
+
+/// Do a cycle end right now
+void cm_cycle_end() {
+  if (cm.cycle_state != CYCLE_OFF) {
+    float value[AXES] = {MACHINE_PROGRAM_STOP};
+    _exec_program_finalize(value, value);
+  }
+}
+
+
+
+/// M0  Queue a program stop
+void cm_program_stop() {
+  float value[AXES] = {MACHINE_PROGRAM_STOP};
+  mp_queue_command(_exec_program_finalize, value, value);
+}
+
+
+/// M1
+void cm_optional_program_stop() {
+  float value[AXES] = {MACHINE_PROGRAM_STOP};
+  mp_queue_command(_exec_program_finalize, value, value);
+}
+
+
+/// M2, M30
+void cm_program_end() {
+  float value[AXES] = {MACHINE_PROGRAM_END};
+  mp_queue_command(_exec_program_finalize, value, value);
+}
+
+
+/// return ASCII char for axis given the axis number
+char cm_get_axis_char(int8_t axis) {
+  char axis_char[] = "XYZABC";
+  if (axis < 0 || axis > AXES) return ' ';
+  return axis_char[axis];
+}
diff --git a/src/machine.h b/src/machine.h
new file mode 100644 (file)
index 0000000..300c942
--- /dev/null
@@ -0,0 +1,568 @@
+/******************************************************************************\
+
+                This file is part of the Buildbotics firmware.
+
+                  Copyright (c) 2015 - 2016 Buildbotics LLC
+                  Copyright (c) 2010 - 2015 Alden S. Hart, Jr.
+                            All rights reserved.
+
+     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 <http://www.gnu.org/licenses/>.
+
+     The software is distributed in the hope that it will be useful, but
+          WITHOUT ANY WARRANTY; without even the implied warranty of
+      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+               Lesser General Public License for more details.
+
+       You should have received a copy of the GNU Lesser General Public
+                License along with the software.  If not, see
+                       <http://www.gnu.org/licenses/>.
+
+                For information regarding this software email:
+                  "Joseph Coffland" <joseph@buildbotics.com>
+
+\******************************************************************************/
+
+#pragma once
+
+
+#include "config.h"
+#include "status.h"
+
+#include <stdint.h>
+#include <stdbool.h>
+
+
+#define TO_MILLIMETERS(a) (cm.gm.units_mode == INCHES ? (a) * MM_PER_INCH : a)
+
+#define DISABLE_SOFT_LIMIT -1000000
+
+
+
+/* Machine state model
+ *
+ * The following main variables track machine state and state
+ * transitions.
+ *        - cm.machine_state    - overall state of machine and program execution
+ *        - cm.cycle_state    - what cycle the machine is executing (or none)
+ *        - cm.motion_state    - state of movement
+ *
+ * Allowed states and combined states:
+ *
+ *   MACHINE STATE     CYCLE STATE   MOTION_STATE    COMBINED_STATE (FYI)
+ *   -------------     ------------  -------------   --------------------
+ *   MACHINE_UNINIT    na            na              (U)
+ *   MACHINE_READY     CYCLE_OFF     MOTION_STOP     (ROS) RESET-OFF-STOP
+ *   MACHINE_PROG_STOP CYCLE_OFF     MOTION_STOP     (SOS) STOP-OFF-STOP
+ *   MACHINE_PROG_END  CYCLE_OFF     MOTION_STOP     (EOS) END-OFF-STOP
+ *
+ *   MACHINE_CYCLE     CYCLE_STARTED MOTION_STOP     (CSS) CYCLE-START-STOP
+ *   MACHINE_CYCLE     CYCLE_STARTED MOTION_RUN      (CSR) CYCLE-START-RUN
+ *   MACHINE_CYCLE     CYCLE_STARTED MOTION_HOLD     (CSH) CYCLE-START-HOLD
+ *   MACHINE_CYCLE     CYCLE_STARTED MOTION_END_HOLD (CSE) CYCLE-START-END_HOLD
+ *
+ *   MACHINE_CYCLE     CYCLE_HOMING  MOTION_STOP     (CHS) CYCLE-HOMING-STOP
+ *   MACHINE_CYCLE     CYCLE_HOMING  MOTION_RUN      (CHR) CYCLE-HOMING-RUN
+ *   MACHINE_CYCLE     CYCLE_HOMING  MOTION_HOLD     (CHH) CYCLE-HOMING-HOLD
+ *   MACHINE_CYCLE     CYCLE_HOMING  MOTION_END_HOLD (CHE) CYCLE-HOMING-END_HOLD
+ */
+
+/// check alignment with messages in config.c / msg_stat strings
+typedef enum {
+  COMBINED_INITIALIZING, // machine is initializing
+  COMBINED_READY,        // machine is ready for use. Also null move STOP state
+  COMBINED_ALARM,        // machine in soft alarm state
+  COMBINED_PROGRAM_STOP, // program stop or no more blocks
+  COMBINED_PROGRAM_END,  // program end
+  COMBINED_RUN,          // motion is running
+  COMBINED_HOLD,         // motion is holding
+  COMBINED_PROBE,        // probe cycle active
+  COMBINED_CYCLE,        // machine is running (cycling)
+  COMBINED_HOMING,       // homing is treated as a cycle
+  COMBINED_SHUTDOWN,     // machine in hard alarm state (shutdown)
+} cmCombinedState_t;
+
+
+typedef enum {
+  MACHINE_INITIALIZING, // machine is initializing
+  MACHINE_READY,        // machine is ready for use
+  MACHINE_ALARM,        // machine in soft alarm state
+  MACHINE_PROGRAM_STOP, // program stop or no more blocks
+  MACHINE_PROGRAM_END,  // program end
+  MACHINE_CYCLE,        // machine is running (cycling)
+  MACHINE_SHUTDOWN,     // machine in hard alarm state (shutdown)
+} cmMachineState_t;
+
+
+typedef enum {
+  CYCLE_OFF,            // machine is idle
+  CYCLE_MACHINING,      // in normal machining cycle
+  CYCLE_PROBE,          // in probe cycle
+  CYCLE_HOMING,         // homing is treated as a specialized cycle
+} cmCycleState_t;
+
+
+typedef enum {
+  MOTION_STOP,          // motion has stopped
+  MOTION_RUN,           // machine is in motion
+  MOTION_HOLD           // feedhold in progress
+} cmMotionState_t;
+
+
+typedef enum {          // feedhold_state machine
+  FEEDHOLD_OFF,         // no feedhold in effect
+  FEEDHOLD_SYNC,        // start hold - sync to latest aline segment
+  FEEDHOLD_PLAN,        // replan blocks for feedhold
+  FEEDHOLD_DECEL,       // decelerate to hold point
+  FEEDHOLD_HOLD,        // holding
+  FEEDHOLD_END_HOLD     // end hold (transient state to OFF)
+} cmFeedholdState_t;
+
+
+typedef enum {          // applies to cm.homing_state
+  HOMING_NOT_HOMED,     // machine is not homed (0=false)
+  HOMING_HOMED,         // machine is homed (1=true)
+  HOMING_WAITING        // machine waiting to be homed
+} cmHomingState_t;
+
+
+typedef enum {          // applies to cm.probe_state
+  PROBE_FAILED,         // probe reached endpoint without triggering
+  PROBE_SUCCEEDED,      // probe was triggered, cm.probe_results has position
+  PROBE_WAITING,        // probe is waiting to be started
+} cmProbeState_t;
+
+
+/* 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)
+ */
+
+/// these are in order to optimized CASE statement
+typedef enum {
+  NEXT_ACTION_DEFAULT,                // Must be zero (invokes motion modes)
+  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 no coord setting
+  NEXT_ACTION_SET_G28_POSITION,       // G28.1 set position in abs coordinates
+  NEXT_ACTION_GOTO_G28_POSITION,      // G28 go to machine position
+  NEXT_ACTION_SET_G30_POSITION,       // G30.1
+  NEXT_ACTION_GOTO_G30_POSITION,      // G30
+  NEXT_ACTION_SET_COORD_DATA,         // G10
+  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_DWELL,                  // G4
+  NEXT_ACTION_STRAIGHT_PROBE          // G38.2
+} cmNextAction_t;
+
+
+typedef enum {                        // G Modal Group 1
+  MOTION_MODE_STRAIGHT_TRAVERSE,      // G0 - straight traverse
+  MOTION_MODE_STRAIGHT_FEED,          // G1 - straight feed
+  MOTION_MODE_CW_ARC,                 // G2 - clockwise arc feed
+  MOTION_MODE_CCW_ARC,                // G3 - counter-clockwise arc feed
+  MOTION_MODE_CANCEL_MOTION_MODE,     // G80
+  MOTION_MODE_STRAIGHT_PROBE,         // G38.2
+  MOTION_MODE_CANNED_CYCLE_81,        // G81 - drilling
+  MOTION_MODE_CANNED_CYCLE_82,        // G82 - drilling with dwell
+  MOTION_MODE_CANNED_CYCLE_83,        // G83 - peck drilling
+  MOTION_MODE_CANNED_CYCLE_84,        // G84 - right hand tapping
+  MOTION_MODE_CANNED_CYCLE_85,        // G85 - boring, no dwell, feed out
+  MOTION_MODE_CANNED_CYCLE_86,        // G86 - boring, spindle stop, rapid out
+  MOTION_MODE_CANNED_CYCLE_87,        // G87 - back boring
+  MOTION_MODE_CANNED_CYCLE_88,        // G88 - boring, spindle stop, manual out
+  MOTION_MODE_CANNED_CYCLE_89,        // G89 - boring, dwell, feed out
+} cmMotionMode_t;
+
+
+typedef enum {   // Used for detecting gcode errors. See NIST section 3.4
+  MODAL_GROUP_G0,     // {G10,G28,G28.1,G92}       non-modal axis commands
+  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
+  MODAL_GROUP_M9,     // {M48,M49}                 speed/feed override switches
+} cmModalGroup_t;
+
+#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 { // plane - translates to:
+  //                          axis_0    axis_1    axis_2
+  PLANE_XY,     // G17    X          Y          Z
+  PLANE_XZ,     // G18    X          Z          Y
+  PLANE_YZ      // G19    Y          Z          X
+} cmPlane_t;
+
+
+typedef enum {
+  INCHES,        // G20
+  MILLIMETERS,   // G21
+  DEGREES        // ABC axes (this value used for displays only)
+} cmUnitsMode_t;
+
+
+typedef enum {
+  ABSOLUTE_COORDS,                // machine coordinate system
+  G54,                            // G54 coordinate system
+  G55,                            // G55 coordinate system
+  G56,                            // G56 coordinate system
+  G57,                            // G57 coordinate system
+  G58,                            // G58 coordinate system
+  G59                             // G59 coordinate system
+} cmCoordSystem_t;
+
+#define COORD_SYSTEM_MAX G59      // set this manually to the last one
+
+/// G Modal Group 13
+typedef enum {
+  /// G61 - hits corners but does not stop if it does not need to.
+  PATH_EXACT_PATH,
+  PATH_EXACT_STOP,                // G61.1 - stops at all corners
+  PATH_CONTINUOUS                 // G64 and typically the default mode
+} cmPathControlMode_t;
+
+
+typedef enum {
+  ABSOLUTE_MODE,                  // G90
+  INCREMENTAL_MODE                // G91
+} cmDistanceMode_t;
+
+
+typedef enum {
+  INVERSE_TIME_MODE,              // G93
+  UNITS_PER_MINUTE_MODE,          // G94
+  UNITS_PER_REVOLUTION_MODE       // G95 (unimplemented)
+} cmFeedRateMode_t;
+
+
+typedef enum {
+  ORIGIN_OFFSET_SET,      // G92 - set origin offsets
+  ORIGIN_OFFSET_CANCEL,   // G92.1 - zero out origin offsets
+  ORIGIN_OFFSET_SUSPEND,  // G92.2 - do not apply offsets, but preserve values
+  ORIGIN_OFFSET_RESUME    // G92.3 - resume application of the suspended offsets
+} cmOriginOffset_t;
+
+
+typedef enum {
+  PROGRAM_STOP,
+  PROGRAM_END
+} cmProgramFlow_t;
+
+
+/// spindle state settings
+typedef enum {
+  SPINDLE_OFF,
+  SPINDLE_CW,
+  SPINDLE_CCW
+} cmSpindleMode_t;
+
+
+/// mist and flood coolant states
+typedef enum {
+  COOLANT_OFF,        // all coolant off
+  COOLANT_ON,         // request coolant on or indicate both coolants are on
+  COOLANT_MIST,       // indicates mist coolant on
+  COOLANT_FLOOD       // indicates flood coolant on
+} cmCoolantState_t;
+
+
+/// used for spindle and arc dir
+typedef enum {
+  DIRECTION_CW,
+  DIRECTION_CCW
+} cmDirection_t;
+
+
+/// axis modes (ordered: see _cm_get_feed_time())
+typedef enum {
+  AXIS_DISABLED,              // kill axis
+  AXIS_STANDARD,              // axis in coordinated motion w/standard behaviors
+  AXIS_INHIBITED,             // axis is computed but not activated
+  AXIS_RADIUS,                // rotary axis calibrated to circumference
+  AXIS_MODE_MAX
+} cmAxisMode_t; // ordering must be preserved.
+
+
+/* Gcode model - The following GCodeModel/GCodeInput structs are used:
+ *
+ * - gm is the core Gcode model state. It keeps the internal gcode
+ *     state model in normalized, canonical form. All values are unit
+ *     converted (to mm) and in the machine coordinate system
+ *     (absolute coordinate system). Gm is owned by the machine layer and
+ *     should be accessed only through cm_ routines.
+ *
+ * - gn is used by the gcode interpreter and is re-initialized for
+ *     each gcode block.It accepts data in the new gcode block in the
+ *     formats present in the block (pre-normalized forms). During
+ *     initialization some state elements are necessarily restored
+ *     from gm.
+ *
+ * - gf is used by the gcode parser interpreter to hold flags for any
+ *     data that has changed in gn during the parse. cm.gf.target[]
+ *     values are also used by the machine during
+ *     set_target().
+ */
+
+
+typedef struct {
+  int32_t line;              // gcode block line number
+  float target[AXES];        // XYZABC where the move should go
+  float work_offset[AXES];   // offset from work coordinate system
+  float move_time;           // optimal time for move given axis constraints
+} MoveState_t;
+
+
+/// Gcode model state
+typedef struct GCodeState {
+  uint32_t line;                      // Gcode block line number
+
+  uint8_t tool;                       // Tool after T and M6
+  uint8_t tool_select;                // T - sets this value
+
+  float feed_rate;                    // F - in mm/min or inverse time mode
+  cmFeedRateMode_t feed_rate_mode;
+  float feed_rate_override_factor;    // 1.0000 x F feed rate.
+  bool feed_rate_override_enable;     // M48, M49
+  float traverse_override_factor;     // 1.0000 x traverse rate
+  bool traverse_override_enable;
+
+  float spindle_speed;                // in RPM
+  cmSpindleMode_t spindle_mode;
+  float spindle_override_factor;      // 1.0000 x S spindle speed
+  bool spindle_override_enable;       // true = override enabled
+
+  cmMotionMode_t motion_mode;         // Group 1 modal motion
+  cmPlane_t select_plane;    // G17, G18, G19
+  cmUnitsMode_t units_mode;           // G20, G21
+  cmCoordSystem_t coord_system;       // G54-G59 - select coordinate system 1-9
+  bool absolute_override;             // G53 true = move in machine coordinates
+  cmPathControlMode_t path_control;   // G61
+  cmDistanceMode_t distance_mode;     // G91
+  cmDistanceMode_t arc_distance_mode; // G91.1
+
+  bool mist_coolant;                  // true = mist on (M7), false = off (M9)
+  bool flood_coolant;                 // true = flood on (M8), false = off (M9)
+
+  cmNextAction_t next_action;         // handles G group 1 moves & non-modals
+  cmProgramFlow_t program_flow;       // used only by the gcode_parser
+
+  // unimplemented gcode parameters
+  // float cutter_radius;           // D - cutter radius compensation (0 is off)
+  // float cutter_length;           // H - cutter length compensation (0 is off)
+
+  // Used for input only
+  float target[AXES];                 // XYZABC where the move should go
+  bool override_enables;              // feed and spindle enable
+  bool tool_change;                   // M6 tool change flag
+
+  float parameter;                    // P - dwell time in sec, G10 coord select
+
+  float arc_radius;                   // R - radius value in arc radius mode
+  float arc_offset[3];                // IJK - used by arc commands
+} GCodeState_t;
+
+
+typedef struct cmAxis {
+  cmAxisMode_t axis_mode;
+  float feedrate_max;    // max velocity in mm/min or deg/min
+  float velocity_max;    // max velocity in mm/min or deg/min
+  float travel_max;      // max work envelope for soft limits
+  float travel_min;      // min work envelope for soft limits
+  float jerk_max;        // max jerk (Jm) in mm/min^3 divided by 1 million
+  float jerk_homing;     // homing jerk (Jh) in mm/min^3 divided by 1 million
+  float recip_jerk;      // reciprocal of current jerk value - with million
+  float junction_dev;    // aka cornering delta
+  float radius;          // radius in mm for rotary axis modes
+  float search_velocity; // homing search velocity
+  float latch_velocity;  // homing latch velocity
+  float latch_backoff;   // backoff from switches prior to homing latch movement
+  float zero_backoff;    // backoff from switches for machine zero
+} AxisConfig_t;
+
+
+typedef struct cmSingleton {          // struct to manage cm globals and cycles
+  // coordinate systems and offsets absolute (G53) + G54,G55,G56,G57,G58,G59
+  float offset[COORDS + 1][AXES];
+  float origin_offset[AXES];          // G92 offsets
+  bool origin_offset_enable;          // G92 offsets enabled/disabled
+
+  float position[AXES];               // model position (not used in gn or gf)
+  float g28_position[AXES];           // stored machine position for G28
+  float g30_position[AXES];           // stored machine position for G30
+
+  // settings for axes X,Y,Z,A B,C
+  AxisConfig_t a[AXES];
+
+  cmCombinedState_t combined_state;    // combination of states for display
+  cmMachineState_t machine_state;
+  cmCycleState_t cycle_state;
+  cmMotionState_t motion_state;
+  cmFeedholdState_t hold_state;        // hold: feedhold sub-state machine
+  cmHomingState_t homing_state;        // home: homing cycle sub-state machine
+  bool homed[AXES];                    // individual axis homing flags
+
+  cmProbeState_t probe_state;
+  float probe_results[AXES];           // probing results
+
+  bool feedhold_requested;             // feedhold character received
+  bool queue_flush_requested;          // queue flush character received
+  bool cycle_start_requested;          // cycle start character received
+
+  // Model states
+  MoveState_t ms;
+  GCodeState_t gm;                     // core gcode model state
+  GCodeState_t gn;                     // gcode input values
+  GCodeState_t gf;                     // gcode input flags
+} cmSingleton_t;
+
+
+extern cmSingleton_t cm;               // machine controller singleton
+
+
+// Model state getters and setters
+uint32_t cm_get_line();
+cmCombinedState_t cm_get_combined_state();
+cmMachineState_t cm_get_machine_state();
+cmCycleState_t cm_get_cycle_state();
+cmMotionState_t cm_get_motion_state();
+cmFeedholdState_t cm_get_hold_state();
+cmHomingState_t cm_get_homing_state();
+cmMotionMode_t cm_get_motion_mode();
+cmCoordSystem_t cm_get_coord_system();
+cmUnitsMode_t cm_get_units_mode();
+cmPlane_t cm_get_select_plane();
+cmPathControlMode_t cm_get_path_control();
+cmDistanceMode_t cm_get_distance_mode();
+cmFeedRateMode_t cm_get_feed_rate_mode();
+uint8_t cm_get_tool();
+cmSpindleMode_t cm_get_spindle_mode();
+bool cm_get_runtime_busy();
+float cm_get_feed_rate();
+
+void cm_set_machine_state(cmMachineState_t machine_state);
+void cm_set_motion_state(cmMotionState_t motion_state);
+void cm_set_motion_mode(cmMotionMode_t motion_mode);
+void cm_set_spindle_mode(cmSpindleMode_t spindle_mode);
+void cm_set_spindle_speed_parameter(float speed);
+void cm_set_tool_number(uint8_t tool);
+void cm_set_absolute_override(bool absolute_override);
+void cm_set_model_line(uint32_t line);
+
+float cm_get_axis_jerk(uint8_t axis);
+void cm_set_axis_jerk(uint8_t axis, float jerk);
+
+// Coordinate systems and offsets
+float cm_get_active_coord_offset(uint8_t axis);
+float cm_get_work_offset(uint8_t axis);
+void cm_set_work_offsets();
+float cm_get_absolute_position(uint8_t axis);
+float cm_get_work_position(uint8_t axis);
+
+// Critical helpers
+void cm_calc_move_time(const float axis_length[], const float axis_square[]);
+void cm_update_model_position_from_runtime();
+void cm_finalize_move();
+stat_t cm_deferred_write_callback();
+void cm_set_model_target(float target[], float flag[]);
+stat_t cm_test_soft_limits(float target[]);
+
+// machining functions defined by NIST [organized by NIST Gcode doc]
+
+// Initialization and termination (4.3.2)
+void machine_init();
+/// enter alarm state. returns same status code
+stat_t cm_alarm(const char *location, stat_t status);
+stat_t cm_clear();
+
+#define CM_ALARM(CODE) cm_alarm(STATUS_LOCATION, CODE)
+
+// Representation (4.3.3)
+void cm_set_plane(cmPlane_t plane);
+void cm_set_units_mode(cmUnitsMode_t mode);
+void cm_set_distance_mode(cmDistanceMode_t mode);
+void cm_set_coord_offsets(cmCoordSystem_t coord_system, float offset[],
+                          float flag[]);
+
+void cm_set_position(int axis, float position);
+void cm_set_absolute_origin(float origin[], float flag[]);
+
+void cm_set_coord_system(cmCoordSystem_t coord_system);
+void cm_set_origin_offsets(float offset[], float flag[]);
+void cm_reset_origin_offsets();
+void cm_suspend_origin_offsets();
+void cm_resume_origin_offsets();
+
+// Free Space Motion (4.3.4)
+stat_t cm_straight_traverse(float target[], float flags[]);
+void cm_set_g28_position();
+stat_t cm_goto_g28_position(float target[], float flags[]);
+void cm_set_g30_position();
+stat_t cm_goto_g30_position(float target[], float flags[]);
+
+// Machining Attributes (4.3.5)
+void cm_set_feed_rate(float feed_rate);
+void cm_set_feed_rate_mode(cmFeedRateMode_t mode);
+void cm_set_path_control(cmPathControlMode_t mode);
+
+// Machining Functions (4.3.6)
+stat_t cm_straight_feed(float target[], float flags[]);
+stat_t cm_arc_feed(float target[], float flags[],
+                   float i, float j, float k,
+                   float radius, uint8_t motion_mode);
+stat_t cm_dwell(float seconds);
+
+// Spindle Functions (4.3.7) see spindle.h
+
+// Tool Functions (4.3.8)
+void cm_select_tool(uint8_t tool);
+void cm_change_tool(uint8_t tool);
+
+// Miscellaneous Functions (4.3.9)
+void cm_mist_coolant_control(bool mist_coolant);
+void cm_flood_coolant_control(bool flood_coolant);
+
+void cm_override_enables(bool flag);
+void cm_feed_rate_override_enable(bool flag);
+void cm_feed_rate_override_factor(bool flag);
+void cm_traverse_override_enable(bool flag);
+void cm_traverse_override_factor(bool flag);
+void cm_spindle_override_enable(bool flag);
+void cm_spindle_override_factor(bool flag);
+
+void cm_message(const char *message);
+
+// Program Functions (4.3.10)
+void cm_request_feedhold();
+void cm_request_queue_flush();
+void cm_request_cycle_start();
+
+void cm_feedhold_callback();
+stat_t cm_queue_flush();
+
+void cm_cycle_start();
+void cm_cycle_end();
+void cm_feedhold();
+void cm_program_stop();
+void cm_optional_program_stop();
+void cm_program_end();
+
+// Cycles
+char cm_get_axis_char(int8_t axis);
index c06e16b448ea9985b9bbb2337418b8b5327e7d4f..936c1f34754b6b7b8d611ec573ec27a8a9ba9556 100644 (file)
@@ -28,7 +28,7 @@
 \******************************************************************************/
 
 #include "hardware.h"
-#include "canonical_machine.h"
+#include "machine.h"
 #include "stepper.h"
 #include "motor.h"
 #include "switch.h"
@@ -43,7 +43,6 @@
 #include "homing.h"
 
 #include "plan/planner.h"
-#include "plan/buffer.h"
 #include "plan/arc.h"
 #include "plan/feedhold.h"
 
@@ -67,7 +66,7 @@ int main() {
   motor_init();                   // motors
   switch_init();                  // switches
   planner_init();                 // motion planning
-  canonical_machine_init();       // gcode machine
+  machine_init();       // gcode machine
   vars_init();                    // configuration variables
   estop_init();                   // emergency stop handler
 
@@ -79,14 +78,12 @@ int main() {
   // main loop
   while (true) {
     hw_reset_handler();                        // handle hard reset requests
-    cm_feedhold_sequencing_callback();         // feedhold state machine
-    mp_plan_hold_callback();                   // plan a feedhold
+    cm_feedhold_callback();                    // feedhold state machine
     cm_arc_callback();                         // arc generation runs
     cm_homing_callback();                      // G28.2 continuation
     cm_probe_callback();                       // G38.2 continuation
     command_callback();                        // process next command
     report_callback();                         // report changes
-
     wdt_reset();
   }
 
index 7782ae9557e62176c1b4b4ee7acf9791b43736b8..fec5842246bc3a8d821ec0e041189238be42a5e5 100644 (file)
@@ -27,7 +27,7 @@
 \******************************************************************************/
 
 /* This module actually contains some parts that belong ion the
- * canonical machine, and other parts that belong at the motion planner
+ * machine, and other parts that belong at the motion planner
  * level, but the whole thing is treated as if it were part of the
  * motion planner.
  */
@@ -282,7 +282,7 @@ static stat_t _compute_arc() {
 
   // g18_correction is used to invert G18 XZ plane arcs for proper CW
   // orientation
-  float g18_correction = (cm.gm.select_plane == CANON_PLANE_XZ) ? -1 : 1;
+  float g18_correction = (cm.gm.select_plane == PLANE_XZ) ? -1 : 1;
 
   if (arc.full_circle) {
     // angular travel always starts as zero for full circles
@@ -356,7 +356,7 @@ static stat_t _compute_arc() {
 }
 
 
-/* Canonical machine entry point for arc
+/* machine entry point for arc
  *
  * Generates an arc by queuing line segments to the move buffer. The arc is
  * approximated by generating a large number of tiny, linear arc_segments.
@@ -390,7 +390,7 @@ stat_t cm_arc_feed(float target[], float flags[], // arc endpoints
   // specification Plane axis 0 and 1 are the arc plane, the linear axis is
   // normal to the arc plane.
   // G17 - the vast majority of arcs are in the G17 (XY) plane
-  if (cm.gm.select_plane == CANON_PLANE_XY) {
+  if (cm.gm.select_plane == PLANE_XY) {
     arc.plane_axis_0 = AXIS_X;
     arc.plane_axis_1 = AXIS_Y;
     arc.linear_axis  = AXIS_Z;
@@ -405,7 +405,7 @@ stat_t cm_arc_feed(float target[], float flags[], // arc endpoints
       // but error if k is present
       return STAT_ARC_SPECIFICATION_ERROR;
 
-  } else if (cm.gm.select_plane == CANON_PLANE_XZ) { // G18
+  } else if (cm.gm.select_plane == PLANE_XZ) { // G18
     arc.plane_axis_0 = AXIS_X;
     arc.plane_axis_1 = AXIS_Z;
     arc.linear_axis  = AXIS_Y;
@@ -415,7 +415,7 @@ stat_t cm_arc_feed(float target[], float flags[], // arc endpoints
         return STAT_ARC_AXIS_MISSING_FOR_SELECTED_PLANE;
     } else if (offset_j) return STAT_ARC_SPECIFICATION_ERROR;
 
-  } else if (cm.gm.select_plane == CANON_PLANE_YZ) { // G19
+  } else if (cm.gm.select_plane == PLANE_YZ) { // G19
     arc.plane_axis_0 = AXIS_Y;
     arc.plane_axis_1 = AXIS_Z;
     arc.linear_axis  = AXIS_X;
index 59da843b4a48fd73bfc79a10a9ea5967e03821f6..c05ab25938b05d2efccb6898a82445f10d8cb935 100644 (file)
@@ -27,7 +27,7 @@
 
 #pragma once
 
-#include "canonical_machine.h"
+#include "machine.h"
 #include "config.h"
 
 #include <stdbool.h>
@@ -74,7 +74,7 @@ typedef struct mpBuffer {         // See Planning Velocity Notes
   struct mpBuffer *nx;            // pointer to next buffer
 
   bf_func_t bf_func;              // callback to buffer exec function
-  cm_exec_t cm_func;              // callback to canonical machine
+  cm_exec_t cm_func;              // callback to machine
 
   float naive_move_time;
 
index 1909b8e2ca503805b1c94e05cceadac1e6e4c10d..b1833c8c1ac6a339faea960cb0e88c4d7ff3d87e 100644 (file)
@@ -30,7 +30,7 @@
 
 #include "buffer.h"
 #include "motor.h"
-#include "canonical_machine.h"
+#include "machine.h"
 #include "planner.h"
 #include "stepper.h"
 #include "rtc.h"
index 0bbd28e3428eca61770ea122035d174157088a8c..545bd5fb88fd5cf06c0f28ef44e5bf6a8b69cef6 100644 (file)
@@ -31,7 +31,7 @@
  *   - A command is called by the Gcode interpreter (cm_<command>, e.g. M code)
  *   - cm_ function calls mp_queue_command which puts it in the planning queue
  *     (bf buffer) which sets some parameters and registers a callback to the
- *     execution function in the canonical machine.
+ *     execution function in the machine.
  *   - When the planning queue gets to the function it calls _exec_command()
  *     which loads a pointer to the bf buffer in stepper.c's next move.
  *   - When the runtime gets to the end of the current activity (sending steps,
@@ -47,7 +47,7 @@
 #include "command.h"
 
 #include "buffer.h"
-#include "canonical_machine.h"
+#include "machine.h"
 #include "stepper.h"
 
 
@@ -69,7 +69,7 @@ void mp_queue_command(cm_exec_t cm_exec, float *value, float *flag) {
 
   bf->move_type = MOVE_TYPE_COMMAND;
   bf->bf_func = _exec_command;    // callback to planner queue exec function
-  bf->cm_func = cm_exec;          // callback to canonical machine exec function
+  bf->cm_func = cm_exec;          // callback to machine exec function
 
   // Store values and flags in planner buffer
   for (int axis = 0; axis < AXES; axis++) {
index d5434710dca0b9acdd69170b3b2588a1f22fefcf..7e2305aa5904cfb1d21fdd5f729bcd67a0ff2da5 100644 (file)
@@ -30,7 +30,7 @@
 #include "dwell.h"
 
 #include "buffer.h"
-#include "canonical_machine.h"
+#include "machine.h"
 #include "stepper.h"
 
 
index 4fc1af715192bde602a1a0ca12599a2caf6dbad3..caa506b7e0508fbc3665c376a6b3009d1fd44ce6 100644 (file)
@@ -765,7 +765,7 @@ stat_t mp_exec_aline(mpBuf_t *bf) {
   else if (mr.section == SECTION_TAIL) status = _exec_aline_tail();
   else return CM_ALARM(STAT_INTERNAL_ERROR); // never supposed to get here
 
-  // Feedhold processing. Refer to canonical_machine.h for state machine
+  // Feedhold processing. Refer to machine.h for state machine
   // Catch the feedhold request and start the planning the hold
   if (cm.hold_state == FEEDHOLD_SYNC) cm.hold_state = FEEDHOLD_PLAN;
 
index 1fbb6e83539368f6bc22f5341924394bf97579d7..007962da2d0068b1498852c0c91263209895780a 100644 (file)
@@ -27,7 +27,6 @@
 
 #pragma once
 
-#include "status.h"
 
 void mp_plan_hold_callback();
 void mp_end_hold();
index c511196ee38ac8047a9c2c0618465327fa1a84d2..1ca69085945356308e8097286d572027dd0f383c 100644 (file)
@@ -31,7 +31,7 @@
 #include "buffer.h"
 #include "stepper.h"
 #include "motor.h"
-#include "canonical_machine.h"
+#include "machine.h"
 #include "motor.h"
 #include "config.h"
 
index bb25aa4800e5a6da2e3bb949088332dc79d882ba..266599a14483b213e7f11bb9e8a330af00b78fe7 100644 (file)
@@ -33,7 +33,7 @@
 #include "exec.h"
 #include "buffer.h"
 #include "zoid.h"
-#include "canonical_machine.h"
+#include "machine.h"
 #include "stepper.h"
 #include "util.h"
 #include "report.h"
index 9247b043c9c9e578cd26d529b1f45d32a0545698..eac0e81163b6ec6c80202ac6d6ae191616f02dd7 100644 (file)
@@ -29,7 +29,7 @@
 
 /* Planner Notes
  *
- * The planner works below the canonical machine and above the
+ * The planner works below the machine and above the
  * motor mapping and stepper execution layers. A rudimentary
  * multitasking capability is implemented for long-running commands
  * such as lines, arcs, and dwells.  These functions are coded as
  * mm), and runtime model (mr).  These are designated as "model",
  * "planner" and "runtime" in function names.
  *
- * The Gcode model is owned by the canonical machine and should
+ * The Gcode model is owned by the machine and should
  * only be accessed by cm_xxxx() functions. Data from the Gcode
  * model is transferred to the planner by the mp_xxx() functions
- * called by the canonical machine.
+ * called by the machine.
  *
  * The planner should only use data in the planner model. When a
  * move (block) is ready for execution the planner data is
@@ -61,7 +61,7 @@
 
 #include "buffer.h"
 #include "arc.h"
-#include "canonical_machine.h"
+#include "machine.h"
 #include "stepper.h"
 #include "motor.h"
 
index bf08ad3c3491fbce18685497364372fb77c2205f..bd46a688b7bd7df36ca705e8a10c2a901c90faca 100644 (file)
@@ -30,7 +30,7 @@
 #pragma once
 
 
-#include "canonical_machine.h" // used for GCodeState_t
+#include "machine.h" // used for GCodeState_t
 #include "util.h"
 
 
index eae282f273e877f48d9e9b4729b00078eee1847b..c48c12c7cde0b3dfa0c8fe46d9a0ce98842dd541 100644 (file)
@@ -26,7 +26,7 @@
 
 \******************************************************************************/
 
-#include "canonical_machine.h"
+#include "machine.h"
 #include "spindle.h"
 #include "switch.h"
 #include "util.h"
index 48e7a6dbce861398fb31ccce9bdb85beeff696f4..6e739b3dfa8fbdac40f88bb4044394062052c18b 100644 (file)
@@ -27,7 +27,7 @@
 
 #pragma once
 
-#include "canonical_machine.h"
+#include "machine.h"
 
 
 void pwm_spindle_init();
index 8bb6fc08e37d00df27d15cc629d5b4f3fa10a2b0..0b78a2f674b7413075f880c7a83189d055974785 100644 (file)
@@ -28,7 +28,7 @@
 
 #pragma once
 
-#include "canonical_machine.h"
+#include "machine.h"
 
 
 void cm_spindle_init();
index 891c072c31be2506073875322d0a90a29d7be226..c597bf679e09ba808b07a163dc4db770a8e04e0e 100644 (file)
@@ -30,7 +30,7 @@
 #include "stepper.h"
 
 #include "config.h"
-#include "canonical_machine.h"
+#include "machine.h"
 #include "plan/exec.h"
 #include "plan/command.h"
 #include "motor.h"
index a08277b0b1c36aa3fe94c4f816b2760828bcc561..9014f30d65925f9e1e1424b85726f17ab2b23798 100644 (file)
@@ -46,7 +46,7 @@
 #include "switch.h"
 
 #include "hardware.h"
-#include "canonical_machine.h"
+#include "machine.h"
 #include "config.h"
 
 #include <avr/interrupt.h>
index 54082bec87157f59f2bc885bf2bd4af6db06f90d..9bc92d7225ddfd9c5ea1546872f80f2a66b8c055 100644 (file)
@@ -30,7 +30,7 @@
 #include "motor.h"
 #include "rtc.h"
 #include "cpp_magic.h"
-#include "canonical_machine.h"
+#include "machine.h"
 #include "plan/calibrate.h"
 
 #include <avr/io.h>