- Fixed bug in move exec that was causing bumping between moves.
authorJoseph Coffland <joseph@cauldrondevelopment.com>
Thu, 22 Feb 2018 06:18:38 +0000 (22:18 -0800)
committerJoseph Coffland <joseph@cauldrondevelopment.com>
Thu, 22 Feb 2018 06:18:38 +0000 (22:18 -0800)
 - Fixed planner bug which could create negative s-curve times.
 - Hide step and optional pause buttons until they are implemented.
 - Fixed pausing problems.
 - Limit number of console messages.
 - Scrollbar on console view.
 - Log debug messages to console in developer mode.
 - Fixed AVR log message source.
 - Fixed step correction.

24 files changed:
CHANGELOG.md
package.json
src/avr/Makefile
src/avr/src/exec.c
src/avr/src/exec.h
src/avr/src/huanyang.c
src/avr/src/huanyang.h
src/avr/src/lcd.h
src/avr/src/line.c
src/avr/src/main.c
src/avr/src/messages.def
src/avr/src/motor.c
src/avr/src/state.c
src/avr/test/Makefile
src/avr/test/hal.c
src/avr/test/planner-test.c
src/jade/templates/console.jade
src/jade/templates/control-view.jade
src/js/console.js
src/py/bbctrl/AVR.py
src/py/bbctrl/Messages.py
src/py/bbctrl/Planner.py
src/py/bbctrl/__init__.py
src/stylus/style.styl

index e47a4e4eb00d59687f90d7c379a0216dcd3e44b1..b8c031d9de9b8677c507ec63ec67fdf14b2d2370 100644 (file)
@@ -1,6 +1,17 @@
 Buildbotics CNC Controller Firmware Change Log
 ==============================================
 
+## v0.3.9
+ - Fixed bug in move exec that was causing bumping between moves.
+ - Fixed planner bug which could create negative s-curve times.
+ - Hide step and optional pause buttons until they are implemented.
+ - Fixed pausing problems.
+ - Limit number of console messages.
+ - Scrollbar on console view.
+ - Log debug messages to console in developer mode.
+ - Fixed AVR log message source.
+ - Fixed step correction.
+
 ## v0.3.8
  - Fixed pwr flags display
  - Added pwr fault flags to indicators
