- 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.
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
{
"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+",
# 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
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
}
-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
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();
#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,
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);
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
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]);
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;
#pragma once
-#include <avr/pgmspace.h>
+#include "pgmspace.h"
#include <stdint.h>
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) {
}
-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;
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;
}
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;
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();
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);
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]);
}
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;
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);
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();
\******************************************************************************/
-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")
// 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
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;
#include "command.h"
#include "stepper.h"
#include "spindle.h"
+#include "estop.h"
#include "report.h"
#include <stdio.h>
* - 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;
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
#include "spindle.h"
#include "i2c.h"
#include "type.h"
-#include "exec.h"
#include "cpp_magic.h"
#include <stdint.h>
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]);
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));
.fa.fa-trash
| 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}}
.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
else {
msg.repeat = 1;
messages.unshift(msg);
+ while (256 < messages.length) messages.pop();
}
msg.ts = Date.now();
# 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
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)
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)
# 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)
# 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
# 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)
margin 1em
.console
+ .console-wrapper
+ max-height 400px
+ overflow-y auto
+
table
width 100%
margin 0.5em 0
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
transform scale(0.75)
margin-top -30px
+ .console
+ margin-bottom 1em
+ clear both
+
@media only screen and (max-width 48em)
.header