Data logger shield

Adafruit Ethernet, Motor, Proto, Wave, Datalogger, GPS Shields - etc!

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
knelson
 
Posts: 8
Joined: Fri May 16, 2014 1:23 pm

Data logger shield

Post by knelson »

Hello-

I am using your assembled data logging shield on 10 weather stations and am having some idiosyncratic issues. I am connecting them to the Sparkfun Arduino Pro 5v. For two months during summer 2014, they worked great. This year, 5 out of 10 do not log any data.

Any help or advice would be helpful!

FYI: New SD cards were purchase at the beginning of June and they have all been formatted using Fat 32.

Below are the problems (summarized for the most recent 10 day measurement cycle):
[*] 2 out of 10 don't don't create any logger files. The SD led flashes slowly and consistently in Red.
[*] 1/10 created a file but only logged data for 5 out of 10 days. The SD led flashes fast and consistent in red.
[*] 1/10 created 99 blank logger files but no LEDs were flashing.
[*]3/10 didn't log any files but no led's were blinking.
[*]2/10 do not maintain the correct date. Nearly all shields required multiple upload cycles to get the data correct. (Batteries were removed for several minutes in between each upload).
[*]1/10 is working great.

The setup is as follows:
[*]Arduino Pro 5v
[*]Adafruit Datalogger shield
[*]Large solar panel, Powerboost 500, LiPo charger, 2500 mah LiPo batteries (All from Adafruit)
[*]SHT-15/45 temp/humidity sensor
[*]2 thermistors
[*]Watermark 200ss soil moisture senor
[*][New this year]Fascinating electronics anemometer
[*][New this year]A diode for solar radiation. http://www.instesre.org/construction/py ... ometer.htm
[*][*]The Adafruit ADS1115 breakout board to increase the resolution of the diode above.
2015-07-24 13.40.23 copy.jpg
2015-07-24 13.40.23 copy.jpg (465.6 KiB) Viewed 599 times
2015-07-24 13.40.13 copy.jpg
2015-07-24 13.40.13 copy.jpg (478.89 KiB) Viewed 599 times

Code: Select all

//--------Libraries---------------------------------------------------------
#include <Sensirion.h>	// Interpret output from Sensiron SHT sensors
#include <SPI.h>			// ???
#include <SD.h>			// Communicate with the SD card and datalogger shield
#include <RTClib.h>		// Communicate with the real time clock
#include <Wire.h>           // i2c functions
#include <Narcoleptic.h>    // low-power sleep routine
#include <Adafruit_ADS1015.h>	//16-bit gain amplifier for pyranometer

//---------Commonly changed parameters----------------------------------------
	const int n_avg = 10;			//Number of samples to average at each time

// ------- Declare Variables ---------------------------------------------------
//Sleeptime
    // low-power sleep counter (1 * 8sec = 8 sec)
	const int sleepint = 7;			     // low-power sleep counter (7 * 8sec = 56 sec)

//Real Time Clock (RTC)
	DateTime now;
	uint32_t m;		// how many milliseconds between grabbing data and logging it. 1000 ms is once a second
	//#define LOG_INTERVAL interval // mills between entries (reduce to take more/faster data)

	#define ECHO_TO_SERIAL 0 // echo data to serial port

//Adafruit Datalogger Shield
	// the logging file
	File logfile;

//Pyranometer
	Adafruit_ADS1115 ads1115;	
	float DtoA;
	const byte gain = 16;
	float adc0; //,adc1,adc2,adc3; //analog to digital variable
	
// Anemometer Stuff
	volatile long rpsTops;  
	float rps;
	float mph;
	volatile unsigned long anem_now;
	volatile unsigned long anem_later;
	volatile float deltatime;
	volatile unsigned long last_micros;
	
//SHT-75 Temperature/Humidity sensor
	uint16_t rawData;
	float sht_temp;
	float sht_humid;
	float sht_dewpt;

//Thermistors (NTCLE100E3-->Brown-Black-Orange) 
	float therm_avg1;
	float therm_avg2;
	
	const int therm_nominal = 10000;	// resistance at 25 degrees C
	const int temp_nominal = 25;   // temp. for nominal resistance (almost always 25 C)
	const int b_coef = 3977;		// The beta coefficient of the thermistor (usually 3000-4000)
	const int series_resistor = 10000;    // the value of the 'other' resistor

