aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin O'Connor <kevin@koconnor.net>2017-03-08 22:26:10 -0500
committerKevin O'Connor <kevin@koconnor.net>2017-03-08 23:02:31 -0500
commit64407dc5d27da8fbc6b1cd6604a0e734da019b59 (patch)
tree33882dec9803e81cb21ab63c578bfde7cd591973
parentb0329465eccc61e704262c8a14d42aaf9897c495 (diff)
downloadkutter-64407dc5d27da8fbc6b1cd6604a0e734da019b59.tar.gz
kutter-64407dc5d27da8fbc6b1cd6604a0e734da019b59.tar.xz
kutter-64407dc5d27da8fbc6b1cd6604a0e734da019b59.zip
klippy: Support FIRMWARE_RESTART command
Add initial support for micro-controller resets via the Arduino reset mechanism. Also, automatically attempt a firmware restart if the printer CRC does not match. Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
-rw-r--r--docs/Todo.md2
-rw-r--r--klippy/gcode.py25
-rw-r--r--klippy/klippy.py36
-rw-r--r--klippy/mcu.py16
-rw-r--r--klippy/serialhdl.py15
5 files changed, 70 insertions, 24 deletions
diff --git a/docs/Todo.md b/docs/Todo.md
index e16196e1..3237fe64 100644
--- a/docs/Todo.md
+++ b/docs/Todo.md
@@ -14,8 +14,6 @@ Host user interaction
* Provide startup scripts so that Klippy can startup at system
bootup.
- * Allow loading of a new config without having to restart the mcu.
-
* Improve gcode interface:
* Provide a better way to handle print nozzle z offsets. The M206
diff --git a/klippy/gcode.py b/klippy/gcode.py
index ce3c3ffe..e0bf6aa9 100644
--- a/klippy/gcode.py
+++ b/klippy/gcode.py
@@ -46,8 +46,8 @@ class GCodeParser:
handlers = ['G1', 'G4', 'G20', 'G21', 'G28', 'G90', 'G91', 'G92',
'M18', 'M82', 'M83', 'M105', 'M110', 'M112', 'M114', 'M115',
'M206', 'M400',
- 'HELP', 'QUERY_ENDSTOPS', 'RESTART', 'CLEAR_SHUTDOWN',
- 'STATUS']
+ 'HELP', 'QUERY_ENDSTOPS', 'CLEAR_SHUTDOWN',
+ 'RESTART', 'FIRMWARE_RESTART', 'STATUS']
if self.heater_nozzle is not None:
handlers.extend(['M104', 'M109', 'PID_TUNE'])
if self.heater_bed is not None:
@@ -119,7 +119,7 @@ class GCodeParser:
self.toolhead.force_shutdown()
self.respond_error('Internal error on command:"%s"' % (cmd,))
if self.is_fileinput:
- self.printer.request_exit_eof()
+ self.printer.request_exit('exit_eof')
break
self.ack()
def process_data(self, eventtime):
@@ -144,7 +144,7 @@ class GCodeParser:
self.fd_handle = self.reactor.register_fd(self.fd, self.process_data)
if not data and self.is_fileinput:
self.motor_heater_off()
- self.printer.request_exit_eof()
+ self.printer.request_exit('exit_eof')
# Response handling
def ack(self, msg=None):
if not self.need_ack or self.is_fileinput:
@@ -373,16 +373,23 @@ class GCodeParser:
self.cmd_default(params)
return
self.printer.mcu.clear_shutdown()
- self.printer.request_restart()
- cmd_RESTART_when_not_ready = True
- cmd_RESTART_help = "Reload config file and restart host software"
- def cmd_RESTART(self, params):
+ self.printer.request_exit('restart')
+ def prep_restart(self):
if self.is_printer_ready:
self.respond_info("Preparing to restart...")
self.motor_heater_off()
self.toolhead.dwell(0.500)
self.toolhead.wait_moves()
- self.printer.request_restart()
+ cmd_RESTART_when_not_ready = True
+ cmd_RESTART_help = "Reload config file and restart host software"
+ def cmd_RESTART(self, params):
+ self.prep_restart()
+ self.printer.request_exit('restart')
+ cmd_FIRMWARE_RESTART_when_not_ready = True
+ cmd_FIRMWARE_RESTART_help = "Restart firmware, host, and reload config"
+ def cmd_FIRMWARE_RESTART(self, params):
+ self.prep_restart()
+ self.printer.request_exit('firmware_restart')
cmd_STATUS_when_not_ready = True
cmd_STATUS_help = "Report the printer status"
def cmd_STATUS(self, params):
diff --git a/klippy/klippy.py b/klippy/klippy.py
index abd92208..412477ad 100644
--- a/klippy/klippy.py
+++ b/klippy/klippy.py
@@ -32,9 +32,9 @@ Protocol error connecting to printer
"""
message_mcu_connect_error = """
-This is an unrecoverable error. Please manually restart the
-micro-controller and then issue the "RESTART" command to
-restart the host software.
+Once the underlying issue is corrected, use the
+"FIRMWARE_RESTART" command to reset the firmware, reload the
+config, and restart the host software.
Error configuring printer
"""
@@ -94,8 +94,10 @@ class ConfigLogger():
logging.info(data.strip())
class Printer:
- def __init__(self, conffile, input_fd, is_fileinput=False, version="?"):
+ def __init__(self, conffile, input_fd, startup_state
+ , is_fileinput=False, version="?"):
self.conffile = conffile
+ self.startup_state = startup_state
self.software_version = version
self.reactor = reactor.Reactor()
self.gcode = gcode.GCodeParser(self, input_fd, is_fileinput)
@@ -226,11 +228,18 @@ class Printer:
self.mcu.disconnect()
except:
logging.exception("Unhandled exception during disconnect")
- def request_restart(self):
- self.run_result = "restart"
- self.reactor.end()
- def request_exit_eof(self):
- self.run_result = "exit_eof"
+ def firmware_restart(self):
+ try:
+ if self.mcu is not None:
+ self.stats(self.reactor.monotonic())
+ self.mcu.disconnect()
+ self.mcu.microcontroller_restart()
+ except:
+ logging.exception("Unhandled exception during firmware_restart")
+ def get_startup_state(self):
+ return self.startup_state
+ def request_exit(self, result="exit"):
+ self.run_result = result
self.reactor.end()
@@ -287,9 +296,11 @@ def main():
logging.info("CPU: %s" % (util.get_cpu_info(),))
# Start firmware
+ res = 'startup'
while 1:
is_fileinput = debuginput is not None
- printer = Printer(conffile, input_fd, is_fileinput, software_version)
+ printer = Printer(
+ conffile, input_fd, res, is_fileinput, software_version)
if debugoutput:
proto_dict = read_dictionary(options.read_dictionary)
printer.set_fileoutput(debugoutput, proto_dict)
@@ -299,6 +310,11 @@ def main():
time.sleep(1.)
logging.info("Restarting printer")
continue
+ elif res == 'firmware_restart':
+ printer.firmware_restart()
+ time.sleep(1.)
+ logging.info("Restarting printer")
+ continue
elif res == 'exit_eof':
printer.disconnect()
break
diff --git a/klippy/mcu.py b/klippy/mcu.py
index 73f99c54..7dae343a 100644
--- a/klippy/mcu.py
+++ b/klippy/mcu.py
@@ -336,8 +336,9 @@ class MCU:
self._config = config
# Serial port
baud = config.getint('baud', 250000)
- serialport = config.get('serial', '/dev/ttyS0')
- self.serial = serialhdl.SerialReader(printer.reactor, serialport, baud)
+ self._serialport = config.get('serial', '/dev/ttyS0')
+ self.serial = serialhdl.SerialReader(
+ printer.reactor, self._serialport, baud)
self.is_shutdown = False
self._shutdown_msg = ""
self._is_fileoutput = False
@@ -423,6 +424,10 @@ class MCU:
def clear_shutdown(self):
logging.info("Sending clear_shutdown command")
self.send(self._clear_shutdown_cmd.encode())
+ def microcontroller_restart(self):
+ logging.info("Attempting a microcontroller reset")
+ self.disconnect()
+ serialhdl.arduino_reset(self._serialport, self._printer.reactor)
def is_fileoutput(self):
return self._is_fileoutput
# Configuration phase
@@ -472,6 +477,7 @@ class MCU:
config_params = self.serial.send_with_response(msg, 'config')
if not config_params['is_config']:
# Send config commands
+ logging.info("Sending printer configuration...")
for c in self._config_cmds:
self.send(self.create_command(c))
if not self._is_fileoutput:
@@ -482,6 +488,12 @@ class MCU:
self._shutdown_msg,))
raise error("Unable to configure printer")
if self._config_crc != config_params['crc']:
+ if self._printer.get_startup_state() != 'firmware_restart':
+ # Attempt a firmware restart to fix the CRC error
+ logging.info(
+ "Printer CRC mismatch - attempting firmware restart")
+ self._printer.request_exit('firmware_restart')
+ self._printer.reactor.pause(0.100)
raise error("Printer CRC does not match config")
move_count = config_params['move_count']
logging.info("Configured (%d moves)" % (move_count,))
diff --git a/klippy/serialhdl.py b/klippy/serialhdl.py
index 1af64000..8112d703 100644
--- a/klippy/serialhdl.py
+++ b/klippy/serialhdl.py
@@ -3,7 +3,7 @@
# Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
-import logging, threading
+import logging, threading, time
import serial
import msgproto, chelper, util
@@ -325,3 +325,16 @@ def stk500v2_leave(ser, reactor):
res = ser.read(4096)
logging.debug("Got %s from stk500v2" % (repr(res),))
ser.baudrate = origbaud
+
+# Attempt an arduino style reset on a serial port
+def arduino_reset(serialport, reactor):
+ # First try opening the port at 1200 baud
+ ser = serial.Serial(serialport, 1200, timeout=0)
+ ser.read(1)
+ time.sleep(0.100)
+ # Then try toggling DTR
+ ser.dtr = True
+ time.sleep(0.100)
+ ser.dtr = False
+ time.sleep(0.100)
+ ser.close()