Youtube demo of the interface (already outdated): https://youtu.be/dR1IvWbTcBw
3D Guardian is my Arduino based DIY gadget which can be used to monitor the environment and perform some actions (e.g. shut down your 3D printer) if the environment moves beyond a critical range. It is mainly designed to serve as a security device for 3D printers (will shut it down in case of a trouble - elevated smoke, CO levels, high temperature etc.), but also serves as a thermostat and fan control if your printer is inside a cabinet with an exhaust pipe with a fan.
The gadget has the following features:
- It is very cheap to build (probably less than 30$ if sourcing the parts from ebay).
- It is open source (anyone can, and in fact should, modify the original Arduino software).
- It is convenient to use (LCD shield 1602, 2 lines x 16 characters, with 5 buttons).
- It uses three LEDs of different colors to tell you instantly the current state (Training, Guarding, Warning or Alarm).
- It uses a loud piezo buzzer (from a broken CO detector), for audible warning and alarm signals.
- In case of an alarm, it sends a signal to a separate (power) module, which has an AC plug, two AC outlets (e.g. if your printer uses two separate PSUs), and a Solid State Relay (SSR; 380V/10A). The plug goes to your house AC grid (or to your UPS), the printer's PSUs are connected to the AC outlets, and the 3D Guardin is connected to the power module via a low voltage / current cable. (I am using an old curled telephone receiver cable.)
- It stores all the important data (input parameters and sensors data) in the non-volatile (EEPROM) memory.
- It is very flexible:
- It can use a different number of sensors (up to seven with Arduino Nano), easily configurable in config.h. The default version is configured for 7 sensors (four thermistors, CO sensor, smoke sensor, and bed resistance sensor). I placed one thermistor at the top of the printer frame (with the smoke detector), second one at the top of the PC mini-tower containing all the electronics for the printer, the third one glued on top of the 16V/25A PSU I am using to heat the bed, the fourth one glued on top of the AC SSR which turns the printer on/off. The CO detector is placed inside the printer enclosure, at the bottom.
- It has a flexible menu system - any number of levels, and one can easily insert new menu items or delete old ones anywhere.
- It has a Training mode (initial mode when using for the first time): just run your printer as usual for a few long printing jobs, and let the gadget record the ranges of the data from all the sensors. Then the gadget can be switched to Guarding mode, where it will use the training data to figure out when to issue a warning or trigger the alarm (shutting down the printer). Meaning - there is no need for a guesswork about "how much is too much?" with any sensor readings.
- If your enclosure has an exhaust fan, the device can be used to control the fan to maintain a target (configurable from the menu) enclosure temperature. Also, there is an easy access menu command, "Case clearing", which will run the fan at full speed at the end of your printing job (for a configurable number of seconds), to remove all the fumes before you open the cabinet.
- 3D Guardian can control a stepper motor, to open/close the exhaust outlet in the printer's enclosure. The outlet is all made of metal (fireproof), and goes to the default "closed lid" state if power disappears. It can also operate a second stepper motor, for the air inlet lid.
- When in Guarding mode, the gadget will issue a warning (visible - via LCD and LED, and audible - short chirps from its piezo) when you start approaching the alarm territory. If this is because the environment changed since the time you trained the device (e.g., it is summer now, and you trained it in the winter, in a cold room), there is a simple menu command to instantly "retrain" the gadget based on the current sensor data (no need to switch to Training mode). This can also be a sign of a failing sensor (check the detailed sensor stats to see what's going on), or perhaps this is a pre-fire situation, so take immediate corrective actions.
- I added WiFi microcontroller ESP8266 (devkit v0.9 with micro-USB connector; 3-4$ on ebay) to the gadget. It communicates with the Arduino Nano via hardware serial connection. This allowed me to add WiFi connectivity to 3D Guardian, and make it a part of my home automation system (using MQTT and OpenHAB servers). Now I can send a shutdown or "clearing case" command to the printer remotely from my cellphone, using Internet. I can also monitor remotely output from all sensors, and get Alarm and Warning signals instantly.
- The device now has the ability to measure resistance of the heated bed "on the fly" (when the bed is in use), by means of a current sensor (a Hall effect based module) and a voltage sensor (Arduino ADC + P-channel MOSFET + a 2-resistor voltage divider). See #Resistance sensor below. This approach should detect early signs of a problem with the bed wiring (arcing, bad contacts, broken wire), and should allow to shut down the printer before fire starts.
- I added Panic button (instantly shuts down the printer) to the controller.
Couple of snapshots of the user interface:
Controller and power units
The controller is already soldered together, I just need to design and print an enclosure for it.
- Main components (controller unit):
- Arduino Nano (2.70$; http://www.ebay.ca/itm/152329968912)
- ESP8266 devkit v0.9 (3.30$; http://www.ebay.ca/itm/272526162060)
- 1602 LCD Board Keypad Shield (2.60$; http://www.ebay.ca/itm/1602-LCD-with-Keypad-Shield-Board-Blue-Backlight-for-Arduino-Duemilanove-Robot-/201556596016)
- DC-DC buck converter (0.75$; http://www.ebay.ca/itm/291934579477)
- Piezo buzzer: I used one from an old smoke or CO alarm. It has three connectors, and requires a transistor + 3 resistors circuit to drive it (at the resonance frequency).
- Minor parts (controller unit)
- Two transistors - almost any silicone NPN transistor will do (they handle currents <100 mA, and voltages <25V). I used the ones I happened to have.
- Panic button: a button style micro switch.
- Dual switch "Prog": used to disable the serial connection between Arduino and ESP8266, when either of the two is being re-programmed. Normally should be in closed position (otherwise the WiFi connectivity, Panic button, and thermal protection of the SSR will not work!). As a reminder, the green LED flashes when the switch is in the off position.
- Diode and LEDs - basic ones.
- Power unit:
- AC solid state relay, 380V/10A (3.50$; http://www.ebay.ca/itm/141916727762)
- 50k thermistor
This unit resides right next to the printer (enclosure). It is connected to the controller unit via 3m RS-232 (25-wire) serial cable, and also connected to nearby +12V PSU (ATX), +16V/25A PSU (for the heated bed), two stepper motors (one opens the exhaust lid, the other one opens the air inlet), and exhaust fan (12V; 2-3 wire types). A few wires go inside the printer's enclosure: +5V/GND (from the controller unit), wires to CO, smoke sensors and the enclosure thermistor ("Th1"; 50k). Two 12 awg wires go to the heated bed. Many more wires (not shown) go from the motherboard (placed inside the printer unit) inside the printer enclosure (hot end, limiting switches, 5 stepper motors, two thermistors - hot end and heated bed, two fans). The printer's display/buttons module is also placed in the printer unit. In addition, +12V/GND from the ATX PSU is sent inside the printer enclosure to power the two 5W/12V COB LED strips.
- Stepper motor driver A4988 (1.05$; http://www.ebay.ca/itm/182216985456)
- Current sensor ACS712-20A (1.80$; http://www.ebay.ca/itm/181847181426). It doesn't seem to be well designed for high amperage circuits; I will use a more expensive (5$) unit with larger screw terminals as a replacement (http://www.ebay.ca/itm/112084032519).
- MOSFET 25A board (technically not part of 3D Guardian, but a must for 3D printers): 5.40$, http://www.ebay.ca/itm/182402244152
- MOSFET N-channel transistor IRFZ44N (used to provide PWM control to a 2/3 wire fan; doesn't need a heat sink for up to 5A fan): 0.16$, http://www.ebay.ca/itm/291548962786
- Low power MOSFET P-channel transistor BS250 (used as a switch, to enable Arduino measuring the bed voltage).
- Minor components: 5.1V Zener diode, 100uF x 25V capacitor, two 50k thermistors, and a few resistors.
- Exhaust fan: 12V, 1...5A. For now I'm using an old CPU fan DELL DB9733-12HBTL (1.35A), which is pretty good in terms of air flow (28 CFM), but not very good in terms of static pressure (~300 Pa). One can find a cheap (~10$) 12V car vacuum (http://www.ebay.ca/itm/131785623948), which is rated at 5A and much higher static pressure of ~2000 Pa - I will try it as a replacement. (I suspect static pressure is much more important in my design than air flow.)
- Two identical (or almost identical) stepper motors connected in series: any 4-wire bipolar motor with the coil resistance <15 Ohm rated for at least 0.7A per coil will do. The two motors should have very similar coil resistance and the same angle per step (say, 1.8 degrees).
- Smoke detector: MQ-2 board from ebay
- CO detector: MQ-7 board from ebay
Here is the source code - it is currently an early beta so likely has some bugs. Use it at your own risk!
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:
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 (http://apcmag.com/arduino-analog-to-digital-converter-how-it-works.htm/). 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.