mirror of
https://github.com/QIDITECH/klipper.git
synced 2026-01-30 23:48:43 +03:00
V1.7.1
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
70
klippy/extras/box_heater_fan.py
Normal file
70
klippy/extras/box_heater_fan.py
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
from . import fan
|
||||||
|
|
||||||
|
PIN_MIN_TIME = 0.100
|
||||||
|
|
||||||
|
class BoxHeaterFan:
|
||||||
|
def __init__(self, config):
|
||||||
|
self.printer = config.get_printer()
|
||||||
|
self.printer.register_event_handler("klippy:ready", self.handle_ready)
|
||||||
|
self.printer.register_event_handler("klippy:connect",
|
||||||
|
self.handle_connect)
|
||||||
|
self.stepper_names = config.getlist("stepper", None)
|
||||||
|
self.stepper_enable = self.printer.load_object(config, 'stepper_enable')
|
||||||
|
self.printer.load_object(config, 'heaters')
|
||||||
|
self.heater_temp = config.getfloat("heater_temp", 50.0)
|
||||||
|
self.heaters = []
|
||||||
|
self.fan = fan.Fan(config)
|
||||||
|
self.fan_speed = config.getfloat('fan_speed', default=1.,
|
||||||
|
minval=0., maxval=1.)
|
||||||
|
self.idle_speed = config.getfloat(
|
||||||
|
'idle_speed', default=self.fan_speed, minval=0., maxval=1.)
|
||||||
|
self.idle_timeout = config.getint("idle_timeout", default=30, minval=0)
|
||||||
|
self.heater_names = config.getlist("heater", ("extruder",))
|
||||||
|
self.last_on = self.idle_timeout
|
||||||
|
self.last_speed = 0.
|
||||||
|
def handle_connect(self):
|
||||||
|
# Heater lookup
|
||||||
|
pheaters = self.printer.lookup_object('heaters')
|
||||||
|
self.heaters = [pheaters.lookup_heater(n) for n in self.heater_names]
|
||||||
|
# Stepper lookup
|
||||||
|
all_steppers = self.stepper_enable.get_steppers()
|
||||||
|
if self.stepper_names is None:
|
||||||
|
self.stepper_names = all_steppers
|
||||||
|
return
|
||||||
|
if not all(x in all_steppers for x in self.stepper_names):
|
||||||
|
raise self.printer.config_error(
|
||||||
|
"One or more of these steppers are unknown: "
|
||||||
|
"%s (valid steppers are: %s)"
|
||||||
|
% (self.stepper_names, ", ".join(all_steppers)))
|
||||||
|
def handle_ready(self):
|
||||||
|
reactor = self.printer.get_reactor()
|
||||||
|
reactor.register_timer(self.callback, reactor.monotonic()+PIN_MIN_TIME)
|
||||||
|
def get_status(self, eventtime):
|
||||||
|
return self.fan.get_status(eventtime)
|
||||||
|
def callback(self, eventtime):
|
||||||
|
speed = 0.
|
||||||
|
active = False
|
||||||
|
for name in self.stepper_names:
|
||||||
|
active |= self.stepper_enable.lookup_enable(name).is_motor_enabled()
|
||||||
|
for heater in self.heaters:
|
||||||
|
current_temp, target_temp = heater.get_temp(eventtime)
|
||||||
|
if target_temp or current_temp > self.heater_temp:
|
||||||
|
active = True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if active:
|
||||||
|
self.last_on = 0
|
||||||
|
speed = self.fan_speed
|
||||||
|
elif self.last_on < self.idle_timeout:
|
||||||
|
speed = self.idle_speed
|
||||||
|
self.last_on += 1
|
||||||
|
if speed != self.last_speed:
|
||||||
|
self.last_speed = speed
|
||||||
|
curtime = self.printer.get_reactor().monotonic()
|
||||||
|
print_time = self.fan.get_mcu().estimated_print_time(curtime)
|
||||||
|
self.fan.set_speed(print_time + PIN_MIN_TIME, speed)
|
||||||
|
return eventtime + 1.
|
||||||
|
|
||||||
|
def load_config_prefix(config):
|
||||||
|
return BoxHeaterFan(config)
|
||||||
Binary file not shown.
94
klippy/extras/buttons_irq.py
Normal file
94
klippy/extras/buttons_irq.py
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
# button_irq.py - Klipper Python module for IRQ-driven buttons
|
||||||
|
#
|
||||||
|
# Copyright (C) 2025 Menson <lms228@163.com>
|
||||||
|
#
|
||||||
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
class _IRQButtonMCU:
|
||||||
|
def __init__(self, printer, mcu, debounce_us):
|
||||||
|
self.printer = printer
|
||||||
|
self.reactor = printer.get_reactor()
|
||||||
|
self.mcu = mcu
|
||||||
|
self.pin_list = []
|
||||||
|
self.callbacks = []
|
||||||
|
self.oid = mcu.create_oid()
|
||||||
|
self.ack_count = 0
|
||||||
|
self.state = 0
|
||||||
|
self.invert = 0
|
||||||
|
self.debounce_us = debounce_us
|
||||||
|
|
||||||
|
self.mcu.register_response(self._handle_state, "irq_button_state", self.oid)
|
||||||
|
self.mcu.register_config_callback(self._build_config)
|
||||||
|
|
||||||
|
def setup_buttons(self, pin_params_list, callback):
|
||||||
|
mask = 0
|
||||||
|
shift = len(self.pin_list)
|
||||||
|
for pin_params in pin_params_list:
|
||||||
|
if pin_params.get('invert', False):
|
||||||
|
self.invert |= 1 << len(self.pin_list)
|
||||||
|
mask |= 1 << len(self.pin_list)
|
||||||
|
self.pin_list.append(pin_params)
|
||||||
|
self.callbacks.append((mask, shift, callback))
|
||||||
|
|
||||||
|
def _build_config(self):
|
||||||
|
logging.info("_irq_button debounce_us=%d", self.debounce_us)
|
||||||
|
for pin in self.pin_list:
|
||||||
|
self.mcu.add_config_cmd(
|
||||||
|
"config_irq_button oid=%d pin=%s pull_up=%d debounce_us=%d invert=%d" %
|
||||||
|
(self.oid, pin['pin'], pin.get('pullup', 0), self.debounce_us, pin.get('invert', 0))
|
||||||
|
)
|
||||||
|
cmd_queue = self.mcu.alloc_command_queue()
|
||||||
|
self.ack_cmd = self.mcu.lookup_command(
|
||||||
|
"irq_button_ack oid=%c count=%c", cq=cmd_queue)
|
||||||
|
|
||||||
|
def _handle_state(self, msg):
|
||||||
|
count = msg["count"]
|
||||||
|
states = msg["state"]
|
||||||
|
#logging.warning("IRQ button state oid=%d count=%d states=%s",
|
||||||
|
# self.oid, count, states)
|
||||||
|
for s in states:
|
||||||
|
s = int(s) ^ self.invert
|
||||||
|
if s != self.state:
|
||||||
|
changed = s ^ self.state
|
||||||
|
self.state = s
|
||||||
|
for mask, shift, callback in self.callbacks:
|
||||||
|
if changed & mask:
|
||||||
|
value = (s & mask) >> shift
|
||||||
|
self.reactor.register_async_callback(
|
||||||
|
lambda et, c=callback, v=value: c(et, v))
|
||||||
|
self.ack_cmd.send([self.oid, count])
|
||||||
|
|
||||||
|
|
||||||
|
class MCU_irq_button:
|
||||||
|
def __init__(self, config):
|
||||||
|
self.printer = config.get_printer()
|
||||||
|
self.irq_buttons = {}
|
||||||
|
|
||||||
|
def register_buttons(self, pins, callback, debounce_us):
|
||||||
|
ppins = self.printer.lookup_object('pins')
|
||||||
|
mcu = mcu_name = None
|
||||||
|
pin_params_list = []
|
||||||
|
|
||||||
|
for pin in pins:
|
||||||
|
pin_params = ppins.lookup_pin(pin, can_invert=True, can_pullup=True)
|
||||||
|
if mcu is not None and pin_params['chip'] != mcu:
|
||||||
|
raise ppins.error("IRQ button pins must be on same MCU")
|
||||||
|
mcu = pin_params['chip']
|
||||||
|
mcu_name = pin_params['chip_name']
|
||||||
|
pin_params_list.append(pin_params)
|
||||||
|
|
||||||
|
mcu_button = self.irq_buttons.get(mcu_name)
|
||||||
|
if (mcu_button is None
|
||||||
|
or len(mcu_button.pin_list) + len(pin_params_list) > 8):
|
||||||
|
logging.warning(
|
||||||
|
"Creating new IRQ button object for MCU '%s' with %d pins",
|
||||||
|
mcu_name, len(pin_params_list))
|
||||||
|
mcu_button = _IRQButtonMCU(self.printer, mcu, debounce_us)
|
||||||
|
self.irq_buttons[mcu_name] = mcu_button
|
||||||
|
|
||||||
|
mcu_button.setup_buttons(pin_params_list, callback)
|
||||||
|
|
||||||
|
def load_config(config):
|
||||||
|
return MCU_irq_button(config)
|
||||||
@@ -16,9 +16,21 @@ class EncoderSensor:
|
|||||||
self.extruder_name = config.get('extruder')
|
self.extruder_name = config.get('extruder')
|
||||||
self.detection_length = config.getfloat(
|
self.detection_length = config.getfloat(
|
||||||
'detection_length', 7., above=0.)
|
'detection_length', 7., above=0.)
|
||||||
|
self.use_irq = config.getboolean('use_irq', False)
|
||||||
|
logging.info(f"motion use_irq?:{self.use_irq}")
|
||||||
|
self.debounce_us = 0
|
||||||
# Configure pins
|
# Configure pins
|
||||||
|
if self.use_irq:
|
||||||
|
logging.info(f"motion use_irq1?:{self.use_irq}")
|
||||||
|
self.debounce_us = config.getint('debounce_us', 0)
|
||||||
|
irq_button = config.printer.load_object(config, "buttons_irq")
|
||||||
|
#irq_button.register_buttons([switch_pin], callback=self.encoder_event, self.debounce_us)
|
||||||
|
irq_button.register_buttons([switch_pin], callback=self.encoder_event, debounce_us = self.debounce_us)
|
||||||
|
else:
|
||||||
|
logging.info(f"motion use_irq2?:{self.use_irq}")
|
||||||
buttons = self.printer.load_object(config, 'buttons')
|
buttons = self.printer.load_object(config, 'buttons')
|
||||||
buttons.register_buttons([switch_pin], self.encoder_event)
|
buttons.register_buttons([switch_pin], self.encoder_event)
|
||||||
|
|
||||||
# Get printer objects
|
# Get printer objects
|
||||||
self.reactor = self.printer.get_reactor()
|
self.reactor = self.printer.get_reactor()
|
||||||
self.runout_helper = filament_switch_sensor.RunoutHelper(config)
|
self.runout_helper = filament_switch_sensor.RunoutHelper(config)
|
||||||
@@ -42,12 +54,27 @@ class EncoderSensor:
|
|||||||
'CLEAR_MOTION_DATA',
|
'CLEAR_MOTION_DATA',
|
||||||
self.cmd_CLEAR_MOTION_DATA
|
self.cmd_CLEAR_MOTION_DATA
|
||||||
)
|
)
|
||||||
|
self.motion_name = config.get_name().split()[1]
|
||||||
|
self.gcode.register_mux_command('SET_MOTION_DETECTION', "NAME", self.motion_name, self.cmd_SET_MOTION_DETECTION)
|
||||||
|
self.gcode.register_mux_command('GET_MOTION_DETECTION', "NAME", self.motion_name, self.cmd_GET_MOTION_DETECTION)
|
||||||
def _update_filament_runout_pos(self, eventtime=None):
|
def _update_filament_runout_pos(self, eventtime=None):
|
||||||
if eventtime is None:
|
if eventtime is None:
|
||||||
eventtime = self.reactor.monotonic()
|
eventtime = self.reactor.monotonic()
|
||||||
self.filament_runout_pos = (
|
self.filament_runout_pos = (
|
||||||
self._get_extruder_pos(eventtime) +
|
self._get_extruder_pos(eventtime) +
|
||||||
self.detection_length)
|
self.detection_length)
|
||||||
|
def cmd_SET_MOTION_DETECTION(self, gcmd):
|
||||||
|
d_l = gcmd.get_float('LENGTH', 0.)
|
||||||
|
if d_l <= 0:
|
||||||
|
self.gcode.respond_info("The LENGTH value is error")
|
||||||
|
else:
|
||||||
|
self.detection_length = d_l
|
||||||
|
self._update_filament_runout_pos()
|
||||||
|
self.gcode.respond_info("the detection_length has changed successsful. The will restart after next restart.")
|
||||||
|
|
||||||
|
def cmd_GET_MOTION_DETECTION(self, gcmd):
|
||||||
|
self.gcode.respond_info("the detection_length is: " + str(self.detection_length))
|
||||||
|
|
||||||
def cmd_CLEAR_MOTION_DATA(self, gcmd):
|
def cmd_CLEAR_MOTION_DATA(self, gcmd):
|
||||||
self._update_filament_runout_pos()
|
self._update_filament_runout_pos()
|
||||||
def _handle_ready(self):
|
def _handle_ready(self):
|
||||||
@@ -75,6 +102,7 @@ class EncoderSensor:
|
|||||||
extruder_pos < self.filament_runout_pos)
|
extruder_pos < self.filament_runout_pos)
|
||||||
return eventtime + CHECK_RUNOUT_TIMEOUT
|
return eventtime + CHECK_RUNOUT_TIMEOUT
|
||||||
def encoder_event(self, eventtime, state):
|
def encoder_event(self, eventtime, state):
|
||||||
|
#self.gcode.respond_info(f"motion state: {state} ")
|
||||||
if self.extruder is not None:
|
if self.extruder is not None:
|
||||||
self._update_filament_runout_pos(eventtime)
|
self._update_filament_runout_pos(eventtime)
|
||||||
self.button_state = state
|
self.button_state = state
|
||||||
@@ -82,5 +110,6 @@ class EncoderSensor:
|
|||||||
# Filament is always assumed to be present on an encoder event
|
# Filament is always assumed to be present on an encoder event
|
||||||
self.runout_helper.note_filament_present(True)
|
self.runout_helper.note_filament_present(True)
|
||||||
|
|
||||||
|
|
||||||
def load_config_prefix(config):
|
def load_config_prefix(config):
|
||||||
return EncoderSensor(config)
|
return EncoderSensor(config)
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ class HeaterCheck:
|
|||||||
self.last_target = self.goal_temp = self.error = 0.
|
self.last_target = self.goal_temp = self.error = 0.
|
||||||
self.goal_systime = self.printer.get_reactor().NEVER
|
self.goal_systime = self.printer.get_reactor().NEVER
|
||||||
self.check_timer = None
|
self.check_timer = None
|
||||||
|
self.is_box_heater = config.getboolean('is_box_heter',False)
|
||||||
def handle_connect(self):
|
def handle_connect(self):
|
||||||
if self.printer.get_start_args().get('debugoutput') is not None:
|
if self.printer.get_start_args().get('debugoutput') is not None:
|
||||||
# Disable verify_heater if outputting to a debug file
|
# Disable verify_heater if outputting to a debug file
|
||||||
@@ -75,6 +76,10 @@ class HeaterCheck:
|
|||||||
self.error = 0.
|
self.error = 0.
|
||||||
else:
|
else:
|
||||||
# Failure due to inability to maintain target temperature
|
# Failure due to inability to maintain target temperature
|
||||||
|
if self.is_box_heater and target > 0:
|
||||||
|
self.box_heater_fault()
|
||||||
|
return eventtime + 1.
|
||||||
|
else:
|
||||||
return self.heater_fault()
|
return self.heater_fault()
|
||||||
elif heater_bed.heater_bed_state == 1 and self.heater_name == "chamber":
|
elif heater_bed.heater_bed_state == 1 and self.heater_name == "chamber":
|
||||||
self.error = 0.
|
self.error = 0.
|
||||||
@@ -100,5 +105,15 @@ class HeaterCheck:
|
|||||||
self.printer.invoke_shutdown(msg + HINT_THERMAL)
|
self.printer.invoke_shutdown(msg + HINT_THERMAL)
|
||||||
return self.printer.get_reactor().NEVER
|
return self.printer.get_reactor().NEVER
|
||||||
|
|
||||||
|
def box_heater_fault(self):
|
||||||
|
msg = "Heater %s not heating at expected rate" % (self.heater_name,)
|
||||||
|
logging.error(msg)
|
||||||
|
self.printer.lookup_object('gcode').respond_info("Code:QDE_005_001; Message:"+ msg)
|
||||||
|
pheaters = self.printer.lookup_object('heaters')
|
||||||
|
heater = pheaters.lookup_heater(self.heater_name)
|
||||||
|
heater.set_temp(0)
|
||||||
|
self.last_target = 0
|
||||||
|
return
|
||||||
|
|
||||||
def load_config_prefix(config):
|
def load_config_prefix(config):
|
||||||
return HeaterCheck(config)
|
return HeaterCheck(config)
|
||||||
|
|||||||
Reference in New Issue
Block a user