Environmental Diagnostics at Fort Union National Monument, Part 4: Open Source Monitoring

Intro
While formulating a monitoring methodology for the adobe ruins at Fort Union, I began to branch and extend my efforts into the late hours of the night into developing some monitoring solutions that might be just as effective–if not more effective–and significantly less cost prohibitive than the propriety solutions.

I chose the Arduino platform to explore this possibility and whipped together a prototype datalogger of my own using an Arduino Uno and a data-logger shield which incorporates both a real-time clock, for accurately time-stamped readings, and an SD card port.

Testing the Logger
Touched on in the last post, part 3, I wanted to investigate various sensor types for monitoring moisture migration patterns in the adobe ruins at Fort Union. These generally include capacitive and ambient methods, or direct and indirect methods for approximating volumetric moisture content (see part 3 for more detail on these). So, I ordered an inexpensive capacitive moisture sensor for the Arduino as well as an ambient RH/temperature sensor in a nifty sintered mesh housing to test these methods out. I later picked up an ambient RH/temp sensor (sans housing) to calibrate the sensor in the waterproof (but moisture permeable) housing.

Next, I had to locate some test material. The first test material I experimented with was simply some soil from just outside the lab. This was mainly because I was very eager to test out my logger, and would do until I found a more suitable test material. After embedding the two sensors I put the soil into a cup and sealed it with some plastic wrap (mainly to prevent the bugs in the soil from crawling out all over my desk, but this turned out to be detrimental to the test as the soil wasn’t able to dry at all). I had to wrap the pcb of the capacitive sensor with electrical tape to prevent any moisture from shorting the circuitry, as this particular sensor is not meant to be embedded past the prongs. The results of this early test were quite informative.

Arduino Logger Experiment 1
Arduino Logger Experiment 1
The drop in RH in the chart above was due me removing the plastic wrap seal from the cup of soil. It is important to note the relationship present between temperature and the electrical conductivity of water that has a significant effect on the reliability of the capacitive sensor. To be able to use this method, both variables would need to be recorded; the temperature values would in turn be used to calibrate the moisture readings.

And as it turns out, the ACL has a small supply of adobe bricks that would serve perfectly. These are not from Fort Union, but are suitable for testing methods of installing and embedding the sensors prior to receiving the actual test samples, which should be arriving to Philadelphia shortly. The adobe brick was quite large, and so to get the most out of a single brick it was cut into three segments. To prep the bricks for the sensor placement and to mimic the installation in the field, a 1″ diameter whole was drilled into the brick segments using a masonry bit at approximately 8″ in depth. One brick was chosen and the two sensors were installed. First, dry soil (removed during drilling) was packed back into the chamber to give the two prongs of the capacitive sensor direct contact. The hypothesis was that over time the loose soil will equilibrate with its surrounding adobe material acting as an accurate proxy for moisture content. An ambient RH/T sensor was added to the board itself to also log the ‘exterior’ conditions to provide a better understanding of the flux involved across/through the brick.

Embedded sensors within an adobe brick.
A test of a capacitive sensor embedded within an adobe brick compared to exterior, ambient RH/T.
Challenges & Setbacks
It is pretty obvious from the chart above that there were some serious technical difficulties encountered during the experiment. For starters, the board power cut in and out, which was most likely due a faulty power supply (which was in this case an old, wall adapter). However other issues manifested as well, especially when testing the sensor in the sintered housing that resulted in overflow errors on the processing end when coupled with the exterior ambient RH/T sensor (which is why I went with the capacitive sensor for the test in the end). Now, full disclosure, I am not a programmer extraordinaire by any means, and I’m sure there is a rather elementary reason why this happened, but I am also very limited in time and have not, unfortunately, returned to this project to debug.

Part of the reason for paying for and using proprietary logging platforms, such as Onset, etc. is the level of assurance that comes with them, especially in remote locations with extreme weather. I.e., you don’t want to install a monitoring system only to find out that 3 days after installing it, the temperature dropped, condensation formed inside the logger case and shorted your batteries…(I have recently learned this from experience).

