Wednesday 28 November 2012

data logging on an SD card v.2

Even though this kind of logging need'nt be too accurate, i still decided to leave an interrupt driven code variation for time keeping.



/*  A simple data logger for the Arduino   */
/*  After some studying of both codes, i came up with this. 
 This is the v.2, with an timer interrupt to keep time, as 
 oposed to the millis() of arduino !
 These example code is in the public domain.
 http://dubworks.blogspot.co.uk/2012/11/moisture-and-temperature-sensors-data.html
 https://github.com/adafruit/Light-and-Temp-logger
 http://www.engblaze.com/microcontroller-tutorial-avr-and-arduino-timer-interrupts/ */
// this code sets up timer CTC for a 1s  @ 16Mhz Clock
// avr-libc library includes
#include <avr/io.h>
#include <avr/interrupt.h>
#include <SD.h>
// constants won't change. Used here to 
// set pin numbers:
const int ledPin =  13;      // the number of the LED pin
// the digital pins that connect to the LEDs
#define redLEDpin 2
#define greenLEDpin 3
int seconds = 0;
int minutes = 0;
int hour = 0;
int day = 0;
int week=0;
// Variables will change:
int ledState = LOW;             // ledState used to set the LED
int logState = LOW;
// the follow variables is a long because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
int Hours_interval = 0;         // Hours interval 
int Minutes_interval = 0;       // Hours interval 
int log_Minutes_interval = 1;          // interval in mnts at which to log
int log_Hours_interval =4;            // interval in hours at which to log
#define ECHO_TO_SERIAL   1 // echo data to serial port
#define WAIT_TO_START    0 // Wait for serial input in setup()
// The analog pins that connect to the sensors
#define Moisture_sensor_Pin 0           // analog 0
#define tempPin 1                // analog 1
#define BANDGAPREF 14            // special indicator that we want to measure the bandgap
#define aref_voltage 3.3         // we tie 3.3V to ARef and measure it with a multimeter!
#define bandgap_voltage 1.1      // this is not super guaranteed but its not -too- off

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

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

void setup()
{ 
  // initialize Timer1
  cli();          // disable global interrupts
  TCCR1A = 0;     // set entire TCCR1A register to 0
  TCCR1B = 0;     // same for TCCR1B
  // set compare match register to desired timer count:
  OCR1A = 15624;
  // turn on CTC mode:
  TCCR1B |= (1 << WGM12);
  // Set CS10 and CS12 bits for 1024 prescaler:
  TCCR1B |= (1 << CS10);
  TCCR1B |= (1 << CS12);
  // enable timer compare interrupt:
  TIMSK1 |= (1 << OCIE1A);
  // enable global interrupts:
  sei();
  // set the digital pin as output:
  pinMode(ledPin, 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");
  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!
    }
  }
  if (! logfile) {
    error("couldnt create file");
  }  
  {
    Serial.print("Logging to: ");
    Serial.println(filename);
  }
  logfile.println("time,moisture,temp,vcc");    
#if ECHO_TO_SERIAL
  Serial.println("time,moisture,temp,vcc");
#endif //ECHO_TO_SERIAL
  // If you want to set the aref to something other than 5v
  analogReference(EXTERNAL);  
}