//Watermark 200ss soil moisture sensor
	int   mV;                    // single mV value
	int   ADval;                 // single ADC value
	int   ADvalp1[n_avg];			 // array of ADC values, polarity 1
	int   ADvalp2[n_avg];			 // array of ADC values, polarity 2
	int   ADval1;                // average ADC value for polarity 1
	int   ADval2;                // average ADC value for polarity 2
	long  R1;                    // resistance for first polarity
	long  R2;                    // resistance for reverse polarity
	long  R2a;				     // intermediate value in R calcualtion
	float Rwm;                   // final resistance of Watermark
	const long  R_ref_wm = 10000;             // 10k fixed resistor value for Watermark half bridge
	const long  ADCmax = 1024;
	long  Vcc;                   // supply/Aref voltage read with secret voltmeter
	float  kPa;                // array to hold Watermark potentials
	float kPa1;				     // intermediate values in kPa calculation
	float kPa2;

//Voltage measurement
	const int band_gap_ref = 14; // special indicator that we want to measure the bandgap
	const int aref_voltage = 5; // we tie 3.3V to ARef and measure it with a multimeter!
	const int bandgap_voltage = 1.1; // this is not super guaranteed but its not -too- off
	float supplyvoltage;

// ------- Declare Pins --------------------------------------------------------
	//Thermistors
	#define therm_pin1 A0		//Soil temperature thermistor 
	#define therm_pin2 A1		//Litter temperature thermistor
	
	//Watermark 200ss sensor
	const int wm_p1 = 5;              // AC half bridge - Watermark 200ss leg
	const int wm_p2 = 4;              // AC half bridge - 10k leg
	const int wm_pin = A2;         // Watermark 200ss output from AC half bridge
	
	//SHT-75 Temperature/Humidity Sensor
	const uint8_t dataPin =  9;              // SHT serial data
	const uint8_t sclkPin =  8;              // SHT serial clock
	const uint8_t ledPin = 13;              // Arduino built-in LED
	const uint8_t sht_pwr = 2;
	
	// Anemometer
	const byte rpssensor = 3;

	//Datalogger Shield
	const int dlogger_pin = 10;

	//LEDs
	#define redLEDpin 2
	#define greenLEDpin 3
	
	//Initialize sensor library objects
	Sensirion sht = Sensirion(dataPin, sclkPin);	// define the sensiron object
	RTC_DS1307 RTC; 								// define the Real Time Clock object  

// --------------------- Setup -------------------------------------------------
void setup(void)
{
	delay(15);	// Wait 15 ms before first cmd
	Wire.begin();            // enable i2c bus
	
	ads1115.begin();
	Serial.print("Gain setting = ");
	switch(gain) {
		case 1: {ads1115.setGain(GAIN_ONE); DtoA=4.096/32768; 
			Serial.println("GAIN_ONE"); break;}
		case 2: {ads1115.setGain(GAIN_TWO); DtoA=2.048/32768; 
			Serial.println("GAIN_TWO"); break;}
		case 3: {ads1115.setGain(GAIN_TWOTHIRDS); DtoA=6.144/32768;
			Serial.println("GAIN_TWOTHIRDS"); break;}
		case 4: {ads1115.setGain(GAIN_FOUR); DtoA=1.024/32768;
			Serial.println("GAIN_FOUR"); break;}
		case 8: {ads1115.setGain(GAIN_EIGHT); DtoA=0.512/32768; 
			Serial.println("GAIN_EIGHT"); break;}
		case 16: {ads1115.setGain(GAIN_SIXTEEN); DtoA=0.256/32768; 
			Serial.println("GAIN_SIXTEEN"); break;}
		default: {Serial.println("Oops... no such gain setting!"); return; }
		}
	Serial.print("DtoA = ");Serial.println(DtoA,8);
	
	initializeSD();
    RTC.begin();

	//RTC.adjust(DateTime(__DATE__, __TIME__));
	if (! RTC.isrunning()) {
	Serial.println("RTC is NOT running!");
	// following line sets the RTC to the date & time this sketch was compiled
	RTC.adjust(DateTime(__DATE__, __TIME__));
	}
		
	analogReference(DEFAULT); // use default (3.3v supply) ADC Vref

	pinMode(wm_p1, OUTPUT);     // make pins for AC half bridge an output
	pinMode(wm_p2, OUTPUT);
	pinMode(dlogger_pin, OUTPUT);
	pinMode(sht_pwr, OUTPUT);
	pinMode(rpssensor, INPUT_PULLUP);
	
	// use debugging LEDs
	pinMode(redLEDpin, OUTPUT);
	pinMode(greenLEDpin, OUTPUT);
	delay(15);
}

