Humidity of the basement

The basement often smells a bit ‘humid’. To get a better idea of the exact degree of humidity, I want to perform continuous measurements during an entire year. Hopefully this will give me some insights about this ‘problem’. Based on the results I will:

  • do nothing
  • start an automated ventilation project
  • start an automated ventilation project, combined with the installation of a ‘air-dryer’

The measurements will be done using an Arduino Uno, an Ethernet shield (holding a SD card slot) and some sensors to measure:

  • inside temperature and humidity
  • outside temperature and humidity
  • inside floor and wall temperature

Arduino code:

/*
   Measure temperature and humidity over serveral sensors.
   Write about every x minutes the current readings to a text file on SD card.
   Each month, a new file will be generated.
   Allow to read current data via a webbrowser.
   Allow to download the log files via a webbrowser --> Todo.

   Format: comma-delimitted flat file
   Columns:
   - currentDateTime (YYYYMMDDHHMMSS)
   - Sensor 1 temperature (degrees Celcius) - Basement
   - Sensor 1 humidity (% relative humidity) - Basement
   - Sensor 2 temperature (degrees Celcius) - Garage --> Todo after POC (copy paste code)
   - Sensor 2 humidity (% relative humidity) - Garage --> Todo after POC (copy paste code)
   - Sensor 3 temperature (degrees Celcius) - Outdoor --> Todo after POC (copy paste code)
   - Sensor 3 humidity (% relative humidity) - Outdoor --> Todo after POC (copy past code)
   - API reading (extract from http://www.meteo.be/) temperature (degrees Celcius) official weather station nearby --> To be implemented. Not enough space on Uno.
   - API reading (extract from http://www.meteo.be/) humidity (% relative humidity) official weather station nearby --> To be implemented. Not enough space on Uno.
   - Sensor 4 temperature (degrees Celcius) - Floormounted (insulated)
   - Sensor 5 temperature (degrees Celcius) - Wallmounted (insulated)

   The circuit
   - DS1307 --> Arduino Uno
     - GND --> GND
     - VCC --> 3V
     - SDA --> A4
     - SCL --> A5
   - Sensor SHT15 #1 --> Arduino Uno:
     - GND --> GND
     - VCC --> VCC 5V (NOT 3V)
     - SDA --> A2
     - SCL --> A3
   - Sensor DS18B20 #1 --> Arduino Uno:
     - GND --> GND
     - VCC --> VCC 5V
     - SDA --> 4.7Kohm --> VCC 5V
           --> D8 (Shared data with other DS18B20 #2)
   - Sensor DS18B20 #2 --> Arduino Uno:
     - GND --> GND
     - VCC --> VCC 5V
     - SDA --> 4.7Kohm --> VCC 5V
           --> D8 (Shared data with DS18B20 #1)
   - Status LED 1: To be completed.
   - Status LED 2: To be completed.
   - Status LED 3: To be completed.

   Created 2017/09/23
   By Maarten Van Damme
   Modified 2017/10/30
   By Maarten Van Damme

   http://projects.familievandamme.be

   Based on samples:
   - https://www.arduino.cc/en/Tutorial/Datalogger
   - http://henrysbench.capnfatz.com/henrys-bench/arduino-sensors-and-input/arduino-tiny-rtc-d1307-tutorial/

   ########################################################################################################################TODO: malloc function implement to free up memory of char arrays.

*/

#include <SPI.h> //Required for SD and Ethernet
#include <SD.h> // Required for SD
#include <Wire.h> //Required for RTC
#include <RTClib.h> // Required for RTC
#include <SHT1X.h> //Required for DHT15
#include <OneWire.h> //Required for DS18B20
#include <DallasTemperature.h> //Required for DS18B20
#include <Ethernet.h> //Required for Webserver

//#include <dht.h> //Required for DHT11

//#define DHT11_PIN 7
#define RTCStatusLedGreen 2
#define RTCStatusLedRed 3
#define SDStatusLedGreen 5
#define SDStatusLedRed 6
#define WriteStatusLedBlue 7
#define DS18B20_ONE_WIRE_PIN 8 //Multiple sensors can be handled on the same pin
#define WriteStatusLedRed 9

#define chipSelectSD 4 //to select SD card
//#define chipSelectEthernet 10 // to select Ethernet

#define cycleTime 10

RTC_DS1307 RTC;//Real time clock
//dht DHT;
//Create an instance of the SHT1X sensor
SHT1x SF_SHT15_1(A2, A3);//Data, SCK --> Ensure the DHT15 gets 5V, 3.3V does weird things!!!
//Setup a oneWire instance to communicate with any OneWire devices
OneWire DS18B20_ONE_WIRE(DS18B20_ONE_WIRE_PIN);
DallasTemperature DS18B20_SENSORS(&DS18B20_ONE_WIRE);