index 6de87946d5d82a535b8bce0168c20a74b7a87da6..a19dd462ddaf9eb466e826c3e6f32b91ba97ad75 100644 (file)
@@ -1,6 +1,6 @@
 {
   "name": "bbctrl",
-  "version": "0.3.8",
+  "version": "0.3.9",
   "homepage": "http://buildbotics.com/",
   "repository": "https://github.com/buildbotics/bbctrl-firmware",
   "license": "GPL-3.0+",
index 3a234d8fc008a819c3b1ee6e10ded4e2e40e707e..304ef1c54337d64dfe11e5c2119d6e0c4c17a5ec 100644 (file)
@@ -1,8 +1,7 @@
 # Makefile for the project Bulidbotics firmware
-PROJECT         = bbctrl-avr-firmware
-MCU             = atxmega192a3u
-CLOCK           = 32000000
-VERSION         = 0.5.0
+PROJECT  = bbctrl-avr-firmware
+MCU      = atxmega192a3u
+CLOCK    = 32000000
 
 TARGET  = $(PROJECT).elf
 
@@ -18,7 +17,7 @@ CFLAGS += -Wno-error=strict-aliasing # for _invsqrt
 CFLAGS += -std=gnu99 -DF_CPU=$(CLOCK)UL -O3
 CFLAGS += -funsigned-bitfields -fpack-struct -fshort-enums -funsigned-char
 CFLAGS += -MD -MP -MT $@ -MF build/dep/$(@F).d
-CFLAGS += -Isrc -DVERSION=\"$(VERSION)\"
+CFLAGS += -Isrc
 
 # Linker flags
 LDFLAGS += $(COMMON) -Wl,-u,vfprintf -lprintf_flt -lm
index 6a3833178a5896d765216fa97baf97455c815d23..ac9227bfe396e2171afa44b7b137bc251fca58ed 100644 (file)
@@ -93,9 +93,6 @@ stat_t exec_move_to_target(float time, const float target[]) {
 }
 
 
-void exec_reset_encoder_counts() {st_set_position(ex.position);}
-
-
 stat_t exec_next() {
   if (!ex.cb && !command_exec()) return STAT_NOP; // Queue empty
   if (!ex.cb) return STAT_AGAIN; // Non-exec command
index bb05dd4e20adf21cb8c7acf0cbb7a2286feba23b..db445b6325b688a64ffa4ac708a950f46169ee78 100644 (file)
@@ -51,5 +51,4 @@ void exec_set_jerk(float j);
 void exec_set_cb(exec_cb_t cb);
 
 stat_t exec_move_to_target(float time, const float target[]);
-void exec_reset_encoder_counts();
 stat_t exec_next();
index 0bd5d3539dbcb3d86e12c1c96d34a918b96e74c8..9e8c88bb1d0037ea43783a6b26f84bffd3a0cb3d 100644 (file)
 #include <string.h>
 #include <math.h>
 
+/*
+  Huanyang supposedly is not quite Modbus compliant.
 
-enum {
-  HUANYANG_FUNC_READ = 1,
-  HUANYANG_FUNC_WRITE,
-  HUANYANG_CTRL_WRITE,
-  HUANYANG_CTRL_READ,
-  HUANYANG_FREQ_WRITE,
-  HUANYANG_RESERVED_1,
-  HUANYANG_RESERVED_2,
-  HUANYANG_LOOP_TEST
-};
+  Message format is:
+
+    [id][cmd][length][data][checksum]
 
+  Where:
 
-enum {
-  HUANYANG_BASE_FREQ = 4,
-  HUANYANG_MAX_FREQ = 5,
-  HUANYANG_MIN_FREQ = 11,
-  HUANYANG_RATED_MOTOR_VOLTAGE = 141,
-  HUANYANG_RATED_MOTOR_CURRENT = 142,
-  HUANYANG_MOTOR_POLE = 143,
-  HUANYANG_RATED_RPM = 144,
-};
+     id       - 1-byte Peer ID
+     cmd      - 1-byte One of hy_cmd_t
+     length   - 1-byte Length data in bytes
+     data     - length bytes - Command arguments
+     checksum - 16-bit CRC: x^16 + x^15 + x^2 + 1 (0xa001) initial: 0xffff
+*/
+
+
+// See VFD manual pg56 3.1.3
+typedef enum {
+  HUANYANG_FUNC_READ = 1, // Use hy_func_addr_t
+  HUANYANG_FUNC_WRITE,    // ?
+  HUANYANG_CTRL_WRITE,    // Use hy_ctrl_state_t
+  HUANYANG_CTRL_READ,     // Use hy_ctrl_addr_t
+  HUANYANG_FREQ_WRITE,    // Write frequency as uint16_t
+  HUANYANG_RESERVED_1,
+  HUANYANG_RESERVED_2,
+  HUANYANG_LOOP_TEST,
+} hy_cmd_t;
 
 
 // See VFD manual pg57 3.1.3.d
-enum {
+typedef enum {
   HUANYANG_TARGET_FREQ,
   HUANYANG_ACTUAL_FREQ,
   HUANYANG_ACTUAL_CURRENT,
@@ -73,26 +79,31 @@ enum {
   HUANYANG_ACV,
   HUANYANG_CONT,
   HUANYANG_TEMPERATURE,
-};
-
-
-enum {
-  HUANYANG_FORWARD = 1,
-  HUANYANG_STOP = 8,
-  HUANYANG_REVERSE = 17,
-};
+} hy_ctrl_addr_t;
 
 
-enum {
+typedef enum {
   HUANYANG_RUN         = 1 << 0,
-  HUANYANG_JOG         = 1 << 1,
-  HUANYANG_COMMAND_RF  = 1 << 2,
-  HUANYANG_RUNNING     = 1 << 3,
-  HUANYANG_JOGGING     = 1 << 4,
-  HUANYANG_RUNNING_RF  = 1 << 5,
-  HUANYANG_BRACKING    = 1 << 6,
-  HUANYANG_TRACK_START = 1 << 7,
-};
+  HUANYANG_FORWARD     = 1 << 1,
+  HUANYANG_REVERSE     = 1 << 2,
+  HUANYANG_STOP        = 1 << 3,
+  HUANYANG_REV_FWD     = 1 << 4,
+  HUANYANG_JOG         = 1 << 5,
+  HUANYANG_JOG_FORWARD = 1 << 6,
+  HUANYANG_JOG_REVERSE = 1 << 1,
+} hy_ctrl_state_t;
+
+
+typedef enum {
+  HUANYANG_STATUS_RUN         = 1 << 0,
+  HUANYANG_STATUS_JOG         = 1 << 1,
+  HUANYANG_STATUS_COMMAND_REV = 1 << 2,
+  HUANYANG_STATUS_RUNNING     = 1 << 3,
+  HUANYANG_STATUS_JOGGING     = 1 << 4,
+  HUANYANG_STATUS_JOGGING_REV = 1 << 5,
+  HUANYANG_STATUS_BRAKING     = 1 << 6,
+  HUANYANG_STATUS_TRACK_START = 1 << 7,
+} hy_ctrl_status_t;
 
 
 typedef bool (*next_command_cb_t)(int index);
@@ -227,17 +238,17 @@ static bool _update(int index) {
   switch (index) {
   case 0: { // Update direction
     uint8_t state = HUANYANG_STOP;
-    if (0 < ha.speed) state = HUANYANG_FORWARD;
-    else if (ha.speed < 0) state = HUANYANG_REVERSE;
+    if (0 < ha.speed) state = HUANYANG_RUN;
+    else if (ha.speed < 0) state = HUANYANG_RUN | HUANYANG_REV_FWD;
 
     _set_command1(HUANYANG_CTRL_WRITE, state);
 
     return true;
   }
 
-  case 1: var = HUANYANG_MAX_FREQ; break;
-  case 2: var = HUANYANG_MIN_FREQ; break;
-  case 3: var = HUANYANG_RATED_RPM; break;
+  case 1: var = HY_PD005_MAX_FREQUENCY; break;
+  case 2: var = HY_PD011_FREQUENCY_LOWER_LIMIT; break;
+  case 3: var = HY_PD144_RATED_MOTOR_RPM; break;
 
   case 4: { // Update freqency
     // Compute frequency in Hz
@@ -319,7 +330,7 @@ static bool _check_response() {
     return false;
   }
 
-  // Check return function code matches sent
+  // Check if response code matches the code we sent
   if (ha.command[1] != ha.response[1]) {
     STATUS_WARNING("huanyang: invalid function code, expected=%u got=%u",
                    ha.command[2], ha.response[2]);
index ce908ddc40aeb418e9bef6174d5b7f9713134d39..fdc44ffa069e234d25746ce3757265e1ec43dcc7 100644 (file)
@@ -35,3 +35,191 @@ void hy_set(float speed);
 void hy_reset();
 void hy_rtc_callback();
 void hy_stop();
+
+/// See Huanyang VFD user manual
+typedef enum {
+  HY_PD000_PARAMETER_LOCK,
+  HY_PD001_SOURCE_OF_OPERATION_COMMANDS,
+  HY_PD002_SOURCE_OF_OPERATING_FREQUENCY,
+  HY_PD003_MAIN_FREQUENCY,
+  HY_PD004_BASE_FREQUENCY,
+  HY_PD005_MAX_FREQUENCY,
+  HY_PD006_INTERMEDIATE_FREQUENCY,
+  HY_PD007_MIN_FREQUENCY,
+  HY_PD008_MAX_VOLTAGE,
+  HY_PD009_INTERMEDIATE_VOLTAGE,
+  HY_PD010_MIN_VOLTAGE,
+  HY_PD011_FREQUENCY_LOWER_LIMIT,
+  HY_PD012_RESERVED,
+  HY_PD013_PARAMETER_RESET,
+  HY_PD014_ACCEL_TIME_1,
+  HY_PD015_DECEL_TIME_1,
+  HY_PD016_ACCEL_TIME_2,
+  HY_PD017_DECEL_TIME_2,
+  HY_PD018_ACCEL_TIME_3,
+  HY_PD019_DECEL_TIME_3,
+  HY_PD020_ACCEL_TIME_4,
+  HY_PD021_DECEL_TIME_4,
+  HY_PD022_FACTORY_RESERVED,
+  HY_PD023_REV_ROTATION_SELECT,
+  HY_PD024_STOP_KEY,
+  HY_PD025_STARTING_MODE,
+  HY_PD026_STOPPING_MODE,
+  HY_PD027_STARTING_FREQUENCY,
+  HY_PD028_STOPPING_FREQUENCY,
+  HY_PD029_DC_BRAKING_TIME_AT_START,
+  HY_PD030_DC_BRAKING_TIME_AT_STOP,
+  HY_PD031_DC_BRAKING_VOLTAGE_LEVEL,
+  HY_PD032_FREQUENCY_TRACK_TIME,
+  HY_PD033_CURRENT_LEVEL_FOR_FREQUENCY_TRACK,
+  HY_PD034_VOLTAGE_RISE_TIME_FOR_FREQUENCY_TRACK,
+  HY_PD035_FREQUENCY_STEP_LENGTH,
+  HY_PD036,
+  HY_PD037,
+  HY_PD038,
+  HY_PD039,
+  HY_PD040,
+  HY_PD041_CARRIER_FREQUENCY,
+  HY_PD042_JOGGING_FREQUENCY,
+  HY_PD043_S_CURVE_TIME,
+  HY_PD044_MULTI_INPUT_FOR,
+  HY_PD045_MULTI_INPUT_REV,
+  HY_PD046_MULTI_INPUT_RST,
+  HY_PD047_MULTI_INPUT_SPH,
+  HY_PD048_MULTI_INPUT_SPM,
+  HY_PD049_MULTI_INPUT_SPL,
+  HY_PD050_MULTI_OUTPUT_DRV,
+  HY_PD051_MULTI_OUTPUT_UPF,
+  HY_PD052_MULTI_OUTPUT_FA_FB_FC,
+  HY_PD053_MULTI_OUTPUT_KA_KB,
+  HY_PD054_MULTI_OUTPUT_AM,
+  HY_PD055_AM_ANALOG_OUTPUT_GAIN,
+  HY_PD056_SKIP_FREQUENCY_1,
+  HY_PD057_SKIP_FREQUENCY_2,
+  HY_PD058_SKIP_FREQUENCY_3,
+  HY_PD059_SKIP_FREQUENCY_RANGE,
+  HY_PD060_UNIFORM_FREQUENCY_1,
+  HY_PD061_UNIFORM_FREQUENCY_2,
+  HY_PD062_UNIFORM_FREQUENCY_RANGE,
+  HY_PD063_TIMER_1_TIME,
+  HY_PD064_TIMER_2_TIME,
+  HY_PD065_COUNTING_VALUE,
+  HY_PD066_INTERMEDIATE_COUNTER,
+  HY_PD067,
+  HY_PD068,
+  HY_PD069,
+  HY_PD070_ANALOG_INPUT,
+  HY_PD071_ANALOG_FILTERING_CONSTANT,
+  HY_PD072_HIGHER_ANALOG_FREQUENCY,
+  HY_PD073_LOWER_ANALOG_FREQUENCY,
+  HY_PD074_BIAS_DIRECTION_AT_HIGHER_FREQUENCY,
+  HY_PD075_BIAS_DIRECTION_AT_LOWER_FREQUENCY,
+  HY_PD076_ANALOG_NEGATIVE_BIAS_REVERSE,
+  HY_PD077_UP_DOWN_FUNCTION,
+  HY_PD078_UP_DOWN_SPEED,
+  HY_PD079,
+  HY_PD080_PLC_OPERATION,
+  HY_PD081_AUTO_PLC,
+  HY_PD082_PLC_RUNNING_DIRECTION,
+  HY_PD083,
+  HY_PD084_PLC_RAMP_TIME,
+  HY_PD085,
+  HY_PD086_FREQUENCY_2,
+  HY_PD087_FREQUENCY_3,
+  HY_PD088_FREQUENCY_4,
+  HY_PD089_FREQUENCY_5,
+  HY_PD090_FREQUENCY_6,
+  HY_PD091_FREQUENCY_7,
+  HY_PD092_FREQUENCY_8,
+  HY_PD093,
+  HY_PD094,
+  HY_PD095,
+  HY_PD096,
+  HY_PD097,
+  HY_PD098,
+  HY_PD099,
+  HY_PD100,
+  HY_PD101_TIMER_1,
+  HY_PD102_TIMER_2,
+  HY_PD103_TIMER_3,
+  HY_PD104_TIMER_4,
+  HY_PD105_TIMER_5,
+  HY_PD106_TIMER_6,
+  HY_PD107_TIMER_7,
+  HY_PD108_TIMER_8,
+  HY_PD109,
+  HY_PD110,
+  HY_PD111,
+  HY_PD112,
+  HY_PD113,
+  HY_PD114,
+  HY_PD115,
+  HY_PD116,
+  HY_PD117_AUTOPLC_MEMORY_FUNCTION,
+  HY_PD118_OVER_VOLTAGE_STALL_PREVENTION,
+  HY_PD119_STALL_PREVENTION_LEVEL_AT_RAMP_UP,
+  HY_PD120_STALL_PREVENTION_LEVEL_AT_CONSTANT_SPEED,
+  HY_PD121_DECEL_TIME_FOR_STALL_PREVENTION_AT_CONSTANT_SPEED,
+  HY_PD122_STALL_PREVENTION_LEVEL_AT_DECELERATION,
+  HY_PD123_OVER_TORQUE_DETECT_MODE,
+  HY_PD124_OVER_TORQUE_DETECT_LEVEL,
+  HY_PD125_OVER_TORQUE_DETECT_TIME,
+  HY_PD126,
+  HY_PD127,
+  HY_PD128,
+  HY_PD129,
+  HY_PD130_NUMBER_OF_AUXILIARY_PUMP,
+  HY_PD131_CONTINUOUS_RUNNING_TIME_OF_AUXILIARY_PUMPS,
+  HY_PD132_INTERLOCKING_TIME_OF_AUXILIARY_PUMP,
+  HY_PD133_HIGH_SPEED_RUNNING_TIME,
+  HY_PD134_LOW_SPEED_RUNNING_TIME,
+  HY_PD135_STOPPING_VOLTAGE_LEVEL,
+  HY_PD136_LASTING_TIME_OF_STOPPING_VOLTAGE_LEVEL,
+  HY_PD137_WAKEUP_VOLTAGE_LEVEL,
+  HY_PD138_SLEEP_FREQUENCY,
+  HY_PD139_LASTING_TIME_OF_SLEEP_FREQUENCY,
+  HY_PD140,
+  HY_PD141_RATED_MOTOR_VOLTAGE,
+  HY_PD142_RATED_MOTOR_CURRENT,
+  HY_PD143_MOTOR_POLE_NUMBER,
+  HY_PD144_RATED_MOTOR_RPM,
+  HY_PD145_AUTO_TORQUE_COMPENSATION,
+  HY_PD146_MOTOR_NO_LOAD_CURRENT,
+  HY_PD147_MOTOR_SLIP_COMPENSATION,
+  HY_PD148,
+  HY_PD149,
+  HY_PD150_AUTO_VOLTAGE_REGULATION,
+  HY_PD151_AUTO_ENERGY_SAVING,
+  HY_PD152_FAULT_RESTART_TIME,
+  HY_PD153_RESTART_AFTER_INSTANTANEOUS_STOP,
+  HY_PD154_ALLOWABLE_POWER_BREAKDOWN_TIME,
+  HY_PD155_NUMBER_OF_ABNORMAL_RESTART,
+  HY_PD156_PROPORTIONAL_CONSTANT,
+  HY_PD157_INTEGRAL_TIME,
+  HY_PD158_DIFFERENTIAL_TIME,
+  HY_PD159_TARGET_VALUE,
+  HY_PD160_PID_TARGET_VALUE,
+  HY_PD161_PID_UPPER_LIMIT,
+  HY_PD162_PID_LOWER_LIMIT,
+  HY_PD163_COMMUNICATION_ADDRESSES,
+  HY_PD164_COMMUNICATION_BAUD_RATE,
+  HY_PD165_COMMUNICATION_DATA_METHOD,
+  HY_PD166,
+  HY_PD167,
+  HY_PD168,
+  HY_PD169,
+  HY_PD170_DISPLAY_ITEMS,
+  HY_PD171_DISPLAY_ITEMS_OPEN,
+  HY_PD172_FAULT_CLEAR,
+  HY_PD173,
+  HY_PD174_RATED_CURRENT_OF_INVERTER,
+  HY_PD175_INVERTER_MODEL,
+  HY_PD176_INVERTER_FREQUENCY_STANDARD,
+  HY_PD177_FAULT_RECORD_1,
+  HY_PD178_FAULT_RECORD_2,
+  HY_PD179_FAULT_RECORD_3,
+  HY_PD180_FAULT_RECORD_4,
+  HY_PD181_SOFTWARE_VERSION,
+  HY_PD182_MANUFACTURE_DATE,
+  HY_PD183_SERIAL_NO,
+} hy_func_addr_t;
index 188bf8166ee2015ee32c92908bc07eb220a02719..b7369f039ad95324491877846203ea6f5d7fde52 100644 (file)
@@ -27,7 +27,7 @@
 
 #pragma once
 
-#include <avr/pgmspace.h>
+#include "pgmspace.h"
 
 #include <stdint.h>
 
index 826686db3afc03984c9af5708317a0a599458586..d7c131ef2cf432c2e81ee045e4c71be8ed2fe419 100644 (file)
@@ -55,16 +55,18 @@ typedef struct {
 static struct {
   line_t line;
 
+  int section;
+  bool stop_section;
+  float current_time;
+  float offset_time;
   int seg;
-  bool stop_seg;
-  float current_t;
 
-  float iD; // Initial segment distance
-  float iV; // Initial segment velocity
-  float iA; // Initial segment acceleration
+  float iD; // Initial section distance
+  float iV; // Initial section velocity
+  float iA; // Initial section acceleration
   float jerk;
   float dist;
-} l = {.current_t = SEGMENT_TIME};
+} l = {.current_time = SEGMENT_TIME};
 
 
 static void _segment_target(float target[AXES], float d) {
@@ -88,21 +90,21 @@ static float _segment_accel(float t) {
 }
 
 
-static bool _segment_next() {
-  while (++l.seg < 7) {
-    float seg_t = l.line.times[l.seg];
-    if (!seg_t) continue;
+static bool _section_next() {
+  while (++l.section < 7) {
+    float section_time = l.line.times[l.section];
+    if (!section_time) continue;
 
-    // Check if last seg
-    bool last_seg = true;
-    for (int i = l.seg + 1; i < 7; i++)
-      if (l.line.times[i]) last_seg = false;
+    // Check if last section
+    bool last_section = true;
+    for (int i = l.section + 1; i < 7; i++)
+      if (l.line.times[i]) last_section = false;
 
-    // Check if stop seg
-    l.stop_seg = last_seg && !l.line.target_vel;
+    // Check if stop section
+    l.stop_section = last_section && !l.line.target_vel;
 
     // Jerk
-    switch (l.seg) {
+    switch (l.section) {
     case 0: case 6: l.jerk = l.line.max_jerk; break;
     case 2: case 4: l.jerk = -l.line.max_jerk; break;
     default: l.jerk = 0;
@@ -110,21 +112,25 @@ static bool _segment_next() {
     exec_set_jerk(l.jerk);
 
     // Acceleration
-    switch (l.seg) {
+    switch (l.section) {
     case 1: case 2: l.iA = l.line.max_jerk * l.line.times[0]; break;
     case 5: case 6: l.iA = -l.line.max_jerk * l.line.times[4]; break;
     default: l.iA = 0;
     }
     exec_set_acceleration(l.iA);
 
-    // Skip segs which do not land on a SEGMENT_TIME
-    if (seg_t < l.current_t && !l.stop_seg) {
-      l.current_t -= l.line.times[l.seg];
-      l.iD = _segment_distance(seg_t);
-      l.iV = _segment_velocity(seg_t);
+    // Skip sections which do not land on a SEGMENT_TIME
+    if (section_time < l.current_time && !l.stop_section) {
+      l.current_time -= l.line.times[l.section];
+      l.iD = _segment_distance(section_time);
+      l.iV = _segment_velocity(section_time);
       continue;
     }
 
+    // Setup section time offset and segment counter
+    l.offset_time = l.current_time;
+    l.seg = 0;
+
     return true;
   }
 
@@ -149,7 +155,7 @@ static stat_t _move(float t, float target[AXES], float v, float a) {
 
 
 static stat_t _pause() {
-  float t = SEGMENT_TIME - l.current_t;
+  float t = SEGMENT_TIME - l.current_time;
   float v = exec_get_velocity();
   float a = exec_get_acceleration();
   float j = l.line.max_jerk;
@@ -176,10 +182,9 @@ static stat_t _pause() {
   l.dist += v * t;
 
   if (l.line.length < l.dist) {
-    // Compute time left in current segment
-    l.current_t = t - (l.dist - l.line.length) / v;
+    // Compute time left in current section
+    l.current_time = t - (l.dist - l.line.length) / v;
 
-    exec_set_velocity(l.line.target_vel);
     exec_set_acceleration(0);
     exec_set_jerk(0);
     _done();
@@ -191,7 +196,7 @@ static stat_t _pause() {
   float target[AXES];
   _segment_target(target, l.dist);
 
-  l.current_t = 0;
+  l.current_time = 0;
   exec_set_jerk(j);
 
   return _move(SEGMENT_TIME, target, v, a);
@@ -199,64 +204,70 @@ static stat_t _pause() {
 
 
 static stat_t _line_exec() {
-  if (state_get() == STATE_STOPPING && (l.seg < 4 || l.line.target_vel)) {
-    if (SEGMENT_TIME < l.current_t) l.current_t = 0;
+  // Pause if requested.  If we are already stopping, just continue.
+  if (state_get() == STATE_STOPPING && (l.section < 4 || l.line.target_vel)) {
+    if (SEGMENT_TIME < l.current_time) l.current_time = 0;
     exec_set_cb(_pause);
     return _pause();
   }
 
-  float seg_t = l.line.times[l.seg];
+  float section_time = l.line.times[l.section];
 
-  // Adjust time if near a stopping point
-  float delta = SEGMENT_TIME;
-  if (l.stop_seg && seg_t - l.current_t < SEGMENT_TIME) {
-    delta += seg_t - l.current_t;
-    l.current_t = seg_t;
+  // Adjust segment time if near a stopping point
+  float seg_time = SEGMENT_TIME;
+  if (l.stop_section && section_time - l.current_time < SEGMENT_TIME) {
+    seg_time += section_time - l.current_time;
+    l.offset_time += section_time - l.current_time;
+    l.current_time = section_time;
   }
 
-  // Compute distance and velocity (Must be computed before changing time)
-  float d = _segment_distance(l.current_t);
-  float v = _segment_velocity(l.current_t);
-  float a = _segment_accel(l.current_t);
+  // Compute distance and velocity.  Must be computed before advancing time.
+  float d = _segment_distance(l.current_time);
+  float v = _segment_velocity(l.current_time);
+  float a = _segment_accel(l.current_time);
 
-  // Advance time
-  l.current_t += SEGMENT_TIME;
+  // Advance time.  This is the start time for the next segment.
+  l.current_time = l.offset_time + ++l.seg * SEGMENT_TIME;
 
-  // Check if segment complete
-  bool lastSeg = false;
-  if (seg_t < l.current_t) {
-    l.current_t -= seg_t;
-    l.iD = _segment_distance(seg_t);
-    l.iV = _segment_velocity(seg_t);
+  // Check if section complete
+  bool lastSection = false;
+  if (section_time < l.current_time) {
+    l.current_time -= section_time;
+    l.iD = _segment_distance(section_time);
+    l.iV = _segment_velocity(section_time);
 
-    // Next segment
-    lastSeg = !_segment_next();
+    // Next section
+    lastSection = !_section_next();
   }
 
   // Do move & update exec
   stat_t status;
 
-  if (lastSeg && l.stop_seg)
+  if (lastSection && l.stop_section)
     // Stop exactly on target to correct for floating-point errors
-    status = _move(delta, l.line.target, 0, 0);
+    status = _move(seg_time, l.line.target, 0, 0);
 
   else {
     // Compute target position from distance
     float target[AXES];
     _segment_target(target, d);
 
-    status = _move(delta, target, v, a);
+    status = _move(seg_time, target, v, a);
     l.dist = d;
   }
 
   // Release exec if we are done
-  if (lastSeg) _done();
+  if (lastSection) {
+    if (state_get() == STATE_STOPPING) state_holding();
+    exec_set_velocity(l.line.target_vel);
+    _done();
+  }
 
   return status;
 }
 
 
-void _print_vector(const char *name, float v[AXES]) {
+void _print_vector(const char *name, float v[4]) {
   printf("%s %f %f %f %f\n", name, v[0], v[1], v[2], v[3]);
 }
 
@@ -290,18 +301,18 @@ stat_t command_line(char *cmd) {
   bool has_time = false;
   while (*cmd) {
     if (*cmd < '0' || '6' < *cmd) break;
-    int seg = *cmd - '0';
+    int section = *cmd - '0';
     cmd++;
 
     float time;
     if (!decode_float(&cmd, &time)) return STAT_BAD_FLOAT;
 
-    if (time < 0) return STAT_BAD_SEG_TIME;
-    line.times[seg] = time;
+    if (time < 0) return STAT_NEGATIVE_SCURVE_TIME;
+    line.times[section] = time;
     if (time) has_time = true;
    }
 
-  if (!has_time) return STAT_BAD_SEG_TIME;
+  if (!has_time) return STAT_ALL_ZERO_SCURVE_TIMES;
 
   // Check for end of command
   if (*cmd) return STAT_INVALID_ARGUMENTS;
@@ -338,9 +349,22 @@ void command_line_exec(void *data) {
   l.iD = l.dist = 0;
   l.iV = exec_get_velocity();
 
-  // Find first segment
-  l.seg = -1;
-  if (!_segment_next()) return;
+  // Find first section
+  l.section = -1;
+  if (!_section_next()) return;
+
+  // Compare start position to actual position
+  float diff[AXES];
+  bool report = false;
+  exec_get_position(diff);
+  for (int i = 0; i < AXES; i++) {
+    diff[i] -= l.line.start[i];
+    if (0.1 < fabs(diff[i])) report = true;
+  }
+
+  if (report)
+    STATUS_DEBUG("diff: %.4f %.4f %.4f %.4f",
+                 diff[0], diff[1], diff[2], diff[3]);
 
   // Set callback
   exec_set_cb(_line_exec);
index 3ddd612a08e2d0dd3058c04438009e1ac91f003d..94b7948ee61d268610e690251a94ffb87a1e2886 100644 (file)
@@ -73,13 +73,12 @@ int main() {
   sei();                          // enable interrupts
 
   // Splash
-  fprintf_P(stdout, PSTR("\n{\"firmware\":\"Buildbotics AVR\","
-                         "\"version\":\"" VERSION "\"}\n"));
+  fprintf_P(stdout, PSTR("\n{\"firmware\":\"Buildbotics AVR\"}\n"));
 
   // Main loop
   while (true) {
     hw_reset_handler();           // handle hard reset requests
-    if (!estop_triggered()) state_callback();
+    state_callback();             // manage state
     command_callback();           // process next command
     report_callback();            // report changes
     wdt_reset();
index cd3efbdd00795e14271eb26dd315939d95f2af5b..ca8bd0525a673110128ae723bb7b10b74b1f2ff0 100644 (file)
 
 \******************************************************************************/
 
-STAT_MSG(OK,                   "OK")
-STAT_MSG(AGAIN,               "Run command again")
-STAT_MSG(NOP,                 "No op")
-STAT_MSG(PAUSE,                "Pause")
-STAT_MSG(INTERNAL_ERROR,       "Internal error")
-STAT_MSG(ESTOP_USER,           "User triggered EStop")
-STAT_MSG(ESTOP_SWITCH,         "Switch triggered EStop")
-STAT_MSG(UNRECOGNIZED_NAME,    "Unrecognized command or variable name")
-STAT_MSG(INVALID_COMMAND,      "Invalid command")
-STAT_MSG(INVALID_ARGUMENTS,    "Invalid argument(s) to command")
-STAT_MSG(TOO_MANY_ARGUMENTS,   "Too many arguments to command")
-STAT_MSG(TOO_FEW_ARGUMENTS,    "Too few arguments to command")
-STAT_MSG(COMMAND_NOT_ACCEPTED, "Command not accepted at this time")
-STAT_MSG(MACHINE_ALARMED,      "Machine alarmed - Command not processed")
-STAT_MSG(EXPECTED_MOVE,        "A move was expected but none was queued")
-STAT_MSG(QUEUE_FULL,           "Queue full")
-STAT_MSG(QUEUE_EMPTY,          "Queue empty")
-STAT_MSG(BAD_FLOAT,            "Failed to parse float")
-STAT_MSG(INVALID_VARIABLE,     "Invalid variable")
-STAT_MSG(INVALID_VALUE,        "Invalid value")
-STAT_MSG(READ_ONLY,            "Variable is read only")
-STAT_MSG(BUFFER_OVERFLOW,      "Buffer overflow")
-STAT_MSG(BAD_SEG_TIME,         "Bad s-curve segment time")
-STAT_MSG(SEEK_NOT_ENABLED,     "Switch not enabled")
-STAT_MSG(SEEK_NOT_FOUND,       "Switch not found")
+STAT_MSG(OK,                    "OK")
+STAT_MSG(AGAIN,                 "Run command again")
+STAT_MSG(NOP,                   "No op")
+STAT_MSG(PAUSE,                 "Pause")
+STAT_MSG(INTERNAL_ERROR,        "Internal error")
+STAT_MSG(ESTOP_USER,            "User triggered EStop")
+STAT_MSG(ESTOP_SWITCH,          "Switch triggered EStop")
+STAT_MSG(UNRECOGNIZED_NAME,     "Unrecognized command or variable name")
+STAT_MSG(INVALID_COMMAND,       "Invalid command")
+STAT_MSG(INVALID_ARGUMENTS,     "Invalid argument(s) to command")
+STAT_MSG(TOO_MANY_ARGUMENTS,    "Too many arguments to command")
+STAT_MSG(TOO_FEW_ARGUMENTS,     "Too few arguments to command")
+STAT_MSG(COMMAND_NOT_ACCEPTED,  "Command not accepted at this time")
+STAT_MSG(MACHINE_ALARMED,       "Machine alarmed - Command not processed")
+STAT_MSG(EXPECTED_MOVE,         "A move was expected but none was queued")
+STAT_MSG(QUEUE_FULL,            "Queue full")
+STAT_MSG(QUEUE_EMPTY,           "Queue empty")
+STAT_MSG(BAD_FLOAT,             "Failed to parse float")
+STAT_MSG(INVALID_VARIABLE,      "Invalid variable")
+STAT_MSG(INVALID_VALUE,         "Invalid value")
+STAT_MSG(READ_ONLY,             "Variable is read only")
+STAT_MSG(BUFFER_OVERFLOW,       "Buffer overflow")
+STAT_MSG(ALL_ZERO_SCURVE_TIMES, "All zero s-curve times")
+STAT_MSG(NEGATIVE_SCURVE_TIME,  "Negative s-curve time")
+STAT_MSG(SEEK_NOT_ENABLED,      "Switch not enabled")
+STAT_MSG(SEEK_NOT_FOUND,        "Switch not found")
index d791d43273566066b472e32379ffc9dca8c7c89e..3484a56da82da7f7a415c65b10064a8e65d2714b 100644 (file)
@@ -298,15 +298,12 @@ void motor_prep_move(int motor, float time, float target) {
   // Error correction
   int16_t correction = abs(m->error);
   if (MIN_HALF_STEP_CORRECTION <= correction) {
-    // Allowed step correction is proportional to velocity
-    int24_t positive_half_steps = half_steps < 0 ? -half_steps : half_steps;
-    int16_t max_correction = (positive_half_steps >> 5) + 1;
-    if (max_correction < correction) correction = max_correction;
+    // Dampen correction oscillation
+    correction >>= 2;
 
+    // Make correction
     if (m->error < 0) correction = -correction;
-
     half_steps += correction;
-    m->error -= correction;
   }
 
   // Positive steps from here on
@@ -425,11 +422,14 @@ void set_power_mode(int motor, uint8_t value) {
 
 char get_motor_axis(int motor) {return motors[motor].axis;}
 
+
 void set_motor_axis(int motor, uint8_t axis) {
   if (MOTORS <= motor || AXES <= axis || axis == motors[motor].axis) return;
   motors[motor].axis = axis;
   axis_map_motors();
-  exec_reset_encoder_counts(); // Reset encoder counts
+
+  // Reset encoder
+  motor_set_position(motor, exec_get_axis_position(axis));
 
   // Check if this is now a slave motor
   motors[motor].slave = false;
index ee6016e11b988df85ae793bc71a597105ecaa595..ddbf2a8a54214b102512773114a80e2fd3985320 100644 (file)
@@ -31,6 +31,7 @@
 #include "command.h"
 #include "stepper.h"
 #include "spindle.h"
+#include "estop.h"
 #include "report.h"
 
 #include <stdio.h>
@@ -157,6 +158,8 @@ void state_estop() {_set_state(STATE_ESTOPPED);}
  *     - when stopped is honored and starts to run anything in the queue
  */
 void state_callback() {
+  if (estop_triggered()) return;
+
   if (s.pause_requested || s.flush_requested) {
     if (s.pause_requested) _set_hold_reason(HOLD_REASON_USER_PAUSE);
     s.pause_requested = false;
index 9425111e43c90c9ad751f69305766797c743f01a..06ece7a576df1231759e7a16f0cf91ef862f33d6 100644 (file)
@@ -1,7 +1,7 @@
 TESTS=planner-test
 
 PLANNER_TEST_SRC = status.c util.c axis.c report.c type.c exec.c base64.c \
-  command.c commands.c vars.c state.c line.c
+  command.c commands.c vars.c state.c line.c scurve.c seek.c
 PLANNER_TEST_SRC := $(patsubst %,../src/%,$(PLANNER_TEST_SRC))
 PLANNER_TEST_SRC += $(wildcard ../src/plan/*.c) planner-test.c hal.c
 
index 8903759658672ebf802b58feb385cb46ac9837ea..3c3b1b677f88beb930864326807ee405ba9ed59e 100644 (file)
@@ -29,7 +29,6 @@
 #include "spindle.h"
 #include "i2c.h"
 #include "type.h"
-#include "exec.h"
 #include "cpp_magic.h"
 
 #include <stdint.h>
@@ -154,12 +153,6 @@ stat_t st_prep_line(float time, const float target[]) {
     ASSERT(-maxTarget < target[i] && target[i] < maxTarget);
   }
 
-  static int32_t line = -1;
-  if (exec_get_line() != line) {
-    line = exec_get_line();
-    printf("line=%d\n", line);
-  }
-
   printf("%0.10f, %0.10f, %0.10f, %0.10f\n",
          time, target[0], target[1], target[2]);
 
index c5e78d9faf81897795417913a5717c5f4fc95e79..ae51a31cd1d568a61ab553b32edbe52b0d6d731d 100644 (file)
@@ -60,12 +60,7 @@ int main(int argc, char *argv[]) {
     switch (status) {
     case STAT_NOP: break;       // No command executed
     case STAT_AGAIN: continue;  // No command executed, try again
-
-    case STAT_OK:                // Move executed
-      //if (!st.move_queued) ALARM(STAT_EXPECTED_MOVE); // No move was queued
-      //st.move_queued = false;
-      //st.move_ready = true;
-      continue;
+    case STAT_OK: continue;     // Move executed
 
     default:
       printf("ERROR: %s\n", status_to_pgmstr(status));
index 57993fd5aed5d426881203e6a65b152fcba7ad38..1e5747c46ee4d417b44d9378c912e4f982ea2c77 100644 (file)
@@ -32,17 +32,18 @@ script#console-template(type="text/x-template")
         .fa.fa-trash
         | &nbsp;Clear
 
-    table
-      tr
-        th Level
-        th Source
-        th Location
-        th Repeat
-        th Message
+    .console-wrapper
+      table
+        tr
+          th Level
+          th Source
+          th Location
+          th Repeat
+          th Message
 
-      tr(v-for="msg in messages", :class="msg.level || 'info'")
-        td {{msg.level  || 'info'}}
-        td {{msg.source || ''}}
-        td {{msg.where  || ''}}
-        td {{msg.repeat}}
-        td {{msg.msg}}
+        tr(v-for="msg in messages", class="log-{{msg.level || 'info'}}")
+          td {{msg.level  || 'info'}}
+          td {{msg.source || ''}}
+          td {{msg.where  || ''}}
+          td {{msg.repeat}}
+          td {{msg.msg}}
index d867a52613e2397146cd5b7d80d5e0ef89ebd5f5..02ed565cc9de47b40555ff2154b99abb14e5d1c6 100644 (file)
@@ -170,11 +170,12 @@ script#control-view-template(type="text/x-template")
         .fa.fa-stop
 
       button.pure-button(title="Pause program at next optional stop (M1).",
-        @click="optional_pause")
+        @click="optional_pause", v-if="false")
         .fa.fa-stop-circle-o
 
       button.pure-button(title="Execute one program step.", @click="step",
-        :disabled="(state.xx != 'READY' && state.xx != 'HOLDING') || !file")
+        :disabled="(state.xx != 'READY' && state.xx != 'HOLDING') || !file",
+        v-if="false")
         .fa.fa-step-forward
 
     .tabs
index b29f738f69b3296927cc7c3b03cf153252ff9382..f10b44210be989c046ef6fd68342594be04b2177 100644 (file)
@@ -63,6 +63,7 @@ module.exports = {
       else {
         msg.repeat = 1;
         messages.unshift(msg);
+        while (256 < messages.length) messages.pop();
       }
       msg.ts = Date.now();
 
index af607413bbc5572d0222302e74903108b36c1d7d..ab7c8df6acaeae44a86ac23feb8800cc33e670c4 100644 (file)
@@ -220,14 +220,14 @@ class AVR():
                 # AVR logging
                 if 'msg' in msg:
                     level = msg.get('level', 'info')
-                    if 'where' in msg: extra = {'where': msg}
+                    if 'where' in msg: extra = {'where': msg['where']}
                     else: extra = None
                     msg = msg['msg']
 
-                    if level == 'info': log.info(msg, extra)
-                    elif level == 'debug': log.debug(msg, extra)
-                    elif level == 'warning': log.warning(msg, extra)
-                    elif level == 'error': log.error(msg, extra)
+                    if   level == 'info':    log.info(msg,    extra = extra)
+                    elif level == 'debug':   log.debug(msg,   extra = extra)
+                    elif level == 'warning': log.warning(msg, extra = extra)
+                    elif level == 'error':   log.error(msg,   extra = extra)
 
                     continue
 
@@ -372,8 +372,7 @@ class AVR():
 
 
     def unpause(self):
-        if self.ctrl.state.get('xx', '') != 'HOLDING' or not self._is_busy():
-            return
+        if self.ctrl.state.get('xx', '') != 'HOLDING': return
 
         self._i2c_command(Cmd.FLUSH)
         self._queue_command(Cmd.RESUME)
index 9ae1bf7f23547f211dd40ffe8dc9cfda8b34af6c..930b628b9afb837e62427676c19793cf77d6579a 100644 (file)
@@ -35,11 +35,14 @@ log = logging.getLogger('Msgs')
 
 class Messages(logging.Handler):
     def __init__(self, ctrl):
-        logging.Handler.__init__(self, logging.WARNING)
+        logging.Handler.__init__(self)
 
         self.ctrl = ctrl
         self.listeners = []
 
+        debug = os.path.exists('/etc/bbctrl-dev-mode')
+        self.setLevel(logging.DEBUG if debug else logging.WARNING)
+
         logging.getLogger().addHandler(self)
 
 
@@ -49,10 +52,11 @@ class Messages(logging.Handler):
 
     # From logging.Handler
     def emit(self, record):
-        msg = dict(
-            level = record.levelname.lower(),
-            source = record.name,
-            msg = record.getMessage())
+        if record.levelno == logging.INFO: return
+
+        msg = dict(level = record.levelname.lower(),
+                   source = record.name,
+                   msg = record.getMessage())
 
         if hasattr(record, 'where'): msg['where'] = record.where
         else: msg['where'] = '%s:%d' % (record.filename, record.lineno)
index 20a1408f723b634609948e7c2c904382faf43614..7b6b96086b16865008e61c35f85063b70d03e27d 100644 (file)
@@ -58,7 +58,7 @@ class Planner():
 
         # Axis mapping for enabled motors
         axis2motor = {}
-        for i in range(3):
+        for i in range(4):
             if state.get('%dpm' % i, False):
                 axis = 'xyzabc'[int(state.get('%dan' % i))]
                 axis2motor[axis] = i
index c8506684b3583bbcb575b4250ae321d915343665..398000bc737ab26178d0d893e643936e2a0583f8 100644 (file)
@@ -95,14 +95,17 @@ def run():
 
     # Init logging
     root = logging.getLogger()
-    root.setLevel(logging.DEBUG if args.verbose else logging.INFO)
+    level = logging.DEBUG if args.verbose else logging.INFO
+    root.setLevel(logging.NOTSET)
     f = logging.Formatter('{levelname[0]}:{name}:{message}', style = '{')
-    h = logging.StreamHandler()
+    h = logging.StreamHandler(level)
+    h.setLevel(level)
     h.setFormatter(f)
     root.addHandler(h)
 
     if args.log:
         h = logging.FileHandler(args.log)
+        h.setLevel(level)
         h.setFormatter(f)
         root.addHandler(h)
 
index ab852430d8920f7e724d717930b36d783b038349..2b0b06b765d2ff23d55a72b67ea118ca4ed29256 100644 (file)
@@ -369,6 +369,10 @@ body
       margin 1em
 
 .console
+  .console-wrapper
+    max-height 400px
+    overflow-y auto
+
   table
     width 100%
     margin 0.5em 0
@@ -383,13 +387,13 @@ body
         margin 0 0.125em
         background-color #fff
 
-      &.error td
+      &.log-error td
         color red
 
-      &.warning td
+      &.log-warning td
         color orange
 
-      &.debug td
+      &.log-debug td
         color green
 
 .indicators
@@ -587,6 +591,10 @@ label.file-upload
     transform scale(0.75)
     margin-top -30px
 
+  .console
+    margin-bottom 1em
+    clear both
+
 
 @media only screen and (max-width 48em)
   .header