From: Joseph Coffland Date: Thu, 26 May 2016 06:14:19 +0000 (-0700) Subject: LCD support, other changes X-Git-Url: https://git.buildbotics.com/?a=commitdiff_plain;h=5002ca7303632aff5603b3b3c1ce095ec7f04f02;p=bbctrl-firmware LCD support, other changes --- diff --git a/Makefile b/Makefile index 9e79f23..658cd52 100644 --- a/Makefile +++ b/Makefile @@ -27,6 +27,13 @@ all: html css js static copy: all cp -r *.py inevent http/ $(DEST) +mount: + mkdir -p $(DEST) + sshfs bbmc@bbctrl.local: $(DEST) + +umount: + fusermount -u $(DEST) + html: templates $(HTML) css: $(CSS_ASSETS) $(CSS_ASSETS).sha256 @@ -91,4 +98,4 @@ clean: tidy dist-clean: clean rm -rf node_modules -.PHONY: all install html css static templates clean tidy +.PHONY: all install html css static templates clean tidy copy mount umount diff --git a/bbctrl.init.d b/bbctrl.init.d index cc1f882..4dad98d 100644 --- a/bbctrl.init.d +++ b/bbctrl.init.d @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash ### BEGIN INIT INFO # Provides: bbctl @@ -10,10 +10,11 @@ # Description: Buildbotics Controller Web service ### END INIT INFO -DAEMON=/usr/local/bin/bbctrl.py +DAEMON=/home/bbmc/bbctrl.py DAEMON_NAME=bbctrl DAEMON_OPTS="" DAEMON_USER=root +DAEMON_DIR=$(dirname $DAEMON) PIDFILE=/var/run/$DAEMON_NAME.pid @@ -23,8 +24,8 @@ PIDFILE=/var/run/$DAEMON_NAME.pid do_start () { log_daemon_msg "Starting system $DAEMON_NAME daemon" start-stop-daemon --start --background --pidfile $PIDFILE --make-pidfile \ - --user $DAEMON_USER --chuid $DAEMON_USER --startas $DAEMON -- \ - $DAEMON_OPTS + --user $DAEMON_USER --chuid $DAEMON_USER --startas $DAEMON \ + --chdir $DAEMON_DIR -- $DAEMON_OPTS log_end_msg $? } diff --git a/bbctrl.py b/bbctrl.py index 69bd1c4..c7d6888 100755 --- a/bbctrl.py +++ b/bbctrl.py @@ -220,11 +220,16 @@ class SerialProcess(multiprocessing.Process): # look for incoming serial data if self.sp.inWaiting() > 0: - data = self.readSerial() - # send it back to tornado - self.output_queue.put(data) try: - print(data.decode('utf-8')) + data = self.readSerial() + data = data.decode('utf-8').strip() + if not data: continue + + # send it back to tornado + self.output_queue.put(data) + + print(data) + except Exception as e: print(e, data) @@ -249,10 +254,13 @@ class Connection(SockJSConnection): def checkQueue(): while not output_queue.empty(): try: - msg = json.loads(output_queue.get()) + data = output_queue.get() + msg = json.loads(data) state.update(msg) - for c in clients: c.send(msg) - except: pass + if clients: clients[0].broadcast(clients, msg) + + except Exception as e: + print('ERROR: {}, data: {}'.format(e, data)) handlers = [ diff --git a/lcd.py b/lcd.py new file mode 100755 index 0000000..405a6b6 --- /dev/null +++ b/lcd.py @@ -0,0 +1,195 @@ +#!/usr/bin/env python + +import smbus +import time + + +# Control flags +REG_SELECT_BIT = 1 << 0 +READ_BIT = 1 << 1 +ENABLE_BIT = 1 << 2 +BACKLIGHT_BIT = 1 << 3 + +# Commands +LCD_CLEAR_DISPLAY = 1 << 0 +LCD_RETURN_HOME = 1 << 1 +LCD_ENTRY_MODE_SET = 1 << 2 +LCD_DISPLAY_CONTROL = 1 << 3 +LCD_CURSOR_SHIFT = 1 << 4 +LCD_FUNCTION_SET = 1 << 5 +LCD_SET_CGRAM_ADDR = 1 << 6 +LCD_SET_DDRAM_ADDR = 1 << 7 + +# Entry Mode Set flags +LCD_ENTRY_SHIFT_DISPLAY = 1 << 0 +LCD_ENTRY_SHIFT_INC = 1 << 1 +LCD_ENTRY_SHIFT_DEC = 0 << 1 + +# Display Control flags +LCD_BLINK_ON = 1 << 0 +LCD_BLINK_OFF = 0 << 0 +LCD_CURSOR_ON = 1 << 1 +LCD_CURSOR_OFF = 0 << 1 +LCD_DISPLAY_ON = 1 << 2 +LCD_DISPLAY_OFF = 0 << 2 + +# Cursor Shift flags +LCD_SHIFT_RIGHT = 1 << 2 +LCD_SHIFT_LEFT = 0 << 2 +LCD_SHIFT_DISPLAY = 1 << 3 +LCD_SHIFT_CURSOR = 0 << 3 + +# Function Set flags +LCD_5x11_DOTS = 1 << 2 +LCD_5x8_DOTS = 0 << 2 +LCD_2_LINE = 1 << 3 +LCD_1_LINE = 0 << 3 +LCD_8_BIT_MODE = 1 << 4 +LCD_4_BIT_MODE = 0 << 4 + +# Text justification flags +JUSTIFY_LEFT = 0 +JUSTIFY_RIGHT = 1 +JUSTIFY_CENTER = 2 + + + +class LCD: + def __init__(self, port, addr, height = 4, width = 20): + self.addr = addr + self.height = height + self.width = width + + self.bus = smbus.SMBus(port) + self.backlight = True + + self.reset() + + + def reset(self): + self.clear() + self.write(LCD_FUNCTION_SET | LCD_2_LINE | LCD_5x8_DOTS | + LCD_4_BIT_MODE) + self.write(LCD_DISPLAY_CONTROL | LCD_DISPLAY_ON) + self.write(LCD_ENTRY_MODE_SET | LCD_ENTRY_SHIFT_INC) + + + def write_i2c(self, data): + if self.backlight: data |= BACKLIGHT_BIT + + self.bus.write_byte(self.addr, data) + time.sleep(0.0001) + + + # Write half of a command to LCD + def write_nibble(self, data): + self.write_i2c(data) + + # Strobe + self.write_i2c(data | ENABLE_BIT) + time.sleep(0.0005) + + self.write_i2c(data & ~ENABLE_BIT) + time.sleep(0.0001) + + + # Write an 8-bit command to LCD + def write(self, cmd, flags = 0): + self.write_nibble(flags | (cmd & 0xf0)) + self.write_nibble(flags | ((cmd << 4) & 0xf0)) + + + def set_cursor(self, on, blink): + data = LCD_DISPLAY_CONTROL + + if on: data |= LCD_CURSOR_ON + if blink: data |= LCD_BLINK_ON + + self.write(data) + + + def set_backlight(self, enable): + self.backlight = enable + self.write_i2c(0) + + + def program_char(self, addr, data): + if addr < 0 or 8 <= addr: return + + self.write(LCD_SET_CGRAM_ADDR | (addr << 3)) + for x in data: + self.write(x, REG_SELECT_BIT) + + + def goto(self, x, y): + if x < 0 or self.width <= x or y < 0 or self.height <= y: return + self.write(LCD_SET_DDRAM_ADDR | (0, 64, 20, 84)[y] + x) + + + def text(self, msg): + for c in msg: + self.write(ord(c), REG_SELECT_BIT) + + + def display(self, line, msg, justify = JUSTIFY_LEFT): + if justify == JUSTIFY_RIGHT: x = self.width - len(msg) + elif justify == JUSTIFY_CENTER: x = (self.width - len(msg)) / 2 + else: x = 0 + + if x < 0: x = 0 + + self.goto(x, line) + self.text(msg) + + + def shift(self, count = 1, right = True, display = True): + cmd = LCD_CURSOR_SHIFT + if right: cmd |= LCD_SHIFT_RIGHT + if display: cmd |= LCD_SHIFT_DISPLAY + + for i in range(count): self.write(cmd) + + + # Clear LCD and move cursor home + def clear(self): + self.write(LCD_CLEAR_DISPLAY) + self.write(LCD_RETURN_HOME) + + + +if __name__ == "__main__": + lcd = LCD(1, 0x27) + + lcd.clear() + + lcd.program_char(0, (0b11011, + 0b11011, + 0b00000, + 0b01100, + 0b01100, + 0b00000, + 0b11011, + 0b11011)) + + lcd.program_char(1, (0b11000, + 0b01100, + 0b00110, + 0b00011, + 0b00011, + 0b00110, + 0b01100, + 0b11000)) + + lcd.program_char(2, (0b00011, + 0b00110, + 0b01100, + 0b11000, + 0b11000, + 0b01100, + 0b00110, + 0b00011)) + + lcd.display(0, '\0' * lcd.width) + lcd.display(1, 'Hello world!', JUSTIFY_CENTER) + lcd.display(2, '\1\2' * (lcd.width / 2)) + lcd.display(3, '12345678901234567890') diff --git a/setup_rpi.sh b/setup_rpi.sh index 5bbf703..4b25426 100755 --- a/setup_rpi.sh +++ b/setup_rpi.sh @@ -62,8 +62,8 @@ EOF fi # Install pacakges -apt-get install -y avahi-daemon avrdude minicom python3-pip -pip-3.2 install tornado sockjs-tornado pyserial +apt-get install -y avahi-daemon avrdude minicom python3-pip i2c-tools +pip-3.2 install tornado sockjs-tornado pyserial smbus # Clean apt-get autoclean @@ -79,6 +79,11 @@ echo "bbmc ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers #sed -i 's/^\(.*ttyAMA0.*\)$/# \1/' /etc/inittab sed -i 's/console=ttyAMA0,115200 //' /boot/cmdline.txt +# Enable I2C +sed -i 's/#dtparam=i2c/dtparam=i2c/' /boot/config.txt +echo i2c-bcm2708 >> /etc/modules +echo i2c-dev >> /etc/modules + # TODO setup input and serial device permissions in udev # TODO install bbctrl w/ init.d script diff --git a/src/jade/index.jade b/src/jade/index.jade index de88d52..cf5fa0f 100644 --- a/src/jade/index.jade +++ b/src/jade/index.jade @@ -71,7 +71,7 @@ html(lang="en") .content(class="{{currentView}}-view") component(:is="currentView + '-view'", :index="index", - :config="config", :template="template") + :config="config", :template="template", keep-alive) #templates include ../../build/templates.jade diff --git a/src/jade/templates/admin-view.jade b/src/jade/templates/admin-view.jade index 853214d..f7cd7b7 100644 --- a/src/jade/templates/admin-view.jade +++ b/src/jade/templates/admin-view.jade @@ -1,15 +1,16 @@ script#admin-view-template(type="text/x-template") - h2 Backup configuration - button.pure-button.pure-button-primary(@click="backup") Backup + #admin + h2 Backup configuration + button.pure-button.pure-button-primary(@click="backup") Backup - h2 Restore configuration - button.pure-button.pure-button-primary(@click="restore") Restore + h2 Restore configuration + button.pure-button.pure-button-primary(@click="restore") Restore - h2 Reset to default configuration - button.pure-button.pure-button-primary(@click="reset") Reset + h2 Reset to default configuration + button.pure-button.pure-button-primary(@click="reset") Reset - h2 Check for new firmware - button.pure-button.pure-button-primary(@click="check") Check + h2 Check for new firmware + button.pure-button.pure-button-primary(@click="check") Check - h2 Upgrade firmware - button.pure-button.pure-button-primary(@click="upgrade") Upgrade + h2 Upgrade firmware + button.pure-button.pure-button-primary(@click="upgrade") Upgrade diff --git a/src/jade/templates/axis-control.jade b/src/jade/templates/axis-control.jade index 286e6e6..5a3a5bd 100644 --- a/src/jade/templates/axis-control.jade +++ b/src/jade/templates/axis-control.jade @@ -1,7 +1,7 @@ script#axis-control-template(type="text/x-template") svg(xmlns="http://www.w3.org/2000/svg", - width="200", height="200", transform="scale(0.8, 0.8)", - xmlns:xlink="http://www.w3.org/1999/xlink") + xmlns:xlink="http://www.w3.org/1999/xlink", + width="200", height="200") defs lineargradient#red stop(offset="0", stop-color="#d26969") @@ -37,7 +37,7 @@ script#axis-control-template(type="text/x-template") gradientunits="userSpaceOnUse", x1="10", y1="10", x2="40", y2="40") - filter#shadow-ring(x="-50%" y="-50%" width="200%" height="200%") + filter#shadow(x="-50%" y="-50%" width="200%" height="200%") feOffset(in="SourceAlpha", dx="3", dy="3") feComponentTransfer feFuncR(type="discrete", tableValues="0.05") @@ -46,185 +46,133 @@ script#axis-control-template(type="text/x-template") feGaussianBlur(result="shadow", stdDeviation="5") feBlend(in="SourceGraphic", in2="shadow", mode="normal") - filter#shadow(x="-50%" y="-50%" width="200%" height="200%") - feOffset(in="SourceAlpha", dx="3", dy="3") - feComponentTransfer - feFuncR(type="discrete", tableValues="0.3") - feFuncG(type="discrete", tableValues="0.3") - feFuncB(type="discrete", tableValues="0.3") - feGaussianBlur(result="shadow", stdDeviation="3") - feBlend(in="SourceGraphic", in2="shadow", mode="normal") - - path#corner-button(d="M47,10 47,0 0,0 0,47 10,47C10,47 26,21 47,10Z", - enable-background="accumulate") - - g.house#house - path(d="m15,26 0,-6 6,-5 6,5 0,6 -4,0 0,-4a4,8 0 0 0 -4,0l0,0 0,4z") - path(d="m27,17 0,-3", fill="none", stroke-width="1.5") - path(d="m13,19 8,-7 8,7", fill="none", stroke-width="1.5") - - path#arrow(d="M-16,9 0,9 0,17 17,0 0,-17 0,-9 -16,-9 -16,9Z") - path#pie-1(d="M107,0 83,0 0,83 0,107A107,107 0 0 0 107,0Z") path#pie-2(d="M83,0 59,0 0,59 0,83A83,83 0 0 0 83,0Z") path#pie-3(d="M59,0 35,0 0,35 0,59A59,59 0 0 0 59,0Z") path#pie-4(d="M35,0 0,0 0,35A35,35 0 0 0 35,0Z") + path#arrow(d="M-16,9 0,9 0,17 17,0 0,-17 0,-9 -16,-9 -16,9Z") + - // Home A - g.button(@click="home(0)", title="Home {{axes[0]}} axis", - filter="url(#shadow)", v-if="enabled[0]") - use(xlink:href="#corner-button", fill="url(#{{colors[0]}}-2)", - transform="matrix(1 0 0 1 9 9)") - text(font-size="14", x="34", y="25") {{axes[0]}} - use(xlink:href="#house") - - - // Home B - g.button(@click="home(1)", title="Home {{axes[1]}} axis", - filter="url(#shadow)", v-if="enabled[1]") - use(xlink:href="#corner-button", fill="url(#{{colors[1]}}-2)", - transform="matrix(-1 0 0 1 241 9)") - text(font-size="14", x="205", y="25") {{axes[1]}} - use(xlink:href="#house", transform="translate(208 0)") - - - // Zero A - g.button(@click="zero(0)", title="Zero {{axes[0]}} axes", - filter="url(#shadow)", v-if="enabled[0]") - use(xlink:href="#corner-button", fill="url(#{{colors[0]}}-2)", - transform="matrix(1 0 0 -1 9 231)") - text(font-size="20", x="12", y="227") ∅ - text(font-size="14", x="30", y="227") {{axes[0]}} - - - // Zero B - g.button(@click="zero(1)", title="Zero {{axes[1]}} axis", - filter="url(#shadow)", v-if="enabled[1]") - use(xlink:href="#corner-button", fill="url(#{{colors[1]}}-2)", - transform="matrix(-1 0 0 -1 241 231)") - text(font-size="14", x="208", y="227") {{axes[1]}} - text(font-size="20", x="221", y="227") ∅ - - - // 100 ring - g.ring(fill="#9f9f9f", filter="url(#shadow-ring)") - use.button(xlink:href="#pie-1", v-if="enabled[0]", - transform="translate(134 121) rotate(-45)", - @click="jog(0, 100)", title="Jog +100 {{axes[0]}}") - - use.button(xlink:href="#pie-1", v-if="enabled[0]", - transform="translate(115 121) rotate(135)", - @click="jog(0, -100)", title="Jog -100 {{axes[0]}}") - - g.button(@click="jog(1, 100)", title="Jog +100 {{axes[1]}}", - v-if="enabled[1]") - use.button(xlink:href="#pie-1", - transform="translate(124 111) rotate(-135)") - text(x="125", y="24", transform="rotate(20 125 125)") 100 - text(x="125", y="24", transform="rotate(-20 125 125)") 100 - use.button(xlink:href="#pie-1", fill="transparent", - transform="translate(124 111) rotate(-135)") - - use.button(xlink:href="#pie-1", v-if="enabled[1]", - transform="translate(124 130) rotate(45)", - @click="jog(1, -100)", title="Jog -100 {{axes[1]}}") - - // 10 ring - g.ring(fill="#c5c5c5", filter="url(#shadow-ring)") - use.button(xlink:href="#pie-2", v-if="enabled[0]", - transform="translate(134 121) rotate(-45)", - @click="jog(0, 10)", title="Jog +10 {{axes[0]}}") - - use.button(xlink:href="#pie-2", v-if="enabled[0]", - transform="translate(115 121) rotate(135)", - @click="jog(0, -10)", title="Jog -10 {{axes[0]}}") - - g.button(@click="jog(1, 10)", title="Jog +10 {{axes[1]}}", - v-if="enabled[1]") - use.button(xlink:href="#pie-2", - transform="translate(124 111) rotate(-135)") - text(x="125", y="50") 10 - use.button(xlink:href="#pie-2", fill="transparent", - transform="translate(124 111) rotate(-135)") - - use.button(xlink:href="#pie-2", v-if="enabled[1]", - transform="translate(124 130) rotate(45)", - @click="jog(1, -10)", title="Jog -10 {{axes[1]}}") - - - // 1 ring - g.ring(fill="#e2e2e2", filter="url(#shadow-ring)") - use.button(xlink:href="#pie-3", v-if="enabled[0]", - transform="translate(134 121) rotate(-45)", - @click="jog(0, 1)", title="Jog +1 {{axes[0]}}") - - use.button(xlink:href="#pie-3", v-if="enabled[0]", - transform="translate(115 121) rotate(135)", - @click="jog(0, -1)", title="Jog -1 {{axes[0]}}") - - g.button(@click="jog(1, 1)", title="Jog +1 {{axes[1]}}", - v-if="enabled[1]") - use.button(xlink:href="#pie-3", - transform="translate(124 111) rotate(-135)") - text(x="125", y="75") 1 - use.button(xlink:href="#pie-3", fill="transparent", - transform="translate(124 111) rotate(-135)") - - use.button(xlink:href="#pie-3", v-if="enabled[1]", - transform="translate(124 130) rotate(45)", - @click="jog(1, -1)", title="Jog -1 {{axes[1]}}") - - - // 0.1 ring - g.ring(fill="#f7f7f7", filter="url(#shadow-ring)") - use.button(xlink:href="#pie-4", v-if="enabled[0]", - transform="translate(134 121) rotate(-45)", - @click="jog(0, 0.1)", title="Jog +0.1 {{axes[0]}}") - - use.button(xlink:href="#pie-4", v-if="enabled[0]", - transform="translate(115 121) rotate(135)", - @click="jog(0, -0.1)", title="Jog -0.1 {{axes[0]}}") - - g.button(@click="jog(1, 0.1)", title="Jog +0.1 {{axes[1]}}", - v-if="enabled[1]") - use.button(xlink:href="#pie-4", - transform="translate(124 111) rotate(-135)") - text(x="125", y="95") 0.1 - use.button(xlink:href="#pie-4", fill="transparent", - transform="translate(124 111) rotate(-135)") - - use.button(xlink:href="#pie-4", v-if="enabled[1]", - transform="translate(124 130) rotate(45)", - @click="jog(1, -0.1)", title="Jog -0.1 {{axes[1]}}") - - - // +A - g.button.arrow(@click="jog(0, 100)", title="Jog +100 {{axes[0]}}", - transform="translate(230 120)", v-if="enabled[0]") - use(xlink:href="#arrow", fill="url(#{{colors[0]}}-1)") - text(x="-12", y="5", font-size="14", textLength="21") +{{axes[0]}} - - - // -A - g.button.arrow(@click="jog(0, -100)", title="Jog -100 {{axes[0]}}", - transform="translate(20 120)", v-if="enabled[0]") - use(xlink:href="#arrow", fill="url(#{{colors[0]}}-1)", - transform="rotate(180)") - text(x="-8", y="5", font-size="14", textLength="16") -{{axes[0]}} - - - // +B - g.button.arrow(@click="jog(1, 100)", title="Jog +100 {{axes[1]}}", - transform="translate(125, 18)", v-if="enabled[1]") - use(xlink:href="#arrow", fill="url(#{{colors[1]}}-1)", - transform="rotate(-90)") - text(x="-8", y="5", font-size="12", textLength="16") +{{axes[1]}} - - - // -B - g.button.arrow(@click="jog(1, -100)", title="Jog -100 {{axes[1]}}", - transform="translate(125, 225)", v-if="enabled[1]") - use(xlink:href="#arrow", fill="url(#{{colors[1]}}-1)", - transform="rotate(90)") - text(x="-7", y="5", font-size="12", textLength="14") -{{axes[1]}} + g(transform="scale(0.8, 0.8)") + // 100 ring + g.ring(fill="#9f9f9f", filter="url(#shadow)") + use.button(xlink:href="#pie-1", v-if="enabled[0]", + transform="translate(134 121) rotate(-45)", + @click="jog(0, 100)", title="Jog +100 {{axes[0]}}") + + use.button(xlink:href="#pie-1", v-if="enabled[0]", + transform="translate(115 121) rotate(135)", + @click="jog(0, -100)", title="Jog -100 {{axes[0]}}") + + g.button(@click="jog(1, 100)", title="Jog +100 {{axes[1]}}", + v-if="enabled[1]") + use.button(xlink:href="#pie-1", + transform="translate(124 111) rotate(-135)") + text(x="125", y="24", transform="rotate(20 125 125)") 100 + text(x="125", y="24", transform="rotate(-20 125 125)") 100 + use.button(xlink:href="#pie-1", fill="transparent", + transform="translate(124 111) rotate(-135)") + + use.button(xlink:href="#pie-1", v-if="enabled[1]", + transform="translate(124 130) rotate(45)", + @click="jog(1, -100)", title="Jog -100 {{axes[1]}}") + + // 10 ring + g.ring(fill="#c5c5c5", filter="url(#shadow)") + use.button(xlink:href="#pie-2", v-if="enabled[0]", + transform="translate(134 121) rotate(-45)", + @click="jog(0, 10)", title="Jog +10 {{axes[0]}}") + + use.button(xlink:href="#pie-2", v-if="enabled[0]", + transform="translate(115 121) rotate(135)", + @click="jog(0, -10)", title="Jog -10 {{axes[0]}}") + + g.button(@click="jog(1, 10)", title="Jog +10 {{axes[1]}}", + v-if="enabled[1]") + use.button(xlink:href="#pie-2", + transform="translate(124 111) rotate(-135)") + text(x="125", y="50") 10 + use.button(xlink:href="#pie-2", fill="transparent", + transform="translate(124 111) rotate(-135)") + + use.button(xlink:href="#pie-2", v-if="enabled[1]", + transform="translate(124 130) rotate(45)", + @click="jog(1, -10)", title="Jog -10 {{axes[1]}}") + + + // 1 ring + g.ring(fill="#e2e2e2", filter="url(#shadow)") + use.button(xlink:href="#pie-3", v-if="enabled[0]", + transform="translate(134 121) rotate(-45)", + @click="jog(0, 1)", title="Jog +1 {{axes[0]}}") + + use.button(xlink:href="#pie-3", v-if="enabled[0]", + transform="translate(115 121) rotate(135)", + @click="jog(0, -1)", title="Jog -1 {{axes[0]}}") + + g.button(@click="jog(1, 1)", title="Jog +1 {{axes[1]}}", + v-if="enabled[1]") + use.button(xlink:href="#pie-3", + transform="translate(124 111) rotate(-135)") + text(x="125", y="75") 1 + use.button(xlink:href="#pie-3", fill="transparent", + transform="translate(124 111) rotate(-135)") + + use.button(xlink:href="#pie-3", v-if="enabled[1]", + transform="translate(124 130) rotate(45)", + @click="jog(1, -1)", title="Jog -1 {{axes[1]}}") + + + // 0.1 ring + g.ring(fill="#f7f7f7", filter="url(#shadow)") + use.button(xlink:href="#pie-4", v-if="enabled[0]", + transform="translate(134 121) rotate(-45)", + @click="jog(0, 0.1)", title="Jog +0.1 {{axes[0]}}") + + use.button(xlink:href="#pie-4", v-if="enabled[0]", + transform="translate(115 121) rotate(135)", + @click="jog(0, -0.1)", title="Jog -0.1 {{axes[0]}}") + + g.button(@click="jog(1, 0.1)", title="Jog +0.1 {{axes[1]}}", + v-if="enabled[1]") + use.button(xlink:href="#pie-4", + transform="translate(124 111) rotate(-135)") + text(x="125", y="95") 0.1 + use.button(xlink:href="#pie-4", fill="transparent", + transform="translate(124 111) rotate(-135)") + + use.button(xlink:href="#pie-4", v-if="enabled[1]", + transform="translate(124 130) rotate(45)", + @click="jog(1, -0.1)", title="Jog -0.1 {{axes[1]}}") + + + // +A + g.button.arrow(@click="jog(0, 100)", title="Jog +100 {{axes[0]}}", + transform="translate(230 120)", v-if="enabled[0]") + use(xlink:href="#arrow", fill="url(#{{colors[0]}}-1)") + text(x="-12", y="5", font-size="14", textLength="21") +{{axes[0]}} + + + // -A + g.button.arrow(@click="jog(0, -100)", title="Jog -100 {{axes[0]}}", + transform="translate(20 120)", v-if="enabled[0]") + use(xlink:href="#arrow", fill="url(#{{colors[0]}}-1)", + transform="rotate(180)") + text(x="-8", y="5", font-size="14", textLength="16") -{{axes[0]}} + + + // +B + g.button.arrow(@click="jog(1, 100)", title="Jog +100 {{axes[1]}}", + transform="translate(125, 18)", v-if="enabled[1]") + use(xlink:href="#arrow", fill="url(#{{colors[1]}}-1)", + transform="rotate(-90)") + text(x="-8", y="5", font-size="12", textLength="16") +{{axes[1]}} + + + // -B + g.button.arrow(@click="jog(1, -100)", title="Jog -100 {{axes[1]}}", + transform="translate(125, 225)", v-if="enabled[1]") + use(xlink:href="#arrow", fill="url(#{{colors[1]}}-1)", + transform="rotate(90)") + text(x="-7", y="5", font-size="12", textLength="14") -{{axes[1]}} diff --git a/src/jade/templates/control-view.jade b/src/jade/templates/control-view.jade index c502df5..a6bb3ed 100644 --- a/src/jade/templates/control-view.jade +++ b/src/jade/templates/control-view.jade @@ -1,118 +1,121 @@ script#control-view-template(type="text/x-template") - .jog - axis-control(axes="XY", :colors="['red', 'green']", - :enabled="[enabled('x'), enabled('y')]", - v-if="enabled('x') || enabled('y')") - axis-control(axes="AZ", :colors="['orange', 'blue']", - :enabled="[enabled('a'), enabled('z')]", - v-if="enabled('a') || enabled('z')") - axis-control(axes="BC", :colors="['cyan', 'purple']", - :enabled="[enabled('b'), enabled('c')]", - v-if="enabled('b') || enabled('c')") - - - .estop(:class="{active: state.es}") - estop(@click="estop") - - - table.info - tr - th Tool - td {{state.t || 0}} - tr - th Velocity - td {{state.v || 0}} mm/min - tr - th Feed - td {{state.f || 0}} mm/min - tr - th Speed - td {{state.s || 0}} RPM - tr - th Direction - td {{state.sd || 'Clockwise'}} - tr - th Mist - td {{state.mist || 'Off'}} - tr - th Coolant - td {{state.coolant || 'Off'}} - - - table.axes - tr - th Actions - th Axis - th Position - th Offset - th Flags - th StallGuard - - each axis in 'xyzabc' - tr.axis(class="axis-#{axis}", v-if="enabled('#{axis}')") - th.actions - button.pure-button - .fa.fa-home - button.pure-button - .fa.fa-dot-circle-o - th.name #{axis} - td.position {{state.#{axis}p || 0 | fixed 4}} - td.offset {{state.#{axis}o || 0 | fixed 4}} - td {{state.#{axis}sf}} - td {{state.#{axis}sgv}} - - - .overrides - | Override: - .override - label Feed - input(type="range", min="-1", max="1", step="0.01", - v-model="feed_override", @change="override_feed") - span.percent {{feed_override | percent 0}} - - .override - label Spindle - input(type="range", min="-1", max="1", step="0.01", - v-model="speed_override", @change="override_speed") - span.percent {{speed_override | percent 0}} - - - .mdi.pure-form - fieldset - button.pure-button.pure-button-primary( - @click="submit_mdi", :disabled="running") MDI - input(v-model="mdi") - - - .toolbar - button.pure-button(@click="home", :disabled="running") - .fa.fa-home - - button.pure-button(@click="play_pause()", :disabled="!file") - .fa(:class="running ? 'fa-pause' : 'fa-play'") - - button.pure-button(@click="stop", :disabled="!running") - .fa.fa-stop - - button.pure-button(@click="optional_stop", :disabled="!file") - .fa.fa-stop-circle-o - - button.pure-button(@click="step", :disabled="running || !file") - .fa.fa-step-forward - - .spacer - - button.pure-button(@click="open", :disabled="running") - .fa.fa-folder-open - - input.gcode-file-input(type="file", @change="upload", style="display:none", - accept=".nc,.gcode,.gc,.ngc") - - button.pure-button(@click="delete", :disabled="!file") - .fa.fa-trash - - select(v-model="file", @change="load", :disabled="running") - option(v-for="file in files", :value="file") {{file}} - - - .gcode {{gcode || 'GCode displays here.'}} + #control + .jog + axis-control(axes="XY", :colors="['red', 'green']", + :enabled="[enabled('x'), enabled('y')]", + v-if="enabled('x') || enabled('y')") + axis-control(axes="AZ", :colors="['orange', 'blue']", + :enabled="[enabled('a'), enabled('z')]", + v-if="enabled('a') || enabled('z')") + axis-control(axes="BC", :colors="['cyan', 'purple']", + :enabled="[enabled('b'), enabled('c')]", + v-if="enabled('b') || enabled('c')") + + + .estop(:class="{active: state.es}") + estop(@click="estop") + + + table.info + tr + th Tool + td {{state.t || 0}} + tr + th Velocity + td {{state.v || 0 | fixed 0}} mm/min + tr + th Feed + td {{state.f || 0}} mm/min + tr + th Speed + td {{state.s || 0}} RPM + tr + th Direction + td {{state.sd || 'Clockwise'}} + tr + th Mist + td {{state.mist || 'Off'}} + tr + th Coolant + td {{state.coolant || 'Off'}} + + + table.axes + tr + th Axis + th Position + th Offset + th Errors + th Actions + + each axis in 'xyzabc' + tr.axis(class="axis-#{axis}", v-if="enabled('#{axis}')") + th.name #{axis} + td.position {{state.#{axis}p || 0 | fixed 4}} + td.offset {{state.#{axis}o || 0 | fixed 4}} + td.errors + .fa.fa-hot(v-if="state.#{axis}t", title="Driver overtemp") + .fa.fa-ban(v-if="state.#{axis}s", title="Motor stalled") + th.actions + button.pure-button(title="Zero #{axis} axis.", + @click="zero('#{axis}')") + | ∅ + button.pure-button(title="Home #{axis} axis.", + @click="home('#{axis}')") + .fa.fa-home + + + .overrides + | Override: + .override + label Feed + input(type="range", min="-1", max="1", step="0.01", + v-model="feed_override", @change="override_feed") + span.percent {{feed_override | percent 0}} + + .override + label Spindle + input(type="range", min="-1", max="1", step="0.01", + v-model="speed_override", @change="override_speed") + span.percent {{speed_override | percent 0}} + + + .mdi.pure-form + fieldset + button.pure-button.pure-button-primary( + @click="submit_mdi", :disabled="running") MDI + input(v-model="mdi", @keyup.enter="submit_mdi") + + + .toolbar + button.pure-button(@click="home", :disabled="running") + .fa.fa-home + + button.pure-button(@click="play_pause()", :disabled="!file") + .fa(:class="running ? 'fa-pause' : 'fa-play'") + + button.pure-button(@click="stop", :disabled="!running") + .fa.fa-stop + + button.pure-button(@click="optional_stop", :disabled="!file") + .fa.fa-stop-circle-o + + button.pure-button(@click="step", :disabled="running || !file") + .fa.fa-step-forward + + .spacer + + button.pure-button(@click="open", :disabled="running") + .fa.fa-folder-open + + input.gcode-file-input(type="file", @change="upload", + style="display:none", accept=".nc,.gcode,.gc,.ngc") + + button.pure-button(@click="delete", :disabled="!file") + .fa.fa-trash + + select(v-model="file", @change="load", :disabled="running") + option(v-for="file in files", :value="file") {{file}} + + + .gcode {{gcode || 'GCode displays here.'}} diff --git a/src/jade/templates/estop.jade b/src/jade/templates/estop.jade index 8b2d120..8f9a865 100644 --- a/src/jade/templates/estop.jade +++ b/src/jade/templates/estop.jade @@ -2,7 +2,7 @@ script#estop-template(type="text/x-template") svg(version="1.1", xmlns:svg="http://www.w3.org/2000/svg", xmlns="http://www.w3.org/2000/svg", xmlns:xlink="http://www.w3.org/1999/xlink", - width="200", height="200", transform="scale(0.92, 0.92)") + width="200", height="200") defs path#text-path-1(style="fill:none;stroke:none", d="m 73.735867,673.1299 c 0,55.10749 44.673453,99.78094 99.780973,99.78094 55.10748,0 99.78093,-44.67345 99.78093,-99.78094 0,-55.10749 -44.67345,-99.78094 -99.78093,-99.78094 -55.10752,0 -99.780973,44.67345 -99.780973,99.78094 z") @@ -55,7 +55,7 @@ script#estop-template(type="text/x-template") fecomposite(in="offset", in2="SourceGraphic", operator="atop") - g(transform="translate(-65, -526)") + g(transform="scale(0.92, 0.92),translate(-65, -526)") // Yellow ring circle.ring(style="fill:#f5e138;filter:url(#filter5266)", cx="173", cy="633", r="100") diff --git a/src/jade/templates/switches-view.jade b/src/jade/templates/switches-view.jade index 0769e09..e1ca46a 100644 --- a/src/jade/templates/switches-view.jade +++ b/src/jade/templates/switches-view.jade @@ -4,7 +4,7 @@ script#switches-view-template(type="text/x-template") form.pure-form.pure-form-aligned fieldset - .switch(v-for="switch in switches") + .switch(v-for="sw in switches") h3 Switch {{$index}} templated-input(v-for="templ in template.switches", :name="$key", - :model.sync="switch[$key]", :template="templ") + :model.sync="sw[$key]", :template="templ") diff --git a/src/js/axis-view.js b/src/js/axis-view.js index a50a385..e6fc680 100644 --- a/src/js/axis-view.js +++ b/src/js/axis-view.js @@ -7,7 +7,10 @@ module.exports = { data: function () { - return {axis: {}} + return { + active: false, + axis: {} + } }, @@ -24,13 +27,14 @@ module.exports = { }, - ready: function () { - this.update(); - }, + attached: function () {this.active = true; this.update()}, + detached: function () {this.active = false}, methods: { update: function () { + if (!this.active) return; + Vue.nextTick(function () { if (this.config.hasOwnProperty('axes')) this.axis = this.config.axes[this.index]; diff --git a/src/js/control-view.js b/src/js/control-view.js index 2948193..51eb769 100644 --- a/src/js/control-view.js +++ b/src/js/control-view.js @@ -59,16 +59,11 @@ module.exports = { this.sock.onmessage = function (e) { var data = e.data; - console.debug(data); + console.debug('msg: ' + JSON.stringify(data)); - for (var key in data) { - this.$set('state.' + key, data[key]); - - for (var axis of ['x', 'y', 'z', 'a']) - if (key == axis + 'pl' && - typeof this.$get('current' + axis) == 'undefined') - this.$set('current' + axis, (32 * data[key]).toFixed()); - } + if (typeof data == 'object') + for (var key in data) + this.$set('state.' + key, data[key]); }.bind(this); this.update(); @@ -171,6 +166,18 @@ module.exports = { var data = {}; data[axis + 'pl'] = x; this.send(data); + }, + + + override_feed: function () {}, + override_speed: function () {}, + step: function () {}, + stop: function () {}, + optional_stop: function () {}, + + + home: function () { + this.sock.send('$calibrate'); } }, diff --git a/src/js/motor-view.js b/src/js/motor-view.js index 2237ff5..534428c 100644 --- a/src/js/motor-view.js +++ b/src/js/motor-view.js @@ -8,6 +8,7 @@ module.exports = { data: function () { return { + active: false, motor: {} } }, @@ -26,13 +27,14 @@ module.exports = { }, - ready: function () { - this.update(); - }, + attached: function () {this.active = true; this.update()}, + detached: function () {this.active = false}, methods: { update: function () { + if (!this.active) return; + Vue.nextTick(function () { if (this.config.hasOwnProperty('motors')) this.motor = this.config.motors[this.index]; diff --git a/src/resources/config-template.json b/src/resources/config-template.json index 4f0fcc3..ecfb57e 100644 --- a/src/resources/config-template.json +++ b/src/resources/config-template.json @@ -170,8 +170,8 @@ "spindle": { "spindle-type": { "type": "enum", - "values": ["RS485", "PWM"], - "default": "RS485", + "values": ["PWM", "HUNAYANG"], + "default": "PWM", "code": "st" }, "spin-polarity": { diff --git a/src/stylus/style.styl b/src/stylus/style.styl index 12a30aa..9c8d4ae 100644 --- a/src/stylus/style.styl +++ b/src/stylus/style.styl @@ -86,9 +86,15 @@ body color #f0f td, th - text-align center padding 2px + th + text-align center + + td + text-align right + font-family Courier + .axis .name text-transform capitalize