Controlling a chamber heater through a relay connected to RPI4 via GPIO and monitored with a DS18B20

Basic Information:

Printer Model: Sovol SV06
MCU / Printerboard: Original MCU + Raspberry Pi 4 as secondary MCU
klippy.log

Hello,

I am trying to control a chamber heater with a relay connected to the RPI4 host via GPIO. (there are 77 °C thermal fuses connected inside the chamber between the heater and the heater’s power cable for safety).

The chamber temperature sensor is a DS18B20 which works fine when used as a standalone sensor like this:

[temperature_sensor Chamber1]
sensor_type: DS18B20
serial_no: 28-186b5e1e64ff
min_temp: 0
max_temp: 125
ds18_report_time: 1.0
sensor_mcu: rpi

However, when I define the heater as follows (after disabling the previous sensor for testing):

[heater_generic chamber_heater]
heater_pin: rpi:gpio23
sensor_type: DS18B20
serial_no: 28-186b5e1e64ff
control: watermark
max_delta: 5.0
sensor_mcu: rpi
min_temp: 0
max_temp: 100
ds18_report_time: 1.0
gcode_id: C

[verify_heater chamber_heater]
max_error: 120
check_gain_time: 120
hysteresis: 5
heating_gain: 2

I am able to set the chamber temperature and the heater starts, but after a few seconds it stops and I get the following error:

MCU ‘rpi’ shutdown: Scheduled digital out event will exceed max_duration

What am I doing wrong here?

Thanks.

klippy.log (196.6 KB)

Good luck, hcet14

Nothing.
Unfortunately, this is a long-standing issue with this sensor type.

I can only recommend using a different sensor, e.g. a plain NTC for your purpose.

Hello,

Thanks for the answers.

I worked around the problem by disabling the check in src\gpiocmds.c in the function “command_queue_digital_out” and rebuilding for the rpi.

Now I can set a temperature for the chamber and the heater is correctly managed: I can see that it goes off when the (temperature + max_delta) is attained and goes on again when (temperature - max_delta) is attained.

So this is good enough for me.

@Sineos: do you have some idea where in the source code to look for to try to fix the bug in a cleaner way? Do you believe somehow the bug is in \src\linux\sensor_ds18b20.c?

Unfortunately not. Also the initial report contains no real pointers: BUG?: Chamber Heater with DS18B20 Temperature Sensor generates "MCU 'mcu' shutdown: Scheduled digital out event will exceed max_duration" failure · Issue #5189 · Klipper3d/klipper · GitHub

Maybe @koconnor can give some hints where to start looking.

Edit:
For some gory details on the implementation, you might want to refer to the initial PR: module: DS18B20, new module for 1-wire temperature sensor (WIP) by theopensourcerer · Pull Request #3462 · Klipper3d/klipper · GitHub

Thank you @Sineos for the links. I will check them.

I tried to launch a print with a temperature for the chamber heater after adapting the PRINT_START macro.

The temperature is being set and the heater starts, however during the pre-print heating phase I get the error “Missed scheduling of next digital out event”.

So I guess deactivating the check has other side effects (who would have thought? :-)).

It is a bit weird since the management is working when setting the temperature manually outside of a print.

klippy_print_fail_heater.log (423.7 KB)

The Klipper heating code was designed around temperature sensors that provide an update every 300ms. The ds18b20 sensor can only provide updates once every second or so. Thus the two are not compatible.

It might be possible to rework the host code to better hanle slower updating sensors (the code in klippy/extras/heaters.py), however, for what it is worth, I’m not sure that’s a particularly good use of developer time.

I’d advise caution here. We’ve had a few cases recently where users effectively disabled safety checks, and then went on to have notable failures at a later date. (Melted printer; could have been much worse.)

The safety feature you disabled is important for handling the case where the host software (or host hardware) fails - if that occurs it is important to get the heater disabled within a few seconds.

As Sineos indicated, the best known solution is to deploy a more robust temperature sensor.

-Kevin

1 Like

Hello,

Thanks @koconnor for the information. I read the code in heaters.py but I don’t see any link with the error messages given by the web interface (“Missed scheduling of next digital out event”) so I gave up.

I tried now to use a Raspberry Pi Pico that I bought, with this temperature sensor:

https://www.amazon.de/DollaTek-Thermistor-Temperatursensor-HT-NTC100K-Hochtemperatur-3D-Drucker/dp/B081JN6Q63/

I connected one pin of the sensor to GP28/ADC2 and the other to pin GND/AGND.

Then I added the following section in printer.cfg (obviously after building and flashing the pico):

[mcu pico]
serial: /dev/ttyACM0

