From: Joseph Coffland Date: Thu, 14 Sep 2017 08:52:40 +0000 (-0700) Subject: Synchronized slave motors, Gamepad configs, Attention to hold/estop reason, Delete... X-Git-Url: https://git.buildbotics.com/?a=commitdiff_plain;h=0f0a151c466179811af7c1e110808bd84003283e;p=bbctrl-firmware Synchronized slave motors, Gamepad configs, Attention to hold/estop reason, Delete all GCode, Confirm GCode delete, Fixed zeroing, Fixed current calculation, Rename => , Reload video when tab selected --- diff --git a/package.json b/package.json index 33531cd..4e92c8a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bbctrl", - "version": "0.1.22", + "version": "0.2.1", "homepage": "https://github.com/buildbotics/rpi-firmware", "license": "GPL 3+", diff --git a/scripts/setup_rpi.sh b/scripts/setup_rpi.sh index 8f02d90..7c3b266 100755 --- a/scripts/setup_rpi.sh +++ b/scripts/setup_rpi.sh @@ -102,6 +102,10 @@ cp upgrade-bbctrl /usr/local/bin echo -e "\ndtoverlay=pi3-disable-bt" >> /boot/config.txt # sudo systemctl disable hciuart +# Install hawkeye +dpkg -i hawkeye_0.5_armhf.deb +echo 'ACTION=="add", KERNEL=="video0", RUN+="/usr/sbin/service hawkeye restart"' > /etc/udev/rules.d/50-hawkeye.rules + # TODO setup input and serial device permissions in udev & forward 80 -> 8080 reboot diff --git a/src/avr/src/axis.c b/src/avr/src/axis.c index 0697aaf..a000439 100644 --- a/src/avr/src/axis.c +++ b/src/avr/src/axis.c @@ -76,7 +76,17 @@ int axis_get_id(char axis) { int axis_get_motor(int axis) {return motor_map[axis];} -void axis_set_motor(int axis, int motor) {motor_map[axis] = motor;} + + +// Map axes to first matching motor +void axis_map_motors() { + for (int axis = 0; axis < AXES; axis++) + for (int motor = 0; motor < MOTORS; motor++) + if (motor_get_axis(motor) == axis) { + motor_map[axis] = motor; + break; + } +} float axis_get_vector_length(const float a[], const float b[]) { @@ -110,11 +120,11 @@ float axis_get_vector_length(const float a[], const float b[]) { AXIS_VAR_SET(NAME, TYPE) +AXIS_SET(homed, bool) + AXIS_GET(velocity_max, float, 0) AXIS_GET(homed, bool, false) -AXIS_SET(homed, bool) AXIS_GET(homing_mode, homing_mode_t, HOMING_MANUAL) -AXIS_SET(homing_mode, homing_mode_t) AXIS_GET(radius, float, 0) AXIS_GET(travel_min, float, 0) AXIS_GET(travel_max, float, 0) @@ -137,6 +147,7 @@ AXIS_VAR_SET(velocity_max, float) AXIS_VAR_SET(radius, float) AXIS_VAR_SET(travel_min, float) AXIS_VAR_SET(travel_max, float) +AXIS_VAR_SET(homing_mode, homing_mode_t) AXIS_VAR_SET(search_velocity, float) AXIS_VAR_SET(latch_velocity, float) AXIS_VAR_SET(zero_backoff, float) diff --git a/src/avr/src/axis.h b/src/avr/src/axis.h index af7f658..0d8251a 100644 --- a/src/avr/src/axis.h +++ b/src/avr/src/axis.h @@ -53,7 +53,7 @@ bool axis_is_enabled(int axis); char axis_get_char(int axis); int axis_get_id(char axis); int axis_get_motor(int axis); -void axis_set_motor(int axis, int motor); +void axis_map_motors(); float axis_get_vector_length(const float a[], const float b[]); float axis_get_velocity_max(int axis); @@ -61,7 +61,6 @@ float axis_get_jerk_max(int axis); bool axis_get_homed(int axis); void axis_set_homed(int axis, bool homed); homing_mode_t axis_get_homing_mode(int axis); -void axis_set_homing_mode(int axis, homing_mode_t mode); float axis_get_radius(int axis); float axis_get_travel_min(int axis); float axis_get_travel_max(int axis); diff --git a/src/avr/src/motor.c b/src/avr/src/motor.c index 4e421ff..b8a43e7 100644 --- a/src/avr/src/motor.c +++ b/src/avr/src/motor.c @@ -52,6 +52,7 @@ typedef struct { // Config uint8_t axis; // map motor to axis + bool slave; uint16_t microsteps; // microsteps per full step bool reverse; motor_power_mode_t power_mode; @@ -136,7 +137,6 @@ void motor_init() { motor_t *m = &motors[motor]; _update_config(motor); - axis_set_motor(m->axis, motor); // IO pins DIRSET_PIN(m->step_pin); // Output @@ -162,9 +162,9 @@ void motor_init() { m->dma->REPCNT = 0; m->dma->CTRLB = 0; m->dma->CTRLA = DMA_CH_SINGLE_bm | DMA_CH_BURSTLEN_1BYTE_gc; - - drv8711_set_microsteps(motor, m->microsteps); } + + axis_map_motors(); } @@ -176,29 +176,7 @@ bool motor_is_enabled(int motor) { int motor_get_axis(int motor) {return motors[motor].axis;} -void motor_set_axis(int motor, uint8_t axis) { - if (MOTORS <= motor || AXES <= axis || axis == motors[motor].axis) return; - axis_set_motor(motors[motor].axis, -1); - motors[motor].axis = axis; - axis_set_motor(axis, motor); -} - - float motor_get_steps_per_unit(int motor) {return motors[motor].steps_per_unit;} -uint16_t motor_get_microsteps(int motor) {return motors[motor].microsteps;} - - -void motor_set_microsteps(int motor, uint16_t microsteps) { - switch (microsteps) { - case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: case 256: - break; - default: return; - } - - motors[motor].microsteps = microsteps; - _update_config(motor); - drv8711_set_microsteps(motor, microsteps); -} void motor_set_position(int motor, int32_t position) { @@ -384,8 +362,13 @@ float get_step_angle(int motor) {return motors[motor].step_angle;} void set_step_angle(int motor, float value) { - motors[motor].step_angle = value; - _update_config(motor); + if (motors[motor].slave) return; + + for (int m = motor; m < MOTORS; m++) + if (motors[m].axis == motors[motor].axis) { + motors[m].step_angle = value; + _update_config(m); + } } @@ -393,8 +376,13 @@ float get_travel(int motor) {return motors[motor].travel_rev;} void set_travel(int motor, float value) { - motors[motor].travel_rev = value; - _update_config(motor); + if (motors[motor].slave) return; + + for (int m = motor; m < MOTORS; m++) + if (motors[m].axis == motors[motor].axis) { + motors[m].travel_rev = value; + _update_config(m); + } } @@ -403,7 +391,21 @@ uint16_t get_microstep(int motor) {return motors[motor].microsteps;} void set_microstep(int motor, uint16_t value) { if (motor < 0 || MOTORS <= motor) return; - motor_set_microsteps(motor, value); + + switch (value) { + case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: case 256: + break; + default: return; + } + + if (motors[motor].slave) return; + + for (int m = motor; m < MOTORS; m++) + if (motors[m].axis == motors[motor].axis) { + motors[m].microsteps = value; + _update_config(m); + drv8711_set_microsteps(m, value); + } } @@ -414,17 +416,41 @@ bool get_reverse(int motor) { void set_reverse(int motor, bool value) {motors[motor].reverse = value;} -char get_motor_axis(int motor) {return motors[motor].axis;} -void set_motor_axis(int motor, uint8_t axis) {motor_set_axis(motor, axis);} uint8_t get_power_mode(int motor) {return motors[motor].power_mode;} void set_power_mode(int motor, uint8_t value) { - if (value <= MOTOR_POWERED_ONLY_WHEN_MOVING) - motors[motor].power_mode = value; - else motors[motor].power_mode = MOTOR_DISABLED; + if (motors[motor].slave) return; + + for (int m = motor; m < MOTORS; m++) + if (motors[m].axis == motors[motor].axis) + motors[m].power_mode = + value <= MOTOR_POWERED_ONLY_WHEN_MOVING ? value : MOTOR_DISABLED; +} + + +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(); + mp_runtime_set_steps_from_position(); // Reset encoder counts + + // Check if this is now a slave motor + motors[motor].slave = false; + for (int m = 0; m < motor; m++) + if (motors[m].axis == motors[motor].axis) { + // Sync with master + set_step_angle(motor, motors[m].step_angle); + set_travel(motor, motors[m].travel_rev); + set_microstep(motor, motors[m].microsteps); + set_power_mode(motor, motors[m].power_mode); + motors[motor].slave = true; + break; + } } diff --git a/src/avr/src/motor.h b/src/avr/src/motor.h index ce568b4..2115bf7 100644 --- a/src/avr/src/motor.h +++ b/src/avr/src/motor.h @@ -47,8 +47,6 @@ void motor_init(); bool motor_is_enabled(int motor); int motor_get_axis(int motor); float motor_get_steps_per_unit(int motor); -uint16_t motor_get_microsteps(int motor); -void motor_set_microsteps(int motor, uint16_t microsteps); void motor_set_position(int motor, int32_t position); int32_t motor_get_position(int motor); diff --git a/src/avr/src/vars.def b/src/avr/src/vars.def index 8346d7c..683abc1 100644 --- a/src/avr/src/vars.def +++ b/src/avr/src/vars.def @@ -60,10 +60,10 @@ VAR(min_sw_mode, ls, uint8_t, MOTORS, 1, 1, "Minimum switch mode") VAR(max_sw_mode, xs, uint8_t, MOTORS, 1, 1, "Maximum switch mode") VAR(estop_mode, et, uint8_t, 0, 1, 1, "Estop switch mode") VAR(probe_mode, pt, uint8_t, 0, 1, 1, "Probe switch mode") -VAR(min_switch, lw, uint8_t, MOTORS, 0, 1, "Minimum switch state") -VAR(max_switch, xw, uint8_t, MOTORS, 0, 1, "Maximum switch state") -VAR(estop_switch, ew, uint8_t, 0, 0, 1, "Estop switch state") -VAR(probe_switch, pw, uint8_t, 0, 0, 1, "Probe switch state") +VAR(min_switch, lw, uint8_t, MOTORS, 0, 1, "Minimum switch state") +VAR(max_switch, xw, uint8_t, MOTORS, 0, 1, "Maximum switch state") +VAR(estop_switch, ew, uint8_t, 0, 0, 1, "Estop switch state") +VAR(probe_switch, pw, uint8_t, 0, 0, 1, "Probe switch state") // Homing VAR(homing_mode, ho, uint8_t, MOTORS, 1, 1, "Homing type") diff --git a/src/avr/test/planner-test.c b/src/avr/test/planner-test.c index 78206ee..3161da5 100644 --- a/src/avr/test/planner-test.c +++ b/src/avr/test/planner-test.c @@ -40,7 +40,7 @@ int main(int argc, char *argv[]) { mp_init(); // motion planning machine_init(); // gcode machine - for (int i = 0; i < 4; i++) axis_set_motor(i, i); + axis_map_motors(); stat_t status = STAT_OK; diff --git a/src/jade/index.jade b/src/jade/index.jade index 3de168c..de30069 100644 --- a/src/jade/index.jade +++ b/src/jade/index.jade @@ -29,7 +29,7 @@ html(lang="en") a#menuLink.menu-link(href="#menu"): span #menu - button.save(:disabled="!modified", class="pure-button button-success" + button.save.pure-button.button-success(:disabled="!modified", @click="save") Save .pure-menu @@ -44,12 +44,12 @@ html(lang="en") a.pure-menu-link(:href="'#motor:' + $index") Motor {{$index}} li.pure-menu-heading - a.pure-menu-link(href="#spindle") Spindle + a.pure-menu-link(href="#tool") Tool li.pure-menu-heading a.pure-menu-link(href="#io") I/O - li.pure-menu-heading + li.pure-menu-heading(style="display:none") a.pure-menu-link(href="#gcode") Gcode li.pure-menu-heading diff --git a/src/jade/templates/admin-view.jade b/src/jade/templates/admin-view.jade index fee2ce4..13150aa 100644 --- a/src/jade/templates/admin-view.jade +++ b/src/jade/templates/admin-view.jade @@ -42,7 +42,7 @@ script#admin-view-template(type="text/x-template") h3(slot="header") Reset to default configuration? p(slot="body") All configuration changes will be lost. div(slot="footer") - button.pure-button.button-error(@click="confirmReset = false") Cancel + button.pure-button(@click="confirmReset = false") Cancel button.pure-button.button-success(@click="reset") OK message(:show.sync="configReset") diff --git a/src/jade/templates/control-view.jade b/src/jade/templates/control-view.jade index d38b7d6..d69fef8 100644 --- a/src/jade/templates/control-view.jade +++ b/src/jade/templates/control-view.jade @@ -23,8 +23,7 @@ script#control-view-template(type="text/x-template") button.pure-button(:disabled="state.x != 'READY'", title="Zero {{'#{axis}' | upper}} axis offset.", - @click="set_position('#{axis}', state['#{axis}p'])") - | ∅ + @click="zero_axis('#{axis}')") ∅ button.pure-button(:disabled="state.x != 'READY'", title="Home {{'#{axis}' | upper}} axis.", @@ -74,11 +73,11 @@ script#control-view-template(type="text/x-template") table.info tr th State - td {{get_state()}} + td(:class="{attention: highlight_reason()}") {{get_state()}} td tr th Reason - td.reason {{get_reason()}} + td.reason(:class="{attention: highlight_reason()}") {{get_reason()}} td tr th Feed @@ -172,18 +171,30 @@ script#control-view-template(type="text/x-template") section#content1.tab-content .toolbar - button.pure-button(title="Upload a new program file.", @click="open", + button.pure-button(title="Upload a new GCode program.", @click="open", :disabled="state.x == 'RUNNING' || state.x == 'STOPPING'") .fa.fa-folder-open input.gcode-file-input(type="file", @change="upload", style="display:none", accept=".nc,.gcode,.gc,.ngc") - button.pure-button(title="Delete current program file.", - @click="delete", :disabled="!file") + button.pure-button(title="Delete current GCode program.", + @click="deleteGCode = true", :disabled="!file") .fa.fa-trash - select(title="Select previously uploaded program files.", + message(:show.sync="deleteGCode") + h3(slot="header") Delete GCode? + p(slot="body") + div(slot="footer") + button.pure-button(@click="deleteGCode = false") Cancel + button.pure-button.button-error(@click="deleteAll") + .fa.fa-trash + |  all + button.pure-button.button-success(@click="deleteCurrent") + .fa.fa-trash + |  selected + + select(title="Select previously uploaded GCode programs.", v-model="file", @change="load", :disabled="state.x == 'RUNNING' || state.x == 'STOPPING'") option(v-for="file in files", :value="file") {{file}} diff --git a/src/jade/templates/spindle-view.jade b/src/jade/templates/spindle-view.jade deleted file mode 100644 index c744d0a..0000000 --- a/src/jade/templates/spindle-view.jade +++ /dev/null @@ -1,8 +0,0 @@ -script#spindle-view-template(type="text/x-template") - #spindle - h1 Spindle Configuration - - form.pure-form.pure-form-aligned - fieldset - templated-input(v-for="templ in template.spindle", :name="$key", - :model.sync="spindle[$key]", :template="templ") diff --git a/src/jade/templates/tool-view.jade b/src/jade/templates/tool-view.jade new file mode 100644 index 0000000..d92eb3a --- /dev/null +++ b/src/jade/templates/tool-view.jade @@ -0,0 +1,8 @@ +script#tool-view-template(type="text/x-template") + #tool + h1 Spindle Configuration + + form.pure-form.pure-form-aligned + fieldset + templated-input(v-for="templ in template.tool", :name="$key", + :model.sync="tool[$key]", :template="templ") diff --git a/src/js/app.js b/src/js/app.js index cdf6107..0690840 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -26,7 +26,7 @@ module.exports = new Vue({ 'loading-view': {template: '

