Project tutorial
Basement/Crawlspace Ventilation System

Basement/Crawlspace Ventilation System © GPL3+

Intelligently reduce the moisture in your basement/crawlspace to help control mildew growth and lower your heating/cooling bill.

  • 3,487 views
  • 8 comments
  • 36 respects

Components and supplies

Apps and online services

About this project

Feature List

  • Inside/outside temperature/humidity sensor.
  • Smart vent fan control with moisture comparison
  • Reduce the moisture in your basement/crawlspace
  • Help reduce mildew growth
  • Smart ventilating saves power

Parts Needed to Build the Ventilation System

Wiring Diagram

OLED Display

So Why Use the IO Expander?

  • Simpler to design
  • Off-the-shelf parts
  • No 1-Wire driver to write
  • No relay driver to write
  • No OLED display driver to write
  • No display fonts to take Arduino code space
  • No humidity sensor driver to write
  • Saves code space on Arduino; only 6106 bytes (19%)
  • Less than a day to write the code
  • Easy to wire using standard RJ11 phone cable
  • No sensor cable length issues
  • Cheaper to build than commercial systems
  • Easy to make changes to adapt to individual requirements
  • Single power supply

Build the System

Connect the Arduino Nano to the IO Expander and program it with the following code. The 6 pin header is the software serial debug port and is not needed in the final installation.

Make sure that you change the ONEWIRE_TO_I2C_ROM defined address to match your 1-Wire to I2C address.

