Fixed work offsets, improved offset/homing user interface, run homing procedure,...
authorJoseph Coffland <joseph@cauldrondevelopment.com>
Sun, 9 Jul 2017 00:47:03 +0000 (17:47 -0700)
committerJoseph Coffland <joseph@cauldrondevelopment.com>
Sun, 9 Jul 2017 00:47:03 +0000 (17:47 -0700)
34 files changed:
avr/src/axis.c
avr/src/axis.h
avr/src/command.c
avr/src/i2c.h
avr/src/machine.c
avr/src/machine.h
avr/src/plan/arc.c
avr/src/plan/buffer.c
avr/src/plan/buffer.h
avr/src/plan/jog.c
avr/src/plan/line.c
avr/src/plan/planner.c
avr/src/varcb.c
avr/src/vars.def
package.json
scripts/install.sh
setup.py
src/jade/templates/admin-view.jade
src/jade/templates/control-view.jade
src/jade/templates/message.jade
src/js/admin-view.js
src/js/control-view.js
src/js/sock.js
src/pwr/main.c
src/py/bbctrl/APIHandler.py
src/py/bbctrl/AVR.py
src/py/bbctrl/Config.py
src/py/bbctrl/Ctrl.py
src/py/bbctrl/LCD.py
src/py/bbctrl/Pwr.py
src/py/bbctrl/Web.py
src/py/bbctrl/__init__.py
src/resources/config-template.json
src/stylus/style.styl

