- Check that axis dimensions fit path plan dimensions.
- Show machine working envelope in path plan viewer.
- Don't reload browser view on reconnect unless controller has reloaded.
+ - Increased max switch backoff search distance.
+ - Improvements for LASER raster GCodes.
+ - Fixed major bug in command queuing.
## v0.3.28
- Show step rate on motor configuration page.
bool active;
uint32_t id;
uint32_t last_empty;
- unsigned count;
- stat_t parse_error;
- int col;
+ volatile uint16_t count;
float position[AXES];
} cmd = {0,};
#undef CMD
-static uint16_t _space() {return sync_q_space();}
-
-
static bool _is_synchronous(char code) {
switch (code) {
#define CMD(CODE, NAME, SYNC, ...) case COMMAND_##NAME: return SYNC;
IF(SYNC)(case COMMAND_##NAME: return command_##NAME##_size();)
#include "command.def"
#undef CMD
- default: break;
}
return 0;
IF(SYNC)(case COMMAND_##NAME: command_##NAME##_exec(data); break;)
#include "command.def"
#undef CMD
- default: break;
}
}
uint8_t *data = (uint8_t *)_data;
unsigned size = _size(code);
+ if (!_is_synchronous(code)) estop_trigger(STAT_Q_INVALID_PUSH);
+ if (sync_q_space() <= size) estop_trigger(STAT_Q_OVERRUN);
+
sync_q_push(code);
for (unsigned i = 0; i < size; i++) sync_q_push(*data++);
if (_is_synchronous(*block)) {
if (estop_triggered()) status = STAT_MACHINE_ALARMED;
else if (state_is_flushing()) status = STAT_NOP; // Flush command
- else if (state_is_resuming() || _space() < _size(*block))
+ else if (state_is_resuming() || sync_q_space() <= _size(*block))
return false; // Wait
}
}
+char command_peek() {return (char)(cmd.count ? sync_q_peek() : 0);}
+
+
+uint8_t *command_next() {
+ if (!cmd.count) return 0;
+ cmd.count--;
+
+ if (sync_q_empty()) estop_trigger(STAT_Q_UNDERRUN);
+
+ static uint8_t data[INPUT_BUFFER_LEN];
+
+ data[0] = sync_q_next();
+
+ if (!_is_synchronous((char)data[0])) estop_trigger(STAT_INVALID_QCMD);
+
+ unsigned size = _size((char)data[0]);
+ for (unsigned i = 0; i < size; i++)
+ data[i + 1] = sync_q_next();
+
+ return data;
+}
+
+
// Returns true if command queued
// Called by exec.c from low-level interrupt
bool command_exec() {
if (!exec_get_velocity() && cmd.count < EXEC_FILL_TARGET &&
!rtc_expired(cmd.last_empty + EXEC_DELAY)) return false;
- char code = (char)sync_q_next();
- unsigned size = _size(code);
-
- static uint8_t data[INPUT_BUFFER_LEN];
- for (unsigned i = 0; i < size; i++)
- data[i] = sync_q_next();
-
- cmd.count--;
+ uint8_t *data = command_next();
state_running();
- _exec_cb(code, data);
+ _exec_cb((char)*data, data + 1);
return true;
}
CMD('s', seek, 1) // [switch][flags:active|error]
CMD('a', set_axis, 1) // [axis][position] Set axis position
CMD('l', line, 1) // [targetVel][maxJerk][axes][times]
+CMD('%', sync_speed, 1) // [offset][speed]
CMD('I', input, 1) // [a|d][port][mode][timeout] Read input
CMD('d', dwell, 1) // [seconds]
CMD('P', pause, 1) // [type] Pause control
void command_set_position(const float position[AXES]);
void command_get_position(float position[AXES]);
void command_reset_position();
+char command_peek();
+uint8_t *command_next();
bool command_exec();
// Serial settings
-#define SERIAL_BAUD USART_BAUD_115200
+#define SERIAL_BAUD USART_BAUD_230400
#define SERIAL_PORT USARTC0
#define SERIAL_DRE_vect USARTC0_DRE_vect
#define SERIAL_RXC_vect USARTC0_RXC_vect
#include "exec.h"
#include "axis.h"
#include "command.h"
+#include "spindle.h"
#include "util.h"
#include "SCurve.h"
} line_t;
+typedef struct {
+ float offset;
+ float speed;
+} speed_t;
+
+
static struct {
line_t line;
float iA; // Initial section acceleration
float jerk;
float lV; // Last velocity
+
+ speed_t speed;
} l;
// Don't allow overshoot
if (l.line.length < d) d = l.line.length;
+ // Handle syncronous speeds
+ if (l.speed.offset < 0 && command_peek() == COMMAND_sync_speed)
+ l.speed = *(speed_t *)(command_next() + 1);
+
+ if (0 <= l.speed.offset && l.speed.offset <= d) {
+ spindle_set_speed(l.speed.speed);
+ l.speed.offset = -1;
+ }
+
// Check if section complete
if (t == section_time) {
if (_section_next()) {
void command_line_exec(void *data) {
l.line = *(line_t *)data;
+ l.speed.offset = -1;
+
// Setup first section
l.seg = 0;
l.iD = 0;
// Set callback
exec_set_cb(_line_exec);
}
+
+
+stat_t command_sync_speed(char *cmd) {
+ speed_t s;
+
+ cmd++; // Skip command code
+
+ // Get target velocity
+ if (!decode_float(&cmd, &s.offset)) return STAT_BAD_FLOAT;
+ if (!decode_float(&cmd, &s.speed)) return STAT_BAD_FLOAT;
+
+ // Queue
+ command_push(COMMAND_sync_speed, &s);
+
+ return STAT_OK;
+}
+
+
+unsigned command_sync_speed_size() {return sizeof(speed_t);}
+void command_sync_speed_exec(void *data) {} // Should not get here
STAT_MSG(STEPPER_NOT_READY, "Stepper driver not ready for move")
STAT_MSG(SHORT_SEG_TIME, "Short segment time")
STAT_MSG(MODBUS_BUF_LENGTH, "Modbus invalid buffer length")
+STAT_MSG(INVALID_QCMD, "Invalid command in queue")
+STAT_MSG(Q_OVERRUN, "Command queue overrun")
+STAT_MSG(Q_UNDERRUN, "Command queue underrun")
+STAT_MSG(Q_INVALID_PUSH, "Invalid command pushed to queue")
// Disable
if (!speed || estop_triggered()) {
- TIMER_PWM.CTRLA = 0;
+ TIMER_PWM.CTRLB = 0; // Disable clock control of pin
OUTCLR_PIN(SPIN_PWM_PIN);
_set_enable(false);
return;
// 100% duty
if (speed == 1 && spindle.max_duty == 1) {
- TIMER_PWM.CTRLB = 0;
+ TIMER_PWM.CTRLB = 0; // Disable clock control of pin
OUTSET_PIN(SPIN_PWM_PIN);
return;
}
case STAT_OK: // Move executed
if (!st.move_queued)
- estop_trigger(STAT_EXPECTED_MOVE); // No move was queued
+ estop_trigger(STAT_EXPECTED_MOVE); // No move was queued
st.move_queued = false;
st.move_ready = true;
break;
SEEK = 's'
SET_AXIS = 'a'
LINE = 'l'
+SYNC_SPEED = '%'
INPUT = 'I'
DWELL = 'd'
PAUSE = 'P'
def set_axis(axis, position): return SET_AXIS + axis + encode_float(position)
-def line(target, exitVel, maxAccel, maxJerk, times):
+def line(target, exitVel, maxAccel, maxJerk, times, speeds):
cmd = LINE
cmd += encode_float(exitVel)
if times[i]:
cmd += str(i) + encode_float(times[i] / 60000) # to mins
+ # Speeds
+ for speed in speeds:
+ cmd += '\n' + sync_speed(speed[0], speed[1])
+
return cmd
-def speed(speed): return '#s=:' + encode_float(speed)
+def speed(speed): return set_float('s', speed)
+
+
+def sync_speed(offset, speed):
+ return SYNC_SPEED + encode_float(offset) + encode_float(speed)
def input(port, mode, timeout):
G28.2 %(axis)s0 F[#<_%(axis)s_search_velocity>]
G38.6 %(axis)s[#<_%(axis)s_home_travel>]
G38.8 %(axis)s[#<_%(axis)s_latch_backoff>] F[#<_%(axis)s_latch_velocity>]
- G38.6 %(axis)s[#<_%(axis)s_latch_backoff> * -1.5]
+ G38.6 %(axis)s[#<_%(axis)s_latch_backoff> * -8]
G91 G0 G53 %(axis)s[#<_%(axis)s_zero_backoff>]
G90 G28.3 %(axis)s[#<_%(axis)s_home_position>]
'''
if type == 'line':
return Cmd.line(block['target'], block['exit-vel'],
block['max-accel'], block['max-jerk'],
- block['times'])
+ block['times'], block.get('speeds', []))
if type == 'set':
name, value = block['name'], block['value']
help = 'HTTP address to bind')
parser.add_argument('-s', '--serial', default = '/dev/ttyAMA0',
help = 'Serial device')
- parser.add_argument('-b', '--baud', default = 115200, type = int,
+ parser.add_argument('-b', '--baud', default = 230400, type = int,
help = 'Serial baud rate')
parser.add_argument('--i2c-port', default = 1, type = int,
help = 'I2C port')