/* IO Expander 
*  
* Basement/Crawlspace Ventilation System v1.0
* 
*/
#include <math.h>
#include <SoftwareSerial.h>
#include <avr/wdt.h>
#include "IOExpander.h"
#define FAHRENHEIT
#define ONEWIRE_TO_I2C_ROM      "i4s71"
#define HUMIDITY_SENSOR_INSIDE  "s6t1"
#define HUMIDITY_SENSOR_OUTSIDE "s8t1"
#define FAN_ON                  "r1o"
#define FAN_OFF                 "r1f"
#define ABSOLUTE_DELTA_FAN_ON   1           // Fan on if absolute humidity delta of inside >= outside 
#define ABSOLUTE_DELTA_FAN_OFF  0.5         // Fan off if absolute humidity delta of inside <= outside
#define OUTSIDE_RELATIVE_FAN_ON 88          // Fan on if outside relative humidity is <= %
#define OUTSIDE_RELATIVE_FAN_OFF 90         // Fan off if outside relative humidity is >= %
#define MINIMUM_TEMPERATURE     15          // Cycle vent on/off if outside temperature <= 15C/59F
#define FAN_ON_TIME             (20*60*1000L) // 20 min
#define FAN_OFF_TIME            (20*60*1000L) // 20 min
//#define SERIAL_DEBUG
#define SERIAL_TIMEOUT  5000                // 5 sec delay between DHT22 reads
#ifdef SERIAL_DEBUG
SoftwareSerial swSerial(8,7);
#endif
struct HS {
 float temp;
 float relative;
 float absolute;
 bool error;
};
int led = 13;
bool init_oled = true;
long ontime, offtime;
#ifdef FAHRENHEIT
#define C2F(temp)   CelsiusToFahrenheit(temp)
float CelsiusToFahrenheit(float celsius)
{
 return ((celsius*9)/5)+32;
}
#else
#define C2F(temp)   (temp)
#endif
void SerialPrint(const char* str, float decimal, char error)
{
 Serial.print(str);
 if (error) Serial.print(F("NA"));
 else Serial.print(decimal, 1);
}
float DewPoint(float temp, float humidity)
{
 float t = (17.625 * temp) / (243.04 + temp);
 float l = log(humidity/100);
 float b = l + t;
 // Use the August-Roche-Magnus approximation
 return (243.04*b)/(17.625-b);
}
#define MOLAR_MASS_OF_WATER     18.01534
#define UNIVERSAL_GAS_CONSTANT  8.21447215
float AbsoluteHumidity(float temp, float relative)
{
 //taken from https://carnotcycle.wordpress.com/2012/08/04/how-to-convert-relative-humidity-to-absolute-humidity/
 //precision is about 0.1°C in range -30 to 35°C
 //August-Roche-Magnus   6.1094 exp(17.625 x T)/(T + 243.04)
 //Buck (1981)     6.1121 exp(17.502 x T)/(T + 240.97)
 //reference https://www.eas.ualberta.ca/jdwilson/EAS372_13/Vomel_CIRES_satvpformulae.html    // Use Buck (1981)
 return (6.1121 * pow(2.718281828, (17.67 * temp) / (temp + 243.5)) * relative * MOLAR_MASS_OF_WATER) / ((273.15 + temp) * UNIVERSAL_GAS_CONSTANT);
}
void ReadHumiditySensor(HS* hs)
{
 SerialCmd("sr");
 if (SerialReadFloat(&hs->temp) &&
     SerialReadFloat(&hs->relative)) {
   //hs->dewpoint = DewPoint(hs->temp, hs->relative);
   hs->absolute = AbsoluteHumidity(hs->temp, hs->relative);
   hs->error = false;
 }  
 else hs->error = true;
 SerialReadUntilDone();
}
void setup() {
 Serial.begin(115200);
#ifdef SERIAL_DEBUG
 swSerial.begin(115200);
#endif  
 pinMode(led, OUTPUT);
 wdt_enable(WDTO_8S);
 offtime = millis() - FAN_OFF_TIME;
}
void loop() {
 HS inside, outside;
 static bool fan = false;
 static bool cycle = false;
 static long last_time = -(60L * 1000L);
 Serial.println();
 if (SerialReadUntilDone()) {
   if (init_oled) {
     if (SerialCmdNoError(ONEWIRE_TO_I2C_ROM)) {
       SerialCmdDone("st10;si;sc;sd");
       init_oled = false;
     }
   }
   // Read the humidity sensors only once a minute or they will self heat if read too quickly
   if (millis() - last_time > 60L * 1000L)
   {
     if (SerialCmdDone(HUMIDITY_SENSOR_INSIDE)) 
       ReadHumiditySensor(&inside);
     if (SerialCmdDone(HUMIDITY_SENSOR_OUTSIDE))
       ReadHumiditySensor(&outside);
     if (inside.error || outside.error) fan = false;
     else {
       if (fan) {
         if (outside.relative >= OUTSIDE_RELATIVE_FAN_OFF || inside.absolute - outside.absolute <= ABSOLUTE_DELTA_FAN_OFF)
           cycle = fan = false;
         else {
           if (cycle && outside.temp <= MINIMUM_TEMPERATURE && 
             millis() - ontime > FAN_ON_TIME) fan = false;  
         }
         if (!fan) offtime = millis();
       }
       else {
         if (outside.relative <= OUTSIDE_RELATIVE_FAN_ON && inside.absolute - outside.absolute >= ABSOLUTE_DELTA_FAN_ON) 
           cycle = fan = true;
         if (cycle && outside.temp <= MINIMUM_TEMPERATURE)
           fan = (millis() - offtime > FAN_OFF_TIME) ? true : false;        
         if (fan) ontime = millis();
       }
     }
     if (fan) SerialCmdDone(FAN_ON);
     else SerialCmdDone(FAN_OFF);
     if (SerialCmdNoError(ONEWIRE_TO_I2C_ROM)) {
       SerialCmdDone("st10;sc;sf0;sa1;sd70,0,\"INSIDE\";sd127,0,\"OUTSIDE\";sf1;sa0;sd0,12,248,\""
 #ifdef FAHRENHEIT
           "F" 
 #else          
           "C" 
 #endif          
           "\";sd0,30,\"%\";sf0;sd0,50,\"g/m\";sd20,46,\"3\";");
       SerialPrint("sf1;sa1;sd70,12,\"", C2F(inside.temp), inside.error);
       SerialPrint("\";sd70,30,\"", inside.relative, inside.error);
       SerialPrint("\";sd70,48,\"", inside.absolute, inside.error);
       SerialPrint("\";sd127,12,\"", C2F(outside.temp), outside.error);
       SerialPrint("\";sd127,30,\"", outside.relative, outside.error);
       SerialPrint("\";sd127,48,\"", outside.absolute, outside.error);
       Serial.print("\";");
       Serial.print("sf0;sa0;sd0,0,\"");
       if (fan) Serial.print("FAN");
       else Serial.print("v1.0");
       Serial.println("\";sd");
       SerialReadUntilDone();
     }
     else init_oled = true;
     last_time = millis();
   }
 }
 else {
   digitalWrite(led, HIGH);
   delay(500);
   digitalWrite(led, LOW);
   delay(500);
   init_oled = true;
 }
 wdt_reset();
}