void log_to_sd()
{
  logState = LOW;
  //Log code to sd card 
  logfile.print(minutes);           // mnts since start
  logfile.print("mnts, "); 
  logfile.print(hour);           // hours since start
  logfile.print("hrs, ");  
  logfile.print(day);           // days since start
  logfile.print("dd, "); 
  logfile.print(week);           // weeks since start
  logfile.print("Week, ");  
#if ECHO_TO_SERIAL
  Serial.print(minutes);         // mnts since start
  Serial.print("mnts, ");  
  Serial.print(hour);         // hours since start
  Serial.print("hrs, "); 
  Serial.print(day);         // days since start
  Serial.print("dd, "); 
  Serial.print(week);         // weeks since start
  Serial.print("Week, "); 
#endif 
  analogRead(Moisture_sensor_Pin);
  delay(10); 
  int Moisture_sensor_PinReading = analogRead(Moisture_sensor_Pin);  
  analogRead(tempPin); 
  delay(10);
  int tempReading = analogRead(tempPin);    
  // converting that reading to voltage, for 3.3v arduino use 3.3, for 5.0, use 5.0
  float voltage = tempReading * aref_voltage / 1024;  
  float temperatureC = (voltage - 0.5) * 100 ;
  //float temperatureF = (temperatureC * 9 / 5) + 32; // uncomment the beggining of this line for F
  logfile.print("Moisture, ");    
  logfile.print(Moisture_sensor_PinReading);
  logfile.print("Temp, ");    
  logfile.print(temperatureC);
#if ECHO_TO_SERIAL
  Serial.print("Moisture, ");   
  Serial.print(Moisture_sensor_PinReading);
  Serial.print("Temp, ");    
  Serial.print(temperatureC);
#endif //ECHO_TO_SERIAL
  // Log the estimated 'VCC' voltage by measuring the internal 1.1v ref
  analogRead(BANDGAPREF); 
  delay(10);
  int refReading = analogRead(BANDGAPREF); 
  float supplyvoltage = (bandgap_voltage * 1024) / refReading; 

  logfile.print("Supply_V, ");
  logfile.print(supplyvoltage);
#if ECHO_TO_SERIAL
  Serial.print("Supply_V, ");   
  Serial.print(supplyvoltage);
#endif // ECHO_TO_SERIAL
  logfile.println();
#if ECHO_TO_SERIAL
  Serial.println();
#endif // ECHO_TO_SERIAL
  digitalWrite(greenLEDpin, LOW);
  // blink LED to show we are syncing data to the card & updating FAT!
  digitalWrite(redLEDpin, HIGH);
  logfile.flush();
  digitalWrite(redLEDpin, LOW);
}

void loop()
{
  // we have a working Timer
  // Routines in the loop: keeping time 
  // set the LED with the ledState of the variable:
  digitalWrite(ledPin, ledState);
  //Keeping time :60 seconds=1minute
  if (seconds==60){
    seconds=0;// Reset seconds, so we do not end up with massive numbers
    minutes = minutes += 1;//Increment 1 minute
    Minutes_interval = Minutes_interval += 1;/*Timer for variable for 
     log, in minutes*/
  }
  //keeping time: 60 minutes=1hour
  if (minutes==60){
    minutes = 0;// Reset minutes, so we do not end up with massive numbers
    hour= hour += 1;//increment 1 hour
    Hours_interval = Hours_interval += 1;/*Timer for variable for 
     log, in minutes*/
  }
  //Keeping time :24 hours = 1 day
  if (hour == 24){
    day = day += 1;//increment 1 day
    hour= 0;// Reset hours, so we do not end up with massive numbers
    minutes = 0;// Reset minutes
  }
  /*Keeping time :7 days = 1 week. 
   This is as far as i gone for logging purposes.  Can keep a quite accurate
   log of the sensors, while the deviation in time is quite minimal */
  if (day == 7 && hour== 23 && minutes == 59 && seconds==59){
    day = 0;// Reset days, so we do not end up with massive numbers
    week = week +=1;//increment 1 week
  };
  /* When the interrup timer gets activated, our log state will trigger 
   both, in turn */
  /* log_Hours_interval if hours prefered; i included both, just in case, 
   for code's readability sake */
  if ( Minutes_interval ==log_Minutes_interval ){
    Minutes_interval =0;//reset the last interval
    Hours_interval = 0; //reset the last interval
    logState = HIGH; // if the LOG is off turn it on and vice-versa:
  }
  // Actual log routine
  if (logState == HIGH){
    log_to_sd(); 
  }  
}

ISR(TIMER1_COMPA_vect)
{
  seconds= seconds += 1; // Increment one second
  // if the LED is off turn it on and vice-versa:
  if (ledState == LOW)
    ledState = HIGH;
  else
    ledState = LOW; 
}

No comments:

Post a Comment

Feel free to contact me with any suggestions, doubts or requests.

Bless