// -------------------------------- Main Loop ----------------------------------
void loop(void)                      // main program
{  
	readRTC();				     // read RTC

	//if(now.minute() == 0 || now.minute() == 2 || now.minute() == 6 || now.minute() == 6 || now.minute() == 8|| now.minute() == 10|| now.minute() == 12|| now.minute() == 14|| now.minute() == 16|| now.minute() == 18|| now.minute() == 20|| now.minute() == 22|| now.minute() == 24|| now.minute() == 26|| now.minute() == 28|| now.minute() == 30|| now.minute() == 32|| now.minute() == 34|| now.minute() == 36|| now.minute() == 38|| now.minute() == 40|| now.minute() == 42|| now.minute() == 44|| now.minute() == 46|| now.minute() == 48|| now.minute() == 50|| now.minute() == 52|| now.minute() == 54|| now.minute() == 56|| now.minute() == 58)
    //if(now.minute() == 0 || now.minute() == 30)	// take measurements every 30 minutes
	//if(now.second() == 0 || now.second() == 10 || now.second() == 20 || now.second() == 30 || now.second() == 40 || now.second() == 50)	
	// Doesn't work unless sleep is < 10 sec--take measurements every 10 seconds

	// take measurements every 15 minutes	
	if(now.minute() == 0 || now.minute() == 15 || now.minute() == 30 || now.minute() == 45)
	{
	readpyrano();
	therm_avg1 = readThermistor(therm_pin1);
	therm_avg2 = readThermistor(therm_pin2);
	readSHT75();

	readWM();	// read Watermark
	readVcc();

	rpsTops = 0;   //Set NbTops to 0 ready for calculations
	anem_now=micros();
	attachInterrupt(1, readrps, FALLING);
    delay(2000);	//counts for the duration specified
    detachInterrupt(1);
	anem_later=micros();
	deltatime=((float)anem_later- (float)anem_now)/1000000;
	rps = (rpsTops)/deltatime;
	mph = 1.758*(rps)+2.121;
		
	//PrintToSerial();                // print to screen
	LogToRam(); 
	writetodisk();
	sleepytime();
	}
	sleepytime();
}
// =============================== End Main Loop ================================

// ****************************** Subroutines ***********************************

//---------- low-power sleep -----------------------------------------------------------
void sleepytime()			     // based on Narcoleptic library routine
{
	//ADCSRA = 0;				// disable ADC
	for(byte i=1; i<=sleepint; i++)	// loop through several times
	{
	Narcoleptic.delay(8000);	// maximium sleep for ATmega328 is 8 sec
	}  
	//ADCSRA |= (1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)|(1<<ADEN);     // set prescaler to 128 and enable ADC 
}

void PrintToSerial()
{
#if ECHO_TO_SERIAL
	Serial.print(m); // milliseconds since start
	Serial.print(", ");
	Serial.print(now.unixtime()); // seconds since 1/1/1970
	Serial.print(", ");
	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('"');
	Serial.print(", "); 
	Serial.print(adc0,8);
	Serial.print(", "); 
	Serial.print(therm_avg1);
	Serial.print(", "); 
	Serial.print(therm_avg2);
	Serial.print(", "); 
	Serial.print(sht_temp);
	Serial.print(", ");
	Serial.print(sht_humid);
	Serial.print(", ");
	Serial.print(sht_dewpt);
	Serial.print(", ");
	Serial.print(kPa,DEC);
	Serial.print(", ");
	Serial.print(rpsTops);
	Serial.print(", ");
	Serial.print(deltatime);
	Serial.print(", ");
	Serial.print(rps);
	Serial.print(", ");
	Serial.print(mph);	
	//Serial.print(", ");
	//Serial.print(supplyvoltage);
	Serial.print(", ");
	Serial.println(Vcc);
#endif //ECHO_TO_SERIAL
}

void initializeSD()
{
	#if WAIT_TO_START
	Serial.println("Type any character to start");
	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(dlogger_pin, OUTPUT);
	  
	// see if the card is present and can be initialized:
	if (!SD.begin(dlogger_pin)) {
		error("Card failed, or not present");
	}
	Serial.println("card initialized.");
	  
	// create a new file
	char filename[] = "LOGGER00.CSV";
	for (byte 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!
		}
	}
		
	if (! logfile) {
		error("couldnt create file");
	}
	
	Serial.print("Logging to: ");
	Serial.println(filename);

	// connect to RTC
	if (!RTC.begin()) {
		logfile.println("RTC failed");
	#if ECHO_TO_SERIAL
		Serial.println("RTC failed");
	#endif //ECHO_TO_SERIAL
  }
  
	logfile.println("millis,stamp,datetime,sol_rad_vlt,therm_avg1,therm_avg2,sht_temp,sht_humid,sht_dewpt,wm200ss_kPa,rpsTops,delta_time,rps,mph,supplyvoltage");
	#if ECHO_TO_SERIAL
		Serial.println("millis,stamp,datetime,sol_rad_vlt,therm_avg1,therm_avg2,sht_temp,sht_humid,sht_dewpt,wm200ss_kPa,rpsTops,delta_time,rps,mph,supplyvoltage");
	#endif //ECHO_TO_SERIAL
}