[temperature_sensor Pico]
sensor_type: NTC 100K MGB18-104F39050L32
sensor_pin: pico:gpio28
min_temp: 0
max_temp: 125

However, it does not work and I get the following error message:

MCU ‘pico’ shutdown: ADC out of range
This generally occurs when a heater temperature exceeds
its configured min_temp or max_temp.
Once the underlying issue is corrected, use the
“FIRMWARE_RESTART” command to reset the firmware, reload the
config, and restart the host software.
Printer is shutdown

Do you have an idea what could be wrong?

klippy_rp_2040_fail.zip (121.3 KB)

You cannot simply connect a NTC to an ADC. It needs a voltage divider with an accurate (and temperature stable) resistor.
See How Easy Is It to Use a Thermistor?! | Arduino Project Hub for an example (surely you will find an abundance of examples, was more or less the first hit)

1 Like

So I soldered a 10K resistor between a wire coming from ADC_VREF and a pin of the thermistor that is connected to GP28/ADC2. The other pin of the thermistor is connected to Ground/AGND.

I tried every possible predefined sensor type in Klipper and none gave good values.

Then I used this table found on a Amazon comment page:

[thermistor winsinn_ht-ntc100k]
temperature1: 25
resistance1: 100000
temperature2: 80
resistance2: 8300
temperature3: 220
resistance3: 127

[temperature_sensor Pico]
sensor_type: winsinn_ht-ntc100k
sensor_pin: pico:gpio28

It seems I am getting values close to the truth, except that they are fluctuating and negative.

How can I reverse the sign of the value?

Have you set the 10K as “pull-up”? Ref to Configuration reference - Klipper documentation

Yes even with this the temperature is still wrong.

I might be misunderstanding your description of your circuit, but it sounds wrong. You might cross-check against, e.g. Make an Arduino Temperature Sensor using Thermistor - Circuit Geeks

I have this circuit:

Except I am using GP28/ADC2 instead of GP26/ADC0. I now use 3V3(OUT) and the same GND as in the picture. The value is still completely wrong.

Try switching the two resistors or switch the GND and 3V3. This will invert the relationship and should bring your readings into the positive.

Are you sure about the 10k pullup?

Usually 4k7 are used.

No I am not sure. I took this value from a link that I found for connecting a Thermistor to a Pi Pico (Raspberry Pi Pico and Thermistor Temperature Sensor)

I am now trying as I originally planned with the relay and DS18B20 connected to the RPI4 since this is a better solution anyway. I can see in heaters.py:

    logging.debug("%s: pwm=%.3f@%.3f (from %.3f@%.3f [%.3f])",
                  self.name, value, pwm_time,
                  self.last_temp, self.last_temp_time, self.target_temp)

I uncommented this and restarted klipper with systemctl restart klipper. However the output is not in klippy.log.

Do you know where the logger debug output of klippy is stored? It is not explained in Debugging - Klipper documentation

10K should work. 4K7 is also just a “foul” compromise.
You would usually calculate it, depending on the temperature range you are aiming for.
Target is to make sure to efficiently use the entire ADC range. The easiest way to calculate is the geometric mean of the min and max resistance at the give temps

Example:

  • Resistance at 20 °C: 125K
  • Resistance at 80 °C: 12.7K

Rb = sqrt(125*12.7) = 39K

There are better and more complex formulas (that I already forgot). In the end it is effecting the ADC resolution and the linearity of the measurement and thus its overall error.
Without any calibration, such a simple NTC measuring circuit is at best an “educated guess”.

1 Like

Hello,

I believe I managed to make the chamber heater work with the DS18B20 sensor with only little changes in the code.

  1. Change to heaters.py
######################################################################
 # Heater
 ######################################################################

+SLOW_SENSOR_THRESHOLD = 1.0
+MAX_DURATION_SLOW_SENSOR = 25
 KELVIN_TO_CELSIUS = -273.15
 MAX_HEAT_TIME = 5.0
 AMBIENT_TEMP = 25.
@@ -53,7 +54,10 @@ class Heater:
         pwm_cycle_time = config.getfloat('pwm_cycle_time', 0.100, above=0.,
                                          maxval=self.pwm_delay)
         self.mcu_pwm.setup_cycle_time(pwm_cycle_time)
-        self.mcu_pwm.setup_max_duration(MAX_HEAT_TIME)
+        if(self.sensor.get_report_time_delta() >= SLOW_SENSOR_THRESHOLD):
+            self.mcu_pwm.setup_max_duration(MAX_DURATION_SLOW_SENSOR)
+        else:
+            self.mcu_pwm.setup_max_duration(MAX_HEAT_TIME)
  1. printer.cfg:
[mcu rpi]
serial: /tmp/klipper_host_mcu

[heater_generic heater_chamber]
heater_pin: rpi:gpio23
sensor_type: DS18B20
serial_no: 28-186b5e1e64ff
control: watermark
max_delta: 2.0
sensor_mcu: rpi
min_temp: 0
max_temp: 100
ds18_report_time: 1.0
pwm_cycle_time: 1.0

[verify_heater heater_chamber]
max_error: 300
check_gain_time:480
hysteresis: 5
heating_gain: 1
  1. Klipper macros:
[gcode_macro SET_HEATER_TEMPERATURE_AND_WAIT]
description: Set heater temperature and wait for temperature to be reached
gcode:
    #Parameters
    {% set heater = params.HEATER %}
    {% set target_temp = params.TARGET|float %}

    SET_HEATER_TEMPERATURE HEATER={heater} TARGET={target_temp}
    {% if target_temp != 0 %}
        TEMPERATURE_WAIT SENSOR="heater_generic {heater}" MINIMUM={target_temp} ; Wait for bed temp (within 1 degree)
    {% endif %}

[gcode_macro PRINT_START]
gcode:
    # Parameters
    {% set bedtemp = params.BED|int %}
    {% set hotendtemp = params.HOTEND|int %}
    {% set chambertemp = params.CHAMBER|default(0)|int %}
    
    # Other variables
    {% set bedtempSlicer = bedtemp %}
    {% set bedtempRange = 10 %}
    {% set maxVelocity = printer.configfile.settings.printer.max_velocity|default(200)|int %}
    {% set maxVelocityAdjusted =  (0.90 * maxVelocity * 60)|int %}

    {% if printer.configfile.settings.safe_z_home %}
        {% set startX = printer.configfile.settings.safe_z_home.home_xy_position[0]|float %}
        {% set startY = printer.configfile.settings.safe_z_home.home_xy_position[1]|float %}
    {% endif %}

    {% set bedtempAlmost = ((bedtemp - 2, 0, printer.heater_bed.temperature|int)|max, bedtemp)|max %}
    {% set hotendtempStepOne = ((hotendtemp, printer[printer.toolhead.extruder].temperature|int)|min, 150)|max %}
    {% set hotendtempStepTwo = ((hotendtemp, printer[printer.toolhead.extruder].temperature|int)|min, 170)|max %}

    # If bed-temp-almost is higher than bed-temp by a maximum of 10C
    {% if bedtempAlmost > bedtemp %}
        {% if (bedtempAlmost - bedtempRange) <= bedtemp %}
            {% set bedtemp = bedtempAlmost %}
        {% endif %}
    {% endif %}

    BED_MESH_PROFILE LOAD=default                        ; NOTE if not using a mesh, comment out this line
    ADJUST_FILAMENT_SENSOR_STATUS ENABLE=1

    G90                                                  ; absolute positioning

    SET_HEATER_TEMPERATURE HEATER=heater_chamber TARGET={chambertemp} ; set and don't wait for chamber temp

    M140 S{bedtempAlmost}                                ; set & don't wait for bed temp
    M104 S{hotendtempStepOne}                            ; set & don't wait for hotend temp
    G28 X Y Z
    {% if printer.configfile.settings.safe_z_home %}
        G1 X{startX} Y{startY} F{maxVelocityAdjusted}
    {% endif %}

    M190 S{bedtempAlmost}                                ; set & wait for bed temp
    M104 S{hotendtempStepTwo}                            ; set & don't wait for hotend temp
    M190 S{bedtemp}                                      ; set & wait for bed temp
    M140 S{bedtempSlicer}                                ; set & don't wait for bed temp ; set temp to sliced setting regardless

    M104 S{hotendtemp}                                   ; set & don't wait for hotend temp
    G28 Z                                                ; final z homing

    G1 X0 Y0 F{maxVelocityAdjusted}
    M109 S{hotendtemp}                                   ; set & wait for hotend temp

    SET_HEATER_TEMPERATURE_AND_WAIT HEATER=heater_chamber TARGET={chambertemp} ; set and wait for chamber temp

    G1 Z20 F3000                                         ; move nozzle away from bed
  1. Machine G-Code in OrcaSlicer:
PRINT_START BED=[first_layer_bed_temperature] HOTEND={first_layer_temperature[initial_extruder]} CHAMBER=[chamber_temperature]

I was able to set a temperature in the filament settings, slice the part, start the print on the printer. The heater starts and the print starts only after the temperature is reached.

I was able to print a OrcaCube until the end with a set temperature of 40°C as a test.

I will test further but it seems to be working as expected.

USE AT YOUR OWN RISK.

1 Like

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.