//Setup webserver
byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
IPAddress ip(192, 168, 1, 177);
EthernetServer server(80);

//Keep track of any errors encountered along the way
boolean SDCardWriteErrors = false;
boolean readyForNextIteration = true;
boolean firstRun = true;

void setup() {

  //Define status LEDs
  pinMode(RTCStatusLedRed, OUTPUT);
  pinMode(RTCStatusLedGreen, OUTPUT);
  pinMode(SDStatusLedRed, OUTPUT);
  pinMode(SDStatusLedGreen, OUTPUT);
  pinMode(WriteStatusLedBlue, OUTPUT);
  pinMode(WriteStatusLedRed, OUTPUT);

  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  // wait for serial port to connect. Needed for native USB port only
  while (!Serial) {
    ; //Just wait until done (only works on Leonardo, not on Uno)
  }
  Wire.begin();
  RTC.begin();
  // Check to see if the RTC is keeping time.  If it is, load the time from your computer.
  if (! RTC.isrunning()) {
    Serial.println("RTC is not running!");
    digitalWrite(RTCStatusLedRed, HIGH);
    // Set the current time to the current compilation time.
    RTC.adjust(DateTime(__DATE__, __TIME__));
  }
  else
  {
    digitalWrite(RTCStatusLedGreen, HIGH);
  }

  // Verify if the SD card is present and can be initialized
  Serial.print("Initializing SD card...");
  if (!SD.begin(chipSelectSD)) {
    Serial.println("SD card init failed, or not present");
    digitalWrite(SDStatusLedRed, HIGH);
    stop();//No use continuing if things go bad at startup
  }
  else
  {
    Serial.println(F("SD card initialized."));
    digitalWrite(SDStatusLedGreen, HIGH);
  }
  DS18B20_SENSORS.begin();

  Ethernet.begin(mac, ip);
  server.begin();
  Serial.print(F("server is at "));
  Serial.println(Ethernet.localIP());
}