Note: If you use the USB port to program the Arduino Nano you must disconnect it from the IO Expander since it is also using the same single serial port, instead if you want to debug use the ICSP port to program the ATmega328P. To enable the software debugging port uncomment the SERIAL_DEBUG definition.

Connect the 110VAC wire to both fans.

Drill a 7/16" and 9/16" hole on either side of the enclosure for the PG7 and PG9. Use a dremel tool to enlarge the holes slightly until the gland fits snug. The PG7 will feed in the 12VDC input voltage, and the PG9 for the sensors and fans.

Find a vent that is opened and not blocked. This will be our exhaust that we will blow the basement/crawlspace air out. Make sure all other vents on the other side are open since these will become your air intake. Close the adjacent air-vents so that you create a regional instead of a local air flow across your entire basement/crawlspace.

Mount the fans on the inside of the air vent using tie wraps. Make sure you have the fans pointing the right direction to blow air out.

Find an existing access point and thread the outside humidity sensor wire into the inside. Make sure the humidity sensor is far enough away from the house and any obstruction so that you are accurately measuring the ambient temperature/humidity. Verify your readings against your locally published weather reports.

Wire the outside humidity sensor into the keystone jack and enclosure and mount it on the inside.

Wire the inside humidity sensor into the keystone jack and enclosure and mount it on the inside. A central location or area that needs additional moisture control is preferable.

Connect the 50ft RJ11 wires to the humidity sensors and run the wires with the fan wire to an available access point where the control enclosure will be installed..

Connect all the wires and assemble/feed all the parts into the control enclosure. If your 50ft RJ11 wires come with pre-crimped connectors you will have to cut them off to feed the wires through the gland and crimp new connectors.

Test the system and make sure everything is operating correctly. To test the relay and fans disconnect the Arduino from the IO Expander and connect it directly to your computer to manually control it. Once you have verified that everything is operating, assemble all the parts into the enclosure using double sided tape and packing foam to secure your boards, and enjoy the benefits and savings of your Smart Moisture Control Ventilation System

Update 2019

After running the ventilation system in my crawlspace for the last couple of months with zero hangs and with a peak relative humidity of greater than 95% after the leak from my hot water heater it has successfully dropped the relative humidity to less than 50%. The ventilation system is an on going control system that works!

Code

Basement/Crawlspace Ventilation SystemC/C++
Use the Arduino Nano to create a Smart Ventilation System
/* IO Expander 
 *  
 * Basement/Crawlspace Ventilation System v1.0
 * 
 */

#include <math.h>
#include <SoftwareSerial.h>
#include <avr/wdt.h>
#include "IOExpander.h"

