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)
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
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;
+}
#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"
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);
#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
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;
_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)) {
#pragma once
+float parse_gcode_number(char **p);
float parse_gcode_expression(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;
}
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,
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);
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;
}
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);
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
/*** 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
*
/*** 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);
}
-/* 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
}
-/* 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);
// 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;
* 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);
}
/// 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] -
/// G92.1
void mach_reset_origin_offsets() {
mach.origin_offset_enable = false;
+
for (int axis = 0; axis < AXES; axis++)
mach.origin_offset[axis] = 0;
}
// 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);
// 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;
}
}
-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;
}
/// 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);
}
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[]);
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();
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")
#include "pgmspace.h"
#include "plan/runtime.h"
-#include "plan/calibrate.h"
#include <util/delay.h>
#include "config.h"
#include "util.h"
+#include <plan/state.h>
+
#include <math.h>
#include <stdbool.h>
#include <string.h>
// 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
// 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);
+}
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();
ex.tail_length = available_length;
ex.exit_velocity = 0;
}
+
+ // Don't error if seek was stopped
+ bf->flags &= ~BUFFER_SEEK_ERROR;
}
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;
}
// 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
// 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
}
}
-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;
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);
}
\******************************************************************************/
#pragma once
-
-#include <stdbool.h>
-
-
-bool mp_jog_busy();
(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];
*
* 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)
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;
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};
mp_state_t state;
mp_cycle_t cycle;
mp_hold_reason_t hold_reason;
+ bool pause;
bool hold_requested;
bool flush_requested;
}
+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);
}
-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;}
// 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
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();
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__)
#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) {
// 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
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) {
// 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
// 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
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
// 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")
{
"name": "bbctrl",
- "version": "0.1.10",
+ "version": "0.1.11",
"homepage": "https://github.com/buildbotics/rpi-firmware",
"license": "GPL 3+",
#!/bin/bash
-UPDATE=$(pwd)/update.tar.bz2
+UPDATE=/var/lib/bbctrl/firmware/update.tar.bz2
if [ ! -e "$UPDATE" ]; then
echo "Missing $UPDATE"
./scripts/install.sh "$*"
cd -
-rm -rf /tmp/update
+rm -rf /tmp/update $UPDATE
'bbctrl = bbctrl:run'
]
},
+ scripts = ['scripts/update-bbctrl', 'scripts/upgrade-bbctrl'],
install_requires = 'tornado sockjs-tornado pyserial pyudev smbus2'.split(),
zip_safe = False,
)
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
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
}
+function escapeHTML(s) {
+ var entityMap = {'&': '&', '<': '<', '>': '>'};
+ return String(s).replace(/[&<>]/g, function (s) {return entityMap[s];});
+}
+
+
module.exports = {
template: '#control-view-template',
props: ['config', 'state'],
last_file: '',
files: [],
axes: 'xyzabc',
- gcode: '',
- history: '',
+ gcode: [],
+ history: [],
console: [],
speed_override: 1,
feed_override: 1
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();
},
if (!file || this.files.indexOf(file) == -1) {
this.file = '';
- this.gcode = '';
+ this.gcode = [];
return;
}
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);
import sockjs.tornado
import logging
import datetime
+import shutil
+import tarfile
import bbctrl
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
(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),
"code": "pm"
},
"drive-current": {
- "type": "float",
+ "type": "aloat",
"min": 0,
"max": 8,
- "unit": "A",
+ "unit": "amps",
"default": 2,
"code": "dc"
},
"type": "float",
"min": 0,
"max": 8,
- "unit": "A",
+ "unit": "Amps",
"default": 0,
"code": "ic"
}
margin 0
.gcode, .history
+ font-family courier
clear both
overflow auto
width 100%
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
&.highlight
background-color #eaeaea
+ .history ul li
+ cursor pointer
+
.mdi
clear both
white-space nowrap