Working pause/unpause/stop
authorJoseph Coffland <joseph@cauldrondevelopment.com>
Sat, 6 Jan 2018 01:33:48 +0000 (17:33 -0800)
committerJoseph Coffland <joseph@cauldrondevelopment.com>
Sun, 14 Jan 2018 23:42:39 +0000 (15:42 -0800)
13 files changed:
src/avr/src/command.def
src/avr/src/command.h
src/avr/src/commands.c
src/avr/src/hardware.c
src/avr/src/hardware.h
src/avr/src/jog.c
src/avr/src/line.c
src/avr/src/motor.c
src/avr/src/state.c
src/avr/src/state.h
src/py/bbctrl/AVR.py
src/py/bbctrl/Cmd.py
src/py/bbctrl/Planner.py

index b4ecdb9cb627298d9d9ea830bf47ce8e23565e23..6a674b5a4d1e83f7b90555a75a253dba47b2b915 100644 (file)
@@ -34,6 +34,7 @@ CMD('d', dwell,     1, "[seconds]")
 CMD('o', out,       1, "Output")
 CMD('p', opt_pause, 1, "Set an optional pause")
 CMD('P', pause,     0, "[optional]")
+CMD('U', unpause,   0, "Unpause")
 CMD('j', jog,       0, "[axes]")
 CMD('h', help,      0, "Print this help screen")
 CMD('r', report,    0, "<0|1>[var] Enable or disable var reporting")
index 613eb1a60e64eaf9aaaee42fbe085b1dd845d736..4a93d1f45f0789e6846dfafc9344373015a01260 100644 (file)
@@ -27,6 +27,7 @@
 
 #pragma once
 
+#include "config.h"
 #include "status.h"
 
 #include <stdbool.h>
