LCD support, other changes
authorJoseph Coffland <joseph@cauldrondevelopment.com>
Thu, 26 May 2016 06:14:19 +0000 (23:14 -0700)
committerJoseph Coffland <joseph@cauldrondevelopment.com>
Thu, 26 May 2016 06:14:19 +0000 (23:14 -0700)
16 files changed:
Makefile
bbctrl.init.d
bbctrl.py
lcd.py [new file with mode: 0755]
setup_rpi.sh
src/jade/index.jade
src/jade/templates/admin-view.jade
src/jade/templates/axis-control.jade
src/jade/templates/control-view.jade
src/jade/templates/estop.jade
src/jade/templates/switches-view.jade
src/js/axis-view.js
src/js/control-view.js
src/js/motor-view.js
src/resources/config-template.json
src/stylus/style.styl

index 9e79f2394ec12af99350aaa43653fe5b34ef4297..658cd527ea566714138f208fa47be2d70aad52fc 100644 (file)
--- 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
index cc1f8822b9a1d1fc2705ad554cce25c9e30c142f..4dad98d75fcbabf2963795c47b38fbe003543d9d 100644 (file)
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
 
 ### BEGIN INIT INFO
 # Provides:          bbctl
 # 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 $?
 }
 
index 69bd1c47049ad2d0150950a11de3dab414c4d41e..c7d6888fff81bd15e20c26d2b69d0442615df061 100755 (executable)
--- 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 (executable)
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')
index 5bbf703f9f9816671f47c703db254eec39a23116..4b2542675f6a3afda2e0f03163ee910db88df5a0 100755 (executable)
@@ -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
 
index de88d52f239790e9301c98c2974110f8acd4efad..cf5fa0fa855a58e6e2ba3031cc83d593481c9101 100644 (file)
@@ -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
index 853214db38658577b7672c1887a17db9af2229d5..f7cd7b72b9d5bd322548fcb24021cc484273589a 100644 (file)
@@ -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
index 286e6e6572fe42319af39faa9884b04d059e6ba2..5a3a5bdab02686b90cdad5f40be4846c64adbb18 100644 (file)
@@ -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") &empty;
-      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") &empty;
-
-
-    // 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]}}
index c502df55ea8537d75b3690deb8fa87884066cd5d..a6bb3ed1d1b6e1dee8113968cf48df2c45ea22d6 100644 (file)
 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}')")
+              | &empty;
+            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.'}}
index 8b2d120f5eff345c0565894ddfc365ec58f9bd31..8f9a865441c2be469712430d2251f77ce56f1362 100644 (file)
@@ -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")
index 0769e099ade75f158de19286a2dc769defa9454b..e1ca46a3182093de149ba423aa9369971e46f3fc 100644 (file)
@@ -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")
index a50a385e46396dfe15d953c825f43e4e7ac422fe..e6fc6802100cbfbcbe50f6305af26013f7e1120e 100644 (file)
@@ -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];
index 29481938aa8e7d3e8cf6e272564890a82923709c..51eb769e6846eda07aab72b00543d3944f13f777 100644 (file)
@@ -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');
     }
   },
 
index 2237ff56dae45f5bf3175910f6d2cb6ee8dabf50..534428cff294e985d88a62f2d7410191cccd0942 100644 (file)
@@ -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];
index 4f0fcc3d1ca88d2cbdb5fe3782344d2f79aec50d..ecfb57e0b86e3377334bdc3c83c8c4797417a58a 100644 (file)
   "spindle": {
     "spindle-type": {
       "type": "enum",
-      "values": ["RS485", "PWM"],
-      "default": "RS485",
+      "values": ["PWM", "HUNAYANG"],
+      "default": "PWM",
       "code": "st"
     },
     "spin-polarity": {
index 12a30aa009b080ef3f8f410c06466e7356bf8916..9c8d4ae01244e26587a2e3c40baac80215568ffa 100644 (file)
@@ -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