void LogToRam()
{
	// milliseconds since start
	logfile.print(m); 
	logfile.print(", ");

	// log time
	logfile.print(now.unixtime()); // seconds since 1/1/1970
	logfile.print(", ");
	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('"');
	logfile.print(", "); 
	logfile.print(adc0,8);
	logfile.print(", ");
	logfile.print(therm_avg1);
	logfile.print(", "); 
	logfile.print(therm_avg2);
	logfile.print(", "); 
	logfile.print(sht_temp);
	logfile.print(", ");
	logfile.print(sht_humid);
	logfile.print(", ");
	logfile.print(sht_dewpt);
	logfile.print(", ");
	logfile.print(kPa,DEC);
	logfile.print(", ");
	logfile.print(rpsTops);
	logfile.print(", ");
	logfile.print(deltatime);
	logfile.print(", ");
	logfile.print(rps);
	logfile.print(", ");
	logfile.print(mph);
	logfile.print(", ");
	//logfile.print(supplyvoltage);
	//logfile.print(", ");
	logfile.println(Vcc);
  }

void writetodisk()
{
	// 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!
	digitalWrite(redLEDpin, HIGH);
	//logfile.close();
	logfile.flush();
	digitalWrite(redLEDpin, LOW);
}
  
void readRTC()
{
   	m = millis();		// log milliseconds since starting
    now = RTC.now();	// fetch the time
}

void readpyrano()
{
	byte i;
	int16_t avg_reading;
	int16_t samples[n_avg];
	
	// take N samples in a row, with a slight delay
	for (i=0; i< n_avg; i++) {
	samples[i] = ads1115.readADC_SingleEnded(0);
	delay(50);
	}
	
	// average all the samples out
	avg_reading = 0;
	for (i=0; i< n_avg; i++) {
		avg_reading += samples[i];
	}
	avg_reading /= n_avg;
	adc0=avg_reading*DtoA;
}

//// This is the function that interrupt calls to measure  rps  
void readrps ()   { 
	if((long)(micros() - last_micros) >= 15 * 1000) {
	// debounce of REED contact. With 15ms speed more than 150 km/h can be measured
		rpsTops++; 
		last_micros = micros();
}
}

void readWM()
{
	byte i;
	readVcc();
	// read supply/ADC ref voltage
    int AChb = wm_pin;                // set ADC channel
    for(i=1; i<=n_avg; i++)             // take n_avg readings with each polarity
    {
		digitalWrite(wm_p1,HIGH);        // set 1st polarity
		digitalWrite(wm_p2,LOW);
		delay(15);
		ADvalp1[i] = analogRead(AChb);  // read voltage at voltage divider
		delay(15);

		digitalWrite(wm_p1,LOW);          // switch polarity
		digitalWrite(wm_p2,HIGH);
		delay(15);
		ADvalp2[i] = analogRead(AChb);  // read voltage
		delay(15);
    }
    digitalWrite(wm_p2,LOW);		     // turn off voltage to Watermarks
    ADval1 = 0;                        // initialize totals
    ADval2 = 0;
    for(i=2; i<=n_avg; i++)                // skip first reading
    {
		ADval1 = ADval1 + ADvalp1[i];    // total last (n_avg-1) readings
		ADval2 = ADval2 + ADvalp2[i];
    }
    ADval1 /= (n_avg-1);                      // calculate average at 1st polarity
    ADval2 /= (n_avg-1);                      // calculate average at 2nd polarity

    R1 = R_ref_wm * ADCmax / long(ADval1) - R_ref_wm; // calculate Rwm under first polarity

    R2a = ADCmax*1000L / long(ADval2) - 1000L; // calculate Rwm under reverse polarity
    R2 = R_ref_wm*1000L / R2a;

    Rwm = (R1 + R2) / 2.0;           // calculate average to get final Rwm

    if(Rwm > 30800) Rwm = 30800;	 // check for out-of-range value

    kPa1 = 4.093 + Rwm*3.213 / 1000.0;  // apply Schock calibration equation
    kPa2 = 1.0 - 0.009733*Rwm / 1000.0 - 0.01205 * 24.0;
    kPa = kPa1 / kPa2;

    if(kPa < 0)                   // check for out-of-range readings
    {
		kPa = 254;
    }
    else if(kPa > 250)
    {
		kPa = 250;
    }
}