index 2ad45112cf940c01853254a12a61c20c633840a9..5a48b78360befe67f452b542d7f9758561eeeb39 100644 (file)
@@ -119,7 +119,7 @@ float axis_get_vector_length(const float a[], const float b[]) {
 AXIS_GET(velocity_max, float, 0)
 AXIS_GET(homed, bool, false)
 AXIS_SET(homed, bool)
-AXIS_GET(homing_mode, homing_mode_t, HOMING_DISABLED)
+AXIS_GET(homing_mode, homing_mode_t, HOMING_MANUAL)
 AXIS_SET(homing_mode, homing_mode_t)
 AXIS_GET(radius, float, 0)
 AXIS_GET(travel_min, float, 0)
@@ -160,7 +160,7 @@ AXIS_VAR_SET(jerk_max, float)
 
 float get_homing_dir(int axis) {
   switch (axes[axis].homing_mode) {
-  case HOMING_DISABLED: break;
+  case HOMING_MANUAL: break;
   case HOMING_STALL_MIN: case HOMING_SWITCH_MIN: return -1;
   case HOMING_STALL_MAX: case HOMING_SWITCH_MAX: return 1;
   }
@@ -170,7 +170,7 @@ float get_homing_dir(int axis) {
 
 float get_home(int axis) {
   switch (axes[axis].homing_mode) {
-  case HOMING_DISABLED: break;
+  case HOMING_MANUAL: 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);
   }
index d577398cf57f558eb514d596e0309b63bfd9df76..eede58f10eba8adb3009a87bc1309f96f7688f6b 100644 (file)
@@ -41,7 +41,7 @@ enum {
 
 
 typedef enum {
-  HOMING_DISABLED,
+  HOMING_MANUAL,
   HOMING_STALL_MIN,
   HOMING_STALL_MAX,
   HOMING_SWITCH_MIN,
index 89e12e26f679784dce5bf7cd7b51f955dd8cca3d..3939751a24c5f0fb24ce6d777179e61317eb817a 100644 (file)
 static char *_cmd = 0;
 
 
-static void _reboot()     {hw_request_hard_reset();}
-
-
-static unsigned _parse_axis(uint8_t axis) {
-  switch (axis) {
-  case 'x': return 0; case 'y': return 1; case 'z': return 2;
-  case 'a': return 3; case 'b': return 4; case 'c': return 5;
-  case 'X': return 0; case 'Y': return 1; case 'Z': return 2;
-  case 'A': return 3; case 'B': return 4; case 'C': return 5;
-  default: return axis;
-  }
-}
-
-
 static void command_i2c_cb(i2c_cmd_t cmd, uint8_t *data, uint8_t length) {
   switch (cmd) {
   case I2C_NULL:                                           break;
@@ -77,11 +63,7 @@ static void command_i2c_cb(i2c_cmd_t cmd, uint8_t *data, uint8_t length) {
   case I2C_STEP:           mp_request_step();              break;
   case I2C_FLUSH:          mp_request_flush();             break;
   case I2C_REPORT:         report_request_full();          break;
-  case I2C_REBOOT:         _reboot();                      break;
-  case I2C_ZERO:
-    if (length == 0) mach_zero_all();
-    else if (length == 1) mach_zero_axis(_parse_axis(*data));
-    break;
+  case I2C_REBOOT:         hw_request_hard_reset();        break;
   }
 }
 
@@ -332,7 +314,7 @@ uint8_t command_report(int argc, char *argv[]) {
 
 
 uint8_t command_reboot(int argc, char *argv[]) {
-  _reboot();
+  hw_request_hard_reset();
   return 0;
 }
 
index d8f4ef0596bf20b191dc271019c496ac0355f18d..766396da258be0739dbd47561ddaac6edb314582 100644 (file)
@@ -44,7 +44,6 @@ typedef enum {
   I2C_FLUSH,
   I2C_REPORT,
   I2C_REBOOT,
-  I2C_ZERO,
 } i2c_cmd_t;
 
 
index f15fb35b5e89ca182aecc162c6993e814bd723fe..73fde299b080224e4d540ccc4622c39ebae435f7 100644 (file)
@@ -122,6 +122,7 @@ coord_system_t mach_get_coord_system() {return mach.gm.coord_system;}
 bool mach_get_absolute_mode() {return mach.gm.absolute_mode;}
 path_mode_t mach_get_path_mode() {return mach.gm.path_mode;}
 bool mach_is_exact_stop() {return mach.gm.path_mode == PATH_EXACT_STOP;}
+bool mach_in_absolute_mode() {return mach.gm.distance_mode == ABSOLUTE_MODE;}
 distance_mode_t mach_get_distance_mode() {return mach.gm.distance_mode;}
 distance_mode_t mach_get_arc_distance_mode() {return mach.gm.arc_distance_mode;}
 
@@ -316,13 +317,13 @@ void mach_set_position_from_runtime() {
  *  Axes that need processing are signaled in @param flags.
  */
 void mach_calc_target(float target[], const float values[],
-                      const bool flags[]) {
+                      const bool flags[], bool absolute) {
   for (int axis = 0; axis < AXES; axis++) {
     target[axis] = mach.position[axis];
     if (!flags[axis] || !axis_is_enabled(axis)) continue;
 
-    target[axis] = mach.gm.distance_mode == ABSOLUTE_MODE ?
-      mach_get_active_coord_offset(axis) : mach.position[axis];
+    target[axis] = absolute ? mach_get_active_coord_offset(axis) :
+      mach.position[axis];
 
     float radius = axis_get_radius(axis);
     if (radius) // Handle radius mode if radius is non-zero
@@ -435,33 +436,16 @@ void mach_set_coord_system(coord_system_t cs) {
 }
 
 
-stat_t mach_zero_all() {
-  for (unsigned axis = 0; axis < AXES; axis++) {
-    stat_t status = mach_zero_axis(axis);
-    if (status != STAT_OK) return status;
-  }
-
-  return STAT_OK;
-}
-
-
-stat_t mach_zero_axis(unsigned axis) {
-  if (!mp_is_quiescent()) return STAT_MACH_NOT_QUIESCENT;
-  if (AXES <= axis) return STAT_INVALID_AXIS;
-
-  mach_set_axis_position(axis, 0);
-
-  return STAT_OK;
-}
-
-
 // G28.3 functions and support
 static stat_t _exec_home(mp_buffer_t *bf) {
-  const float *origin = bf->target;
+  const float *target = bf->target;
   const float *flags = bf->unit;
 
   for (int axis = 0; axis < AXES; axis++)
-    if (flags[axis]) mp_runtime_set_axis_position(axis, origin[axis]);
+    if (flags[axis]) {
+      mp_runtime_set_axis_position(axis, target[axis]);
+      axis_set_homed(axis, true);
+    }
 
   mp_runtime_set_steps_from_position();
 
@@ -483,16 +467,17 @@ static stat_t _exec_home(mp_buffer_t *bf) {
 void mach_set_home(float origin[], bool flags[]) {
   mp_buffer_t *bf = mp_queue_get_tail();
 
+  // Compute target position
+  mach_calc_target(bf->target, origin, flags, true);
+
   for (int axis = 0; axis < AXES; axis++)
     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] -= mach_get_active_coord_offset(axis);
+      mach.position[axis] = bf->target[axis];
+      mp_set_axis_position(axis, bf->target[axis]); // set mm position
+      bf->unit[axis] = true;
 
-      bf->target[axis] = origin[axis];
-      bf->unit[axis] = flags[axis];
-    }
+    } else bf->unit[axis] = false;
 
   // Synchronized update of runtime position
   mp_queue_push_nonstop(_exec_home, mach_get_line());
@@ -517,6 +502,8 @@ void mach_set_origin_offsets(float offset[], bool flags[]) {
     if (flags[axis])
       mach.origin_offset[axis] = mach.position[axis] -
         mach.offset[mach.gm.coord_system][axis] - TO_MM(offset[axis]);
+
+  mach_update_work_offsets();                 // update resolved offsets
 }
 
 
@@ -526,15 +513,23 @@ void mach_reset_origin_offsets() {
 
   for (int axis = 0; axis < AXES; axis++)
     mach.origin_offset[axis] = 0;
+
+  mach_update_work_offsets();                 // update resolved offsets
 }
 
 
 /// G92.2
-void mach_suspend_origin_offsets() {mach.origin_offset_enable = false;}
+void mach_suspend_origin_offsets() {
+  mach.origin_offset_enable = false;
+  mach_update_work_offsets();                 // update resolved offsets
+}
 
 
 /// G92.3
-void mach_resume_origin_offsets() {mach.origin_offset_enable = true;}
+void mach_resume_origin_offsets() {
+  mach.origin_offset_enable = true;
+  mach_update_work_offsets();                 // update resolved offsets
+}
 
 
 stat_t mach_plan_line(float target[], switch_id_t sw) {
@@ -582,7 +577,7 @@ static stat_t _feed(float values[], bool flags[], switch_id_t sw) {
 
   // Compute target position
   float target[AXES];
-  mach_calc_target(target, values, flags);
+  mach_calc_target(target, values, flags, mach_in_absolute_mode());
 
   // test soft limits
   stat_t status = mach_test_soft_limits(target);
@@ -657,7 +652,7 @@ stat_t mach_seek(float target[], bool flags[], motion_mode_t mode) {
   switch_id_t sw = SW_PROBE;
 
   for (int axis = 0; axis < AXES; axis++)
-    if (flags[axis]) {
+    if (flags[axis] && isfinite(target[axis])) {
       // Convert to incremental move
       if (mach.gm.distance_mode == ABSOLUTE_MODE)
         target[axis] += mach.position[axis];
index 271959f55e1ed757bd213ca42809b9af06dd3446..51e04802b6f63fbd8795e800f930f2bb4f44f694 100644 (file)
@@ -52,6 +52,7 @@ coord_system_t mach_get_coord_system();
 bool mach_get_absolute_mode();
 path_mode_t mach_get_path_mode();
 bool mach_is_exact_stop();
+bool mach_in_absolute_mode();
 distance_mode_t mach_get_distance_mode();
 distance_mode_t mach_get_arc_distance_mode();
 
@@ -71,7 +72,8 @@ 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[]);
+void mach_calc_target(float target[], const float values[], const bool flags[],
+                      bool absolute);
 stat_t mach_test_soft_limits(float target[]);
 
 // machining functions defined by NIST [organized by NIST Gcode doc]
@@ -91,9 +93,6 @@ void mach_set_coord_system(coord_system_t coord_system);
 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_origin_offsets(float offset[], bool flags[]);
 void mach_reset_origin_offsets();
 void mach_suspend_origin_offsets();
index 25ad833ec91538abe31f162f37179f2db56248a2..2cb1fcc0847e5741c36d9279543342a67a4299d3 100644 (file)
@@ -435,7 +435,7 @@ stat_t mach_arc_feed(float values[], bool values_f[],   // arc endpoints
 
   // Set model target
   const float *position = mach_get_position();
-  mach_calc_target(arc.target, values, values_f);
+  mach_calc_target(arc.target, values, values_f, mach_in_absolute_mode());
 
   // in radius mode it's an error for start == end
   if (radius_f && fp_EQ(position[AXIS_X], arc.target[AXIS_X]) &&
index 9943d2125b5640155689e3a99b91a7eb316f0a38..6a1ad0a8481aa16f5a6482d8c44ac122c8bb3a8a 100644 (file)
@@ -216,6 +216,8 @@ void mp_buffer_print(const mp_buffer_t *bf) {
 void mp_buffer_validate(const mp_buffer_t *bp) {
   ASSERT(bp);
 
+  if (!(bp->flags & BUFFER_LINE)) return; // Only check line buffers
+
   ASSERT(isfinite(bp->value));
 
   ASSERT(isfinite(bp->target[0]) && isfinite(bp->target[1]) &&
index 96efd16e0b6ca46bb5903b0014f940119ea8a25c..de1d4d5041ac7976a3a15af746384d474c330ac4 100644 (file)
@@ -51,6 +51,7 @@ typedef enum {
   BUFFER_RAPID        = 1 << 5,
   BUFFER_INVERSE_TIME = 1 << 6,
   BUFFER_EXACT_STOP   = 1 << 7,
+  BUFFER_LINE         = 1 << 8,
 } buffer_flags_t;
 
 
index 346cc926a3ed6407f5a635eee86d029ee8425598..92ff6fb4b42302884b348a625a84f0ca1a21674e 100644 (file)
 #include <stdlib.h>
 
 
+typedef struct {
+  float delta;
+  float t;
+  bool changed;
+
+  int sign;
+  float velocity;
+  float next;
+  float initial;
+  float target;
+} jog_axis_t;
+
+
 typedef struct {
   bool writing;
+  bool done;
+
   float Vi;
   float Vt;
-  float velocity_delta[AXES];
-  float velocity_t[AXES];
 
-  int sign[AXES];
-  float velocity[AXES];
-  float next_velocity[AXES];
-  float initial_velocity[AXES];
-  float target_velocity[AXES];
+  jog_axis_t axes[AXES];
 } jog_runtime_t;
 
 
 static jog_runtime_t jr;
 
 
-static stat_t _exec_jog(mp_buffer_t *bf) {
-  // Load next velocity
-  bool changed = false;
-  bool done = true;
-  if (!jr.writing)
-    for (int axis = 0; axis < AXES; axis++) {
-      if (!axis_is_enabled(axis)) continue;
+static bool _next_axis_velocity(int axis) {
+  jog_axis_t *a = &jr.axes[axis];
 
-      float Vn = jr.next_velocity[axis] * axis_get_velocity_max(axis);
-      float Vi = jr.velocity[axis];
-      float Vt = jr.target_velocity[axis];
+  float Vn = a->next * axis_get_velocity_max(axis);
+  float Vi = a->velocity;
+  float Vt = a->target;
 
-      if (JOG_MIN_VELOCITY < fabs(Vn)) done = false;
+  if (JOG_MIN_VELOCITY < fabs(Vn)) jr.done = false;
 
-      if (!fp_ZERO(Vi) && (Vn < 0) != (Vi < 0))
-        Vn = 0; // Plan to zero on sign change
+  if (!fp_ZERO(Vi) && (Vn < 0) != (Vi < 0))
+    Vn = 0; // Plan to zero on sign change
 
-      if (fabs(Vn) < JOG_MIN_VELOCITY) Vn = 0;
+  if (fabs(Vn) < JOG_MIN_VELOCITY) Vn = 0;
 
-      if (Vt != Vn) {
-        jr.target_velocity[axis] = Vn;
-        if (Vn) jr.sign[axis] = Vn < 0 ? -1 : 1;
-        changed = true;
-      }
-    }
+  if (Vt == Vn) return false; // No change
 
-  float velocity_sqr = 0;
+  a->target = Vn;
+  if (Vn) a->sign = Vn < 0 ? -1 : 1;
 
-  // Compute per axis velocities
-  for (int axis = 0; axis < AXES; axis++) {
-    if (!axis_is_enabled(axis)) continue;
+  return true;
+}
 
-    float V = fabs(jr.velocity[axis]);
-    float Vt = fabs(jr.target_velocity[axis]);
 
-    if (changed) {
-      if (fp_EQ(V, Vt)) {
-        V = Vt;
-        jr.velocity_t[axis] = 1;
+static float _compute_axis_velocity(int axis) {
+  jog_axis_t *a = &jr.axes[axis];
 
-      } else {
-        // Compute axis max jerk
-        float jerk = axis_get_jerk_max(axis) * JERK_MULTIPLIER;
+  float V = fabs(a->velocity);
+  float Vt = fabs(a->target);
 
-        // Compute length to velocity given max jerk
-        float length = mp_get_target_length(V, Vt, jerk * JOG_JERK_MULT);
+  if (JOG_MIN_VELOCITY < Vt) jr.done = false;
 
-        // Compute move time
-        float move_time = 2 * length / (V + Vt);
+  if (fp_EQ(V, Vt)) return Vt;
 
-        if (move_time < SEGMENT_TIME) {
-          V = Vt;
-          jr.velocity_t[axis] = 1;
+  if (a->changed) {
+    // Compute axis max jerk
+    float jerk = axis_get_jerk_max(axis) * JERK_MULTIPLIER;
 
-        } else {
-          jr.initial_velocity[axis] = V;
-          jr.velocity_t[axis] = jr.velocity_delta[axis] =
-            SEGMENT_TIME / move_time;
-        }
-      }
-    }
+    // Compute length to velocity given max jerk
+    float length = mp_get_target_length(V, Vt, jerk * JOG_JERK_MULT);
 
-    if (jr.velocity_t[axis] < 1) {
-      // Compute quintic Bezier curve
-      V = velocity_curve(jr.initial_velocity[axis], Vt, jr.velocity_t[axis]);
-      jr.velocity_t[axis] += jr.velocity_delta[axis];
+    // Compute move time
+    float move_time = 2 * length / (V + Vt);
 
-    } else V = Vt;
+    if (move_time <= SEGMENT_TIME) return Vt;
 
-    if (JOG_MIN_VELOCITY < V || JOG_MIN_VELOCITY < Vt) done = false;
+    a->initial = V;
+    a->t = a->delta = SEGMENT_TIME / move_time;
+  }
 
+  if (a->t <= 0) return V;
+  if (1 <= a->t) return Vt;
+
+  // Compute quintic Bezier curve
+  V = velocity_curve(a->initial, Vt, a->t);
+  a->t += a->delta;
+
+  return V;
+}
+
+
+static stat_t _exec_jog(mp_buffer_t *bf) {
+  // Load next velocity
+  jr.done = true;
+
+  if (!jr.writing)
+    for (int axis = 0; axis < AXES; axis++) {
+      if (!axis_is_enabled(axis)) continue;
+      jr.axes[axis].changed = _next_axis_velocity(axis);
+    }
+
+  float velocity_sqr = 0;
+
+  // Compute per axis velocities
+  for (int axis = 0; axis < AXES; axis++) {
+    if (!axis_is_enabled(axis)) continue;
+    float V = _compute_axis_velocity(axis);
     velocity_sqr += square(V);
-    jr.velocity[axis] = V * jr.sign[axis];
+    jr.axes[axis].velocity = V * jr.axes[axis].sign;
+    if (JOG_MIN_VELOCITY < V) jr.done = false;
   }
 
   // Check if we are done
-  if (done) {
+  if (jr.done) {
     // Update machine position
     mach_set_position_from_runtime();
     mp_set_cycle(CYCLE_MACHINING); // Default cycle
@@ -151,7 +166,7 @@ static stat_t _exec_jog(mp_buffer_t *bf) {
   float target[AXES];
   for (int axis = 0; axis < AXES; axis++)
     target[axis] = mp_runtime_get_axis_position(axis) +
-      jr.velocity[axis] * SEGMENT_TIME;
+      jr.axes[axis].velocity * SEGMENT_TIME;
 
   // Set velocity and target
   mp_runtime_set_velocity(sqrt(velocity_sqr));
@@ -178,7 +193,7 @@ uint8_t command_jog(int argc, char *argv[]) {
 
   jr.writing = true;
   for (int axis = 0; axis < AXES; axis++)
-    jr.next_velocity[axis] = velocity[axis];
+    jr.axes[axis].next = velocity[axis];
   jr.writing = false;
 
   if (mp_get_cycle() != CYCLE_JOGGING) {
index e83b5832f44192c1eb941e804a658d98a3601bf4..2f080b0c4ef476c2ef0984ad6fb491a4dc679ad8 100644 (file)
@@ -35,6 +35,7 @@
 #include "buffer.h"
 
 #include <stdio.h>
+#include <float.h>
 
 
 /* Sonny's algorithm - simple
@@ -114,7 +115,7 @@ static float _get_junction_vmax(const float a_unit[], const float b_unit[]) {
   for (int axis = 0; axis < AXES; axis++)
     costheta -= a_unit[axis] * b_unit[axis];
 
-  if (costheta < -0.99) return 10000000;  // straight line cases
+  if (costheta < -0.99) return FLT_MAX;   // straight line cases
   if (0.99 < costheta)  return 0;         // reversal cases
 
   // Fuse the junction deviations into a vector sum
@@ -244,8 +245,15 @@ static void _calc_max_velocities(mp_buffer_t *bf, float move_time,
 
   bf->cruise_vmax = bf->length / move_time; // target velocity requested
 
-  float junction_velocity =
-    _get_junction_vmax(mp_buffer_prev(bf)->unit, bf->unit);
+  float junction_velocity = FLT_MAX;
+
+  mp_buffer_t *prev = mp_buffer_prev(bf);
+  while (prev->state != BUFFER_OFF)
+    if (prev->flags & BUFFER_LINE) {
+      _get_junction_vmax(prev->unit, bf->unit);
+      break;
+
+    } else prev = mp_buffer_prev(prev);
 
   bf->entry_vmax = min(bf->cruise_vmax, junction_velocity);
   bf->delta_vmax = mp_get_target_velocity(0, bf->length, bf);
@@ -416,6 +424,7 @@ stat_t mp_aline(const float target[], buffer_flags_t flags, switch_id_t sw,
 
   // Note, the following lines must remain in order.
   bf->line = line;              // Planner needs this when planning steps
+  flags |= BUFFER_LINE;
   bf->flags = flags;            // Move flags
   bf->sw = sw;                  // Seek switche
   mp_plan(bf);                  // Plan block list
index 55e51f44d83665f5e2a857f455f4a0c773711932..6be174e0d927a1e7b4014e2e39eda74ccc10725e 100644 (file)
@@ -605,7 +605,6 @@ void mp_queue_push_nonstop(buffer_cb_t cb, uint32_t line) {
   mp_buffer_t *bp = mp_queue_get_tail();
 
   bp->entry_vmax = bp->cruise_vmax = bp->exit_vmax = INFINITY;
-  copy_vector(bp->unit, bp->prev->unit);
   bp->flags |= BUFFER_REPLANNABLE;
 
   mp_queue_push(cb, line);
index 283b1561b344fea013abb0d06167d296387c4305..3650fb54e8b65ff854855198734414b0f2785a81 100644 (file)
 #include "plan/state.h"
 
 // Axis
-float get_position(int axis) {return mp_runtime_get_work_position(axis);}
+float get_axis_mach_coord(int axis) {return mp_runtime_get_axis_position(axis);}
 
 
-void set_position(int axis, float position) {
+void set_axis_mach_coord(int axis, float position) {
   mach_set_axis_position(axis, position);
 }
 
 
+float get_axis_work_coord(int axis) {return mp_runtime_get_work_position(axis);}
+
+
 // GCode getters
 int32_t get_line() {return mp_runtime_get_line();}
 PGM_P get_unit() {return gs_get_units_pgmstr(mach_get_units());}
index dee84463efc4b7c5993368b7009ec6e43a8a45c4..70fb49a03e36160a1d29062d0099ed9a9ec6d59c 100644 (file)
@@ -68,14 +68,16 @@ 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(home,           hp, float,    MOTORS, 0, 1, "Home position")
+VAR(homed,           h, bool,     MOTORS, 0, 1, "True if axis is homed")
 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")
 VAR(zero_backoff,   zb, float,    MOTORS, 1, 1, "Homing zero backoff")
 
 // Axis
-VAR(position,        p, float,    AXES,   1, 1, "Current axis position")
+VAR(axis_mach_coord, p, float,    AXES,   1, 1, "Axis machine coordinate")
+VAR(axis_work_coord, w, float,    AXES,   0, 1, "Axis work coordinate")
 
 // Spindle
 VAR(spindle_type,   st, uint8_t,  0,      1, 1, "PWM=0 or HUANYANG=1")
index 284f3a8d3ec72d1f460bdfd0741e9ac75c3f2e0a..bbaf7fee79aa5d9144cf6e7155fc91db4ecfbb17 100644 (file)
@@ -1,6 +1,6 @@
 {
   "name": "bbctrl",
-  "version": "0.1.11",
+  "version": "0.1.12",
   "homepage": "https://github.com/buildbotics/rpi-firmware",
   "license": "GPL 3+",
 
index cdc912cb68b2b7d3c1fb1d8894af448f9f2f42fa..8761ae92c6c93b98aaa8a2231af05b1e480a26f2 100755 (executable)
@@ -24,6 +24,6 @@ fi
 
 if $UPDATE_PY; then
     rm -rf /usr/local/lib/python*/dist-packages/bbctrl-*
-    ./setup.py install
+    ./setup.py install --force
     service bbctrl start
 fi
index 8a8ca359085d8a354fc47cbf266bd4cb3c01d2a1..97a104e925ddecc0a444bb7d48fbcca07168970b 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -24,7 +24,11 @@ setup(
             'bbctrl = bbctrl:run'
             ]
         },
-    scripts = ['scripts/update-bbctrl', 'scripts/upgrade-bbctrl'],
+    scripts = [
+        'scripts/update-bbctrl',
+        'scripts/upgrade-bbctrl',
+        'scripts/sethostname',
+        ],
     install_requires = 'tornado sockjs-tornado pyserial pyudev smbus2'.split(),
     zip_safe = False,
     )
index 5256b274c83a91022b9ea9b41c564e672db7d802..fee2ce4c8b1a3f2650b4029de3d5fa6cadc83c7d 100644 (file)
@@ -1,5 +1,31 @@
 script#admin-view-template(type="text/x-template")
   #admin
+    h2 Hostname
+    .pure-form.pure-form-aligned
+      .pure-control-group
+        label(for="hostname") Hostname
+        input(name="hostname", v-model="hostname", @keyup.enter="set_hostname")
+        button.pure-button.pure-button-primary(@click="set_hostname") Set
+
+    h2 Remote SSH User
+    .pure-form.pure-form-aligned
+      .pure-control-group
+        label(for="username") Username
+        input(name="username", v-model="username", @keyup.enter="set_username")
+        button.pure-button.pure-button-primary(@click="set_username") Set
+
+    .pure-form.pure-form-aligned
+      .pure-control-group
+        label(for="current") Current Password
+        input(name="current", v-model="current", type="password")
+      .pure-control-group
+        label(for="pass1") New Password
+        input(name="pass1", v-model="password", type="password")
+      .pure-control-group
+        label(for="pass2") New Password
+        input(name="pass2", v-model="password2", type="password")
+        button.pure-button.pure-button-primary(@click="set_password") Set
+
     h2 Configuration
     button.pure-button.pure-button-primary(@click="backup") Backup
 
@@ -40,3 +66,17 @@ script#admin-view-template(type="text/x-template")
     message(:show.sync="firmwareUpgrading")
       h3(slot="header") Firmware upgrading
       p(slot="body") Please wait. . .
+
+    message(:show.sync="hostnameSet")
+      h3(slot="header") Hostname Set
+      p(slot="body")
+        | Hostname was successfuly set to {{hostname}}.
+        | Poweroff and restart the controller for this change to take effect.
+
+    message(:show.sync="passwordSet")
+      h3(slot="header") Password Set
+      p(slot="body")
+
+    message(:show.sync="usernameSet")
+      h3(slot="header") Username Set
+      p(slot="body")
index d814a0aa59c8c56cb019fe22ff7aa1c0eb23518f..f7faa883e76a85b024914969acddd8bdeda33d2c 100644 (file)
@@ -4,27 +4,73 @@ script#control-view-template(type="text/x-template")
       tr
         th.name Axis
         th.position Position
+        th.absolute Absolute
         th.offset Offset
-        th.errors Errors
         th.actions Actions
 
       each axis in 'xyzabc'
-        tr.axis(class="axis-#{axis}", v-if="enabled('#{axis}')")
+        tr.axis(:class="{'homed': is_homed('#{axis}'), 'axis-#{axis}': true}",
+          v-if="enabled('#{axis}')")
           th.name #{axis}
-          td.position {{state.#{axis}p || 0 | fixed 3}}
-          td.offset {{state.#{axis}o || 0 | fixed 3}}
-          td.errors
-            .fa.fa-hot(v-if="state.#{axis}t", title="Driver overtemp.")
-            .fa.fa-ban(v-if="state.#{axis}s", title="Motor stalled.")
+          td.position {{state.#{axis}w || 0 | fixed 3}}
+          td.absolute {{state.#{axis}p || 0 | fixed 3}}
+          td.offset {{(state.#{axis}w - state.#{axis}p) || 0 | fixed 3}}
           th.actions
-            button.pure-button(title="Zero {{'#{axis}' | upper}} axis.",
-              @click="zero('#{axis}')")
+            button.pure-button(
+              title="Set {{'#{axis}' | upper}} axis position.",
+              @click="show_set_position('#{axis}')")
+              .fa.fa-cog
+
+            button.pure-button(
+              title="Zero {{'#{axis}' | upper}} axis offset.",
+              @click="set_position('#{axis}', state['#{axis}p'])")
               | &empty;
+
             button.pure-button(title="Home {{'#{axis}' | upper}} axis.",
               @click="home('#{axis}')")
               .fa.fa-home
 
-    table.info
+            message(:show.sync="position_msg['#{axis}']")
+              h3(slot="header") Set {{'#{axis}' | upper}} axis position
+
+              div(slot="body")
+                .pure-form
+                  .pure-control-group
+                    label Position
+                    input(v-model="axis_position",
+                      @keyup.enter="set_position('#{axis}', axis_position)")
+                p
+
+              div(slot="footer")
+                button.pure-button(@click="position_msg['#{axis}'] = false")
+                  | Cancel
+
+                button.pure-button.button-success(
+                  @click="set_position('#{axis}', axis_position)") Set
+
+            message(:show.sync="manual_home['#{axis}']")
+              h3(slot="header") Manually home {{'#{axis}' | upper}} axis
+
+              div(slot="body")
+                p Set axis absolute position.
+
+                .pure-form
+                  .pure-control-group
+                    label Absolute
+                    input(v-model="axis_position",
+                      @keyup.enter="set_home('#{axis}', axis_position)")
+
+                p
+
+              div(slot="footer")
+                button.pure-button(@click="manual_home['#{axis}'] = false")
+                  | Cancel
+
+                button.pure-button.button-success(
+                  title="Home {{'#{axis}' | upper}} axis.",
+                  @click="set_home('#{axis}', axis_position)") Set
+
+     table.info
       tr
         th State
         td {{get_state()}}
index 98b16e34d98753723a2754a2aed8d6461c1a0696..fdc3d135c9993630b627ad4a5429fe4a0837f4cd 100644 (file)
@@ -4,8 +4,10 @@ script#message-template(type="text/x-template")
       .modal-container
         .modal-header
           slot(name="header") default header
+
         .modal-body
           slot(name="body") default body
+
         .modal-footer
           slot(name="footer")
             button.pure-button.button-success(@click="show = false") OK
index 9de8c795ced35e8e19604001a77e5813e58c2f97..b85c8fa9dd6ca8ab6ce7efc448601678d279c94f 100644 (file)
@@ -15,7 +15,15 @@ module.exports = {
       confirmReset: false,
       configReset: false,
       firmwareUpgrading: false,
+      hostnameSet: false,
+      usernameSet: false,
+      passwordSet: false,
       latest: '',
+      hostname: '',
+      username: '',
+      current: '',
+      password: '',
+      password2: ''
     }
   },
 
@@ -27,10 +35,57 @@ module.exports = {
   },
 
 
-  ready: function () {},
+  ready: function () {
+    api.get('hostname').done(function (hostname) {
+      this.hostname = hostname;
+    }.bind(this));
+    api.get('remote/username').done(function (username) {
+      this.username = username;
+    }.bind(this));
+  },
 
 
   methods: {
+    set_hostname: function () {
+      api.put('hostname', {hostname: this.hostname}).done(function () {
+        this.hostnameSet = true;
+      }.bind(this)).fail(function (error) {
+        alert('Set hostname failed: ' + JSON.stringify(error));
+      })
+    },
+
+
+    set_username: function () {
+      api.put('remote/username', {username: this.username}).done(function () {
+        this.usernameSet = true;
+      }.bind(this)).fail(function (error) {
+        alert('Set username failed: ' + JSON.stringify(error));
+      })
+    },
+
+
+    set_password: function () {
+      if (this.password != this.password2) {
+        alert('Passwords to not match');
+        return;
+      }
+
+      if (this.password.length < 6) {
+        alert('Password too short');
+        return;
+      }
+
+      api.put('remote/password', {
+        current: this.current,
+        password: this.password
+      }).done(function () {
+        this.passwordSet = true;
+      }.bind(this)).fail(function (error) {
+        alert('Set password failed: ' + JSON.stringify(error));
+      })
+    },
+
+
     backup: function () {
       document.getElementById('download-target').src = '/api/config/download';
     },
index b182a0baedee62bbaa3c210d136488c709fdeca6..2ac67ec7e1584040ec7ee60c44358f48fae452d7 100644 (file)
@@ -36,7 +36,11 @@ module.exports = {
       history: [],
       console: [],
       speed_override: 1,
-      feed_override: 1
+      feed_override: 1,
+      manual_home: {x: false, y: false, z: false, a: false, b: false, c: false},
+      position_msg:
+      {x: false, y: false, z: false, a: false, b: false, c: false},
+      axis_position: 0
     }
   },
 
@@ -94,16 +98,33 @@ module.exports = {
     },
 
 
-    enabled: function (axis) {
+    get_axis_motor_id: function (axis) {
       var axis = axis.toLowerCase();
 
       for (var i = 0; i < this.config.motors.length; i++) {
         var motor = this.config.motors[i];
-        if (motor.axis.toLowerCase() == axis &&
-            (motor.enabled || typeof motor.enabled == 'undefined')) return true;
+        if (motor.axis.toLowerCase() == axis) return i;
       }
 
-      return false;
+      return -1;
+    },
+
+
+    get_axis_motor: function (axis) {
+      var motor = this.get_axis_motor_id(axis);
+      if (motor != -1) return this.config.motors[motor];
+    },
+
+
+    enabled: function (axis) {
+      var motor = this.get_axis_motor(axis);
+      return typeof motor != 'undefined' && motor['power-mode'] != 'disabled';
+    },
+
+
+    is_homed: function (axis) {
+      var motor = this.get_axis_motor_id(axis);
+      return motor != -1 && this.state[motor + 'h'];
     },
 
 
@@ -199,11 +220,36 @@ module.exports = {
     },
 
 
-    home: function () {api.put('home')},
+    home: function (axis) {
+      var motor = this.get_axis_motor(axis);
+      if (motor['homing-mode'] == 'manual') {
+        this.axis_position = this.state[axis + 'w'];
+        this.manual_home[axis] = true;
 
+      } else api.put('home' + (typeof axis == 'undefined' ? '' : ('/' + axis)));
+    },
 
-    zero: function (axis) {
-      api.put('zero' + (typeof axis == 'undefined' ? '' : '/' + axis));
+
+    set_home: function (axis, position) {
+      this.manual_home[axis] = false;
+      api.put('home/' + axis + '/set', {position: parseFloat(position)});
+    },
+
+
+    show_set_position: function (axis) {
+      this.axis_position = 0;
+      this.position_msg[axis] = true;
+    },
+
+
+    get_offset: function (axis) {
+      return this.state[axis + 'w'] - this.state[axis + 'p'];
+    },
+
+
+    set_position: function (axis, position) {
+      this.position_msg[axis] = false;
+      api.put('position/' + axis, {position: parseFloat(position)});
     },
 
 
@@ -225,9 +271,7 @@ module.exports = {
     step: function () {api.put('step/' + this.file).done(this.update)},
 
 
-    override_feed: function () {
-      api.put('override/feed/' + this.feed_override)
-    },
+    override_feed: function () {api.put('override/feed/' + this.feed_override)},
 
 
     override_speed: function () {
index 244a046129f297f81073f1894f831ce3d31cfae3..908d53da84bf296a255e44338f53f0944e9f4f31 100644 (file)
@@ -5,7 +5,7 @@ var Sock = function (url, retry, timeout) {
   if (!(this instanceof Sock)) return new Sock(url, retry);
 
   if (typeof retry == 'undefined') retry = 2000;
-  if (typeof timeout == 'undefined') timeout = 5000;
+  if (typeof timeout == 'undefined') timeout = 8000;
 
   this.url = url;
   this.retry = retry;
index 1aa7cc217b9e0363443a9be9f5c2bdf6c3b02626..b6e124acf033ab3224378c4ee3a9a19ee9068c67 100644 (file)
@@ -254,6 +254,8 @@ int main() {
   GATE_DDR = (1 << GATE1_PIN) | (1 << 2);
   GATE_PORT = (1 << GATE1_PIN) | (1 << 2);
 
+  while (true) continue;
+
   // Start ADC
   adc_conversion();
 
index 4e6bf58316fd56635943fe5fb376341ed2619b2a..53e1eb15b63500330e31b8ac81b4357d4be56d70 100644 (file)
@@ -41,7 +41,12 @@ class APIHandler(tornado.web.RequestHandler):
 
     def write_error(self, status_code, **kwargs):
         e = {}
-        e['message'] = str(kwargs['exc_info'][1])
+
+        if 'message' in kwargs: e['message'] = kwargs['message']
+        elif 'exc_info' in kwargs:
+            e['message'] = str(kwargs['exc_info'][1])
+        else: e['message'] = 'Unknown error'
+
         e['code'] = status_code
 
         self.write_json(e)
index c00b7263f040e7262a756db1d3f3583c7b39305e..8a67423988ecc0b052deeb765937342db527924d 100644 (file)
@@ -21,13 +21,27 @@ I2C_STEP           = 6
 I2C_FLUSH          = 7
 I2C_REPORT         = 8
 I2C_REBOOT         = 9
-I2C_ZERO           = 10
 
 
 machine_state_vars = '''
   xp yp zp ap bp cp u s f t fm pa cs ao pc dm ad fo so mc fc
 '''.split()
 
+# Axis homing procedure
+#   - Set axis unhomed
+#   - Find switch
+#   - Backoff switch
+#   - Find switch more accurately
+#   - Backoff to machine zero
+#   - Set axis home position
+axis_homing_procedure = '''
+  G28.2 %(axis)c0 F[#<%(axis)c.sv>]
+  G38.6 %(axis)c[#<%(axis)c.hd> * [#<%(axis)c.tm> - #<%(axis)c.tn>]]
+  G38.8 %(axis)c[#<%(axis)c.hd> * -#<%(axis)c.lb>] F[#<%(axis)c.lv>]
+  G38.6 %(axis)c[#<%(axis)c.hd> * #<%(axis)c.lb> * 1.5]
+  G0 %(axis)c[#<%(axis)c.hd> * -#<%(axis)c.zb> + #<%(axis)cp>]
+  G28.3 %(axis)c[#<%(axis)c.hp>]
+'''
 
 class AVR():
     def __init__(self, ctrl):
@@ -267,7 +281,18 @@ class AVR():
         self.queue_command('${}{}={}'.format(index, code, value))
 
 
-    def home(self): log.debug('NYI')
+    def home(self, axis, position = None):
+        if self.stream is not None: raise Exception('Busy, cannot home')
+
+        if position is not None:
+            self.queue_command('G28.3 %c%f' % (axis, position))
+
+        else:
+            gcode = axis_homing_procedure % {'axis': axis}
+            for line in gcode.splitlines():
+                self.queue_command(line.strip())
+
+
     def estop(self): self._i2c_command(I2C_ESTOP)
     def clear(self): self._i2c_command(I2C_CLEAR)
 
@@ -292,7 +317,12 @@ class AVR():
         # Resume processing once current queue of GCode commands has flushed
         self.queue_command('$resume')
 
+
     def pause(self): self._i2c_command(I2C_PAUSE)
     def unpause(self): self._i2c_command(I2C_RUN)
     def optional_pause(self): self._i2c_command(I2C_OPTIONAL_PAUSE)
-    def zero(self, axis = None): self._i2c_command(I2C_ZERO, byte = axis)
+
+
+    def set_position(self, axis, position):
+        if self.stream is not None: raise Exception('Busy, cannot set position')
+        self.queue_command('G92 %c%f' % (axis, position))
index 559030095ba2f9fe41bee4ce6dc45c50e9e8b127..b4a23b3ec7f9936e973544aced45563ba813cfa0 100644 (file)
@@ -64,7 +64,11 @@ class Config(object):
     def encode_cmd(self, index, value, spec):
         if not 'code' in spec: return
 
-        if spec['type'] == 'enum': value = spec['values'].index(value)
+        if spec['type'] == 'enum':
+            if value in spec['values']:
+                value = spec['values'].index(value)
+            else: value = spec['default']
+
         elif spec['type'] == 'bool': value = 1 if value else 0
         elif spec['type'] == 'percent': value /= 100.0
 
index 27e60431c576bc28a6124f02e48663353ae0227c..5e2749e8ff0a1796ace1a9c70f2df5d2009c2930 100644 (file)
@@ -1,4 +1,5 @@
 import logging
+import subprocess
 
 import bbctrl
 
@@ -6,6 +7,25 @@ import bbctrl
 log = logging.getLogger('Ctrl')
 
 
+class IPPage(bbctrl.LCDPage):
+    def update(self):
+        p = subprocess.Popen(['hostname', '-I'], stdout = subprocess.PIPE)
+        ips = p.communicate()[0].decode('utf-8').split()
+
+        p = subprocess.Popen(['hostname'], stdout = subprocess.PIPE)
+        hostname = p.communicate()[0].decode('utf-8').strip()
+
+        self.clear()
+
+        self.text('Host: %s' % hostname[0:14], 0, 0)
+
+        for i in range(min(3, len(ips))):
+            self.text('IP: %s' % ips[i], 0, i + 1)
+
+
+    def activate(self): self.update()
+
+
 class Ctrl(object):
     def __init__(self, args, ioloop):
         self.args = args
@@ -20,3 +40,5 @@ class Ctrl(object):
         self.pwr = bbctrl.Pwr(self)
 
         self.avr.connect()
+
+        self.lcd.add_new_page(IPPage(self.lcd))
index ba072db6351260a86a95a69702b4ce4ac3d7d36f..5b270fcdb7ee32b6d5e680cd94ca2cabab35e215 100644 (file)
@@ -7,7 +7,7 @@ import tornado.ioloop
 log = logging.getLogger('LCD')
 
 
-class Page:
+class LCDPage:
     def __init__(self, lcd, text = None):
         self.lcd = lcd
         self.data = lcd.new_screen()
@@ -16,6 +16,10 @@ class Page:
             self.text(text, (lcd.width - len(text)) // 2, 1)
 
 
+    def activate(self): pass
+    def deactivate(self): pass
+
+
     def put(self, c, x, y):
         y += x // self.lcd.width
         x %= self.lcd.width
@@ -31,6 +35,12 @@ class Page:
             self.put(c, x, y)
             x += 1
 
+
+    def clear(self):
+        self.data = self.lcd.new_screen()
+        self.lcd.redraw = True
+
+
     def shift_left(self): pass
     def shift_right(self): pass
     def shift_up(self): pass
@@ -63,7 +73,7 @@ class LCD:
 
     def set_message(self, msg):
         try:
-            self.load_page(Page(self, msg))
+            self.load_page(LCDPage(self, msg))
             self._update()
         except IOError as e:
             log.error('LCD communication failed: %s' % e)
@@ -73,12 +83,12 @@ class LCD:
         return [[' ' for y in range(self.height)] for x in range(self.width)]
 
 
-    def new_page(self): return Page(self)
+    def new_page(self): return LCDPage(self)
     def add_page(self, page): self.pages.append(page)
 
 
-    def add_new_page(self):
-        page = self.new_page()
+    def add_new_page(self, page = None):
+        if page is None: page = self.new_page()
         page.id = len(self.pages)
         self.add_page(page)
         return page
@@ -86,6 +96,8 @@ class LCD:
 
     def load_page(self, page):
         if self.page != page:
+            if self.page is not None: self.page.deactivate()
+            page.activate()
             self.page = page
             self.redraw = True
             self.update()
index 0e9be6ee3ed56f456746551623d664c6c5ab2a43..397f84eb1720258d025909cdc0f5c40e393d7ef5 100644 (file)
@@ -5,7 +5,7 @@ import bbctrl
 log = logging.getLogger('PWR')
 
 
-# Must match regs in pwr firmare
+# Must match regs in pwr firmware
 TEMP_REG        = 0
 VIN_REG         = 1
 VOUT_REG        = 2
index 72448bbe1194ca097e0a096ab16f76dffdabd37f..57b7a39971d95f12e7e2cd36922371d27a9fbfe5 100644 (file)
@@ -7,6 +7,7 @@ import logging
 import datetime
 import shutil
 import tarfile
+import subprocess
 
 import bbctrl
 
@@ -14,6 +15,89 @@ import bbctrl
 log = logging.getLogger('Web')
 
 
+def call_get_output(cmd):
+    p = subprocess.Popen(cmd, stdout = subprocess.PIPE)
+    s = p.communicate()[0].decode('utf-8')
+    if p.returncode: raise Exception('Command failed')
+    return s
+
+
+class HostnameHandler(bbctrl.APIHandler):
+    def get(self):
+        p = subprocess.Popen(['hostname'], stdout = subprocess.PIPE)
+        hostname = p.communicate()[0].decode('utf-8').strip()
+        self.write_json(hostname)
+
+
+    def put(self):
+        if 'hostname' in self.json:
+            if subprocess.call(['/usr/local/bin/sethostname',
+                                self.json['hostname']]) == 0:
+                self.write_json('ok')
+                return
+
+        self.send_error(400, message = 'Failed to set hostname: %s' % self.json)
+
+
+def get_username():
+    return call_get_output(['getent', 'passwd', '1001']).split(':')[0]
+
+
+class UsernameHandler(bbctrl.APIHandler):
+    def get(self): self.write_json(get_username())
+
+
+    def put(self):
+        if 'username' in self.json:
+            username = get_username()
+
+            if subprocess.call(['usermod', '-l', self.json['username'],
+                                username]) == 0:
+                self.write_json('ok')
+                return
+
+        self.send_error(400, message = 'Failed to set username: %s' % self.json)
+
+
+class PasswordHandler(bbctrl.APIHandler):
+    def put(self):
+        if 'current' in self.json and 'password' in self.json:
+            # Get current user name
+            username = get_username()
+
+            # Get current password
+            s = call_get_output(['getent', 'shadow', username])
+            password = s.split(':')[1].split('$')
+
+            # Check password type
+            if password[1] != '1':
+                self.send_error(400, message =
+                                "Don't know how to update non-MD5 password")
+                return
+
+            # Check current password
+            cmd = ['openssl', 'passwd', '-salt', password[2], '-1',
+                   self.json['current']]
+            s = call_get_output(cmd).strip()
+            if s.split('$') != password:
+                print('%s != %s' % (s.split('$'), password))
+                self.send_error(401, message = 'Wrong password')
+                return
+
+            # Set password
+            s = '%s:%s' % (username, self.json['password'])
+            s = s.encode('utf-8')
+
+            p = subprocess.Popen(['chpasswd', '-c', 'MD5'],
+                                 stdin = subprocess.PIPE)
+            p.communicate(input = s)
+
+            if p.returncode == 0:
+                self.write_json('ok')
+                return
+
+        self.send_error(400, message = 'Failed to set password')
+
 
 class ConfigLoadHandler(bbctrl.APIHandler):
     def get(self): self.write_json(self.ctrl.config.load())
@@ -55,20 +139,26 @@ class FirmwareUpdateHandler(bbctrl.APIHandler):
         with open('firmware/update.tar.bz2', 'wb') as f:
             f.write(firmware['body'])
 
-        import subprocess
-        ret = subprocess.Popen(['update-bbctrl'])
+        subprocess.Popen(['/usr/local/bin/update-bbctrl'])
 
         self.write_json('ok')
 
 
 class UpgradeHandler(bbctrl.APIHandler):
-    def put_ok(self):
-        import subprocess
-        ret = subprocess.Popen(['upgrade-bbctrl'])
+    def put_ok(self): subprocess.Popen(['/usr/local/bin/upgrade-bbctrl'])
 
 
 class HomeHandler(bbctrl.APIHandler):
-    def put_ok(self): self.ctrl.avr.home()
+    def put_ok(self, axis, set_home):
+        if axis is not None: axis = ord(axis[1:2].lower())
+
+        if set_home:
+            if not 'position' in self.json:
+                raise Exception('Missing "position"')
+
+            self.ctrl.avr.home(axis, self.json['position'])
+
+        else: self.ctrl.avr.home(axis)
 
 
 class StartHandler(bbctrl.APIHandler):
@@ -103,10 +193,9 @@ class StepHandler(bbctrl.APIHandler):
     def put_ok(self, path): self.ctrl.avr.step(path)
 
 
-class ZeroHandler(bbctrl.APIHandler):
+class PositionHandler(bbctrl.APIHandler):
     def put_ok(self, axis):
-        if axis is not None: axis = ord(axis[1:].lower())
-        self.ctrl.avr.zero(axis)
+        self.ctrl.avr.set_position(ord(axis.lower()), self.json['position'])
 
 
 class OverrideFeedHandler(bbctrl.APIHandler):
@@ -189,6 +278,9 @@ class Web(tornado.web.Application):
 
         handlers = [
             (r'/websocket', WSConnection),
+            (r'/api/hostname', HostnameHandler),
+            (r'/api/remote/username', UsernameHandler),
+            (r'/api/remote/password', PasswordHandler),
             (r'/api/config/load', ConfigLoadHandler),
             (r'/api/config/download', ConfigDownloadHandler),
             (r'/api/config/save', ConfigSaveHandler),
@@ -196,7 +288,7 @@ class Web(tornado.web.Application):
             (r'/api/firmware/update', FirmwareUpdateHandler),
             (r'/api/upgrade', UpgradeHandler),
             (r'/api/file(/.+)?', bbctrl.FileHandler),
-            (r'/api/home', HomeHandler),
+            (r'/api/home(/[xyzabcXYZABC](/set)?)?', HomeHandler),
             (r'/api/start(/.+)', StartHandler),
             (r'/api/estop', EStopHandler),
             (r'/api/clear', ClearHandler),
@@ -205,7 +297,7 @@ class Web(tornado.web.Application):
             (r'/api/unpause', UnpauseHandler),
             (r'/api/pause/optional', OptionalPauseHandler),
             (r'/api/step(/.+)', StepHandler),
-            (r'/api/zero(/[xyzabcXYZABC])?', ZeroHandler),
+            (r'/api/position/([xyzabcXYZABC])', PositionHandler),
             (r'/api/override/feed/([\d.]+)', OverrideFeedHandler),
             (r'/api/override/speed/([\d.]+)', OverrideSpeedHandler),
             (r'/(.*)', StaticFileHandler,
index 0b67bef129beaedc16504610e4ca86ad5bbabf9f..e2712356dea77a6fb653e3033ab9d622ea3a2734 100755 (executable)
@@ -13,7 +13,7 @@ from bbctrl.APIHandler import APIHandler
 from bbctrl.FileHandler import FileHandler
 from bbctrl.GCodeStream import GCodeStream
 from bbctrl.Config import Config
-from bbctrl.LCD import LCD
+from bbctrl.LCD import LCD, LCDPage
 from bbctrl.AVR import AVR
 from bbctrl.Web import Web
 from bbctrl.Jog import Jog
index 14f3bb1a8238b75f01502b680f02b450e338e44e..613e96d33b49194413b8cef1d1397b60e260cc6c 100644 (file)
@@ -17,7 +17,7 @@
         "code": "pm"
       },
       "drive-current": {
-        "type": "aloat",
+        "type": "float",
         "min": 0,
         "max": 8,
         "unit": "amps",
@@ -28,7 +28,7 @@
         "type": "float",
         "min": 0,
         "max": 8,
-        "unit": "Amps",
+        "unit": "amps",
         "default": 0,
         "code": "ic"
       }
       "homing-mode": {
         "type": "enum",
         "values": [
-          "disabled", "stall-min", "stall-max", "switch-min", "switch-max"
+          "manual", "stall-min", "stall-max", "switch-min", "switch-max"
         ],
-        "default": "disabled",
+        "default": "manual",
         "code": "ho"
       },
       "search-velocity": {
index ee556f2f543e52b390ffc21777934f639effa7fd..7cc3302d68a4159dfb06a498f114464a5ce5e9f3 100644 (file)
@@ -180,6 +180,10 @@ body
       font-family Courier
 
     .axis
+      &.homed
+        background-color #ccffcc
+        color #000
+
       .name
         text-transform capitalize
 
@@ -190,12 +194,8 @@ body
       .position
         width 99%
 
-      .offset
-        min-width 8em
-
-      .errors
+      .absolute, .offset
         min-width 6em
-        white-space normal
 
   .jog svg
     text
@@ -432,12 +432,20 @@ body
       font-family Helvetica, Arial, sans-serif
 
 
+.modal-header
+  text-decoration underline
+
+.modal-footer
+  text-align right
+
 .modal-enter, .modal-leave
   opacity 0
 
-.modal-enter .modal-container, .modal-leave .modal-container
+.modal-enter .modal-container
   transform scale(1.1)
 
+.modal-leave .modal-container
+  transform scale(0.9)
 
 label.file-upload
   display inline-block
@@ -461,7 +469,7 @@ label.file-upload
           font-size 24pt
           line-height 24pt
 
-      .offset, .errors
+      .absolute, .offset
         display none
 
     > *:nth-of-type(n)