#define FAHRENHEIT
#define ONEWIRE_TO_I2C_ROM      "i4s71"
#define HUMIDITY_SENSOR_INSIDE  "s6t1"
#define HUMIDITY_SENSOR_OUTSIDE "s8t1"
#define FAN_ON                  "r1o"
#define FAN_OFF                 "r1f"
#define ABSOLUTE_DELTA_FAN_ON   1           // Fan on if absolute humidity delta of inside >= outside 
#define ABSOLUTE_DELTA_FAN_OFF  0.5         // Fan off if absolute humidity delta of inside <= outside
#define OUTSIDE_RELATIVE_FAN_ON 88          // Fan on if outside relative humidity is <= %
#define OUTSIDE_RELATIVE_FAN_OFF 90         // Fan off if outside relative humidity is >= %
#define MINIMUM_TEMPERATURE     15          // Cycle vent on/off if outside temperature <= 15C/59F
#define FAN_ON_TIME             (20*60*1000L) // 20 min
#define FAN_OFF_TIME            (20*60*1000L) // 20 min

//#define SERIAL_DEBUG
#define SERIAL_TIMEOUT  5000                // 5 sec delay between DHT22 reads

#ifdef SERIAL_DEBUG
SoftwareSerial swSerial(8,7);
#endif

struct HS {
  float temp;
  float relative;
  float absolute;
  bool error;
};

int led = 13;
bool init_oled = true;
long ontime, offtime;

#ifdef FAHRENHEIT
#define C2F(temp)   CelsiusToFahrenheit(temp)
float CelsiusToFahrenheit(float celsius)
{
  return ((celsius*9)/5)+32;
}
#else
#define C2F(temp)   (temp)
#endif

void SerialPrint(const char* str, float decimal, char error)
{
  Serial.print(str);
  if (error) Serial.print(F("NA"));
  else Serial.print(decimal, 1);
}

float DewPoint(float temp, float humidity)
{
  float t = (17.625 * temp) / (243.04 + temp);
  float l = log(humidity/100);
  float b = l + t;
  // Use the August-Roche-Magnus approximation
  return (243.04*b)/(17.625-b);
}

#define MOLAR_MASS_OF_WATER     18.01534
#define UNIVERSAL_GAS_CONSTANT  8.21447215

float AbsoluteHumidity(float temp, float relative)
{
  //taken from https://carnotcycle.wordpress.com/2012/08/04/how-to-convert-relative-humidity-to-absolute-humidity/
  //precision is about 0.1°C in range -30 to 35°C
  //August-Roche-Magnus   6.1094 exp(17.625 x T)/(T + 243.04)
  //Buck (1981)     6.1121 exp(17.502 x T)/(T + 240.97)
  //reference https://www.eas.ualberta.ca/jdwilson/EAS372_13/Vomel_CIRES_satvpformulae.html    // Use Buck (1981)
  return (6.1121 * pow(2.718281828, (17.67 * temp) / (temp + 243.5)) * relative * MOLAR_MASS_OF_WATER) / ((273.15 + temp) * UNIVERSAL_GAS_CONSTANT);
}

void ReadHumiditySensor(HS* hs)
{
  SerialCmd("sr");
  if (SerialReadFloat(&hs->temp) &&
      SerialReadFloat(&hs->relative)) {
    //hs->dewpoint = DewPoint(hs->temp, hs->relative);
    hs->absolute = AbsoluteHumidity(hs->temp, hs->relative);
    hs->error = false;
  }  
  else hs->error = true;
  SerialReadUntilDone();
}

void setup() {
  Serial.begin(115200);
#ifdef SERIAL_DEBUG
  swSerial.begin(115200);
#endif  
  pinMode(led, OUTPUT);
  wdt_enable(WDTO_8S);
  offtime = millis() - FAN_OFF_TIME;
}

