Home and probing base functionality working
authorJoseph Coffland <joseph@cauldrondevelopment.com>
Tue, 4 Jul 2017 00:55:53 +0000 (17:55 -0700)
committerJoseph Coffland <joseph@cauldrondevelopment.com>
Tue, 4 Jul 2017 00:55:53 +0000 (17:55 -0700)
33 files changed:
Makefile
avr/src/axis.c
avr/src/command.c
avr/src/gcode_expr.c
avr/src/gcode_expr.h
avr/src/gcode_parser.c
avr/src/gcode_state.h
avr/src/machine.c
avr/src/machine.h
avr/src/messages.def
avr/src/motor.c
avr/src/plan/arc.c
avr/src/plan/arc.h
avr/src/plan/exec.c
avr/src/plan/jog.c
avr/src/plan/jog.h
avr/src/plan/line.c
avr/src/plan/planner.c
avr/src/plan/runtime.c
avr/src/plan/state.c
avr/src/plan/state.h
avr/src/status.h
avr/src/varcb.c
avr/src/vars.c
avr/src/vars.def
package.json
scripts/update-bbctrl [changed mode: 0644->0755]
setup.py
src/jade/templates/control-view.jade
src/js/control-view.js
src/py/bbctrl/Web.py
src/resources/config-template.json
src/stylus/style.styl

index 13dd22e7d66860c5bab9fdba6ee46d6b6622459f..c432c8f4baab5ade05fe0deb2ed64b1e1f431ac8 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -49,8 +49,10 @@ publish: pkg
        echo -n $(VERSION) > dist/latest.txt
        rsync $(RSYNC_OPTS) dist/$(PKG_NAME).tar.bz2 dist/latest.txt $(PUB_PATH)/
 
-install: pkg
-       rsync dist/$(PKG_NAME).tar.bz2 bbmc@bbctrl.local:update.tar.bz2
+update: pkg
+       http_proxy= curl -i -X PUT -H "Content-Type: multipart/form-data" \
+         -F "firmware=@dist/$(PKG_NAME).tar.bz2" \
+         http://bbctrl.local/api/firmware/update
 
 mount:
        mkdir -p $(DEST)
index 30b744cc821f79f7a2f1f13fce8e3a7f26d9d5e0..2ad45112cf940c01853254a12a61c20c633840a9 100644 (file)
@@ -130,7 +130,6 @@ AXIS_GET(zero_backoff, float, 0)
 AXIS_GET(latch_backoff, float, 0)
 AXIS_GET(recip_jerk, float, 0)
 