void loop() {

  DateTime currentDateTime = RTC.now();

  //char dht11_1_temperature[7];
  //char dht11_1_humidity[7];
  char dht15_1_temperature[7];
  char dht15_1_humidity[7];
  char DS18B20_1_temperature[7];
  char DS18B20_2_temperature[7];
  char timestamp_YYYYMMDDHHMMSS[20];

  if (currentDateTime.minute() % cycleTime == 0 and readyForNextIteration or firstRun) //Run every x minutes ONCE, excluding first run
  {
    //Read DHT11
    //int chk = DHT.read11(DHT11_PIN);

    DS18B20_SENSORS.requestTemperatures();

    char csvLine[100] = "";

    //dtostrf(DHT.temperature, 5 , 2, dht11_1_temperature);
    //dtostrf(DHT.humidity, 5 , 2, dht11_1_humidity);
    dtostrf(SF_SHT15_1.readTemperatureC(), 5 , 2, dht15_1_temperature);
    dtostrf(SF_SHT15_1.readHumidity(), 5 , 2, dht15_1_humidity);
    dtostrf(DS18B20_SENSORS.getTempCByIndex(0), 5 , 2, DS18B20_1_temperature);
    dtostrf(DS18B20_SENSORS.getTempCByIndex(1), 5 , 2, DS18B20_2_temperature);

    char timestamp_YYYYMM[6];//To construct file name.
    //consider using dtostrf instead of sprintf
    sprintf(timestamp_YYYYMMDDHHMMSS, "%04d/%02d/%02d %02d:%02d:%02d",
            currentDateTime.year(),
            currentDateTime.month(),
            currentDateTime.day(),
            currentDateTime.hour(),
            currentDateTime.minute(),
            currentDateTime.second());
    //consider using dtostrf instead of sprintf
    sprintf(timestamp_YYYYMM, "%04d%02d",
            currentDateTime.year(),
            currentDateTime.month());

    //Constructing output
    strcat(csvLine, timestamp_YYYYMMDDHHMMSS);
    strcat(csvLine, ",");
    strcat(csvLine, dht15_1_temperature);
    strcat(csvLine, ",");
    strcat(csvLine, dht15_1_humidity);
    strcat(csvLine, ",");
    strcat(csvLine, DS18B20_1_temperature);
    strcat(csvLine, ",");
    strcat(csvLine, DS18B20_2_temperature);

    Serial.println(csvLine);

    char csvFileName[15];
    //consider using dtostrf instead of sprintf
    sprintf(csvFileName, "%s%s%s", "H", timestamp_YYYYMM, ".txt");
    //Open a file on the SD card. If the file is opened for writing, it will be created if it doesn't already exist
    // /* 
    //This part works perfectly as long as the ethernet code is not active. 
    //Probably it has something to do with the SS pins ... and bad support of the current arduino clone shield

    File csvFile = SD.open(csvFileName, FILE_WRITE);

    // If the CSV file is available, write data to it
    if (csvFile) {
      csvFile.println(csvLine);
      csvFile.close();
      led_blink(WriteStatusLedBlue);
      Serial.println(csvLine);//For testing only
    }
    // If sh*t hits the fan...
    else {
      Serial.print(F("\nCannot access file or SD card. "));
      Serial.print(F("Ensure a valid file name is used. e.g. FAT16 can only handle 8.3 type file names! "));
      Serial.print(F("Current file name: " ));
      Serial.print(csvFileName);
      Serial.println(F("."));
      SDCardWriteErrors = true;
    }
    // */
    //Ensure the code doesn't run multiple times within the same minute
    readyForNextIteration = false;
    firstRun = false;
  }
  else
  {
    //Todo: implement trigger events such as opening doors...
  }

  //Enable next iteration one the 10 minute cycle has passed
  if (currentDateTime.minute() % cycleTime != 0)
  {
    readyForNextIteration = true;
  }

  // listen for incoming clients
  EthernetClient client = server.available();
  if (client) {
    // A http request ends with a blank line
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {

        char c = client.read();
        Serial.write(c);
        // if you've gotten to the end of the line (received a newline
        // character) and the line is blank, the http request has ended,
        // so you can send a reply
        if (c == '\n' && currentLineIsBlank) {
          // send a standard http response header
          client.println(F("HTTP/1.1 200 OK"));
          client.println("Content-Type: text/html");
          client.println("Connection: close");  // Ensure the connection will be closed after completion of the response
          client.println("Refresh: 60");
          client.println();
          client.println("<!DOCTYPE HTML>");
          client.println("<html>");

          client.println(F("<h1>Basement humidity measurement.</h1>"));
          client.println(F("<p>Last measurement: <b>"));
          client.println(timestamp_YYYYMMDDHHMMSS);
          client.println(F("</b>"));
          client.println(F("<p>"));
          client.println(F("<p>DHT15 - Temperature: <b>"));
          client.println(dht15_1_temperature);
          client.println(F("&deg;C</b>"));
          client.println(F("<p>"));
          client.println(F("<p>DHT15 - Humidity: <b>"));
          client.println(dht15_1_humidity);
          client.println(F("%RH</b>"));
          client.println(F("<p>"));
          client.println(F("<p>DS18B20 #1 - Temperature: <b>"));
          client.println(DS18B20_1_temperature);
          client.println(F("&deg;C</b>"));
          client.println(F("<p>"));
          client.println(F("<p>"));
          client.println(F("<p>DS18B20 #2 - Temperature: <b>"));
          client.println(DS18B20_2_temperature);
          client.println(F("&deg;C</b>"));
          client.println(F("<p>"));
          client.println(F("</html>"));
          break;
        }
        if (c == '\n') {
          // you're starting a new line
          currentLineIsBlank = true;
        } else if (c != '\r') {
          // you've gotten a character on the current line
          currentLineIsBlank = false;
        }
      }
    }
    // give the web browser time to receive the data
    delay(1);
    // close the connection:
    client.stop();
    Serial.println("client disconnected");
  }

  //Indicate if along the way something went wrong since the last 'reboot'.
  digitalWrite(WriteStatusLedRed, SDCardWriteErrors);
}

//Turn led on and off rapidly few times
void led_blink(int ledPin)
{
  for (int i = 0; i < 20; i++)
  {
    digitalWrite(ledPin, HIGH);
    delay(100);
    digitalWrite(ledPin, LOW);
    delay(100);
  }
}

//Create endless loop
void stop()
{
  while (true)
  {
    delay(1000);
  }
}

// Print DS18B20 serial number (handy when multiple sensors are connected to the same data line).
/*
  void printSerialNumber(byte *addr) {
  byte i;
  for ( i = 0; i < 8; i++) {
    Serial.print(F("0x"));
    if (addr[i] < 16) {
      Serial.print('0');
    }
    Serial.print(addr[i], HEX);
    if (i < 7) {
      Serial.print(F(", "));
    }
  }
  }
*/

 

Sources:

  • Data logger: https://www.arduino.cc/en/Tutorial/Datalogger
  • Tiny RTC: http://henrysbench.capnfatz.com/henrys-bench/arduino-sensors-and-input/arduino-tiny-rtc-d1307-tutorial/

 

Leave a Reply

Your email address will not be published.