There is certainly great promise with an open source platform, that if coupled with a weather tight enclosure (complete with desiccant packets), solar panel, and rechargeable battery could easily match the performance quality of any brand-name competition. This is with the added benefit, of course, of being able to design and tune the system to meet your exact needs, as well as be able to debug and fix with easily interchangeable parts (all found on Amazon).

If anyone reads this and also happens to understand a bit more about C/C++ please feel free comment and provide suggestions! Hopefully, I will set aside some more time in the future to return to this project. That’s all for now!

This is the script I pulled together and modified from existing code snippets around the web for my Arduino-based data logger. When reading two sensors (2-prong, capacitive sensor and an ambient RH/T DHT22) the code works fine. However, with more than 2 sensors after some time an overflow error is thrown. I’m sure there is an elementary solution to all of this, hopefully I can learn more and debug it on my own but if anyone out there has any suggestions let me know. For more information check out my post “Environmental Diagnostics at Fort Union National Monument, Part 4: Open Source Monitoring”.

#include "DHT.h"
#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include "RTClib.h"
#include <Sensirion.h>

// A simple data logger for the Arduino analog pins

// how many milliseconds between grabbing data and logging it. 1000 ms is once a second
#define LOG_INTERVAL  2000 // mills between entries (reduce to take more/faster data)

// how many milliseconds before writing the logged data permanently to disk
// set it to the LOG_INTERVAL to write each time (safest)
// set it to 10*LOG_INTERVAL to write all data every 10 datareads, you could lose up to 
// the last 10 reads if power is lost but it uses less power and is much faster!
#define SYNC_INTERVAL 10*LOG_INTERVAL // mills between calls to flush() - to write data to the card
uint32_t syncTime = 0; // time of last sync()

#define ECHO_TO_SERIAL   1 // echo data to serial port
#define WAIT_TO_START    1 // Wait for serial input in setup()


#define DHTPIN 5  
#define DHTTYPE DHT22   // DHT 22  (AM2302), AM2321

//going to use onboard LED as indicator======================================================
// the digital pins that connect to the LEDs
#define redLEDpin 9
#define greenLEDpin 8
========================================================================================

RTC_DS1307 RTC; // define the Real Time Clock object
DHT dht(DHTPIN, DHTTYPE);

// for the data logging shield, we use digital pin 10 for the SD cs line
const int chipSelect = 10;

const uint8_t mc1dataPin  =  2;
const uint8_t mc1clockPin =  3;

Sensirion tempSensor = Sensirion(mc1dataPin, mc1clockPin);

int mc2pin = 0;
int mc2val = 0;


// the logging file
File logfile;

void error(char *str)
{
  Serial.print("error: ");
  Serial.println(str);
  pinMode(redLEDpin,OUTPUT);

  // red LED indicates error
  digitalWrite(redLEDpin, HIGH);
  
 
  while(1);
}

void setup(void)
{
  pinMode(greenLEDpin,OUTPUT);
  Serial.begin(9600);
  Serial.println();
  
  // use debugging LEDs
  // pinMode(redLEDpin, OUTPUT);
  // pinMode(greenLEDpin, OUTPUT);
  
#if WAIT_TO_START
  Serial.println("Type any character to start");
  digitalWrite(LED_BUILTIN, HIGH);
  while (!Serial.available());
#endif //WAIT_TO_START

  // initialize the SD card
  Serial.print("Initializing SD card...");
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:
  pinMode(10, OUTPUT);
  
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    error("Card failed, or not present");
  }
  Serial.println("card initialized.");
  
  // create a new file
  char filename[] = "LOGGER00.CSV";
  for (uint8_t i = 0; i < 100; i++) {
    filename[6] = i/10 + '0';
    filename[7] = i%10 + '0';
    if (! SD.exists(filename)) {
      // only open a new file if it doesn't exist
      logfile = SD.open(filename, FILE_WRITE); 
      break;  // leave the loop!
    }
  }

  //didnt work. probably not necessary though...

  // String filename = String(now.day()) + "_" + String(now.month()) + "_" + String(now.year()) + ".csv";
  // char str[16] = {0};
  // filename.toCharArray(str, 16);

  // if(! SD.exists(filename))
  // {
  //   logfile = SD.open(filename, FILE_WRITE);
  // }
  
  // if (! logfile) {
  //   error("couldnt create file");
  // }
  //==========================================

  Serial.print("Logging to: ");
  Serial.println(filename);

  // connect to RTC
  Wire.begin();  
  if (!RTC.begin()) {
    logfile.println("RTC failed");
#if ECHO_TO_SERIAL
    Serial.println("RTC failed");
#endif  //ECHO_TO_SERIAL
  }
  
  //print headers to CSV=====================================
  logfile.println("millis,stamp,year,month,day,hour,minute,second,temp,RH,mc2value,rmRH,rmTemp");    