-
 /* Note on jerk functions
  *
  * Jerk values can be rather large. Jerk values are stored in the system in
@@ -157,3 +156,23 @@ AXIS_VAR_SET(latch_velocity, float)
 AXIS_VAR_SET(zero_backoff, float)
 AXIS_VAR_SET(latch_backoff, float)
 AXIS_VAR_SET(jerk_max, float)
+
+
+float get_homing_dir(int axis) {
+  switch (axes[axis].homing_mode) {
+  case HOMING_DISABLED: break;
+  case HOMING_STALL_MIN: case HOMING_SWITCH_MIN: return -1;
+  case HOMING_STALL_MAX: case HOMING_SWITCH_MAX: return 1;
+  }
+  return 0;
+}
+
+
+float get_home(int axis) {
+  switch (axes[axis].homing_mode) {
+  case HOMING_DISABLED: break;
+  case HOMING_STALL_MIN: case HOMING_SWITCH_MIN: return get_travel_min(axis);
+  case HOMING_STALL_MAX: case HOMING_SWITCH_MAX: return get_travel_max(axis);
+  }
+  return NAN;
+}
index 5e7917257ea46b22fe56ea43ee778122ba831338..89e12e26f679784dce5bf7cd7b51f955dd8cca3d 100644 (file)
 #include "vars.h"
 #include "estop.h"
 #include "i2c.h"
-#include "plan/jog.h"
-#include "plan/calibrate.h"
 #include "plan/buffer.h"
-#include "plan/arc.h"
 #include "plan/state.h"
 #include "config.h"
 #include "pgmspace.h"
@@ -268,11 +265,7 @@ void command_callback() {
   default:
     if (estop_triggered()) {status = STAT_MACHINE_ALARMED; break;}
     else if (mp_is_flushing()) break; // Flush GCode command
-    else if (!mp_queue_get_room() ||
-             mp_is_resuming() ||
-             mach_arc_active() ||
-             calibrate_busy() ||
-             mp_jog_busy()) return; // Wait
+    else if (!mp_is_ready()) return;  // Wait for motion planner
 
     // Parse and execute GCode command
     status = gc_gcode_parser(_cmd);
index 083bdb6b75c55a47ad1b55d0f4cf4f9ac363b16b..8ac1a50aef476f99022f56d0d54e37ba856d0a71 100644 (file)
@@ -35,7 +35,7 @@
 #include <stdlib.h>
 
 
-static float _parse_gcode_number(char **p) {
+float parse_gcode_number(char **p) {
   // Avoid parsing G0X10 as a hexadecimal number
   if (**p == '0' && toupper(*(*p + 1)) == 'X') {
     (*p)++; // pointer points to X
@@ -246,8 +246,6 @@ float parse_gcode_expression(char **p) {
   bool unary = true; // Used to detect unary minus
 
   while (!parser.error && **p) {
-    if (_op_empty() && parser.valPtr == 1) break; // We're done
-
     switch (**p) {
     case ' ': case '\n': case '\r': case '\t': (*p)++; break;
     case '#': _val_push(_parse_gcode_var(p)); unary = false; break;
@@ -260,12 +258,13 @@ float parse_gcode_expression(char **p) {
         _op_apply();
 
       _op_pop(); // Pop opening bracket
+      if (_op_empty() && parser.valPtr == 1) return _val_pop();
       unary = false;
       break;
 
     default:
       if (isdigit(**p) || **p == '.') {
-        _val_push(_parse_gcode_number(p));
+        _val_push(parse_gcode_number(p));
         unary = false;
 
       } else if (isalpha(**p)) {
index c449f52d1cb7066669b8a89ab43a8645aa41eea0..d7a963a0dd292226f7010385f43c1f4f2d2d6978 100644 (file)
@@ -29,4 +29,5 @@
 #pragma once
 
 
+float parse_gcode_number(char **p);
 float parse_gcode_expression(char **p);
index 642436beae1d7747a89f0ce6f6161c48cd833b68..97fbe407c2d91f8b0cc4b9185780df6f0ef2f80f 100644 (file)
@@ -81,7 +81,11 @@ static char *_parse_gcode_comment(char *p) {
 
 
 static stat_t _parse_gcode_value(char **p, float *value) {
-  *value = parse_gcode_expression(p);
+  while (isspace(**p)) (*p)++; // skip whitespace
+
+  if (**p == '[') *value = parse_gcode_expression(p);
+  else *value = parse_gcode_number(p);
+
   return parser.error;
 }
 
@@ -201,8 +205,11 @@ static stat_t _execute_gcode_block() {
   case NEXT_ACTION_GOTO_G30_POSITION: // G30
     status = mach_goto_g30_position(parser.gn.target, parser.gf.target);
     break;
-  case NEXT_ACTION_SET_ABSOLUTE_ORIGIN: // G28.3
-    mach_set_absolute_origin(parser.gn.target, parser.gf.target);
+  case NEXT_ACTION_CLEAR_HOME: // G28.2
+    mach_clear_home(parser.gf.target);
+    break;
+  case NEXT_ACTION_SET_HOME: // G28.3
+    mach_set_home(parser.gn.target, parser.gf.target);
     break;
   case NEXT_ACTION_SET_COORD_DATA:
     mach_set_coord_offsets(parser.gn.parameter, parser.gn.target,
@@ -261,25 +268,26 @@ static stat_t _execute_gcode_block() {
       status = mach_probe(parser.gn.target, parser.gf.target,
                           parser.gn.motion_mode);
       break;
-    case MOTION_MODE_SEEK_CLOSE_ERR: // G38.6
+    case MOTION_MODE_SEEK_CLOSE_ERR:           // G38.6
       status = mach_seek(parser.gn.target, parser.gf.target,
                          parser.gn.motion_mode);
       break;
-    case MOTION_MODE_SEEK_CLOSE:     // G38.7
+    case MOTION_MODE_SEEK_CLOSE:               // G38.7
       status = mach_seek(parser.gn.target, parser.gf.target,
                          parser.gn.motion_mode);
       break;
-    case MOTION_MODE_SEEK_OPEN_ERR:  // G38.8
+    case MOTION_MODE_SEEK_OPEN_ERR:            // G38.8
       status = mach_seek(parser.gn.target, parser.gf.target,
                          parser.gn.motion_mode);
       break;
-    case MOTION_MODE_SEEK_OPEN:      // G38.9
+    case MOTION_MODE_SEEK_OPEN:                // G38.9
       status = mach_seek(parser.gn.target, parser.gf.target,
                          parser.gn.motion_mode);
       break;
     default: break; // Should not get here
     }
   }
+
   // un-set absolute override once the move is planned
   mach_set_absolute_mode(false);
 
@@ -322,7 +330,8 @@ static stat_t _process_gcode_word(char letter, float value) {
         SET_MODAL(MODAL_GROUP_G0, next_action, NEXT_ACTION_GOTO_G28_POSITION);
       case 1:
         SET_MODAL(MODAL_GROUP_G0, next_action, NEXT_ACTION_SET_G28_POSITION);
-      case 3: SET_NON_MODAL(next_action, NEXT_ACTION_SET_ABSOLUTE_ORIGIN);
+      case 2: SET_NON_MODAL(next_action, NEXT_ACTION_CLEAR_HOME);
+      case 3: SET_NON_MODAL(next_action, NEXT_ACTION_SET_HOME);
       default: return STAT_GCODE_COMMAND_UNSUPPORTED;
       }
       break;
@@ -374,11 +383,9 @@ static stat_t _process_gcode_word(char letter, float value) {
       }
       break;
 
-    case 64: SET_MODAL(MODAL_GROUP_G13,path_mode, PATH_CONTINUOUS);
+    case 64: SET_MODAL(MODAL_GROUP_G13, path_mode, PATH_CONTINUOUS);
     case 80: SET_MODAL(MODAL_GROUP_G1, motion_mode,
                        MOTION_MODE_CANCEL_MOTION_MODE);
-      // case 90: SET_MODAL(MODAL_GROUP_G3, distance_mode, ABSOLUTE_MODE);
-      // case 91: SET_MODAL(MODAL_GROUP_G3, distance_mode, INCREMENTAL_MODE);
     case 90:
       switch (_point(value)) {
       case 0: SET_MODAL(MODAL_GROUP_G3, distance_mode, ABSOLUTE_MODE);
index 8c9767781b0193b66def224a828f2edcf10257bd..4dc266555379341d1e3f04c1a5f97953f87c7c1f 100644 (file)
@@ -46,7 +46,8 @@ typedef enum {
   NEXT_ACTION_SET_COORD_DATA,           // G10
   NEXT_ACTION_GOTO_G28_POSITION,        // G28 go to machine position
   NEXT_ACTION_SET_G28_POSITION,         // G28.1 set position in abs coordinates
-  NEXT_ACTION_SET_ABSOLUTE_ORIGIN,      // G28.3 origin set
+  NEXT_ACTION_CLEAR_HOME,               // G28.3 clear axis home
+  NEXT_ACTION_SET_HOME,                 // G28.3 set axis home position
   NEXT_ACTION_GOTO_G30_POSITION,        // G30
   NEXT_ACTION_SET_G30_POSITION,         // G30.1
   NEXT_ACTION_SET_ORIGIN_OFFSETS,       // G92
index 50e29ad1ff1aa1bc5f85f3be23829aecdc57ebee..f15fb35b5e89ca182aecc162c6993e814bd723fe 100644 (file)
@@ -259,19 +259,49 @@ void mach_set_position(const float position[]) {
 
 /*** Get position of axis in absolute coordinates
  *
- * NOTE: Machine position is always returned in mm mode.  No units conversion
+ * NOTE: Machine position is always returned in mm mode.  No unit conversion
  * is performed.
  */
 float mach_get_axis_position(uint8_t axis) {return mach.position[axis];}
 
 
