Added support for DMM DYN4 VFD.
Added copyright to LCD boot up.
Support more gamepads.
time.clock() deprecaited.
Logging fixes.
## v0.4.16
- Improved axis under/over warning tooltip.
+ - Added support for DMM DYN4 VFD.
+ - Only enable ``rapid-auto-off`` in PWM mode. #272
+ - Support more gamepads.
## v0.4.15
- Set GCode variables ``#5400`` and ``#<_tool>``.
$(PUG) -O pug-opts.js -P $< -o $(TARGET_DIR) || (rm -f $@; exit 1)
pylint:
- pylint3 -E $(shell find src/py -name \*.py | grep -v flycheck_)
+ pylint -E $(shell find src/py -name \*.py | grep -v flycheck_)
jshint:
./node_modules/jshint/bin/jshint --config jshint.json src/js/*.js
def enter_cgroup():
- with open('/sys/fs/cgroup/memory/chrome/tasks', 'w') as f:
- f.write(str(os.getpid()))
+ cgroup = '/sys/fs/cgroup/memory/chrome/tasks'
+
+ if os.path.exists(cgroup):
+ with open(cgroup, 'w') as f:
+ f.write(str(os.getpid()))
# Start browser
systemctl stop bbctrl
# Update service
+ mkdir -p /var/lib/bbctrl
rm -f /etc/init.d/bbctrl
cp scripts/bbctrl.service /etc/systemd/system/
systemctl daemon-reload
# Install packages
apt-get install -y avahi-daemon avrdude minicom python3-pip python3-smbus \
i2c-tools python3-rpi.gpio libjpeg8 wiringpi dnsmasq hostapd \
- iptables-persistent chromium-browser xorg rpd-plym-splash samba
+ iptables-persistent chromium-browser xorg rpd-plym-splash samba ratpoison \
+ libpython3.5
pip3 install --upgrade tornado sockjs-tornado pyserial
# Clean
echo i2c-bcm2708 >> /etc/modules
echo i2c-dev >> /etc/modules
-# Install bbctrl w/ init.d script
-cp bbctrl.init.d /etc/init.d/bbctrl
-chmod +x /etc/init.d/bbctrl
-update-rc.d bbctrl defaults
-
# Disable Pi 3 USART BlueTooth swap
echo -e "\ndtoverlay=pi3-disable-bt" >> /boot/config.txt
rm -f /etc/systemd/system/multi-user.target.wants/hciuart.service
-# Install hawkeye
-dpkg -i hawkeye_0.6_armhf.deb
-sed -i 's/localhost/0.0.0.0/' /etc/hawkeye/hawkeye.conf
-echo 'ACTION=="add", KERNEL=="video0", RUN+="/usr/sbin/service hawkeye restart"' > /etc/udev/rules.d/50-hawkeye.rules
-adduser hawkeye video
-
# Disable HDMI to save power and remount /boot read-only
sed -i 's/^exit 0$//' /etc/rc.local
echo "mount -o remount,ro /boot" >> /etc/rc.local
# Install bbctrl
tar xf /mnt/host/bbctrl-*.tar.bz2
cd $(basename bbctrl-*.tar.bz2 .tar.bz2)
-./setup.py install
+./scripts/install.sh
cd ..
rm -rf $(basename bbctrl-*.tar.bz2 .tar.bz2)
# Clean up
apt-get autoremove -y
apt-get autoclean -y
+apt-get clean
void _splash(uint8_t addr) {
lcd_init(addr);
- lcd_goto(addr, 1, 1);
+ lcd_goto(addr, 0, 0);
lcd_pgmstr(addr, PSTR("Controller booting"));
- lcd_goto(addr, 3, 2);
+ lcd_goto(addr, 0, 1);
lcd_pgmstr(addr, PSTR("Please wait..."));
+ lcd_goto(addr, 0, 3);
+ lcd_pgmstr(addr, PSTR("(c) Buildbotics LLC"));
}
SPINDLE_TYPE_OMRON_MX2,
SPINDLE_TYPE_V70,
SPINDLE_TYPE_WJ200,
+ SPINDLE_TYPE_DMM_DYN4,
} spindle_type_t;
};
+const vfd_reg_t dmm_dyn4_regs[] PROGMEM = {
+ {REG_MAX_FREQ_FIXED, 0x00, 6000}, // Maximum operating frequency
+ {REG_FREQ_SIGN_SET, 0x0e, 0}, // Set frequency in 0.1Hz
+ {REG_STOP_WRITE, 0x0e, 0}, // Stop
+ {REG_FREQ_SIGN_READ, 0x14, 0}, // Read operating frequency
+ {REG_STATUS_READ, 0x02, 0}, // Read status
+ {REG_DISABLED},
+};
+
+
// Same as OMRON MX2
#define wj200_regs omron_mx2_regs
case SPINDLE_TYPE_OMRON_MX2: _load(omron_mx2_regs); break;
case SPINDLE_TYPE_V70: _load(v70_regs); break;
case SPINDLE_TYPE_WJ200: _load(wj200_regs); break;
+ case SPINDLE_TYPE_DMM_DYN4: _load(dmm_dyn4_regs); break;
default: break;
}
|
a(href="https://buildbotics.com/upload/vfd/wj200.pdf",
target="_blank") WJ200 VFD manual
+
+ .notes(v-if="tool_type.startsWith('DMM DYN4')")
+ h2 Notes
+ p Set the following using the VFD's front panel.
+ table.modbus-regs.fixed-regs
+ tr
+ th Address
+ th Value
+ th Meaning
+ th Description
+ tr
+ td.reg-addr 40001
+ td.reg-value 1
+ td Drive ID
+ td Must match #[tt bus-id] above
+ tr
+ td.reg-addr 40002
+ td.reg-value 0
+ td RS232, relative mode, enable
+ td Drive config
+ tr
+ td.reg-addr 40035
+ td.reg-value 0
+ td 8-bit data, 1 start, no parity, two stop
+ td Communication format
+ tr
+ td.reg-addr 40036
+ td.reg-value 1
+ td 9600 BAUD rate
+ td Must match #[tt baud] above
+ p
+ | Other settings according to the
+ |
+ a(href="https://buildbotics.com/upload/vfd/dmm_dyn4.pdf",
+ target="_blank") DMM DYN4 VFD manual
self.camera = app.camera
- @web.asynchronous
def get(self):
self.request.connection.stream.max_write_buffer_size = 10000
import inevent
from inevent.Constants import *
+type2 = [
+ 'USB Gamepad',
+ 'Logitech Logitech RumblePad 2 USB'
+]
+
+config1 = {
+ "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],
+}
+
+config2 = {
+ "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],
+}
+
# Listen for input events
class Jog(inevent.JogHandler):
def __init__(self, ctrl):
+ super().__init__(ctrl.log.get('JogHandler'))
+
self.ctrl = ctrl
self.log = ctrl.log.get('Jog')
- config = {
- "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)
-
self.v = [0.0] * 4
self.lastV = self.v
self.callback()
- self.processor = inevent.InEvent(ctrl.ioloop, self, types = ['js'])
+ self.processor = inevent.InEvent(ctrl.ioloop, self, types = ['js'],
+ log = ctrl.log.get('InEvent'))
+
+
+ # From JobHandler
+ def get_config(self, name):
+ if name in type2: return config2
+
+ # Microsoft X-Box 360 pad
+ # Logitech Gamepad F310
+ return config1
def up(self): self.ctrl.lcd.page_up()
r'(?P<msg>.*)$')
+def clock(): return time.process_time()
+
+
def compute_unit(a, b):
unit = dict()
length = 0
def _run(self):
- start = time.clock()
+ start = clock()
line = 0
maxLine = 0
- maxLineTime = time.clock()
+ maxLineTime = clock()
position = {axis: 0 for axis in 'xyz'}
rapid = False
line = cmd['value']
if maxLine < line:
maxLine = line
- maxLineTime = time.clock()
+ maxLineTime = clock()
elif cmd['name'] == 'speed':
s = cmd['value']
elif cmd['type'] == 'dwell': self.time += cmd['seconds']
- if args.max_time < time.clock() - start:
+ if args.max_time < clock() - start:
raise Exception('Max planning time (%d sec) exceeded.' %
args.max_time)
- if args.max_loop < time.clock() - maxLineTime:
+ if args.max_loop < clock() - maxLineTime:
raise Exception('Max loop time (%d sec) exceeded.' %
args.max_loop)
from inevent.EventState import EventState
-log = logging.getLogger('inevent')
-
EVIOCGRAB = ioctl._IOW(ord('E'), 0x90, "i") # Grab/Release device
ABS_DISTANCE, ABS_TILT_X, ABS_TILT_Y, ABS_TOOL_WIDTH]
- def __init__(self, devIndex, devType, devName):
+ def __init__(self, devIndex, devType, devName, log = None):
"""
Opens the given /dev/input/event file and grabs it.
self.devIndex = devIndex
self.devType = devType
self.devName = devName
+ self.log = log if log else logging.getLogger('inevent')
+
self.filename = "/dev/input/event" + str(devIndex)
self.filehandle = os.open(self.filename, os.O_RDWR)
self.state = EventState()
return event
except Exception as e:
- log.info('Reading event: %s' % e)
+ self.log.info('Reading event: %s' % e)
def __enter__(self): return self
+++ /dev/null
-################################################################################
-# #
-# This file is part of the Buildbotics firmware. #
-# #
-# Copyright (c) 2015 - 2020, Buildbotics LLC, All rights reserved. #
-# #
-# This Source describes Open Hardware and is licensed under the #
-# CERN-OHL-S v2. #
-# #
-# You may redistribute and modify this Source and make products #
-# using it under the terms of the CERN-OHL-S v2 (https:/cern.ch/cern-ohl). #
-# This Source is distributed WITHOUT ANY EXPRESS OR IMPLIED #
-# WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY QUALITY AND FITNESS #
-# FOR A PARTICULAR PURPOSE. Please see the CERN-OHL-S v2 for applicable #
-# conditions. #
-# #
-# Source location: https://github.com/buildbotics #
-# #
-# As per CERN-OHL-S v2 section 4, should You produce hardware based on #
-# these sources, You must maintain the Source Location clearly visible on #
-# the external case of the CNC Controller or other product you make using #
-# this Source. #
-# #
-# For more information, email info@buildbotics.com #
-# #
-################################################################################
-
-# The inevent Python module was adapted from pi3d.event from the pi3d
-# project.
-#
-# Copyright (c) 2016, Joseph Coffland, Cauldron Development LLC.
-# Copyright (c) 2015, Tim Skillman.
-# Copyright (c) 2015, Paddy Gaunt.
-# Copyright (c) 2015, Tom Ritchford.
-#
-# Permission is hereby granted, free of charge, to any person
-# obtaining a copy of this software and associated documentation files
-# (the "Software"), to deal in the Software without restriction,
-# including without limitation the rights to use, copy, modify, merge,
-# publish, distribute, sublicense, and/or sell copies of the Software,
-# and to permit persons to whom the Software is furnished to do so,
-# subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be
-# included in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
-# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-# SOFTWARE.
-
-import re
-import logging
-from inevent.Constants import *
-
-log = logging.getLogger('inevent')
-
-
-def test_bit(nlst, b):
- index = b / 32
- bit = b % 32
- return index < len(nlst) and nlst[index] & (1 << bit)
-
-
-def EvToStr(events):
- s = []
-
- if test_bit(events, EV_SYN): s.append("EV_SYN")
- if test_bit(events, EV_KEY): s.append("EV_KEY")
- if test_bit(events, EV_REL): s.append("EV_REL")
- if test_bit(events, EV_ABS): s.append("EV_ABS")
- if test_bit(events, EV_MSC): s.append("EV_MSC")
- if test_bit(events, EV_LED): s.append("EV_LED")
- if test_bit(events, EV_SND): s.append("EV_SND")
- if test_bit(events, EV_REP): s.append("EV_REP")
- if test_bit(events, EV_FF): s.append("EV_FF" )
- if test_bit(events, EV_PWR): s.append("EV_PWR")
- if test_bit(events, EV_FF_STATUS): s.append("EV_FF_STATUS")
-
- return s
-
-
-class DeviceCapabilities(object):
- def __init__(self, firstLine, filehandle):
- self.EV_SYNevents = []
- self.EV_KEYevents = []
- self.EV_RELevents = []
- self.EV_ABSevents = []
- self.EV_MSCevents = []
- self.EV_LEDevents = []
- self.EV_SNDevents = []
- self.EV_REPevents = []
- self.EV_FFevents = []
- self.EV_PWRevents = []
- self.EV_FF_STATUSevents = []
- self.eventTypes = []
-
- match = re.search(".*Bus=([0-9A-Fa-f]+).*Vendor=([0-9A-Fa-f]+).*"
- "Product=([0-9A-Fa-f]+).*Version=([0-9A-Fa-f]+).*",
- firstLine)
-
- if not match:
- log.warning("Do not understand device ID: %s", firstLine)
- self.bus = 0
- self.vendor = 0
- self.product = 0
- self.version = 0
-
- else:
- self.bus = int(match.group(1), base = 16)
- self.vendor = int(match.group(2), base = 16)
- self.product = int(match.group(3), base = 16)
- self.version = int(match.group(4), base = 16)
-
- for line in filehandle:
- if not line.strip(): break
-
- if line[0] == "N":
- match = re.search('Name="([^"]+)"', line)
- if match: self.name = match.group(1)
- else: self.name = "UNKNOWN"
-
- elif line[0] == "P":
- match = re.search('Phys=(.+)', line)
- if match: self.phys = match.group(1)
- else: self.phys = "UNKNOWN"
-
- elif line[0] == "S":
- match = re.search('Sysfs=(.+)', line)
- if match: self.sysfs = match.group(1)
- else: self.sysfs = "UNKNOWN"
-
- elif line[0] == "U":
- match = re.search('Uniq=(.*)', line)
- if match: self.uniq = match.group(1)
- else: self.uniq = "UNKNOWN"
-
- elif line[0] == "H":
- match = re.search('Handlers=(.+)', line)
- if match: self.handlers = match.group(1).split()
- else: self.handlers = []
-
- elif line[:5] == "B: EV":
- eventsNums = [int(x, base = 16) for x in line[6:].split()]
- eventsNums.reverse()
- self.eventTypes = eventsNums
-
- elif line[:6] == "B: KEY":
- eventsNums = [int(x, base = 16) for x in line[7:].split()]
- eventsNums.reverse()
- self.EV_KEYevents = eventsNums
-
- elif line[:6] == "B: ABS":
- eventsNums = [int(x, base = 16) for x in line[7:].split()]
- eventsNums.reverse()
- self.EV_ABSevents = eventsNums
-
- elif line[:6] == "B: MSC":
- eventsNums = [int(x, base = 16) for x in line[7:].split()]
- eventsNums.reverse()
- self.EV_MSCevents = eventsNums
-
- elif line[:6] == "B: REL":
- eventsNums = [int(x, base = 16) for x in line[7:].split()]
- eventsNums.reverse()
- self.EV_RELevents = eventsNums
-
- elif line[:6] == "B: LED":
- eventsNums = [int(x, base = 16) for x in line[7:].split()]
- eventsNums.reverse()
- self.EV_LEDevents = eventsNums
-
- for handler in self.handlers:
- if handler[:5] == "event": self.eventIndex = int(handler[5:])
-
- self.isMouse = False
- self.isKeyboard = False
- self.isJoystick = False
-
-
- def doesProduce(self, eventType, eventCode):
- return test_bit(self.eventTypes, eventType) and (
- (eventType == EV_SYN and test_bit(self.EV_SYNevents, eventCode)) or
- (eventType == EV_KEY and test_bit(self.EV_KEYevents, eventCode)) or
- (eventType == EV_REL and test_bit(self.EV_RELevents, eventCode)) or
- (eventType == EV_ABS and test_bit(self.EV_ABSevents, eventCode)) or
- (eventType == EV_MSC and test_bit(self.EV_MSCevents, eventCode)) or
- (eventType == EV_LED and test_bit(self.EV_LEDevents, eventCode)) or
- (eventType == EV_SND and test_bit(self.EV_SNDevents, eventCode)) or
- (eventType == EV_REP and test_bit(self.EV_REPevents, eventCode)) or
- (eventType == EV_FF and test_bit(self.EV_FFevents, eventCode)) or
- (eventType == EV_PWR and test_bit(self.EV_PWRevents, eventCode)) or
- (eventType == EV_FF_STATUS and
- test_bit(self.EV_FF_STATUSevents, eventCode)))
-
-
- def __str__(self):
- return (
- ("%s\n"
- "Bus: %s Vendor: %s Product: %s Version: %s\n"
- "Phys: %s\n"
- "Sysfs: %s\n"
- "Uniq: %s\n"
- "Handlers: %s Event Index: %s\n"
- "Keyboard: %s Mouse: %s Joystick: %s\n"
- "Events: %s") % (
- self.name, self.bus, self.vendor, self.product, self.version, self.phys,
- self.sysfs, self.uniq, self.handlers, self.eventIndex, self.isKeyboard,
- self.isMouse, self.isJoystick, EvToStr(self.eventTypes)))
-
-
-deviceCapabilities = []
-
-
-def get_devices(filename = "/proc/bus/input/devices"):
- global deviceCapabilities
-
- with open("/proc/bus/input/devices", "r") as filehandle:
- for line in filehandle:
- if line[0] == "I":
- deviceCapabilities.append(DeviceCapabilities(line, filehandle))
-
- return deviceCapabilities
-
-
-def print_devices():
- devs = get_devices()
-
- for dev in devs:
- print(str(dev))
- print(" ABS: {}"
- .format([x for x in range(64) if test_bit(dev.EV_ABSevents, x)]))
- print(" REL: {}"
- .format([x for x in range(64) if test_bit(dev.EV_RELevents, x)]))
- print(" MSC: {}"
- .format([x for x in range(64) if test_bit(dev.EV_MSCevents, x)]))
- print(" KEY: {}"
- .format([x for x in range(512) if test_bit(dev.EV_KEYevents, x)]))
- print(" LED: {}"
- .format([x for x in range(64) if test_bit(dev.EV_LEDevents, x)]))
- print()
from inevent.EventStream import EventStream
-log = logging.getLogger('inevent')
-
_KEYS = (k for k in vars(Keys) if not k.startswith('_'))
KEY_CODE = dict((k, getattr(Keys, k)) for k in _KEYS)
CODE_KEY = {}
The keys are listed in inevent.Constants.py or /usr/include/linux/input.h
Note that the key names refer to a US keyboard.
"""
- def __init__(self, ioloop, cb, types = 'kbd mouse js'.split()):
+ def __init__(self, ioloop, cb, types = 'kbd mouse js'.split(), log = None):
self.ioloop = ioloop
self.cb = cb
self.streams = []
self.handler = EventHandler()
self.types = types
+ self.log = log if log else logging.getLogger('inevent')
self.udevCtx = pyudev.Context()
self.udevMon = pyudev.Monitor.from_netlink(self.udevCtx)
def get_dev_name(self, index):
try:
dev = self.get_dev(index)
- return dev.parent.attributes.asstring('name').decode('utf-8')
- except: pass
+ return dev.parent.attributes.asstring('name').strip()
+ except Exception as e:
+ self.log.error(e)
def find_devices(self, types):
self.ioloop.add_handler(stream.filehandle, self.stream_handler,
self.ioloop.READ)
- log.info('Added %s[%d] %s', devType, devIndex, devName)
+ self.log.info('Added %s[%d] %s', devType, devIndex, devName)
except OSError as e:
- log.warning('Failed to add %s[%d]: %s', devType, devIndex, e)
+ self.log.warning('Failed to add %s[%d]: %s', devType, devIndex, e)
def remove_stream(self, devIndex):
stream.release()
self.cb.clear()
- log.info('Removed %s[%d]', stream.devType, devIndex)
+ self.log.info('Removed %s[%d]', stream.devType, devIndex)
def key_state(self, key):
from inevent.Constants import *
-log = logging.getLogger('inevent')
-log.setLevel(logging.INFO)
-
-
def axes_to_string(axes):
s = ''
for axis in axes:
class JogHandler:
- def __init__(self, config):
- self.config = config
+ def __init__(self, log = None):
self.reset()
+ self.log = log if log else logging.getLogger('inevent')
+
def changed(self):
- log.info(axes_to_string(self.axes) + ' x {:d}'.format(self.speed))
+ self.log.info(axes_to_string(self.axes) + ' x {:d}'.format(self.speed))
- def up(self): log.debug('up')
- def down(self): log.debug('down')
- def left(self): log.debug('left')
- def right(self): log.debug('right')
+ def up(self): self.log.debug('up')
+ def down(self): self.log.debug('down')
+ def left(self): self.log.debug('left')
+ def right(self): self.log.debug('right')
def reset(self):
self.changed()
- def get_config(self, name):
- if name in self.config: return self.config[name]
- return self.config['default']
+ def get_config(self, name): raise Exception('No configs')
def event(self, event, state, dev_name):
if index == 0: self.horizontal_lock = True
if index == 1: self.vertical_lock = True
- log.debug(event_to_string(event, state))
+ self.log.debug(event_to_string(event, state))
# Update axes
old_axes = list(self.axes)
################################################################################
import time
-import logging
-
-
-log = logging.getLogger('LCD')
# Control flags
"values": ["Disabled", "PWM Spindle", "Huanyang VFD", "Custom Modbus VFD",
"AC-Tech VFD", "Nowforever VFD", "Delta VFD015M21A (Beta)",
"YL600, YL620, YL620-A VFD (Beta)", "FR-D700 (Beta)",
- "Sunfar E300 (Beta)", "OMRON MX2", "V70", "WJ200"],
+ "Sunfar E300 (Beta)", "OMRON MX2", "V70", "WJ200",
+ "DMM DYN4 (Beta)"],
"default": "Disabled",
"code": "st"
},