After doing some research and given it some thought, I think I have converged to the method which would allow not just detect fire (all those smoke and temperature sensors normally used with these devices), but prevent it from ever happening - by detecting the early signs of a trouble.

Here is my reasoning:

(a) The most likely place where the fire can start around the printer is in the highest current circuit subject to constant movement and vibrations - which is the heated bed wiring (including the connectors and screw terminals), and to a lesser degree - the hot end wiring.

(b) It appears to me the best way to identify a faulty wiring or connector is by measuring its resistance in the active circuit. Just measuring the voltage or current alone would not be sufficient - there will be lots of harmless situations when either or both would be fluctuating a lot (e.g., house AC power flickering, the constant turning the bed on and off by the printer etc.). The resistance (voltage / amperage), on the other hand, shouldn't fluctuate at all under normal conditions. If e.g. there is a house AC voltage spike, the bed's voltage can increase momentarily, but so will its amperage (because the bed is a passive load, with a fixed resistance), with the measured resistance staying constant. But if, on the other hand, there is a real problem - loose connection, or the wire partially or completely broken, the (time-averaged) resistance will increase beyond its normal operational range.

(c) One can use a cheap (2$) and very reliable current sensor based on Hall effect chip (ACS712; there are 5A, 20A, and 30A models; for a 12V heated bed a 20A unit would be a good fit), and a simple voltage dividing circuit with two resistors to let Arduino measure both the current to the bed and voltage across the bed.

(d) The tricky part is that analogue measurements are slow in Arduino - it will take ~100us for the current, and the same time for the voltage measurement. And given that the voltage to the bed is frequently turned on and off by the printer (especially if using PID method of heating in Merlin or SkyNet firmware - which is using PWM with a frequency of a few hundred Hz), it is not immediately obvious if one can come up with a reliable way of measuring the bed's resistance using Arduino.

And here is my idea how to implement a reliable way of measuring the bed's resistance. The main assumptions are

  • The full power on-off-on cycle for the bed should be longer than three consecutive analogue measurements (300us). To be on the safe side, that'll mean the PWM frequency (if you are using PID heating) should be <=1kHz. This is totally not a problem with the stock Anet v1.0 firmware (no PID used), and will probably be okay with Marlin with the default PID frequency. To be on the safe side though, in the latter case I would define the variable SLOW_PWM_HEATERS in Marlin; according to this link, this will reduce the PWM frequency to a very safe value of 7.6 Hz.
  • The transitions "bed on->off" and "bed off->on" are much shorter than one analogue measurement on Arduino (100us). I strongly suspect that is the case, given that the switching is done by fast circuits: digital Arduino pin (~1us) and MOSFET transistor (~1-3us).

