Introduction: Crawl Space Monitor (aka: No More Frozen Pipes!!)

Water to my house comes from my well through an unheated crawl space. All of the kitchen and bathroom plumbing runs through this space as well. (Indoor plumbing was a slap-on afterthought in the mid 70s on this house!) I've been using heat lamps on "stock tank" thermostatic plugs to maintain a temperature above freezing. There were a couple significant problems with this arrangement:

1 - No visibility. The first indication of burned out bulbs is frozen pipes!
2 - Sometimes the plugs wouldn't shut off. That made for nasty surprises come the electric bill.
3 - No granularity. I kept 3 bulbs "online"(750 watts total) and it was an all or nothing solution. (2 bulbs wouldn't always handle it.)

After being introduced to Arduino, and seeing some of the things other people were doing with it, I decided I would give it a whirl. I will admit right out of the chute that I have shamelessly snagged and tweaked sample code from other people's projects to make this work, though ultimately I have re-written most everything.

Initially, I built this "WiFi Weather Station" that I found on and modified it. Instead of updating a web site, I used Amazon Web Services to send me SMS status updates. I also added the control of some 110V relays ( I then got "smart" and decided to "ruggedize" it -- well -- something shorted something and I got a puff of the magic blue smoke. Everything fried... Not having another CC3000 WiFi breakout, I did things differently this time. I built it to be interactively monitored via serial interface and then added an EZ-Link Bluetooth FTDI interface. (No more dragging the laptop under the house for software updates!!!) I also built a Python interface that connects to the unit via the Bluetooth, queries it regularly, and displays status information on my Mac. (There is also a "human interface" that can be accessed by any terminal emulation software.)

As a result of the re-write and removing all the WiFi and RTC code, the project has shrunk in size from over 29K to barely 10K. It has also improved reliability to the extent that the hardware watchdog hasn't triggered at all in the couple weeks it's been running and I've been tweaking.

2/17/16 Update/note: In an attempt to get proper formatting on some of the code (especially the indenting of the Python code), things went from ugly to unusable. I am sure the issue is on my end somewhere, and I will endeavor to figure it out. Until then, I have added links to the code files through DropBox. They should be accessible to anyone. If not, please let me know so I can get them to you another way!

Step 1: Problems to Solve

The system needed to do the following things for me:

1 - monitor the temperature in the crawl space.
2 - turn on heat lamps as needed to maintain the temperature above freezing.
3 - when not operating, periodically test the bulbs and give me visibility of their status.
4 - give me visibility of the temperature and system status, including:
- is the system running?
- what is the temperature NOW?
- what is the coldest the temperature got?
- how many bulbs ran?
- how many bulbs test good?
- what is my total time in "light minutes" (aka "burn time")?
5 - do all of the above without me needing to crawl under the house!!!

I decided that the easiest way to test for bulb operation was with a light sensor.

Some other issues I wanted to address was the cycle time on the lights. Too slow, and I am burning unnecessary electricity. Too fast, and I run the risk of burning them out from all the switching on & off with the related heating up and cooling off.

Step 2: The Hardware

2 250 watt heat lamps
1 500 watt work lamp (one of my heat lamps disappeared, so this is a stand-in)
Arduino Uno
DHT22 Temperature/Humitidy sensor
GA1A12S202 light sensor
PowerSwitch 110V relays
Bluefruit EZ-Link Serial Interface & Programmer
High-tech case (sandwich sized Rubbermaid tub)
Cable gland
1/2 sized breadboard
Acrylic plate for breadboard and Arduino
Assorted jumper wires.
Coleman 5-outlet "workshop strip"

I also used an Adafruit Trinket as a hardware watchdog, but it has proven to be unneeded (jinx, of course!) and I wrote a separate instructable about it so I won't repeat that here. The Coleman pigtail was a a nice find, as it gave me 4 outlets for my heat lamps PLUS an outlet for the Arduino power supply without any additional splitters or power strips involved. Rated at a full 15 Amps with a switch and an internal breaker, it could handle everything I could pull through a single outlet.

Step 3: The Approach

While the system is application built to sit around waiting, and to do some things relatively slowly, what I didn't want to do was build a system where the controller was sitting in delay() cycles being unresponsive. I also wanted to be able to change the configuration parameters as close to on-the-fly as I could get -- certainly not in a manner that required re-writing code or doing mass search-and-replace operations on the source.

I found Bill Earl's most excellent articles on "Multitasking the Arduino" (start here: and got busy. By creating "timer" and "heater" classes I was able to do all the timing functions I wanted without using delay() (with only a couple exceptions) and configure the bulbs ("heaters") with a single line of code for each one.

Step 4: Wiring It Up.

The Fritzing diagram does not include the Bluefruit EZ-Link

Arduino 5V & Ground to the breadboard bus
DHT22 pin 1 to 5V bus
DHT22 pin 2 to Arduino pin 7
DHT22 pin 4 to Ground bus
10K resistor between DHT22 pins 1 and 2
GA1A12S202 VCC pin to 5V bus
GA1A12S202 GND pin to Ground bus
GA1A12S202 OUT pin to Arduino A0
Arduino 3V pin to Arduino AREF pin
Relay Ground leads to Ground bus
Relay 1 power lead to Arduino A1
Relay 2 power lead to Arduino A2
Relay 3 power lead to Arduino A3
Relay 4 power lead to Arduino A4

Most of these connections can be rearranged as you want. The only one that is critical is the OUT lead on the light sensor needs to go to an analog pin. This pin-out will work with my code as-written.

If you are using the hardware watchdog, you will see my code puts the heartbeat out on Arduino pin 2.

Step 5: The Arduino Code, Main Sketch

Step 6: Notes on the Code

The following lines of code create the instances of the heater and define the operating parameters:

// Heater(relayPin, onTemp(f), offTemp(f), minMinutes, testInterval(minutes), luxDelta)
Heater heater1 = Heater(A1, 38, 43, 20, 1440, 5);
Heater heater2 = Heater(A2, 36, 41, 20, 1440, 5);
Heater heater3 = Heater(A3, 34, 39, 20, 1440, 5);
Heater heater4 = Heater(A4, 32, 37, 20, 1440, 5);

(And yes, I defined all 4 heaters even though I am only running 3 at the moment. I would still need to get another relay, but then adding the 4th heater would be as simple as plugging it in.)

I stagger their trigger temperatures, starting at 38 degrees for the first and ending at 32 for the non-existent 4th. One of the things I found when I first started cobbling this together was that I needed to give a range on the temperature as well as define a minimum "burn time", or I was cycling lights on and off like crazy. Here I give each of them a 5 degree spread as well as a 20 minute minimum burn time. I set the test interval to 24 hours and set 5 lux as the minimum light reading I needed to determine a bulb was still working. Pretty much everything that needs configuring is right here in these 4 lines of code.

Step 7: The Arduino Code, Classes

I created 3 classes for this project. They were "timer", "heater" and "accumulator". With a little more thought I should be able to fold accumulator into timer, but I haven't yet. Here they are in full:




Step 8: Monitoring the System

I created a single interface to two separate monitors. It is an interactive session over the serial console. In my case I am using the Bluefruit EZ-Link so I can access the system without crawling under the house or trying to snake a USB cable in between the floor joists! An added benefit of the EZ-Link is that I can upload new program code to the Arduino over the Bluetooth as well.

The "human" interface can be accessed (Bluetooth or physical cable) with any terminal emulation software, including the Arduino IDE's serial monitor. When you initially connect, there is no response, but the key presses "u" (for "update") and "t" (for "test") will get you the output shown in the screen shot. "m" ("monitor") and "s" ("sys check") get you the same data but in a much less readable format. These are intended to be "scraped" by another program for automatic display. I put together a Python script that does just that. Any other key garners the error message displayed.

You will see a value for "burn time" -- think of this like "bulb minutes" -- 1 bulb for 10 minutes = 10 minutes, 3 bulbs for 10 minutes = 30 minutes.

Step 9: The Python Script

Step 10: Still to Do...

It may not be pretty, or perfect, but it's effective and proving itself to be reliable. AND, I have not had any frozen pipe issues yet this winter!!!

I have a hit-list of things to do. Of course, now that it works, I may or may not ever get around to accomplishing most of these items:

Get Bluetooth running on one of my Raspberry Pi's so I can create a dedicated monitor.
Learn some more Python - then clean up the Python interface. That separation of elements is not on purpose and I don't understand why it is there.
Add an interface to something like Adafruit's IO service so I can monitor it from anywhere.
Add text message alerting.
Move to a smaller controller (possibly a Metro Mini or a Trinket Pro?), less expensive relays, and better packaging.
Get it off a breadboard and onto a "Perma Proto" board.
Configuration parameters in EEPROM.
A more granular interface that will indicate -which- bulbs are good, and possibly even burn time for the individual bulbs.

As I get them done I will come back and update this Instructable.

Step 11: Update 3/16, "permanent" Build

Getting a good break in the cold weather, I have retrieved the unit and moved it to a smaller controller (I had intended to use a Trinket Pro, but had an Adafruit Metro Mini sitting around unclaimed by any other project), soldered it up on a Perma-Proto board, and put it all in a better case. Based on how reliable it has been, I did not put the hardware watchdog back on it. I am still only using 3 lamps/relays where the system will handle 4. The Bluetooth module is on a soldered header, so can be removed if I need it somewhere else. There were no code changes necessary to move to the new controller - a simple recompile and load had me up and running in a matter of minutes. (The Metro Mini has an identical pinout as the Arduino Uno and is also an ATMega328 processor.)

Step 12: Update 12/1/2018 - Welcome to the IoT!

The system has worked flawlessly for us. After two rather severe winters, NO frozen pipes. In fact, the system was able to maintain the pipes without ever burning more than 2 bulbs. Having the 3rd bulb online was nice insurance, but we have not ever needed it to-date.

Coming into year 3 for the system, the Bluetooth module failed. We also built a new house, so the monitoring system is well outside of Bluetooth range. (The old house is staying up for a while, but not forever.) In the intervening time, I have been doing a lot with the ESP8266 WiFi enabled processor; both in Adafruit Feather format and in the open-source "NodeMCU" format. The NodeMCU can generally be found on Amazon for around $5 - much less if you buy in bulk and/or from someone like AliExpress.

This new version maintains the serial interface, so it could still be used with a Bluetooth module or direct USB serial connection and the previous python script, however, the new version has a web-page interface. As written, it includes the following feature:

A WiFi network manager to eliminate hard-coding WiFi credentials.

The ability to update the firmware over-the-air using the Arduino IDE (as long as you are on the same WiFi network -- note that after doing a USB upload to the device, a reset is needed before OTA updates will work). PLEASE change the OTA password on line 6 to be unique to you!!

A web page that displays the same data the python script does, with an automatic refresh every minute. I did not put any kind of security on the page, because it is display-only.

You can find the new code here. Do note that the pin names change when moving to the NodeMCU.