void readSHT75()
{
	digitalWrite(sht_pwr, HIGH);
	delay(15);
	sht.measTemp(&rawData);                // sht.meas(TEMP, &rawData, BLOCK)
	sht_temp = sht.calcTemp(rawData);
	sht.measHumi(&rawData);                // sht.meas(HUMI, &rawData, BLOCK)
	sht_humid = sht.calcHumi(rawData, sht_temp);
	sht_dewpt = sht.calcDewpoint(sht_humid, sht_temp);
	digitalWrite(sht_pwr, LOW);
}

float readThermistor(int THERMISTORPIN)
{
	byte i;
	float avg_reading;
	int samples[n_avg];
	
	// take N samples in a row, with a slight delay
	for (i=0; i< n_avg; i++) {
	samples[i] = analogRead(THERMISTORPIN);
	delay(15);
	}
	 
	  // average all the samples out
	avg_reading = 0;
	for (i=0; i< n_avg; i++) {
		avg_reading += samples[i];
	}
	avg_reading /= n_avg;
	 
	avg_reading = 1023 / avg_reading - 1;	// convert the value to resistance
	avg_reading = series_resistor / avg_reading;

	float steinhart;
	steinhart = avg_reading / therm_nominal;     // (R/Ro)
	steinhart = log(steinhart);                  // ln(R/Ro)
	steinhart /= b_coef;                   // 1/B * ln(R/Ro)
	steinhart += 1.0 / (temp_nominal + 273.15); // + (1/To)
	steinhart = 1.0 / steinhart;                 // Invert
	steinhart -= 273.15;                         // convert to C
	
	return(steinhart);
  }

//--------- secret voltmeter -----------------------------------------------------------
void readVcc()				     // using internal ATmega328 voltage
{                        		     // read internal 1.1V ref against Vcc
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  delay(31);                         // wait for Vref to settle
  ADCSRA |= _BV(ADSC);               // convert to mV
  while (bit_is_set(ADCSRA,ADSC));
  Vcc = ADCL;
  Vcc |= ADCH<<8;
  Vcc = 1126400L / Vcc;             // back-calculate Vcc in mV
}

void error(char *str)
{
  Serial.print("error: ");
  Serial.println(str);
  
  // red LED indicates error
  digitalWrite(redLEDpin, HIGH);

  while(1);
}

User avatar
adafruit_support_mike
 
Posts: 67454
Joined: Thu Feb 11, 2010 2:51 pm

Re: Data logger shield

Post by adafruit_support_mike »

Did you collect those results with the devices in the field, or in the lab?

User avatar
knelson
 
Posts: 8
Joined: Fri May 16, 2014 1:23 pm

Re: Data logger shield

Post by knelson »

All the results mentioned above come from the field. Our sites are at about 9500 feet and daytime temperatures get up to about 80° on a clear day. All of the electronics are sealed inside a good waterproof case and we have had no signs of moisture. We had some funny behavior in the lab before deployment as well but thought we had things sorted out. We had the greatest inconsistencies with the real time clock and had some SD cards that stopped allowing us to format it and wouldn't log data. That's why we purchased (and tested) all new SD cards before we deployed in the field. The SD cards are SanDisk four gig. I also had some trouble with the ADS 1115 in that it gives me negative voltages when it shouldn't. I chalked this up to being a personal issue resulting from not enough testing. The is set to 16 because the diode returns very low voltages. The other potential issue with the idiosyncratic behavior is that we come in about 75% of RAM and log at every time point. These are set at 15 minute intervals in the field.

Thanks for the help! I wish I could have a full-fledged electrical engineer on staff but it's unlikely that will happen for a few years.

User avatar
adafruit_support_mike
 
Posts: 67454
Joined: Thu Feb 11, 2010 2:51 pm

Re: Data logger shield

Post by adafruit_support_mike »

I don't see anything out of the ordinary in the photos you posted, and if the same hardware/code behaves differently in different units, it's probably related to specific hardware or the local conditions.

That kind of thing can be tricky to isolate, but let's start by testing the correlation between location and behavior. Do the units fail to work in the lab after you've brought them back from the field? If not, try swapping locations to see if a good unit shows the same signs of trouble when you trade locations with a bad one (and see if the bad one behaves well in the new location).

Locked
Please be positive and constructive with your questions and comments.

Return to “Arduino Shields from Adafruit”