Sunday, August 3, 2014

Data Logging with an SD Card Reader

     In order to store the data obtained during flight onto an SD card, we used a simple LC Studio SD card Reader. I was unable to find a datasheet for this particular device, but interfacing with an Arduino Uno or Nano is simple (uses the SD library included with the Arduino software). The pinout is as follows:

GND   to GND
MISO  to D12
SCK    to D13
MOSI  to D11
CS       to D10
5.5+     to 5V

     The Arduino software includes this example data logging code:    
/*
  SD card datalogger

 This example shows how to log data from three analog sensors
 to an SD card using the SD library.
    
 The circuit:
 * analog sensors on analog ins 0, 1, and 2
 * SD card attached to SPI bus as follows:
 ** MOSI - pin 11
 ** MISO - pin 12
 ** CLK - pin 13
 ** CS - pin 4

 created  24 Nov 2010
 modified 9 Apr 2012
 by Tom Igoe

 This example code is in the public domain.
    
 */

#include <SD.h>

// On the Ethernet Shield, CS is pin 4. Note that even if it's not
// used as the CS pin, the hardware CS pin (10 on most Arduino boards,
// 53 on the Mega) must be left as an output or the SD library
// functions will not work.
const int chipSelect = 4;

void setup()
{
 // Open serial communications and wait for port to open:
  Serial.begin(9600);
   while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }


  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)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("card initialized.");
}

void loop()
{
  // make a string for assembling the data to log:
  String dataString = "";

  // read three sensors and append to the string:
  for (int analogPin = 0; analogPin < 3; analogPin++) {
    int sensor = analogRead(analogPin);
    dataString += String(sensor);
    if (analogPin < 2) {
      dataString += ",";
    }
  }

  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  File dataFile = SD.open("datalog.txt", FILE_WRITE);

  // if the file is available, write to it:
  if (dataFile) {
    dataFile.println(dataString);
    dataFile.close();
    // print to the serial port too:
    Serial.println(dataString);
  } 
  // if the file isn't open, pop up an error:
  else {
    Serial.println("error opening datalog.txt");
  }
}

     Of course, this simple code is not sufficient for the amount of data we are recording during the flight. It took some effort, but I was able to come up with a code that could handle this amount of data. There are a very clever 7 lines of code used to create files arbitrarily that were borrowed from LadyAda. This technique appeared to solve the problem I was having writing massive amounts of data to the SD card (I could only previously write about 95 KB). Here is the final code in its entirety, which writes data to the SD card in a CSV format (very Excel friendly!) . Please feel free to make it better for the next launch!



//High Altitude Ballon Project DataLogger
//Mt. San Antonio College Operation Get High
//Humidity, Temperature, Light Sensors, Pressure/Temperature/Accelerometer/Gyro/Magnetometer, Radiation Sensor, ADXL Accelerometer, and GPS
#include<SD.h>
#include<Wire.h>
#include<I2Cdev.h>
#include<dht.h> //Humidity and Temperature Sensor Library
#include<MS561101BA.h> //Barometer Library
#include<MPU6050.h> 
#include<HMC5883L_2.h>
#include<SoftwareSerial.h>
//#include<TinyGPS.h>


#define ACCEL_X A0
#define ACCEL_Y A1
#define ACCEL_Z A2
#define DHT21_PIN A3
#define LOG_INTERVAL 500
#define SYNC_INTERVAL 1000
#define NMEA 80

#define logRatio 3 //Only send data every fourth trial
byte logCounter=0;
///////////////////////////////Radiation Sensor//////////////////////////////
  // the pin we are interested in
volatile long bleep=0;    // a counter to see how many times the pin has changed
long cmd=0;     // a place to put our serial data
//////////////////////////////////////////////////////////////////////////////

uint32_t syncTime=0;
dht DHT; //Humidity/Temperature
MS561101BA baro = MS561101BA(); //Barometer
MPU6050 accelgyro; //Accel/Gyro
HMC5883L_2 mag; //Magnetometer