Loading...

'}, 'control-view': require('./control-view'), 'motor-view': require('./motor-view'), - 'spindle-view': require('./spindle-view'), + 'tool-view': require('./tool-view'), 'io-view': require('./io-view'), 'gcode-view': require('./gcode-view'), 'admin-view': require('./admin-view') diff --git a/src/js/control-view.js b/src/js/control-view.js index 7427a61..3b92f09 100644 --- a/src/js/control-view.js +++ b/src/js/control-view.js @@ -22,7 +22,7 @@ function escapeHTML(s) { module.exports = { template: '#control-view-template', - props: ['config', 'state'], + props: ['config', 'template', 'state'], data: function () { @@ -41,7 +41,8 @@ module.exports = { position_msg: {x: false, y: false, z: false, a: false, b: false, c: false}, axis_position: 0, - video_url: '' + video_url: '', + deleteGCode: false } }, @@ -94,9 +95,8 @@ module.exports = { }, - send: function (msg) { - this.$dispatch('send', msg); - }, + highlight_reason: function () {return this.get_reason() != ''}, + send: function (msg) {this.$dispatch('send', msg)}, get_axis_motor_id: function (axis) { @@ -117,9 +117,21 @@ module.exports = { }, - enabled: function (axis) { + get_axis_motor_param: function (axis, name) { var motor = this.get_axis_motor(axis); - return typeof motor != 'undefined' && motor['power-mode'] != 'disabled'; + if (typeof motor == 'undefined') return; + if (typeof motor[name] != 'undefined') return motor[name]; + + for (var section in this.template['motors']) { + var sec = this.template['motors'][section]; + if (typeof sec[name] != 'undefined') return sec[name]['default']; + } + }, + + + enabled: function (axis) { + var pm = this.get_axis_motor_param(axis, 'power-mode') + return typeof pm != 'undefined' && pm != 'disabled'; }, @@ -148,6 +160,7 @@ module.exports = { update: function () { + // Update file list api.get('file') .done(function (files) { var index = files.indexOf(this.file); @@ -215,9 +228,15 @@ module.exports = { }, - delete: function () { - if (!this.file) return; - api.delete('file/' + this.file).done(this.update); + deleteCurrent: function () { + if (this.file) api.delete('file/' + this.file).done(this.update); + this.deleteGCode = false; + }, + + + deleteAll: function () { + api.delete('file').done(this.update); + this.deleteGCode = false; }, @@ -225,11 +244,9 @@ module.exports = { if (typeof axis == 'undefined') api.put('home'); else { - var motor = this.get_axis_motor(axis); - - if (motor['homing-mode'] == 'manual') - this.manual_home[axis] = true; - else api.put('home/' + axis); + if (this.get_axis_motor_param(axis, 'homing-mode') != 'manual') + api.put('home/' + axis); + else this.manual_home[axis] = true; } }, @@ -253,10 +270,13 @@ module.exports = { set_position: function (axis, position) { this.position_msg[axis] = false; - api.put('position/' + axis, {position: 0}); + api.put('position/' + axis, {'position': parseFloat(position)}); }, + zero_axis: function (axis) {this.set_position(axis, 0)}, + + start_pause: function () { if (this.state.x == 'RUNNING') this.pause(); @@ -297,7 +317,8 @@ module.exports = { load_video: function () { - this.video_url = '//' + document.location.hostname + ':8000/stream/0'; + this.video_url = '//' + document.location.hostname + ':8000/stream/0?=' + + Math.random(); } } } diff --git a/src/js/motor-view.js b/src/js/motor-view.js index 534428c..adc7d11 100644 --- a/src/js/motor-view.js +++ b/src/js/motor-view.js @@ -21,6 +21,7 @@ module.exports = { events: { 'input-changed': function() { + this.slave_update(); this.$dispatch('config-changed'); return false; } @@ -32,6 +33,28 @@ module.exports = { methods: { + slave_update: function () { + var slave = false; + for (var i = 0; i < this.index; i++) + if (this.motor['axis'] == this.config.motors[i]['axis']) + slave = true; + + var el = $(this.$el); + + if (slave) { + el.find('.axis .units').text('(slave motor)'); + el.find('.limits, .homing, .motion').find('input, select') + .attr('disabled', 1); + el.find('.power-mode select').attr('disabled', 1); + el.find('.motion .reverse input').removeAttr('disabled'); + + } else { + el.find('.axis .units').text(''); + el.find('input,select').removeAttr('disabled'); + } + }, + + update: function () { if (!this.active) return; @@ -46,6 +69,8 @@ module.exports = { if (!this.motor.hasOwnProperty(key)) this.$set('motor["' + key + '"]', template[category][key].default); + + this.slave_update(); }.bind(this)); } } diff --git a/src/js/spindle-view.js b/src/js/spindle-view.js deleted file mode 100644 index 475b306..0000000 --- a/src/js/spindle-view.js +++ /dev/null @@ -1,42 +0,0 @@ -'use strict' - - -module.exports = { - template: '#spindle-view-template', - props: ['config', 'template'], - - - data: function () { - return { - spindle: {} - } - }, - - - events: { - 'input-changed': function() { - this.$dispatch('config-changed'); - return false; - } - }, - - - ready: function () { - this.update(); - }, - - - methods: { - update: function () { - Vue.nextTick(function () { - if (this.config.hasOwnProperty('spindle')) - this.spindle = this.config.spindle; - - var template = this.template.spindle; - for (var key in template) - if (!this.spindle.hasOwnProperty(key)) - this.$set('spindle["' + key + '"]', template[key].default); - }.bind(this)); - } - } -} diff --git a/src/js/tool-view.js b/src/js/tool-view.js new file mode 100644 index 0000000..00fe6fe --- /dev/null +++ b/src/js/tool-view.js @@ -0,0 +1,42 @@ +'use strict' + + +module.exports = { + template: '#tool-view-template', + props: ['config', 'template'], + + + data: function () { + return { + tool: {} + } + }, + + + events: { + 'input-changed': function() { + this.$dispatch('config-changed'); + return false; + } + }, + + + ready: function () { + this.update(); + }, + + + methods: { + update: function () { + Vue.nextTick(function () { + if (this.config.hasOwnProperty('tool')) + this.tool = this.config.tool; + + var template = this.template.tool; + for (var key in template) + if (!this.tool.hasOwnProperty(key)) + this.$set('tool["' + key + '"]', template[key].default); + }.bind(this)); + } + } +} diff --git a/src/pwr/main.c b/src/pwr/main.c index 1aa7cc2..58ba6d5 100644 --- a/src/pwr/main.c +++ b/src/pwr/main.c @@ -138,11 +138,7 @@ inline static uint16_t convert_voltage(uint16_t sample) { inline static uint16_t convert_current(uint16_t sample) { -#define CR1 1000 -#define CR2 137 - - // TODO This isn't correct - return sample * (VREF / 1024.0 * (CR1 + CR2) / CR2 * 100); + return sample * (VREF / 1024.0 * 1970); } @@ -272,6 +268,13 @@ int main() { OCR0A = 0; // 0% duty cycle _delay_ms(3000); } + + if (10 < get_reg(MOTOR_REG)) { + OCR0A = 0; // 0% duty cycle + TCCR0A = 0; + GATE_DDR = 0; + _delay_ms(1000); + } } return 0; diff --git a/src/py/bbctrl/AVR.py b/src/py/bbctrl/AVR.py index 8d94361..9381039 100644 --- a/src/py/bbctrl/AVR.py +++ b/src/py/bbctrl/AVR.py @@ -91,7 +91,6 @@ class AVR(): self.stop(); self.ctrl.config.config_avr() self._restore_machine_state() - self.report() except Exception as e: log.warning('Connect failed: %s', e) @@ -129,6 +128,8 @@ class AVR(): self.queue_command('${}={}'.format(var, value)) + self.queue_command('$$') # Refresh all vars, must come after above + def report(self): self._i2c_command(I2C_REPORT) @@ -246,6 +247,12 @@ class AVR(): return motor + def _is_axis_homed(self, axis): + motor = self._find_motor(axis) + if axis is None: return False + return self.vars['%dh' % motor] + + def _update_lcd(self, msg): if 'x' in msg or 'c' in msg: v = self.vars @@ -346,4 +353,6 @@ class AVR(): def set_position(self, axis, position): if self.stream is not None: raise Exception('Busy, cannot set position') - self.queue_command('G92 %c%f' % (axis, position)) + if self._is_axis_homed('%c' % axis): + self.queue_command('G92 %c%f' % (axis, position)) + else: self.queue_command('$%cp=%f' % (axis, position)) diff --git a/src/py/bbctrl/Config.py b/src/py/bbctrl/Config.py index d0e5c91..c377cae 100644 --- a/src/py/bbctrl/Config.py +++ b/src/py/bbctrl/Config.py @@ -13,11 +13,11 @@ default_config = { {"axis": "X"}, {"axis": "Y"}, {"axis": "Z"}, - {"axis": "A"}, + {"axis": "A", "power-mode" : "disabled"}, ], "switches": {}, "outputs": {}, - "spindle": {}, + "tool": {}, "gcode": {}, } diff --git a/src/py/bbctrl/FileHandler.py b/src/py/bbctrl/FileHandler.py index c51eb23..576f959 100644 --- a/src/py/bbctrl/FileHandler.py +++ b/src/py/bbctrl/FileHandler.py @@ -7,8 +7,14 @@ class FileHandler(bbctrl.APIHandler): def delete_ok(self, path): - path = 'upload' + path - if os.path.exists(path): os.unlink(path) + if not path: + if os.path.exists('upload'): + for path in os.listdir('upload'): + if os.path.isfile('upload/' + path): + os.unlink('upload/' + path) + else: + path = 'upload' + path + if os.path.exists(path): os.unlink(path) def put_ok(self, path): diff --git a/src/py/bbctrl/Jog.py b/src/py/bbctrl/Jog.py index ac88ee4..48e5de0 100644 --- a/src/py/bbctrl/Jog.py +++ b/src/py/bbctrl/Jog.py @@ -8,11 +8,23 @@ class Jog(inevent.JogHandler): self.ctrl = ctrl config = { - "deadband": 0.1, - "axes": [ABS_X, ABS_Y, ABS_RZ, ABS_Z], - "arrows": [ABS_HAT0X, ABS_HAT0Y], - "speed": [0x120, 0x121, 0x122, 0x123], - "activate": [0x124, 0x126, 0x125, 0x127], + "Logitech Logitech RumblePad 2 USB": { + "deadband": 0.1, + "axes": [ABS_X, ABS_Y, ABS_RZ, ABS_Z], + "dir": [1, -1, -1, 1], + "arrows": [ABS_HAT0X, ABS_HAT0Y], + "speed": [0x120, 0x121, 0x122, 0x123], + "lock": [0x124, 0x125], + }, + + "default": { + "deadband": 0.1, + "axes": [ABS_X, ABS_Y, ABS_RY, ABS_RX], + "dir": [1, -1, -1, 1], + "arrows": [ABS_HAT0X, ABS_HAT0Y], + "speed": [0x133, 0x130, 0x131, 0x134], + "lock": [0x136, 0x137], + } } super().__init__(config) @@ -48,5 +60,3 @@ class Jog(inevent.JogHandler): if self.speed == 4: scale = 1.0 self.v = [x * scale for x in self.axes] - self.v[ABS_Y] = -self.v[ABS_Y] - self.v[ABS_Z] = -self.v[ABS_Z] diff --git a/src/py/inevent/AbsAxisScaling.py b/src/py/inevent/AbsAxisScaling.py index bd4baa1..c5ddcf5 100644 --- a/src/py/inevent/AbsAxisScaling.py +++ b/src/py/inevent/AbsAxisScaling.py @@ -77,4 +77,3 @@ class AbsAxisScaling(object): """ return (float(value) - float(self.minimum)) / \ float(self.maximum - self.minimum) * 2.0 - 1.0 - diff --git a/src/py/inevent/EventHandler.py b/src/py/inevent/EventHandler.py index 97642503..699c80b 100644 --- a/src/py/inevent/EventHandler.py +++ b/src/py/inevent/EventHandler.py @@ -42,7 +42,7 @@ class EventHandler(object): self.buttons = dict() - def event(self, event, handler): + def event(self, event, handler, name): """ Handles the given event. @@ -80,7 +80,7 @@ class EventHandler(object): elif event.type == EV_ABS: state.abs[event.code] = event.stream.scale(event.code, event.value) - if handler: handler(event, state) + if handler: handler.event(event, state, name) def key_state(self, code): diff --git a/src/py/inevent/EventStream.py b/src/py/inevent/EventStream.py index a69bb38..ba2709c 100644 --- a/src/py/inevent/EventStream.py +++ b/src/py/inevent/EventStream.py @@ -89,7 +89,7 @@ class EventStream(object): ABS_DISTANCE, ABS_TILT_X, ABS_TILT_Y, ABS_TOOL_WIDTH] - def __init__(self, devIndex, devType): + def __init__(self, devIndex, devType, devName): """ Opens the given /dev/input/event file and grabs it. @@ -97,6 +97,7 @@ class EventStream(object): """ self.devIndex = devIndex self.devType = devType + self.devName = devName self.filename = "/dev/input/event" + str(devIndex) self.filehandle = os.open(self.filename, os.O_RDWR) self.state = EventState() diff --git a/src/py/inevent/InEvent.py b/src/py/inevent/InEvent.py index 4a89e99..400cea7 100644 --- a/src/py/inevent/InEvent.py +++ b/src/py/inevent/InEvent.py @@ -55,33 +55,6 @@ def key_to_code(key): def code_to_key(code): return CODE_KEY.get(code, '') -def find_devices(types): - """Finds the event indices of all devices of the specified types. - - A type is a string on the handlers line of /proc/bus/input/devices. - Keyboards use "kbd", mice use "mouse" and joysticks (and gamepads) use "js". - - Returns a list of integer indexes N, where /dev/input/eventN is the event - stream for each device. - - If butNot is given it holds a list of tuples which the returned values should - not match. - - All devices of each type are returned; if you have two mice, they will both - be used. - """ - with open("/proc/bus/input/devices", "r") as filehandle: - for line in filehandle: - if line[0] == "H": - for type in types: - if type in line: - match = re.search("event([0-9]+)", line) - index = match and match.group(1) - if index: yield int(index), type - break - - - class InEvent(object): """Encapsulates the entire InEvent subsystem. @@ -124,17 +97,54 @@ class InEvent(object): self.handler = EventHandler() self.types = types - devs = list(find_devices(types)) - for index, type in devs: - self.add_stream(index, type) - self.udevCtx = pyudev.Context() self.udevMon = pyudev.Monitor.from_netlink(self.udevCtx) self.udevMon.filter_by(subsystem = 'input') + + devs = list(self.find_devices(types)) + for index, type, name in devs: + self.add_stream(index, type, name) + self.udevMon.start() ioloop.add_handler(self.udevMon.fileno(), self.udev_handler, ioloop.READ) + def get_dev(self, index): + return pyudev.Device.from_name(self.udevCtx, 'input', 'event%s' % index) + + def get_dev_name(self, index): + dev = self.get_dev(index) + for name, value in dev.parent.attributes.items(): + if name == 'name': return value.decode('utf-8') + + + def find_devices(self, types): + """Finds the event indices of all devices of the specified types. + + A type is a string on the handlers line of /proc/bus/input/devices. + Keyboards use "kbd", mice use "mouse" and joysticks (and gamepads) use "js". + + Returns a list of integer indexes N, where /dev/input/eventN is the event + stream for each device. + + If butNot is given it holds a list of tuples which the returned values + should not match. + + All devices of each type are returned; if you have two mice, they will both + be used. + """ + with open("/proc/bus/input/devices", "r") as filehandle: + for line in filehandle: + if line[0] == "H": + for type in types: + if type in line: + match = re.search("event([0-9]+)", line) + index = match and match.group(1) + if index: + yield int(index), type, self.get_dev_name(index) + break + + def process_udev_event(self): action, device = self.udevMon.receive_device() if device is None: return @@ -145,9 +155,13 @@ class InEvent(object): devIndex = int(devIndex) if action == 'add': - for index, devType in find_devices(self.types): + devName = None + for name, value in device.attributes.items(): + if name == 'name': devName = value + + for index, devType, devName in self.find_devices(self.types): if index == devIndex: - self.add_stream(devIndex, devType) + self.add_stream(devIndex, devType, devName) break if action == 'remove': @@ -159,7 +173,7 @@ class InEvent(object): if stream.filehandle == fd: while True: event = stream.next() - if event: self.handler.event(event, self.cb) + if event: self.handler.event(event, self.cb, stream.devName) else: break @@ -167,15 +181,15 @@ class InEvent(object): self.process_udev_event() - def add_stream(self, devIndex, devType): + def add_stream(self, devIndex, devType, devName): try: - stream = EventStream(devIndex, devType) + stream = EventStream(devIndex, devType, devName) self.streams.append(stream) self.ioloop.add_handler(stream.filehandle, self.stream_handler, self.ioloop.READ) - log.info('Added %s[%d]', devType, devIndex) + log.info('Added %s[%d] %s', devType, devIndex, devName) except OSError as e: log.warning('Failed to add %s[%d]: %s', devType, devIndex, e) @@ -187,6 +201,7 @@ class InEvent(object): self.streams.remove(stream) self.ioloop.remove_handler(stream.filehandle) stream.release() + self.cb.clear() log.info('Removed %s[%d]', stream.devType, devIndex) diff --git a/src/py/inevent/JogHandler.py b/src/py/inevent/JogHandler.py index 62023e5..47d9f25 100644 --- a/src/py/inevent/JogHandler.py +++ b/src/py/inevent/JogHandler.py @@ -32,9 +32,7 @@ def event_to_string(event, state): class JogHandler: def __init__(self, config): self.config = config - self.axes = [0.0, 0.0, 0.0, 0.0] - self.speed = 3 - self.activate = 0 + self.reset() def changed(self): @@ -47,17 +45,35 @@ class JogHandler: def right(self): log.debug('right') - def __call__(self, event, state): + def reset(self): + self.axes = [0.0, 0.0, 0.0, 0.0] + self.speed = 3 + self.vertical_lock = 0 + self.horizontal_lock = 0 + + + def clear(self): + self.reset() + self.changed() + + + def get_config(self, name): + if name in self.config: return self.config[name] + return self.config['default'] + + + def event(self, event, state, dev_name): if event.type not in [EV_ABS, EV_REL, EV_KEY]: return + config = self.get_config(dev_name) changed = False # Process event - if event.type == EV_ABS and event.code in self.config['axes']: + if event.type == EV_ABS and event.code in config['axes']: pass - elif event.type == EV_ABS and event.code in self.config['arrows']: - axis = self.config['arrows'].index(event.code) + elif event.type == EV_ABS and event.code in config['arrows']: + axis = config['arrows'].index(event.code) if event.value < 0: if axis == 1: self.up() @@ -67,16 +83,19 @@ class JogHandler: if axis == 1: self.down() else: self.right() - elif event.type == EV_KEY and event.code in self.config['speed']: + elif event.type == EV_KEY and event.code in config['speed']: old_speed = self.speed - self.speed = self.config['speed'].index(event.code) + 1 + self.speed = config['speed'].index(event.code) + 1 if self.speed != old_speed: changed = True - elif event.type == EV_KEY and event.code in self.config['activate']: - index = self.config['activate'].index(event.code) + elif event.type == EV_KEY and event.code in config['lock']: + index = config['lock'].index(event.code) - if event.value: self.activate |= 1 << index - else: self.activate &= ~(1 << index) + self.horizontal_lock, self.vertical_lock = False, False + + if event.value: + if index == 0: self.horizontal_lock = True + if index == 1: self.vertical_lock = True log.debug(event_to_string(event, state)) @@ -84,10 +103,16 @@ class JogHandler: old_axes = list(self.axes) for axis in range(4): - self.axes[axis] = event.stream.state.abs[self.config['axes'][axis]] - if abs(self.axes[axis]) < self.config['deadband']: + self.axes[axis] = event.stream.state.abs[config['axes'][axis]] + self.axes[axis] *= config['dir'][axis] + + if abs(self.axes[axis]) < config['deadband']: self.axes[axis] = 0 - if not (1 << axis) & self.activate and self.activate: + + if self.horizontal_lock and axis not in [0, 3]: + self.axes[axis] = 0 + + if self.vertical_lock and axis not in [1, 2]: self.axes[axis] = 0 if old_axes != self.axes: changed = True diff --git a/src/resources/config-defaults.json b/src/resources/config-defaults.json deleted file mode 100644 index 8593c1d..0000000 --- a/src/resources/config-defaults.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "motors": [ - { - "axis": "X" - }, - { - "axis": "Y" - }, - { - "axis": "Z" - }, - { - "axis": "A" - }, - ], - - "spindle": {}, -} diff --git a/src/resources/config-template.json b/src/resources/config-template.json index f48a9c5..cf47233 100644 --- a/src/resources/config-template.json +++ b/src/resources/config-template.json @@ -21,7 +21,7 @@ "min": 0, "max": 8, "unit": "amps", - "default": 2, + "default": 1.5, "code": "dc" }, "idle-current": { @@ -35,20 +35,10 @@ }, "motion": { - "step-angle": { - "type": "float", - "min": 0, - "max": 360, - "step": 0.1, - "unit": "degrees", - "default": 1.8, - "code": "sa" - }, - "travel-per-rev": { - "type": "float", - "unit": "mm", - "default": 5, - "code": "tr" + "reverse": { + "type": "bool", + "default": false, + "code": "rv" }, "microsteps": { "type": "int", @@ -57,24 +47,34 @@ "default": 32, "code": "mi" }, - "reverse": { - "type": "bool", - "default": false, - "code": "rv" - }, "max-velocity": { "type": "float", "min": 0, "unit": "mm/min", - "default": 5000, + "default": 6000, "code": "vm" }, "max-jerk": { "type": "float", "min": 0, "unit": "mm/min³", - "default": 10, + "default": 50, "code": "jm" + }, + "step-angle": { + "type": "float", + "min": 0, + "max": 360, + "step": 0.1, + "unit": "degrees", + "default": 1.8, + "code": "sa" + }, + "travel-per-rev": { + "type": "float", + "unit": "mm", + "default": 5, + "code": "tr" } }, @@ -143,7 +143,7 @@ } }, - "spindle": { + "tool": { "spindle-type": { "type": "enum", "values": ["HUANYANG", "PWM"], diff --git a/src/stylus/style.styl b/src/stylus/style.styl index 3915154..c729d3f 100644 --- a/src/stylus/style.styl +++ b/src/stylus/style.styl @@ -5,7 +5,16 @@ body display none .button-success:not([disabled]) - background rgb(28, 184, 65) + background-color #1cb841 + +.button-error:not([disabled]) + background-color #ca3c3c + +.button-warning:not([disabled]) + background-color #df7514 + +.button-secondary:not([disabled]) + background-color #42b8dd .header, .content padding 0 @@ -55,6 +64,15 @@ body .success background green +@keyframes attention + 50% + opacity 0.5 + +.attention + background-color #f5e138 + color #000 + animation attention 2s step-start 0s infinite + .status color #eee text-align center @@ -259,6 +277,7 @@ body clear right .override + display none /* Hidden for now */ margin 0.5em 0 white-space nowrap