Help with Python script

Basic Information:

Printer Model: Custom
MCU / Printerboard: Costom STM32H723
Host / SBC : Raspberry Pi4
klippy.log

Describe your issue:

I made a custom 600x600 printer and I made a custom controller board that is based on STM32H723.
I’m more of a safety orientated guy so my board includes two safety relays (one for the hot ends and one for the bed), they are connected to a small MCU that acts as a watchdog timer. It needs kicking from the STM32H723 if Klipper is not in shutdown mode. The purpose is to protect the printer (and maybe my house) from freezes, lockups, and even shorted Mosfets or Tirac.
currently I made it woerking with macros but the problem is that they freeze when homing or heating up. I got an Idea to have a custom python script that runs in the background all the time if klipper is not in shutdown mode. I tried chatGPT and I got some code but it is not toggling the pin on the MCU. And I can’t see any errors in klippy log. Cane someone else help me out. Or any other ideas?

import logging
class WatchdogToggle:
    def __init__(self, config):
        logging.info("Starting watchdog")
        self.printer = config.get_printer()
        self.reactor = self.printer.get_reactor()
        self.current_state = 0
        self.toggle_interval = config.getfloat('toggle_interval', 1.0)  # Toggle every 1 second
        ppins = self.printer.lookup_object('pins')
        self.is_pwm = config.getboolean('pwm', False)
        if self.is_pwm:
            self.mcu_pin = ppins.setup_pin('pwm', config.get('pin'))
            max_duration = self.mcu_pin.get_mcu().max_nominal_duration()
            cycle_time = config.getfloat('cycle_time', 0.100, above=0.,
                                         maxval=max_duration)
            hardware_pwm = config.getboolean('hardware_pwm', False)
            self.mcu_pin.setup_cycle_time(cycle_time, hardware_pwm)
            self.scale = config.getfloat('scale', 1., above=0.)
        else:
            self.mcu_pin = ppins.setup_pin('digital_out', config.get('pin'))
            self.scale = 1.
        self.mcu_pin.setup_max_duration(0.)
        self.timer = self.reactor.register_timer(self._toggle_pin, self.reactor.NEVER)
        self.printer.register_event_handler('klippy:ready', self._start_timer)
        self.printer.register_event_handler('klippy:shutdown', self._handle_shutdown)
        self.last_value = config.getfloat(
            'value', 0., minval=0., maxval=self.scale) / self.scale
        self.shutdown_value = config.getfloat(
            'shutdown_value', 0., minval=0., maxval=self.scale) / self.scale
        self.mcu_pin.setup_start_value(self.last_value, self.shutdown_value)

    def get_status(self, eventtime):
        return {'value': self.last_value}

    def _start_timer(self):
        try:
            print_time = self.reactor.monotonic() + 0.1
            self.current_state = 0
            logging.info("Initialized pin  to low at print_time %f",  print_time)
            self.reactor.update_timer(self.timer, self.reactor.NOW)
            logging.info("Watchdog toggle timer started for pin")
        except Exception as e:
            logging.error("Error starting timer: %s", str(e))
            raise

    def _handle_shutdown(self):
        try:
            print_time = self.reactor.monotonic() + 0.1
            self.output_pin.set_digital(print_time, 0)
            logging.info("Shutdown detected, set to low at print_time %f and stopped timer", print_time)
            self.reactor.update_timer(self.timer, self.reactor.NEVER)
        except Exception as e:
            logging.error("Error in handle_shutdown: %s", str(e))

    def _toggle_pin(self, eventtime):
        try:
            idle_timeout = self.printer.lookup_object('idle_timeout')
            printer_state = idle_timeout.get_status(eventtime)['state']
            logging.debug("Printer state: %s", printer_state)
            if printer_state == "Shutdown":
                print_time = eventtime + 0.1
                self.mcu_pin.set_digital(print_time, 0)
                logging.info("Printer in shutdown, stopping toggle and setting to low at print_time %f", print_time)
                return self.reactor.NEVER
            self.current_state = 1 - self.current_state
            print_time = eventtime + 0.1
            self.mcu_pin.set_digital(print_time, self.current_state)
            logging.info("Toggled to %d at eventtime %f, print_time %f", self.current_state, eventtime, print_time)
            return eventtime + self.toggle_interval
        except Exception as e:
            logging.error("Error in toggle_pin for pin: %s", str(e))
            return self.reactor.NEVER

    def _set_pin(self, print_time, value):
        if value == self.last_value:
            return "discard", 0.
        self.last_value = value
        if self.is_pwm:
            self.mcu_pin.set_pwm(print_time, value)
        else:
            self.mcu_pin.set_digital(print_time, value)

    def _template_update(self, text):
        try:
            value = float(text)
        except ValueError as e:
            logging.exception("output_pin template render error")
            value = 0.

def load_config_prefix(config):
    return WatchdogToggle(config)

Thank you

Hi @TadyTheFish ,

I really really would not recommend ChatGPT for writing Klipper code. It’s fine for simple logic, but there’s simply too much that can go wrong in Klipper, especially with heaters.

I would take a look at my extras tutorial here to get used to writing extras, then to toggle the pin I would reverse-engineer output_pin.py.

If you want to be even more safe, I would actually recommend putting the relay pins directly on the Pi (Normally-Open), and not setting up a host MCU. That way, you can disable the pins directly from the Pi, and, worst-case-scenario, shut down the Pi to disable the relays.

2 Likes

I totally agree with you about chat GPT i just wanted to get a working example and go from there since I have no idea how to send gpio commands to the MCU.
I have a board drawn up already with the described connections. So if it would be possible to solve this in software I would be happy enoug. I tried to reverse engineer the outpu_pin extra but I really lack sone infor on what is going on in there and how is it done. My posted code runs in the background only pin toggling does not work. Something is missing and ai have absolutley no idea on what

Oh sorry I forgot to reply to you that I have seen your toutorials and I thing you are doing an amazing job, its just that I’m too much of a noob when it cones to python that I would really like to see an tutorial on controling phisical pins of the host mcu for example, and maybe a description on how commands are processed (priority and such things)

1 Like

this is exactly what I am looking for thank you for this work.

1 Like

What do you need? I made the code work