int16_t ax, ay, az; //Accel
int16_t gx, gy, gz; //Gyro
int16_t mx, my, mz; //Mag

//TinyGPS gps;
SoftwareSerial ss(4, 3); //Pins used by the GPS

int chipSelect=10; //SD card
int BH1750_Device=0x23; //Light Sensor#1
int BH1750_Device2=0x5c; //Light Sensor#2
char c[60]; //GPS Array
unsigned int Lux; //Light Sensor_1 Output
unsigned int Lux2; //Light Sensor_2 Output
unsigned long time;
//char c; //For GPS
File logfile;

void error(char *str)
{
  Serial.print("error: ");
  Serial.println(str);
  while(1);
}

void setup() {
  Serial.begin(115200);
  ss.begin(9600);
  
  accelgyro.setI2CMasterModeEnabled(false);
  accelgyro.setI2CBypassEnabled(true) ;
  accelgyro.setSleepEnabled(false);
  
  //pinMode(PIN, INPUT);     //set the pin to input
  //digitalWrite(PIN, HIGH); //use the internal pullup resistor
  attachInterrupt(0, bleepcount,FALLING); // attach a PinChange Interrupt to our pin on the rising edge
  
  pinMode(10, OUTPUT);
  Configure_BH1750(); //Configure Light Sensor
  baro.init(MS561101BA_ADDR_CSB_LOW);
  accelgyro.initialize();
  mag.initialize();
  

  Serial.println("Initializing the SD card.");
  delay(100);
  //Check to see if SD card is operational
  if (!SD.begin(chipSelect)) {
    Serial.println("Initialization was unsuccessful.");
    return;
  }
  else {
    Serial.println("Initialization is complete.");
  }
   delay(500); 
  //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);
  
  if (logfile) {
    String header = "Time, Lux1, Lux2, Humidity, Temp, Pressure, Temp2, AX, AY, AZ, GX, GY, GZ, MX, MY, MZ, Heading, AX, AY, AZ, Radiation, GPS"; //Add sensor headings here
    logfile.println(header);
    Serial.println(header);
  }
  
}
  void loop() {
    delay((LOG_INTERVAL -1) - (millis() % LOG_INTERVAL)); //maybe
    int count = 0;
     while (ss.available()) //Read the GPS
       {
         c[count] = ss.read();
         count++;
         //Serial.write(c); // uncomment this line if you want to see the GPS data flowing
         //gps.encode(c); // Did a new valid sentence come in?
       }
    int chk = DHT.read21(DHT21_PIN);
    int sensorx=analogRead(ACCEL_X);
    int sensory=analogRead(ACCEL_Y);
    int sensorz=analogRead(ACCEL_Z);
    float pression=NULL;
    float  temperature=NULL; //Barometer
    accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
    mag.getHeading(&mx, &my, &mz);
    float heading_2 = atan2(my, mx);
    if(heading_2 < 0)
      heading_2 += 2 * M_PI; 
    
    while(pression == NULL) {
    pression = baro.getPressure(MS561101BA_OSR_4096);
    }
    while(temperature == NULL) {
    temperature = baro.getTemperature(MS561101BA_OSR_4096);
    }
    time=millis();
    Lux=BH1750_Read(); //Light Sensor data
    Lux2=BH1750_Read2(); //Light Sensor2 data

    if (logfile) {
      logfile.print(time);
      logfile.print(",");
      logfile.print(Lux,DEC);
      logfile.print(",");
      logfile.print(Lux2,DEC);
      logfile.print(",");
      logfile.print(DHT.humidity,1);
      logfile.print(",");
      logfile.print(DHT.temperature,1);
      logfile.print(",");
      logfile.print(pression);
      logfile.print(",");
      logfile.print(temperature);
      logfile.print(",");
      logfile.print(ax);
      logfile.print(",");
      logfile.print(ay); 
      logfile.print(",");
      logfile.print(az);
      logfile.print(","); 
      logfile.print(gx);
      logfile.print(",");
      logfile.print(gy);
      logfile.print(","); 
      logfile.print(gz);
      logfile.print(",");
      logfile.print(mx); 
      logfile.print(",");
      logfile.print(my); 
      logfile.print(",");
      logfile.print(mz);
      logfile.print(","); 
      logfile.print(heading_2 * 180/M_PI);
      logfile.print(",");
      logfile.print(sensorx);
      logfile.print(",");
      logfile.print(sensory);
      logfile.print(",");
      logfile.print(sensorz);
      logfile.print(",");
      logfile.print(bleep, DEC);
      logfile.print(",");
      logfile.println(c);

      if (logCounter==logRatio) {
        for(int i=7; i < count-12; i++){
        Serial.print(c[i]);
      }
      Serial.print(",");
      Serial.print(time);
      Serial.print(",");
      Serial.print(Lux,DEC);
      Serial.print(",");
      Serial.print(Lux2,DEC);
      Serial.print(",");
      Serial.print(DHT.humidity,1);
      Serial.print(",");
      Serial.print(DHT.temperature,1);
      Serial.print(",");
      Serial.print(pression);
      Serial.print(",");
      Serial.print(int (temperature));      
      Serial.print(",");
      Serial.print(ax);
      Serial.print(",");
      Serial.print(ay); 
      Serial.print(",");
      Serial.print(az);
      Serial.print(","); 
      Serial.print(gx);
      Serial.print(",");
      Serial.print(gy);
      Serial.print(","); 
      Serial.print(gz);
      Serial.print(",");
      //Serial.print(mx); 
      //Serial.print(",");
      //Serial.print(my); 
      //Serial.print(",");
      //Serial.print(mz);
      //Serial.print(","); 
      Serial.print(int (heading_2 * 180/M_PI)); 
      
      
      Serial.print(",");
      Serial.print(sensorx);
      Serial.print(",");
      Serial.print(sensory);
      Serial.print(",");
      Serial.print(sensorz);
      Serial.print(",");
      Serial.print(bleep, DEC);
      
      Serial.println();
      logCounter=0;
      }
      logCounter++;
    }
  
    else {
      Serial.println("Could not write to file.");
    }
  
  if ((millis() - syncTime) < SYNC_INTERVAL) return;
  syncTime = millis();
  

    logfile.flush();
  }
    ////////////////////////////////////////////////////////////////////////////////////
  // Code for Light Sensor
  ////////////////////////////////////////////////////////////////////////////////////
 unsigned int BH1750_Read() {  //Read Light Sensor#1
    unsigned int i=0;
    Wire.beginTransmission(BH1750_Device);
    Wire.requestFrom(BH1750_Device,2);
    while (Wire.available()) {
      i <<=8; 
      i |=Wire.read();
    }
    Wire.endTransmission();
    return i/1.2;
}

unsigned int BH1750_Read2() { //Read Light Sensor#2
  unsigned int i=0;
  Wire.beginTransmission(BH1750_Device2);
  Wire.requestFrom(BH1750_Device2,2);
  while (Wire.available()) {
    i <<=8; 
    i |=Wire.read();
  }
  Wire.endTransmission();
  return i/1.2;
}

 void Configure_BH1750() {
  Wire.beginTransmission(BH1750_Device); //Light Sensor#1
  Wire.write(0x10);
  Wire.endTransmission();
  Wire.beginTransmission(BH1750_Device2); //Light Sensor#2
  Wire.write(0x10);
  Wire.endTransmission();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Code for Radiation Sensor
////////////////////////////////////////////////////////////////////////////////////////////
void bleepcount()
{

  bleep++;
  
}


     All libraries used (and much more information on our sensor system) can be found here https://trello.com/b/R7i2QIi4/hab-electrical-board. Also, when formatting an SD card I suggest using the SDFormatter V4.0 which can be found here https://www.sdcard.org/downloads/formatter_4/.


    

No comments:

Post a Comment