#if ECHO_TO_SERIAL
  Serial.println("millis,stamp,year,month,day,hour,minute,second,temp,RH,mc2value,rmRH,rmTemp"); 
#endif //ECHO_TO_SERIAL
 
  // If you want to set the aref to something other than 5v
  // analogReference(EXTERNAL);

dht.begin();

}




void loop(void)
{
  DateTime now;

  // delay for the amount of time we want between readings
  delay((LOG_INTERVAL -1) - (millis() % LOG_INTERVAL));
  
  // digitalWrite(greenLEDpin, HIGH);
    digitalWrite(greenLEDpin, HIGH);
  // log milliseconds since starting
  uint32_t m = millis();
  logfile.print('"');

  logfile.print(m);           // milliseconds since start
  logfile.print(",");    
#if ECHO_TO_SERIAL
  Serial.print('"');
  Serial.print(m);         // milliseconds since start
  Serial.print(",");  
#endif

  // fetch the time
  now = RTC.now();
  // log time
  logfile.print(now.unixtime()); // seconds since 1/1/1970
  logfile.print(",");
  logfile.print(now.year(), DEC);
  logfile.print(",");
  logfile.print(now.month(), DEC);
  logfile.print(",");
  logfile.print(now.day(), DEC);
  logfile.print(",");
  logfile.print(now.hour(), DEC);
  logfile.print(",");
  logfile.print(now.minute(), DEC);
  logfile.print(",");
  logfile.print(now.second(), DEC);
  logfile.print(",");
#if ECHO_TO_SERIAL
  Serial.print(now.unixtime()); // seconds since 1/1/1970
  Serial.print(",");
  Serial.print(now.year(), DEC);
  Serial.print(",");
  Serial.print(now.month(), DEC);
  Serial.print(",");
  Serial.print(now.day(), DEC);
  Serial.print(",");
  Serial.print(now.hour(), DEC);
  Serial.print(",");
  Serial.print(now.minute(), DEC);
  Serial.print(",");
  Serial.print(now.second(), DEC);
  Serial.print(",");

#endif //ECHO_TO_SERIAL

  float temperature;
  float humidity;
//  float dewpoint;
  
  // for DHT
  float h = dht.readHumidity();
  // Read temperature as Celsius (the default)
  float t = dht.readTemperature();
  // Read temperature as Fahrenheit (isFahrenheit = true)
  //  float f = dht.readTemperature(true);

//  tempSensor.measure(&temperature, &humidity, &dewpoint);

  tempSensor.measure(&temperature, &humidity);
  mc2val = analogRead(mc2pin);

  Serial.print(temperature);
  Serial.print(",");
  Serial.print(humidity);
  Serial.print(",");
//  Serial.print(dewpoint);
//  Serial.print(",");
  Serial.print(mc2val);
  Serial.print(",");
  Serial.print(h);
  Serial.print(",");
  Serial.print(t);

  logfile.print(temperature);
  logfile.print(",");
  logfile.print(humidity);
  logfile.print(",");
//  logfile.print(dewpoint);
//  logfile.print(",");
  logfile.print(mc2val);
  logfile.print(",");
  logfile.print(h);
  logfile.print(",");
  logfile.print(t);

  //close string/CSV line
  Serial.print('"');

  // Now we write data to disk! Don't sync too often - requires 2048 bytes of I/O to SD card
  // which uses a bunch of power and takes time
  if ((millis() - syncTime) < SYNC_INTERVAL) return;
  syncTime = millis();
  
  // blink LED to show we are syncing data to the card & updating FAT!
  logfile.flush();
  digitalWrite(greenLEDpin, LOW);
  
}



Leave a Comment