-/* Critical helpers
+/* Set the position of a single axis in the model, planner and runtime
  *
- * Core functions supporting the machining functions
- * These functions are not part of the NIST defined functions
+ * This command sets an axis/axes to a position provided as an argument.
+ * This is useful for setting origins for 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
+ * when you know there are no more moves in the planner and that all motion
+ * has stopped.
  */
+void mach_set_axis_position(unsigned axis, float position) {
+  //if (!mp_is_quiescent()) ALARM(STAT_MACH_NOT_QUIESCENT);
+  if (AXES <= axis) return;
+
+  // TODO should set work position, accounting for offsets
+
+  mach.position[axis] = position;
+  mp_set_axis_position(axis, position);
+  mp_runtime_set_axis_position(axis, position);
+  mp_runtime_set_steps_from_position();
+}
 
 
+/// Do not call this function while machine is moving or queue is not empty
+void mach_set_position_from_runtime() {
+  for (int axis = 0; axis < AXES; axis++) {
+    mach.position[axis] = mp_runtime_get_work_position(axis);
+    mp_set_axis_position(axis, mach.position[axis]);
+  }
+}
+
 
 /*** Calculate target vector
  *
@@ -308,17 +338,13 @@ void mach_calc_target(float target[], const float values[],
 
 /*** Return error code if soft limit is exceeded
  *
- * Must be called with target properly set in GM struct.  Best done
- * after mach_calc_target().
- *
- * Tests for soft limit for any 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.
+ * Tests for soft limit for any axis if min and max are different values.  You
+ * can set min and max to 0 to disable soft limits for an axis.
  */
 stat_t mach_test_soft_limits(float target[]) {
   for (int axis = 0; axis < AXES; axis++) {
+    if (!axis_is_enabled(axis) || !axis_get_homed(axis)) continue;
+
     float min = axis_get_travel_min(axis);
     float max = axis_get_travel_max(axis);
 
@@ -333,9 +359,10 @@ stat_t mach_test_soft_limits(float target[]) {
 }
 
 
-/* machining functions
- *    Values are passed in pre-unit_converted state (from gn structure)
- *    All operations occur on gm (current model state)
+/* 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
@@ -408,33 +435,6 @@ void mach_set_coord_system(coord_system_t cs) {
 }
 
 
-/* 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 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
- * when you know there are no more moves in the planner and that all motion
- * has stopped.
- */
-void mach_set_axis_position(unsigned axis, float position) {
-  //if (!mp_is_quiescent()) ALARM(STAT_MACH_NOT_QUIESCENT);
-  if (AXES <= axis) return;
-
-  mach.position[axis] = position;
-  mp_set_axis_position(axis, position);
-  mp_runtime_set_axis_position(axis, position);
-  mp_runtime_set_steps_from_position();
-}
-
-
 stat_t mach_zero_all() {
   for (unsigned axis = 0; axis < AXES; axis++) {
     stat_t status = mach_zero_axis(axis);
@@ -456,7 +456,7 @@ stat_t mach_zero_axis(unsigned axis) {
 
 
 // G28.3 functions and support
-static stat_t _exec_absolute_origin(mp_buffer_t *bf) {
+static stat_t _exec_home(mp_buffer_t *bf) {
   const float *origin = bf->target;
   const float *flags = bf->unit;
 
@@ -480,20 +480,28 @@ static stat_t _exec_absolute_origin(mp_buffer_t *bf) {
  * the planner queue.  This includes the runtime position and the step
  * recording done by the encoders.
  */
-void mach_set_absolute_origin(float origin[], bool flags[]) {
-  float value[AXES];
+void mach_set_home(float origin[], bool flags[]) {
+  mp_buffer_t *bf = mp_queue_get_tail();
 
   for (int axis = 0; axis < AXES; axis++)
-    if (flags[axis]) {
-      value[axis] = TO_MM(origin[axis]);
-      mach.position[axis] = value[axis];           // set model position
-      mp_set_axis_position(axis, value[axis]);     // set mm position
+    if (flags[axis] && isfinite(origin[axis])) {
+      // TODO What about work offsets?
+      mach.position[axis] = TO_MM(origin[axis]);       // set model position
+      mp_set_axis_position(axis, mach.position[axis]); // set mm position
+      axis_set_homed(axis, true);
+
+      bf->target[axis] = origin[axis];
+      bf->unit[axis] = flags[axis];
     }
 
-  mp_buffer_t *bf = mp_queue_get_tail();
-  copy_vector(bf->target, origin);
-  copy_vector(bf->unit, flags);
-  mp_queue_push_nonstop(_exec_absolute_origin, mach_get_line());
+  // Synchronized update of runtime position
+  mp_queue_push_nonstop(_exec_home, mach_get_line());
+}
+
+
+void mach_clear_home(bool flags[]) {
+  for (int axis = 0; axis < AXES; axis++)
+    if (flags[axis]) axis_set_homed(axis, false);
 }
 
 
@@ -503,8 +511,8 @@ void mach_set_absolute_origin(float origin[], bool flags[]) {
 
 /// G92
 void mach_set_origin_offsets(float offset[], bool flags[]) {
-  // set offsets in the Gcode model extended context
   mach.origin_offset_enable = true;
+
   for (int axis = 0; axis < AXES; axis++)
     if (flags[axis])
       mach.origin_offset[axis] = mach.position[axis] -
@@ -515,6 +523,7 @@ void mach_set_origin_offsets(float offset[], bool flags[]) {
 /// G92.1
 void mach_reset_origin_offsets() {
   mach.origin_offset_enable = false;
+
   for (int axis = 0; axis < AXES; axis++)
     mach.origin_offset[axis] = 0;
 }
@@ -566,6 +575,12 @@ stat_t mach_plan_line(float target[], switch_id_t sw) {
 
 // Free Space Motion (4.3.4)
 static stat_t _feed(float values[], bool flags[], switch_id_t sw) {
+  // Trap inverse time mode wo/ feed rate
+  if (!mach_is_rapid() && mach.gm.feed_mode == INVERSE_TIME_MODE &&
+      !parser.gf.feed_rate)
+    return STAT_GCODE_FEEDRATE_NOT_SPECIFIED;
+
+  // Compute target position
   float target[AXES];
   mach_calc_target(target, values, flags);
 
@@ -575,10 +590,10 @@ static stat_t _feed(float values[], bool flags[], switch_id_t sw) {
 
   // prep and plan the move
   mach_update_work_offsets();                 // update resolved offsets
-  mach_plan_line(target, sw);
+  RITORNO(mach_plan_line(target, sw));
   copy_vector(mach.position, target);         // update model position
 
-  return status;
+  return STAT_OK;
 }
 
 
@@ -629,32 +644,56 @@ stat_t mach_probe(float values[], bool flags[], motion_mode_t mode) {
 }
 
 
-stat_t mach_seek(float values[], bool flags[], motion_mode_t mode) {
-  mach_set_motion_mode(mode);
+stat_t _exec_set_seek_position(mp_buffer_t *bf) {
+  mach_set_position_from_runtime();
+  mp_pause_queue(false);
+  return STAT_NOOP; // No move queued
+}
 
-  float target[AXES];
-  mach_calc_target(target, values, flags);
+
+stat_t mach_seek(float target[], bool flags[], motion_mode_t mode) {
+  mach_set_motion_mode(mode);
 
   switch_id_t sw = SW_PROBE;
 
   for (int axis = 0; axis < AXES; axis++)
     if (flags[axis]) {
+      // Convert to incremental move
+      if (mach.gm.distance_mode == ABSOLUTE_MODE)
+        target[axis] += mach.position[axis];
+
       if (!axis_is_enabled(axis)) return STAT_SEEK_AXIS_DISABLED;
       if (sw != SW_PROBE) return STAT_SEEK_MULTIPLE_AXES;
       if (fp_EQ(target[axis], mach.position[axis])) return STAT_SEEK_ZERO_MOVE;
 
       bool min = target[axis] < mach.position[axis];
+
+      if (mode == MOTION_MODE_SEEK_OPEN_ERR ||
+          mode == MOTION_MODE_SEEK_OPEN) min = !min;
+
       switch (axis) {
       case AXIS_X: sw = min ? SW_MIN_X : SW_MAX_X; break;
       case AXIS_Y: sw = min ? SW_MIN_Y : SW_MAX_Y; break;
       case AXIS_Z: sw = min ? SW_MIN_Z : SW_MAX_Z; break;
       case AXIS_A: sw = min ? SW_MIN_A : SW_MAX_A; break;
       }
+
+      if (!switch_is_enabled(sw)) return STAT_SEEK_SWITCH_DISABLED;
+
+      STATUS_DEBUG("Axis target %f, position %f, planner %f, runtime %f",
+                   target[axis], mach.position[axis],
+                   mp_get_axis_position(axis),
+                   mp_runtime_get_axis_position(axis));
     }
 
   if (sw == SW_PROBE) return STAT_SEEK_MISSING_AXIS;
 
-  return _feed(values, flags, sw);
+  RITORNO(_feed(target, flags, sw));
+
+  mp_pause_queue(true);
+  mp_queue_push_nonstop(_exec_set_seek_position, mach_get_line());
+
+  return STAT_OK;
 }
 
 
@@ -689,11 +728,6 @@ void mach_set_path_mode(path_mode_t mode) {
 
 /// G1
 stat_t mach_feed(float values[], bool flags[]) {
-  // trap zero feed rate condition
-  if (fp_ZERO(mach.gm.feed_rate) ||
-      (mach.gm.feed_mode == INVERSE_TIME_MODE && !parser.gf.feed_rate))
-    return STAT_GCODE_FEEDRATE_NOT_SPECIFIED;
-
   mach_set_motion_mode(MOTION_MODE_FEED);
   return _feed(values, flags, 0);
 }
index c053441ebbc3d7f2bb41b62497b5c787e8ed6198..271959f55e1ed757bd213ca42809b9af06dd3446 100644 (file)
@@ -67,6 +67,8 @@ void mach_update_work_offsets();
 const float *mach_get_position();
 void mach_set_position(const float position[]);
 float mach_get_axis_position(uint8_t axis);
+void mach_set_axis_position(unsigned axis, float position);
+void mach_set_position_from_runtime();
 
 // Critical helpers
 void mach_calc_target(float target[], const float values[], const bool flags[]);
@@ -84,14 +86,14 @@ void mach_set_distance_mode(distance_mode_t mode);
 void mach_set_arc_distance_mode(distance_mode_t mode);
 void mach_set_coord_offsets(coord_system_t coord_system, float offset[],
                             bool flags[]);
+void mach_set_coord_system(coord_system_t coord_system);
 
-void mach_set_axis_position(unsigned axis, float position);
-void mach_set_absolute_origin(float origin[], bool flags[]);
+void mach_set_home(float origin[], bool flags[]);
+void mach_clear_home(bool flags[]);
 
 stat_t mach_zero_all();
 stat_t mach_zero_axis(unsigned axis);
 
-void mach_set_coord_system(coord_system_t coord_system);
 void mach_set_origin_offsets(float offset[], bool flags[]);
 void mach_reset_origin_offsets();
 void mach_suspend_origin_offsets();
index ca4a85af130be3ab5e7471c7e0420b0c81c52229..c69933b3fa0ab90f939e442953d428bcf97215cd 100644 (file)
@@ -121,6 +121,7 @@ STAT_MSG(MOVE_DURING_PROBE, "Probing error - move during probe")
 STAT_MSG(SEEK_MULTIPLE_AXES, "Seek error - multiple axes specified")
 STAT_MSG(SEEK_MISSING_AXIS, "Seek error - no axis specified")
 STAT_MSG(SEEK_AXIS_DISABLED, "Seek error - specified axis is disabled")
+STAT_MSG(SEEK_SWITCH_DISABLED, "Seek error - target switch is disabled")
 STAT_MSG(SEEK_ZERO_MOVE, "Seek error - axis move too short or zero")
 STAT_MSG(SEEK_SWTICH_NOT_FOUND, "Seek error - move end before switch change")
 
index 7419bc25649d00bf44deb569ff016d336cfa844c..4e421ff06da32596fd4eb818af1933c8096059cd 100644 (file)
@@ -40,7 +40,6 @@
 #include "pgmspace.h"
 
 #include "plan/runtime.h"
-#include "plan/calibrate.h"
 
 #include <util/delay.h>
 
index 7f6d38292a7e52a1380c4958604da2d87c6a1723..25ad833ec91538abe31f162f37179f2db56248a2 100644 (file)
@@ -40,6 +40,8 @@
 #include "config.h"
 #include "util.h"
 
+#include <plan/state.h>
+
 #include <math.h>
 #include <stdbool.h>
 #include <string.h>
@@ -462,6 +464,7 @@ stat_t mach_arc_feed(float values[], bool values_f[],   // arc endpoints
   // Note, arc soft limits are not tested here
 
   arc.running = true;                         // Enable arc run in callback
+  mp_pause_queue(true);                       // Hold queue until arc is done
   mach_arc_callback();                        // Queue initial arc moves
   mach_set_position(arc.target);              // update model position
 
@@ -494,14 +497,14 @@ void mach_arc_callback() {
     // run the line
     mach_plan_line(arc.position, 0);
 
-    if (!--arc.segments) arc.running = false;
+    if (!--arc.segments) mach_abort_arc();
   }
 }
 
 
-bool mach_arc_active() {return arc.running;}
-
-
 /// Stop arc movement without maintaining position
 /// OK to call if no arc is running
-void mach_abort_arc() {arc.running = false;}
+void mach_abort_arc() {
+  arc.running = false;
+  mp_pause_queue(false);
+}
index b54ed7f0c09512a52d4b37a032f92ef8f94acc09..13a047fa772f4334e2d494c95a963eb0c5ab2491 100644 (file)
@@ -44,5 +44,4 @@ stat_t mach_arc_feed(float target[], bool flags[], float offsets[],
                      float P, bool P_f, bool modal_g1_f,
                      motion_mode_t motion_mode);
 void mach_arc_callback();
-bool mach_arc_active();
 void mach_abort_arc();
index dc19f4aab626876eafb8f78daefc7293bfbda637..281427548721e01f705883c375110d11dcb09ccd 100644 (file)
@@ -248,6 +248,9 @@ static void _plan_hold() {
     ex.tail_length = available_length;
     ex.exit_velocity = 0;
   }
+
+  // Don't error if seek was stopped
+  bf->flags &= ~BUFFER_SEEK_ERROR;
 }
 
 
@@ -273,6 +276,10 @@ static stat_t _exec_aline_init(mp_buffer_t *bf) {
   ex.cruise_velocity = bf->cruise_velocity;
   ex.exit_velocity = bf->exit_velocity;
 
+  // Enforce min cruise velocity
+  // TODO How does cruise_velocity ever end up zero when length is non-zero?
+  if (ex.cruise_velocity < 10) ex.cruise_velocity = 10;
+
   ex.section = SECTION_HEAD;
   ex.section_new = true;
   ex.hold_planned = false;
@@ -363,10 +370,15 @@ stat_t mp_exec_aline(mp_buffer_t *bf) {
   }
 
   // If seeking, end move when switch is in target state.
-  if ((bf->flags & BUFFER_SEEK_CLOSE && !switch_is_active(bf->sw)) ||
-      (bf->flags & BUFFER_SEEK_OPEN && switch_is_active(bf->sw))) {
-    mp_runtime_set_velocity(0);
-    return STAT_NOOP;
+  if ((((bf->flags & BUFFER_SEEK_CLOSE) && switch_is_active(bf->sw)) ||
+       ((bf->flags & BUFFER_SEEK_OPEN) && !switch_is_active(bf->sw))) &&
+      !ex.hold_planned) {
+    if (!fp_ZERO(mp_runtime_get_velocity())) _plan_hold();
+    else {
+      mp_runtime_set_velocity(0);
+      bf->flags &= ~BUFFER_SEEK_ERROR;
+      return STAT_NOOP;
+    }
   }
 
   // Plan holds
index e2a968262eb8aa3318b649846a9b0ba0a5b057cc..346cc926a3ed6407f5a635eee86d029ee8425598 100644 (file)
@@ -140,10 +140,9 @@ static stat_t _exec_jog(mp_buffer_t *bf) {
   // Check if we are done
   if (done) {
     // Update machine position
-    for (int axis = 0; axis < AXES; axis++)
-      mach_set_axis_position(axis, mp_runtime_get_work_position(axis));
-
+    mach_set_position_from_runtime();
     mp_set_cycle(CYCLE_MACHINING); // Default cycle
+    mp_pause_queue(false);
 
     return STAT_NOOP; // Done, no move executed
   }
@@ -163,11 +162,8 @@ static stat_t _exec_jog(mp_buffer_t *bf) {
 }
 
 
-bool mp_jog_busy() {return mp_get_cycle() == CYCLE_JOGGING;}
-
-
 uint8_t command_jog(int argc, char *argv[]) {
-  if (!mp_jog_busy() &&
+  if (mp_get_cycle() != CYCLE_JOGGING &&
       (mp_get_state() != STATE_READY || mp_get_cycle() != CYCLE_MACHINING))
     return STAT_NOOP;
 
@@ -178,15 +174,16 @@ uint8_t command_jog(int argc, char *argv[]) {
     else velocity[axis] = 0;
 
   // Reset
-  if (!mp_jog_busy()) memset(&jr, 0, sizeof(jr));
+  if (mp_get_cycle() != CYCLE_JOGGING) memset(&jr, 0, sizeof(jr));
 
   jr.writing = true;
   for (int axis = 0; axis < AXES; axis++)
     jr.next_velocity[axis] = velocity[axis];
   jr.writing = false;
 
-  if (!mp_jog_busy()) {
+  if (mp_get_cycle() != CYCLE_JOGGING) {
     mp_set_cycle(CYCLE_JOGGING);
+    mp_pause_queue(true);
     mp_queue_push_nonstop(_exec_jog, -1);
   }
 
index 4dd2e68179330f55210602abe3308b90784b220a..f2bfa693403b81a8441a502d12d9fcb7046cbcd8 100644 (file)
@@ -26,8 +26,3 @@
 \******************************************************************************/
 
 #pragma once
-
-#include <stdbool.h>
-
-
-bool mp_jog_busy();
index 989f2d6d8ecd39aff5ecea2ca3e6beb616c795ad..e83b5832f44192c1eb941e804a658d98a3601bf4 100644 (file)
@@ -375,6 +375,10 @@ stat_t mp_aline(const float target[], buffer_flags_t flags, switch_id_t sw,
              (flags & BUFFER_EXACT_STOP) ? "true" : "false",
              feed_rate, feed_override, line);
 
+  // Trap zero feed rate condition
+  if (!(flags & BUFFER_RAPID) && fp_ZERO(feed_rate))
+    return STAT_GCODE_FEEDRATE_NOT_SPECIFIED;
+
   // Compute axis and move lengths
   float axis_length[AXES];
   float axis_square[AXES];
index 02dafdb84ee1374f640548b69bc858698c89ce31..55e51f44d83665f5e2a857f455f4a0c773711932 100644 (file)
@@ -670,7 +670,8 @@ float mp_get_target_length(float Vi, float Vf, float jerk) {
  *
  * A convenient function for determining Vf target velocity for a given
  * initial velocity (Vi), length (L), and maximum jerk (Jm).  Equation e) is
- * b) solved for Vf.  Equation f) is c) solved for Vf.  Use f) (obviously)
+ * b) solved for Vf.  Equation f) is equation c) solved for Vf.  We use f) since
+ * it is more simple.
  *
  *   e)  Vf = (sqrt(L) * (L / sqrt(1 / Jm))^(1/6) +
  *            (1 / Jm)^(1/4) * Vi) / (1 / Jm)^(1/4)
index b191af0842f215a4408dc68bffe83ca549342313..a9c1bf134458666a2969128e3e3ff226963107b5 100644 (file)
@@ -51,6 +51,7 @@ typedef struct {
   int32_t line;            // Current move GCode line number
   uint8_t tool;            // Active tool
 
+#if 0 // TODO These are not currently being set
   float feed;
   feed_mode_t feed_mode;
   float feed_override;
@@ -63,8 +64,7 @@ typedef struct {
   path_mode_t path_mode;
   distance_mode_t distance_mode;
   distance_mode_t arc_distance_mode;
-
-  float previous_error[MOTORS];
+#endif
 } mp_runtime_t;
 
 static mp_runtime_t rt = {0};
index a8abd2bc81791950b0d3bb48962351e3d37cfcfb..29c161b616f24d74c0f07ff542543b5ee778dcb9 100644 (file)
@@ -45,6 +45,7 @@ typedef struct {
   mp_state_t state;
   mp_cycle_t cycle;
   mp_hold_reason_t hold_reason;
+  bool pause;
 
   bool hold_requested;
   bool flush_requested;
@@ -154,6 +155,14 @@ bool mp_is_quiescent() {
 }
 
 
+bool mp_is_ready() {
+  return mp_queue_get_room() && !mp_is_resuming() && !ps.pause;
+}
+
+
+void mp_pause_queue(bool x) {ps.pause = x;}
+
+
 void mp_state_optional_pause() {
   if (ps.optional_pause_requested) {
     mp_set_hold_reason(HOLD_REASON_USER_PAUSE);
@@ -178,7 +187,10 @@ void mp_state_idle() {
 }
 
 
-void mp_state_estop() {_set_state(STATE_ESTOPPED);}
+void mp_state_estop() {
+  _set_state(STATE_ESTOPPED);
+  mp_pause_queue(false);
+}
 
 
 void mp_request_hold() {ps.hold_requested = true;}
@@ -233,8 +245,7 @@ void mp_state_callback() {
       // NOTE The following uses low-level mp calls for absolute position.
       // Reset to actual machine position.  Otherwise machine is set to the
       // position of the last queued move.
-      for (int axis = 0; axis < AXES; axis++)
-        mach_set_axis_position(axis, mp_runtime_get_axis_position(axis));
+      mach_set_position_from_runtime();
     }
 
     // Stop spindle
index df9a9d9b9e497945bc24a27e61eb5dc65a2bdb2c..a287c42fc57420f8e6792bae5c727d494176e89a 100644 (file)
@@ -76,6 +76,8 @@ void mp_set_hold_reason(mp_hold_reason_t reason);
 bool mp_is_flushing();
 bool mp_is_resuming();
 bool mp_is_quiescent();
+bool mp_is_ready();
+void mp_pause_queue(bool x);
 
 void mp_state_optional_pause();
 void mp_state_holding();
index ef27054dd1cef8f1951bc9786ae374970bf9fcfb..5e2b91d493d7393e40c0d3994eb94dc7153acd63 100644 (file)
@@ -75,7 +75,7 @@ stat_t status_alarm(const char *location, stat_t status, const char *msg);
   STATUS_MESSAGE(STAT_LEVEL_INFO, STAT_OK, MSG, ##__VA_ARGS__)
 
 #define STATUS_DEBUG(MSG, ...)                                  \
-  STATUS_MESSAGE(STAT_LEVEL_WARNING, STAT_OK, MSG, ##__VA_ARGS__)
+  STATUS_MESSAGE(STAT_LEVEL_DEBUG, STAT_OK, MSG, ##__VA_ARGS__)
 
 #define STATUS_WARNING(MSG, ...)                                \
   STATUS_MESSAGE(STAT_LEVEL_WARNING, STAT_OK, MSG, ##__VA_ARGS__)
index ecbd56af3b4cd854986e92ea812a70f3c6bd8be9..283b1561b344fea013abb0d06167d296387c4305 100644 (file)
@@ -34,7 +34,7 @@
 #include "plan/state.h"
 
 // Axis
-float get_position(int axis) {return mp_runtime_get_axis_position(axis);}
+float get_position(int axis) {return mp_runtime_get_work_position(axis);}
 
 
 void set_position(int axis, float position) {
index c05a4760513be31892a8b3a48f3578d762897178..70d676277f0be20792013c46f0e5ce493ce26d2c 100644 (file)
@@ -69,6 +69,7 @@ MAP(EQ_FUNC, SEMI, flags_t, string, pstring, uint8_t, uint16_t, int32_t, char);
 
 // String
 static void var_print_string(string s) {printf_P(PSTR("\"%s\""), s);}
+static float var_string_to_float(string s) {return 0;}
 
 
 // Program string
@@ -83,6 +84,8 @@ static void var_print_flags_t(flags_t x) {
   print_status_flags(x);
 }
 
+static float var_flags_t_to_float(flags_t x) {return x;}
+
 
 // Float
 static bool var_eq_float(float a, float b) {
@@ -141,11 +144,7 @@ static float var_char_to_float(char x) {return x;}
 
 // int8
 #if 0
-static void var_print_int8_t(int8_t x) {
-  printf_P(PSTR("%"PRIi8), x);
-}
-
-
+static void var_print_int8_t(int8_t x) {printf_P(PSTR("%"PRIi8), x);}
 static int8_t var_parse_int8_t(const char *value) {return strtol(value, 0, 0);}
 static float var_int8_t_to_float(int8_t x) {return x;}
 #endif
@@ -177,6 +176,7 @@ static float var_uint16_t_to_float(uint16_t x) {return x;}
 
 // int32
 static void var_print_int32_t(int32_t x) {printf_P(PSTR("%"PRIi32), x);}
+static float var_int32_t_to_float(int32_t x) {return x;}
 
 
 // Ensure no code is used more than once
@@ -387,15 +387,14 @@ float vars_get_number(const char *_name) {
 
   int i;
 #define VAR(NAME, CODE, TYPE, INDEX, SET, ...)                          \
-  IF(SET)                                                               \
-    (if (!strcmp(IF_ELSE(INDEX)(name + 1, name), #CODE)) {              \
-      IF(INDEX)                                                         \
-        (i = strchr(INDEX##_LABEL, name[0]) - INDEX##_LABEL;            \
-         if (INDEX <= i) return 0);                                     \
+  if (!strcmp(IF_ELSE(INDEX)(name + 1, name), #CODE)) {                 \
+    IF(INDEX)                                                           \
+      (i = strchr(INDEX##_LABEL, name[0]) - INDEX##_LABEL;              \
+       if (INDEX <= i) return 0);                                       \
                                                                         \
-      TYPE x = get_##NAME(IF(INDEX)(i));                                \
-      return var_##TYPE##_to_float(x);                                  \
-    })                                                                  \
+    TYPE x = get_##NAME(IF(INDEX)(i));                                  \
+    return var_##TYPE##_to_float(x);                                    \
+  }                                                                     \
 
 #include "vars.def"
 #undef VAR
index f8878f192c2766f8ff30b0e835371df5aa34a8c7..dee84463efc4b7c5993368b7009ec6e43a8a45c4 100644 (file)
@@ -67,6 +67,8 @@ VAR(probe_switch,   pw, bool,     0,      0, 1, "Probe switch state")
 
 // Homing
 VAR(homing_mode,    ho, uint8_t,  MOTORS, 1, 1, "Homing type")
+VAR(homing_dir,     hd, float,    MOTORS, 0, 1, "Homing direction")
+VAR(home,            h, float,    MOTORS, 0, 1, "Home position")
 VAR(search_velocity,sv, float,    MOTORS, 1, 1, "Homing search velocity")
 VAR(latch_velocity, lv, float,    MOTORS, 1, 1, "Homing latch velocity")
 VAR(latch_backoff,  lb, float,    MOTORS, 1, 1, "Homing latch backoff")
index 4740ce26f6ad94388de67c31c934a7b76eee8468..284f3a8d3ec72d1f460bdfd0741e9ac75c3f2e0a 100644 (file)
@@ -1,6 +1,6 @@
 {
   "name": "bbctrl",
-  "version": "0.1.10",
+  "version": "0.1.11",
   "homepage": "https://github.com/buildbotics/rpi-firmware",
   "license": "GPL 3+",
 
old mode 100644 (file)
new mode 100755 (executable)
index 0930949..739203b
@@ -1,6 +1,6 @@
 #!/bin/bash
 
-UPDATE=$(pwd)/update.tar.bz2
+UPDATE=/var/lib/bbctrl/firmware/update.tar.bz2
 
 if [ ! -e "$UPDATE" ]; then
   echo "Missing $UPDATE"
@@ -16,4 +16,4 @@ cd *
 ./scripts/install.sh "$*"
 
 cd -
-rm -rf /tmp/update
+rm -rf /tmp/update $UPDATE
index 635645095c6c73b1d9742a5d3eb2e31aa870f616..8a8ca359085d8a354fc47cbf266bd4cb3c01d2a1 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -24,6 +24,7 @@ setup(
             'bbctrl = bbctrl:run'
             ]
         },
+    scripts = ['scripts/update-bbctrl', 'scripts/upgrade-bbctrl'],
     install_requires = 'tornado sockjs-tornado pyserial pyudev smbus2'.split(),
     zip_safe = False,
     )
index de9c2cd8dfab9b5b79baa845b6715d2179aabe80..d814a0aa59c8c56cb019fe22ff7aa1c0eb23518f 100644 (file)
@@ -139,7 +139,12 @@ script#control-view-template(type="text/x-template")
             option(v-for="file in files", :value="file") {{file}}
 
         .gcode(:class="{placeholder: !gcode}")
-          | {{{gcode || 'GCode displays here.'}}}
+          span(v-if="!gcode.length") GCode displays here.
+          ul
+            li(v-for="item in gcode", id="gcode-line-{{$index}}",
+               track-by="$index")
+              span {{$index + 1}}
+              | {{item}}
 
       section#content2.tab-content
         .mdi.pure-form
@@ -150,7 +155,11 @@ script#control-view-template(type="text/x-template")
             input(v-model="mdi", @keyup.enter="submit_mdi")
 
         .history(:class="{placeholder: !history}")
-          | {{history || 'MDI history displays here.'}}
+          span(v-if="!history.length") MDI history displays here.
+          ul
+            li(v-for="item in history", @click="load_history($index)",
+               track-by="$index")
+              | {{item}}
 
       section#content3.tab-content
         .jog
index 0740e49df3255dec0ff2670e44a1471d7e83cf46..b182a0baedee62bbaa3c210d136488c709fdeca6 100644 (file)
@@ -14,6 +14,12 @@ function _msg_equal(a, b) {
 }
 
 
+function escapeHTML(s) {
+  var entityMap = {'&': '&amp;', '<': '&lt;', '>': '&gt;'};
+  return String(s).replace(/[&<>]/g, function (s) {return entityMap[s];});
+}
+
+
 module.exports = {
   template: '#control-view-template',
   props: ['config', 'state'],
@@ -26,8 +32,8 @@ module.exports = {
       last_file: '',
       files: [],
       axes: 'xyzabc',
-      gcode: '',
-      history: '',
+      gcode: [],
+      history: [],
       console: [],
       speed_override: 1,
       feed_override: 1
@@ -134,10 +140,15 @@ module.exports = {
 
     submit_mdi: function () {
       this.send(this.mdi);
-      this.history = this.mdi + '\n' + this.history;
+      if (!this.history.length || this.history[0] != this.mdi)
+        this.history.unshift(this.mdi);
+      this.mdi = '';
     },
 
 
+    load_history: function (index) {this.mdi = this.history[index];},
+
+
     open: function (e) {
       $('.gcode-file-input').click();
     },
@@ -166,7 +177,7 @@ module.exports = {
 
       if (!file || this.files.indexOf(file) == -1) {
         this.file = '';
-        this.gcode = '';
+        this.gcode = [];
         return;
       }
 
@@ -174,17 +185,7 @@ module.exports = {
 
       api.get('file/' + file)
         .done(function (data) {
-          var lines = data.trimRight().split(/\r?\n/);
-          var html = '<ul>';
-
-          for (var i = 0; i < lines.length; i++)
-            // TODO escape HTML chars in lines
-            html += '<li id="gcode-line-' + i + '">' +
-            '<span>' + (i + 1) + '</span>' + lines[i] + '</li>';
-
-          html += '</ul>';
-
-          this.gcode = html;
+          this.gcode = data.trimRight().split(/\r?\n/);
           this.last_file = file;
 
           Vue.nextTick(this.update_gcode_line);
index 6bcf721818718f0264d5576eee00c58361a28589..72448bbe1194ca097e0a096ab16f76dffdabd37f 100644 (file)
@@ -5,6 +5,8 @@ import tornado
 import sockjs.tornado
 import logging
 import datetime
+import shutil
+import tarfile
 
 import bbctrl
 
@@ -36,6 +38,29 @@ class ConfigResetHandler(bbctrl.APIHandler):
     def put_ok(self): self.ctrl.config.reset()
 
 
+class FirmwareUpdateHandler(bbctrl.APIHandler):
+    def prepare(self): pass
+
+
+    def put(self):
+        # Only allow this function in dev mode
+        if not os.path.exists('/etc/bbctrl-dev-mode'):
+            self.send_error(403, message = 'Not in dev mode')
+            return
+
+        firmware = self.request.files['firmware'][0]
+
+        if not os.path.exists('firmware'): os.mkdir('firmware')
+
+        with open('firmware/update.tar.bz2', 'wb') as f:
+            f.write(firmware['body'])
+
+        import subprocess
+        ret = subprocess.Popen(['update-bbctrl'])
+
+        self.write_json('ok')
+
+
 class UpgradeHandler(bbctrl.APIHandler):
     def put_ok(self):
         import subprocess
@@ -168,6 +193,7 @@ class Web(tornado.web.Application):
             (r'/api/config/download', ConfigDownloadHandler),
             (r'/api/config/save', ConfigSaveHandler),
             (r'/api/config/reset', ConfigResetHandler),
+            (r'/api/firmware/update', FirmwareUpdateHandler),
             (r'/api/upgrade', UpgradeHandler),
             (r'/api/file(/.+)?', bbctrl.FileHandler),
             (r'/api/home', HomeHandler),
index 9c50a2f798fff1f17e634cc52cda98922ba9f75d..14f3bb1a8238b75f01502b680f02b450e338e44e 100644 (file)
         "code": "pm"
       },
       "drive-current": {
-        "type": "float",
+        "type": "aloat",
         "min": 0,
         "max": 8,
-        "unit": "A",
+        "unit": "amps",
         "default": 2,
         "code": "dc"
       },
@@ -28,7 +28,7 @@
         "type": "float",
         "min": 0,
         "max": 8,
-        "unit": "A",
+        "unit": "Amps",
         "default": 0,
         "code": "ic"
       }
index 757b7bbd72b8a2c9acdeb4bfb934081d52087a65..ee556f2f543e52b390ffc21777934f639effa7fd 100644 (file)
@@ -297,6 +297,7 @@ body
       margin 0
 
   .gcode, .history
+    font-family courier
     clear both
     overflow auto
     width 100%
@@ -304,12 +305,12 @@ body
     min-width 99%
     height 200px
     padding 0.25em
-    white-space pre
+    white-space nowrap
 
     &.placeholder
       color #aaa
 
-  .gcode ul
+  .gcode ul, .history ul
     margin 0
     padding 0
     list-style none
@@ -326,6 +327,9 @@ body
       &.highlight
         background-color #eaeaea
 
+  .history ul li
+    cursor pointer
+
   .mdi
     clear both
     white-space nowrap