@@ -41,11 +42,13 @@ typedef enum {
 
 
 void command_init();
-bool command_is_busy();
 bool command_is_active();
 unsigned command_get_count();
 void command_print_help();
 void command_flush_queue();
 void command_push(char code, void *data);
 bool command_callback();
+void command_set_position(const float position[AXES]);
+void command_get_position(float position[AXES]);
+void command_reset_position();
 bool command_exec();
index cceec0e904f5114b9050a7d0b73905ba1bd966af..c9b11e2dacc8b2f4eaaa00a0d46c128c70aba4cc 100644 (file)
@@ -85,24 +85,6 @@ stat_t command_reboot(char *cmd) {
 }
 
 
-stat_t command_resume(char *cmd) {
-  state_request_resume();
-  return STAT_OK;
-}
-
-
-stat_t command_step(char *cmd) {
-  state_request_step();
-  return STAT_OK;
-}
-
-
-stat_t command_flush(char *cmd) {
-  state_request_flush();
-  return STAT_OK;
-}
-
-
 stat_t command_dump(char *cmd) {
   report_request_full();
   return STAT_OK;
index 36b77d3216232400a2f68e642f70190701a4dcbd..b4d937c6d85c14cc71df3e9da2f49fe4ab28eea7 100644 (file)
@@ -43,7 +43,6 @@
 typedef struct {
   char id[26];
   bool hard_reset;         // flag to perform a hard reset
-  bool bootloader;         // flag to enter the bootloader
 } hw_t;
 
 static hw_t hw = {{0}};
@@ -146,18 +145,9 @@ void hw_reset_handler() {
 
     hw_hard_reset();
   }
-
-  if (hw.bootloader) {
-    // TODO enable bootloader interrupt vectors and jump to BOOT_SECTION_START
-    hw.bootloader = false;
-  }
 }
 
 
-/// Executes a software reset using CCPWrite
-void hw_request_bootloader() {hw.bootloader = true;}
-
-
 uint8_t hw_disable_watchdog() {
   uint8_t state = WDT.CTRL;
   wdt_disable();
index 4f1acf6593116dd6b8a419320146c13ce0b703ef..8dbd9286a9ec773ae2ebff06c11b62d49b71a2aa 100644 (file)
@@ -37,7 +37,5 @@ void hw_request_hard_reset();
 void hw_hard_reset();
 void hw_reset_handler();
 
-void hw_request_bootloader();
-
 uint8_t hw_disable_watchdog();
 void hw_restore_watchdog(uint8_t state);
index bcb8faf46e6e2b4159fdc285198a5b2d8f5bfea1..eaeed054e5527de8034c8ecc5ff52ea2d8870f4e 100644 (file)
@@ -220,7 +220,9 @@ static float _compute_axis_velocity(int axis) {
   // Compute next accel
   a->accel = scurve_next_accel(SEGMENT_TIME, V, Vt, a->accel, jerk);
 
-  // TODO limit acceleration
+  // Limit acceleration
+  if (axis_get_accel_max(axis) < fabs(a->accel))
+    a->accel = (a->accel < 0 ? -1 : 1) * axis_get_accel_max(axis);
 
   return V + a->accel * SEGMENT_TIME;
 }
index 37ab3a19e1ac6e28ea80e4dadcaf5570cbddef4f..69ff58bd33f817c8aaf22feab278882ea053cf61 100644 (file)
@@ -131,16 +131,6 @@ static bool _segment_next() {
 }
 
 
-static stat_t _hold() {
-  if (state_get() != STATE_HOLDING) {
-    exec_set_cb(0);
-    return STAT_AGAIN;
-  }
-
-  return STAT_NOP;
-}
-
-
 static stat_t _pause() {
   float t = SEGMENT_TIME - l.current_t;
   float v = exec_get_velocity();
@@ -152,13 +142,17 @@ static stat_t _pause() {
     exec_set_acceleration(0);
     exec_set_jerk(0);
     state_holding();
-    exec_set_cb(_hold);
-    return _hold();
+    exec_set_cb(0);
+
+    return STAT_NOP;
   }
 
   // Compute new velocity and acceleration
   a = scurve_next_accel(t, v, 0, a, j);
-  if (l.line.max_accel < a) a = l.line.max_accel;
+  if (l.line.max_accel < fabs(a)) {
+    a = (a < 0 ? -1 : 1) * l.line.max_accel;
+    j = 0;
+  } else if (0 < a) j = -j;
   v += a * t;
 
   // Compute distance that will be traveled
@@ -259,13 +253,12 @@ void _print_vector(const char *name, float v[AXES]) {
 
 
 stat_t command_line(char *cmd) {
-  static float next_start[AXES];
   line_t line = {};
 
   cmd++; // Skip command code
 
   // Get start position
-  copy_vector(line.start, next_start);
+  command_get_position(line.start);
 
   // Get target velocity
   if (!decode_float(&cmd, &line.target_vel)) return STAT_BAD_FLOAT;
@@ -309,7 +302,7 @@ stat_t command_line(char *cmd) {
   if (*cmd) return STAT_INVALID_ARGUMENTS;
 
   // Set next start position
-  copy_vector(next_start, line.target);
+  command_set_position(line.target);
 
   // Compute direction vector
   for (int i = 0; i < AXES; i++)
index 4a8142bf503077e362708bbc060814abb0d95b24..543230fee556b05106f8a720c9a44e91d3642cff 100644 (file)
@@ -200,7 +200,7 @@ static void _update_power(int motor) {
     // Fall through
 
   case MOTOR_ALWAYS_POWERED:
-    // TODO is ~5ms enough time to enable the motor?
+    // NOTE, we have ~5ms to enable the motor
     drv8711_set_state(motor, DRV8711_ACTIVE);
     break;
 
index 4130349fdc3d9b487ad7d14aef97a01edaa8d5e2..9f6a2222df1d98ed939f6f92288b0fce43cc7b0d 100644 (file)
@@ -33,7 +33,7 @@
 #include "spindle.h"
 #include "report.h"
 
-#include <stdbool.h>
+#include <stdio.h>
 
 
 static struct {
@@ -66,11 +66,12 @@ PGM_P state_get_pgmstr(state_t state) {
 
 PGM_P state_get_hold_reason_pgmstr(hold_reason_t reason) {
   switch (reason) {
-  case HOLD_REASON_USER_PAUSE:    return PSTR("USER");
-  case HOLD_REASON_PROGRAM_PAUSE: return PSTR("PROGRAM");
-  case HOLD_REASON_PROGRAM_END:   return PSTR("END");
-  case HOLD_REASON_PALLET_CHANGE: return PSTR("PALLET");
-  case HOLD_REASON_TOOL_CHANGE:   return PSTR("TOOL");
+  case HOLD_REASON_USER_PAUSE:    return PSTR("User paused");
+  case HOLD_REASON_PROGRAM_PAUSE: return PSTR("Program paused");
+  case HOLD_REASON_PROGRAM_END:   return PSTR("Program end");
+  case HOLD_REASON_PALLET_CHANGE: return PSTR("Pallet change");
+  case HOLD_REASON_TOOL_CHANGE:   return PSTR("Tool change");
+  case HOLD_REASON_STEPPING:      return PSTR("Stepping");
   }
 
   return PSTR("INVALID");
@@ -102,17 +103,11 @@ bool state_is_resuming() {return s.resume_requested;}
 
 bool state_is_quiescent() {
   return (state_get() == STATE_READY || state_get() == STATE_HOLDING) &&
-    !st_is_busy() && !command_is_busy();
+    !st_is_busy();
 }
 
 
-static void _set_plan_steps(bool plan_steps) {} // TODO
-
-
-void state_holding() {
-  _set_state(STATE_HOLDING);
-  _set_plan_steps(false);
-}
+void state_holding() {_set_state(STATE_HOLDING);}
 
 
 void state_optional_pause() {
@@ -130,17 +125,6 @@ void state_running() {
 
 void state_idle() {if (state_get() == STATE_RUNNING) _set_state(STATE_READY);}
 void state_estop() {_set_state(STATE_ESTOPPED);}
-void state_request_pause() {s.pause_requested = true;}
-void state_request_start() {s.start_requested = true;}
-void state_request_flush() {s.flush_requested = true;}
-void state_request_resume() {if (s.flush_requested) s.resume_requested = true;}
-void state_request_optional_pause() {s.optional_pause_requested = true;}
-
-
-void state_request_step() {
-  _set_plan_steps(true);
-  s.start_requested = true;
-}
 
 
 /*** Pauses, queue flushes and starts are all related.  Request functions
@@ -172,11 +156,12 @@ void state_callback() {
     if (state_get() == STATE_RUNNING) _set_state(STATE_STOPPING);
   }
 
-  // Only flush queue when idle or holding.
+  // Only flush queue when idle or holding
   if (s.flush_requested && state_is_quiescent()) {
     command_flush_queue();
 
     // Stop spindle
+    // TODO Spindle should not be stopped when pausing
     spindle_stop();
 
     // Resume
@@ -208,7 +193,31 @@ PGM_P get_hold_reason() {return state_get_hold_reason_pgmstr(s.hold_reason);}
 
 // Command callbacks
 stat_t command_pause(char *cmd) {
-  if (cmd[1] == '1') state_request_optional_pause();
-  else state_request_pause();
+  if (cmd[1] == '1') s.optional_pause_requested = true;
+  else s.pause_requested = true;
+  return STAT_OK;
+}
+
+
+stat_t command_unpause(char *cmd) {
+  s.start_requested = true;
+  return STAT_OK;
+}
+
+
+stat_t command_resume(char *cmd) {
+  if (s.flush_requested) s.resume_requested = true;
+  return STAT_OK;
+}
+
+
+stat_t command_step(char *cmd) {
+  // TODO
+  return STAT_OK;
+}
+
+
+stat_t command_flush(char *cmd) {
+  s.flush_requested = true;
   return STAT_OK;
 }
index e6c658b24fb4e400401969501f14fb98cafee8b7..7e496e2e51286fba613a43eb5f31718a20f16d5d 100644 (file)
@@ -47,6 +47,7 @@ typedef enum {
   HOLD_REASON_PROGRAM_END,
   HOLD_REASON_PALLET_CHANGE,
   HOLD_REASON_TOOL_CHANGE,
+  HOLD_REASON_STEPPING,
 } hold_reason_t;
 
 
@@ -66,11 +67,4 @@ void state_running();
 void state_idle();
 void state_estop();
 
-void state_request_pause();
-void state_request_start();
-void state_request_flush();
-void state_request_resume();
-void state_request_optional_pause();
-void state_request_step();
-
 void state_callback();
index 2a9e3c7ea39e49b7ff3787065e0c53f103d7d694..318dffc62a1494fcd4353238e5c5eaead26f18b4 100644 (file)
@@ -116,7 +116,7 @@ class AVR():
                 if isinstance(value, str): value = '"' + value + '"'
                 if isinstance(value, bool): value = int(value)
 
-                self.queue_command('${}={}'.format(var, value))
+                self.set('', var, value)
 
         self.queue_command('$$') # Refresh all vars, must come after above
 
@@ -142,7 +142,7 @@ class AVR():
             if self.ctrl.ioloop.READ & events: self.serial_read()
             if self.ctrl.ioloop.WRITE & events: self.serial_write()
         except Exception as e:
-            log.error('Serial handler error:', e)
+            log.error('Serial handler error: %s', e)
 
 
     def serial_write(self):
@@ -166,10 +166,7 @@ class AVR():
         elif self.stream is not None:
             cmd = self.stream.next()
 
-            if cmd is None:
-                self.set_write(False)
-                self.stream = None
-
+            if cmd is None: self.set_write(False)
             else: self.load_next_command(cmd)
 
         # Else stop writing
@@ -206,6 +203,7 @@ class AVR():
                 update.update(msg)
                 log.debug(line)
 
+                # Don't overwrite duplicate `msg`
                 if 'msg' in msg: break
 
         if update:
@@ -218,6 +216,11 @@ class AVR():
 
             self.vars.update(update)
 
+            if self.stream is not None:
+                self.stream.update(update)
+                if not self.stream.is_running():
+                    self.stream = None
+
             try:
                 self._update_lcd(update)
 
@@ -295,6 +298,7 @@ class AVR():
 
     def home(self, axis, position = None):
         if self.stream is not None: raise Exception('Busy, cannot home')
+        raise Exception('NYI') # TODO
 
         if position is not None:
             self.queue_command('G28.3 %c%f' % (axis, position))
@@ -317,10 +321,7 @@ class AVR():
 
     def start(self, path):
         if self.stream is not None: raise Exception('Busy, cannot start file')
-
-        if path:
-            self._start_sending_gcode(path)
-            self._i2c_command(Cmd.RUN)
+        if path: self._start_sending_gcode(path)
 
 
     def step(self, path):
@@ -333,16 +334,28 @@ class AVR():
         self._i2c_command(Cmd.FLUSH)
         self._stop_sending_gcode()
         # Resume processing once current queue of GCode commands has flushed
-        self.queue_command('c')
+        self.queue_command(Cmd.RESUME)
 
 
     def pause(self): self._i2c_command(Cmd.PAUSE, byte = 0)
-    def unpause(self): self._i2c_command(Cmd.RUN)
+
+
+    def unpause(self):
+        if self.vars.get('x', '') != 'HOLDING' or self.stream is None: return
+
+        self._i2c_command(Cmd.FLUSH)
+        self.queue_command(Cmd.RESUME)
+        self.stream.restart()
+        self.set_write(True)
+        self._i2c_command(Cmd.UNPAUSE)
+
+
     def optional_pause(self): self._i2c_command(Cmd.PAUSE, byte = 1)
 
 
     def set_position(self, axis, position):
         if self.stream is not None: raise Exception('Busy, cannot set position')
         if self._is_axis_homed('%c' % axis):
+            raise Exception('NYI') # TODO
             self.queue_command('G92 %c%f' % (axis, position))
         else: self.queue_command('$%cp=%f' % (axis, position))
index 32107ba96016602960acc2cb9f5651380d77d85b..ca1ac3a8c3b683c065f6a63c74c850c1eea0c0f5 100644 (file)
@@ -5,13 +5,14 @@ import logging
 log = logging.getLogger('Cmd')
 
 # TODO, sync this up with AVR code
-REPORT = 'r'
-PAUSE  = 'P'
-ESTOP  = 'E'
-CLEAR  = 'C'
-FLUSH  = 'F'
-STEP   = 'S'
-RUN    = 'p'
+REPORT  = 'r'
+PAUSE   = 'P'
+UNPAUSE = 'U'
+ESTOP   = 'E'
+CLEAR   = 'C'
+FLUSH   = 'F'
+STEP    = 'S'
+RESUME  = 'c'
 
 
 def encode_float(x):
index f396ec9c76c63b98a5ab6b673a46a592ebd6057f..6d71b7a31f49862884034f3ba5568911c1ad0023 100644 (file)
@@ -11,6 +11,8 @@ class Planner():
     def __init__(self, ctrl, path):
         self.ctrl = ctrl
         self.path = path
+        self.lastID = -1
+        self.done = False
 
         vars = ctrl.avr.vars
 
@@ -51,9 +53,32 @@ class Planner():
         self.reset()
 
 
+    def is_running(self): return self.planner.is_running()
+
+
+    def update(self, update):
+        if 'id' in update:
+            id = update['id']
+            if id: self.planner.release(id - 1)
+
+
+    def restart(self):
+        vars = self.ctrl.avr.vars
+        id = vars['id']
+
+        position = {}
+        for axis in 'xyzabc':
+            if (axis + 'p') in vars:
+                position[axis] = vars[axis + 'p']
+
+        self.planner.restart(id, position)
+        self.done = False
+
+
     def reset(self):
         self.planner = gplan.Planner(self.config)
         self.planner.load('upload' + self.path)
+        self.done = False
 
 
     def encode(self, block):
@@ -75,4 +100,13 @@ class Planner():
 
     def next(self):
         if self.planner.has_more():
-            return self.encode(self.planner.next())
+            cmd = self.planner.next()
+            self.lastID = cmd['id']
+            return self.encode(cmd)
+
+        if not self.done:
+            self.done = True
+
+            # Cause last cmd to flush when complete
+            if 0 <= self.lastID:
+                return '#id=%d' % (self.lastID + 1)