I suggest the following algorithm to measure the bed's resistance dynamically:

  • Each measurement is always done as a group of three analogue measurements on Arduino: Voltage_1 (V1), Current (I), and Voltage_2 (V2).
  • If any of the two voltages are below ~1/2 of the normal voltage (say, <6V), the whole resistance measurement is discarded for this cycle.
  • The bed's resistance R is computed as R=max(V1,V2)/I.
  • It is a good idea to average out the resistance values over a number of measurements (say, over 100 measurements each second). As R behaves poorly in the statistical sense when the contact is flaky (the current I occasionally goes to zero, so we are dividing by zero and getting infinity resistance), a much better way is to average the inverse resistance values (1/R = I/max(V1,V2)). Once the averaging is done, we can recover the resistance by computing 1/<1/R>, where <1/R> is the averaged inverse resistance value.
  • One can train the Arduino device to detect the full range of resistance measurements under normal conditions. (Inevitably there will be errors of measurements, plus some temperature dependence of the bed's resistance.) After the training is done, the device would be switched into Guarding mode. If the bed's resistance becomes substantially larger than the largest trained value, this will be a strong indication of bad wiring, and the printer will be shut down immediately.

To understand the details of the algorithm, let's consider all possible scenarios of a normally functioning bed which is switched on and off regularly.

Case A: no on->off or off->on transition during the three measurements (V1-I-V2)

  • Always on: V>6V in the whole interval, we accept the measurements, and compute R=max(V1,V2)/I.
  • Always off: V<6V in the whole interval, so we discard the measurement.

Case B: on->off or off->on transition during the voltage measurement (V1 or V2)

In this case, V1 or V2 will measure a smaller value than normal. If it is <6V, we will discard the R computation:

If it is >6V for both voltages, we will compute R as usual, R=max(V1,V2)/I. Because of the "max" function, we'll end up using the larger other (not affected by the transition) voltage value:

If transition was on->off during the V1 measurement, we'll discard R as V2 will be <6V (zero; same for off->on transition during V2 measurement):

Case C: on->off or off->on transition during the current measurement (I)

In this case, either V1 or V2 voltage will be close to zero (<6V), so we will discard the R value:

Other considerations

So it looks like we'll always get good resistance measurements, unaffected by the bed's switching on and off in the course of its normal operation. If, on the other hand, the wiring has issues, voltages will stay roughly the same, but the current will have significant drops. This will get reflected in the time-averaged resistance value, which will become larger in case of a trouble.

In reality, even though one analogue measurement takes ~100us on Arduino, the actual sampling of the data is much shorter. Arduino's ADC uses so called "sample and hold" method of conversion (e.g. this link): it has an RC filter (14 pF & 100k) at the entrance. At some point the capacitor is briefly connected to the voltage to be measured, and then disconnected, and the much slower process of digitizing is performed not on the input voltage (which can be highly variable), but on the charge of the capacitor. The time it takes to charge the capacitor (~1.4us) is much shorter than the whole ADC conversion time (~100us). In the context of my algorithm that means that very rarely I'll be getting intermediate voltage values; in most cases I'll either be getting zeros or full voltages. This actually makes my algorithm even more stable.

What happens if the house AC has a spike or a short drop in voltage? It all depends on how fast and strong is the voltage change. If the voltage drop happens during the measurement (which would have a fairly small probability) and is <50%, resistance will be computed as usual. If the drop is fast (<200us) this can affect the accuracy of the resistance measurement. E.g. if the fast drop happens during the current measurement, we'll get normal V but reduced I -> larger R. This could be a "fake positive" (larger R means possible wiring issue), but as I am averaging R (or rather 1/R) over 100 measurements every second, this will likely be discarded as a minor disturbance. Moreover, using UPS with the printer should completely eliminate such problems.

Another interesting point: apparently it is possible to reduce the Arduino's ADC measurement time by a factor of 8 without loosing much accuracy ( Meaning that the whole resistance measurement will only take ~40us instead of the usual 300us. This should make the resistance measurements much more reliable in the presence of spiky AC voltage, and would allow to use even higher PWM frequency for PID heating (up to ~ 10kHz).

Real life performance

Here is my very first test of the resistance sensor with a heated bed:

It suggests that the sensor should be able to detect problem with bed wiring resulting in averaged resistance increases as small as 2% - so it is very sensitive to potential fire-causing factors.

The black line is for individual measurements (groups of three measurements done in a very quick succession - voltage, current, again voltage, done 100 times per second; resistance computed using my algorithm to skip transient events - bed switching on and off). The alarm functionality will use the averaged resistance values (red dots; one dot averages over 100 individual measurements).

In this experiment, the bed was initially heated from 55C to 60C, and then stayed at 60C (MOSFET started switching on and off every ~10s). So initially the resitance is increasing (it grows with temperature), and then stays constant, with some noise. The individual measurements noise is ~5%, and is mostly caused by occasional larger voltage values, with current staying the same (not sure why). Once we hit the target temperature, the average resistance deviates <0.5% from the mean.

When we train 3D Guardian over a long period (with long printing jobs), the global average of the bed resistance will be close to its value at the normal printing temperature (say, 120C for ABS). The alram is triggered when the locally (over 1s of measurements) averaged value goes above the globally averaged value by the maximum trained deviation times 2. (In this plot, the maximum trained deviation would be at the hump in the middle, when we just hit the target temperature.) Judging from this plot, for properly trained situation (when the right side of this plot would be extended many times over), the alarm will be triggered when the bed resistance averaged over 1s goes ~2% above it's normal value at the normal printing temperaure. This should be sufficiently sensitive to detect any wiring issues (which could cause fire) - broken wire, poorly fastened screw, arcing.