void loop() {
  HS inside, outside;
  static bool fan = false;
  static bool cycle = false;
  static long last_time = -(60L * 1000L);

  Serial.println();
  if (SerialReadUntilDone()) {
    if (init_oled) {
      if (SerialCmdNoError(ONEWIRE_TO_I2C_ROM)) {
        SerialCmdDone("st10;si;sc;sd");
        init_oled = false;
      }
    }

    // Read the humidity sensors only once a minute or they will self heat if read too quickly
    if (millis() - last_time > 60L * 1000L)
    {
      if (SerialCmdDone(HUMIDITY_SENSOR_INSIDE)) 
        ReadHumiditySensor(&inside);
  
      if (SerialCmdDone(HUMIDITY_SENSOR_OUTSIDE))
        ReadHumiditySensor(&outside);
  
      if (inside.error || outside.error) fan = false;
      else {
        if (fan) {
          if (outside.relative >= OUTSIDE_RELATIVE_FAN_OFF || inside.absolute - outside.absolute <= ABSOLUTE_DELTA_FAN_OFF)
            cycle = fan = false;
          else {
            if (cycle && outside.temp <= MINIMUM_TEMPERATURE && 
              millis() - ontime > FAN_ON_TIME) fan = false;  
          }
          if (!fan) offtime = millis();
        }
        else {
          if (outside.relative <= OUTSIDE_RELATIVE_FAN_ON && inside.absolute - outside.absolute >= ABSOLUTE_DELTA_FAN_ON) 
            cycle = fan = true;
          if (cycle && outside.temp <= MINIMUM_TEMPERATURE)
            fan = (millis() - offtime > FAN_OFF_TIME) ? true : false;        
          if (fan) ontime = millis();
        }
      }
  
      if (fan) SerialCmdDone(FAN_ON);
      else SerialCmdDone(FAN_OFF);
  
      if (SerialCmdNoError(ONEWIRE_TO_I2C_ROM)) {
        SerialCmdDone("st10;sc;sf0;sa1;sd70,0,\"INSIDE\";sd127,0,\"OUTSIDE\";sf1;sa0;sd0,12,248,\""
  #ifdef FAHRENHEIT
            "F" 
  #else          
            "C" 
  #endif          
            "\";sd0,30,\"%\";sf0;sd0,50,\"g/m\";sd20,46,\"3\";");
        SerialPrint("sf1;sa1;sd70,12,\"", C2F(inside.temp), inside.error);
        SerialPrint("\";sd70,30,\"", inside.relative, inside.error);
        SerialPrint("\";sd70,48,\"", inside.absolute, inside.error);
        SerialPrint("\";sd127,12,\"", C2F(outside.temp), outside.error);
        SerialPrint("\";sd127,30,\"", outside.relative, outside.error);
        SerialPrint("\";sd127,48,\"", outside.absolute, outside.error);
        Serial.print("\";");
        Serial.print("sf0;sa0;sd0,0,\"");
        if (fan) Serial.print("FAN");
        else Serial.print("v1.0");
        Serial.println("\";sd");
        SerialReadUntilDone();
      }
      else init_oled = true;

      last_time = millis();
    }
  }
  else {
    digitalWrite(led, HIGH);
    delay(500);
    digitalWrite(led, LOW);
    delay(500);
    init_oled = true;
  }
  wdt_reset();
}

Schematics

Basement/Crawlspace Ventilation System
Use the Arduino Nano to create a Smart Ventilation System.
Wiring ic1r9d54zn

Comments

Similar projects you might like

Automation of ventilation system

Project tutorial by Team iMinds-iot

  • 2,831 views
  • 0 comments
  • 7 respects

The hydroMazing Smart Garden System

Project tutorial by Cory Potter

  • 23,883 views
  • 11 comments
  • 110 respects

Home Plant Watering System

Project tutorial by Alexander

  • 16,098 views
  • 2 comments
  • 45 respects

Secure Watering System

Project in progress by foss-he-men

  • 2,637 views
  • 0 comments
  • 4 respects

Home Automation system using Raspberry Pi

Project tutorial by Christian Kratky

  • 129,640 views
  • 34 comments
  • 418 respects

Herb Box Eco System

Project tutorial by Walter Heger

  • 39,226 views
  • 24 comments
  • 259 respects
Add projectSign up / Login