Problem with data logging with multiple sensors

Ok so I changed the "quote" to "code", and now my code should be easier to read :confused:

I'm currently working on a near space balloon engineering project for school, and right now when I try to log data to a micro SD card from multiple sensors, whenever it finishes logging and I check the micro SD card the data becomes all garbled up :frowning:

Here's the code:

#include <SD.h>
#include <Wire.h>
#include <ADXL345.h>
#include <Adafruit_BMP085.h>

File dataFile;

int gyroPin = 0;
int gyroZero = 495;

int tiltXPin = 1;
int tiltYPin = 2;

unsigned long millisecond;

Adafruit_BMP085 bmp;

#define THRESH_TAP 0x40
#define OFSX 0
#define OFSY 0
#define OFSZ 0
#define DUR 0x30
#define LATENT 0x40
#define WINDOW 0xFF
#define THRESH_ACT 0
#define THRESH_INACT 0
#define TIME_INACT 0
#define ACT_INACT_CTL 0
#define THRESH_FF 0
#define TIME_FF 0
#define TAP_AXES B00000111
#define BW_RATE 0
#define POWER_CTL B00001001
#define INT_ENABLE B01100000
#define INT_MAP B00100000
#define DATA_FORMAT B00001010
#define FIFO_CTL 0

byte X0, X1;
byte Y0, Y1;
byte Z0, Z1;
byte int_source;

ADXL345 myACC;

int operationCount = 0;

void setup() {
  Serial.begin(9600); 
  pinMode(1, INPUT);
  pinMode(2, INPUT);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(0, INPUT);
  pinMode(10, OUTPUT);
  myACC.setTHRESH_TAP(THRESH_TAP);
  myACC.setDUR(DUR);
  myACC.setLATENT(LATENT);
  myACC.setWINDOW(WINDOW);
  myACC.setTAP_AXES(TAP_AXES);
  myACC.setPOWER_CTL(POWER_CTL);
  myACC.setINT_MAP(INT_MAP);
  myACC.setDATA_FORMAT(DATA_FORMAT);
  myACC.setINT_ENABLE(INT_ENABLE);

  if (!SD.begin(10)) {
    Serial.println("SD init fail");
    delay(1000);
    setup();
  } 
  else {
    Serial.println("SD init'd");
    Serial.println();
  }
}

void loop() {
  // start writing readings to file
  Serial.print("accl start  ");
  dataFile = SD.open("log.csv", FILE_WRITE);
  Serial.print("accl writing  ");
  // initialize accelerometer
  myACC.readDATAX(&X0, &X1);
  dataFile.print(X0 + (X1 << 8));
  dataFile.print(",");
  myACC.readDATAY(&Y0, &Y1);
  dataFile.print(Y0 + (Y1 << 8));
  dataFile.print(",");
  myACC.readDATAZ(&Z0, &Z1);
  dataFile.print(Z0 + (Z1 << 8));
  dataFile.print(",");
  //myACC.readINT_SOURCE(&int_source);
  dataFile.close();
  Serial.println("accl idle");

  // initialize gyro
  Serial.print("gyro start  ");
  dataFile = SD.open("log.csv", FILE_WRITE);
  Serial.print("gyro writing  ");
  int numberOfReadings = 30;
  int runningVal;
  int runningValForAvg = 0;
  int avgVal;
  int maxVal = 0;

  for (int i=0;i<numberOfReadings;i++) {
    runningVal = abs(analogRead(gyroPin)-gyroZero);
    runningValForAvg += runningVal;
    if (runningVal > maxVal) {
      maxVal = runningVal;
    }
  }

  avgVal = runningValForAvg / numberOfReadings;

  dataFile.print(maxVal);
  dataFile.print(",");
  dataFile.print(avgVal);
  dataFile.print(",");
  dataFile.close();
  Serial.println("gyro idle");

  // initialize tilt sensor
  Serial.print("tilt start  ");
  dataFile = SD.open("log.csv", FILE_WRITE);
  Serial.print("tilt writing  ");
  float tiltX = analogRead(tiltXPin);
  float tiltY = analogRead(tiltYPin);
  dataFile.print(tiltX);
  dataFile.print(",");
  dataFile.print(tiltY);
  dataFile.print(",");
  dataFile.close();
  Serial.println("tilt idle");

  // initialize barometer
  Serial.print("baro start ");
  dataFile = SD.open("log.csv", FILE_WRITE);
  Serial.print("baro writing ");
  dataFile.print(bmp.readTemperature());
  dataFile.print(",");

  dataFile.print(bmp.readPressure());
  dataFile.print(",");

  dataFile.print(bmp.readAltitude());
  dataFile.print(",");

  dataFile.print(bmp.readAltitude(101500));
  dataFile.print(",");
  dataFile.close();
  Serial.println("baro idle");

  // initialize time
  Serial.print("time start  ");
  dataFile = SD.open("log.csv", FILE_WRITE);
  Serial.print("time writing  ");
  millisecond = millis();
  int second = millisecond/1000;
  millisecond = millisecond - (second*1000);
  int minute = second/60;
  second = second - (minute*60);
  int hour = minute/60;
  minute = minute - (hour*60);
  dataFile.print(millisecond);
  dataFile.print(",");
  dataFile.print(second);
  dataFile.print(",");
  dataFile.print(minute);
  dataFile.print(",");
  dataFile.println(hour);
  dataFile.close();
  Serial.println("time idle");
  Serial.println();
  operationCount = operationCount + 1;
  if (operationCount >= 10) {
    Serial.println("halt");
    halt();
  }
  delay(2500);
}

void halt() {
  while (1);
}

And here's the data output (note how it was only supposed to log 10 times whereas in the output it was logged 20 times with incorrect data in between):

4,5,253,2,0,504.00,509.00,238,0,0,0
6,5,252,3,0,504.00,509.00,827,2,0,0
5,5,253,3,1,503.00,509.00,415,5,0,0
6,5,253,2,0,503.00,509.00,4,8,0,0
5,4,254,3,1,504.00,509.00,593,10,0,0
5,5,252,2,0,505.00,509.00,180,13,0,0
5,6,255,2,0,504.00,509.00,769,15,0,0
4,5,253,2,0,504.00,509.00,358,18,0,0
7,4,253,2,0,504.00,509.00,946,20,0,0
6,4,252,2,0,504.00,509.00,534,23,0,0
3,6,253,2,0,506.00,512.00,102,0,0,0
7,4,254,2,0,503.00,509.00,691,2,0,0
5,5,252,3,0,504.00,509.00,279,5,0,0
5,6,252,2,0,503.00,509.00,867,7,0,0
5,4,252,2,0,503.00,509.00,460,10,0,0
5,5,253,3,0,504.00,509.00,48,13,0,0
5,5,254,3,0,504.00,509.00,637,15,0,0
5,5,252,2,0,503.00,509.00,226,18,0,0
5,4,253,2,0,503.00,509.00,814,20,0,0
5,6,253,2,0,504.00,509.00,404,23,0,0

Please help... I really don't know what to do! :~

Chose the tag "code" instead of "quote" to put your code, please.

DrOmni:
Please help... I really don't know what to do! :~

Well, the first thing to do is get rid of that stupid character with the sunglasses. Your can be sure that 8) is not a legitimate command and your sketch is pretty well guaranteed not to compile, so nobody is going to bother. The fact the you actually get some valid numbers suggests the problem is merely one of the order of commands and easily fixed, but who would know?

Why don't you follow this example? Notice that what they do is to write everything to one string and then write the string to the file. You can try something like this.

Regards.

I believe that the problem is in the section of the barometer, because the 2 float number (from the tiltX and tiltY) are write right and the follow data is the data from the time. You have to had 15 numbers and you only have 11, so there are missing 4 the same number of the barometer section. I don't see the initialization of the barometer in the setup() is this right?

luisilva:
I believe that the problem is in the section of the barometer, because the 2 float number (from the tiltX and tiltY) are write right and the follow data is the data from the time. You have to had 15 numbers and you only have 11, so there are missing 4 the same number of the barometer section. I don't see the initialization of the barometer in the setup() is this right?

The tiltX and tiltY belong to the tilt sensor that we pulled from our FRC robot and yeah, I think I should double check on the initialization of the barometer. However, I really don't think this is the main problem since the data is all garbled up and that theoretically has nothing to do with initialization, right? Thanks for the help nonetheless!

Garbled, why do you think that?
For example, in the first line:

Accelerometer:
X = 4
Y = 5
Z = 253

Gyro:
maxVal=2
avgVal=0

Tilt sensor:
tiltX = 504.00
tiltY = 509.00

Barometer:
there's no data for this sensor!

Time:
Millisecond = 238
Second = 0
Minute = 0
Hour = 0

The explanation for the 20 lines instead of 10 is because the program had run 2 times and don't delete the previous values:

4,5,253,2,0,504.00,509.00,238,0,0,0   <- starting measure at 0:0:0.238
6,5,252,3,0,504.00,509.00,827,2,0,0
5,5,253,3,1,503.00,509.00,415,5,0,0
6,5,253,2,0,503.00,509.00,4,8,0,0
5,4,254,3,1,504.00,509.00,593,10,0,0
5,5,252,2,0,505.00,509.00,180,13,0,0
5,6,255,2,0,504.00,509.00,769,15,0,0
4,5,253,2,0,504.00,509.00,358,18,0,0
7,4,253,2,0,504.00,509.00,946,20,0,0
6,4,252,2,0,504.00,509.00,534,23,0,0
3,6,253,2,0,506.00,512.00,102,0,0,0    <- restarting measure at 0:0:0.102
7,4,254,2,0,503.00,509.00,691,2,0,0
5,5,252,3,0,504.00,509.00,279,5,0,0
5,6,252,2,0,503.00,509.00,867,7,0,0
5,4,252,2,0,503.00,509.00,460,10,0,0
5,5,253,3,0,504.00,509.00,48,13,0,0
5,5,254,3,0,504.00,509.00,637,15,0,0
5,5,252,2,0,503.00,509.00,226,18,0,0
5,4,253,2,0,503.00,509.00,814,20,0,0
5,6,253,2,0,504.00,509.00,404,23,0,0

So I think that the program is reading all the sensors and writing the values correctly to the SD card (but the barometer).

You would do well to start every line with a time stamp in seconds or millis(), RTC or Arduino time.
The least you'd be able to do is time between events. Unix seconds is good for long-term logging.
If one event logs real time then that will be stamped and there will be the time correlation.

luisilva:
Garbled, why do you think that?
For example, in the first line:

Accelerometer:
X = 4
Y = 5
Z = 253

Gyro:
maxVal=2
avgVal=0

Tilt sensor:
tiltX = 504.00
tiltY = 509.00

Barometer:
there's no data for this sensor!

Time:
Millisecond = 238
Second = 0
Minute = 0
Hour = 0

The explanation for the 20 lines instead of 10 is because the program had run 2 times and don't delete the previous values:

4,5,253,2,0,504.00,509.00,238,0,0,0   <- starting measure at 0:0:0.238

6,5,252,3,0,504.00,509.00,827,2,0,0
5,5,253,3,1,503.00,509.00,415,5,0,0
6,5,253,2,0,503.00,509.00,4,8,0,0
5,4,254,3,1,504.00,509.00,593,10,0,0
5,5,252,2,0,505.00,509.00,180,13,0,0
5,6,255,2,0,504.00,509.00,769,15,0,0
4,5,253,2,0,504.00,509.00,358,18,0,0
7,4,253,2,0,504.00,509.00,946,20,0,0
6,4,252,2,0,504.00,509.00,534,23,0,0





3,6,253,2,0,506.00,512.00,102,0,0,0    <- restarting measure at 0:0:0.102
7,4,254,2,0,503.00,509.00,691,2,0,0
5,5,252,3,0,504.00,509.00,279,5,0,0
5,6,252,2,0,503.00,509.00,867,7,0,0
5,4,252,2,0,503.00,509.00,460,10,0,0
5,5,253,3,0,504.00,509.00,48,13,0,0
5,5,254,3,0,504.00,509.00,637,15,0,0
5,5,252,2,0,503.00,509.00,226,18,0,0
5,4,253,2,0,503.00,509.00,814,20,0,0
5,6,253,2,0,504.00,509.00,404,23,0,0




So I think that the program is reading all the sensors and writing the values correctly to the SD card (but the barometer).

Why would the program run 2 times? Cuz I tried several times after this attempt and the amount of times that the program runs becomes seemingly random (first attempt: 20, second attempt: 14, third attempt: 17, fourth: 10)...

So is there anything I can change in my code to prevent this from happening? Thanks a lot for the help!

Don't call setup() from within setup().

If there's any doubt at all about whether you're creating to a new file or appending to an existing file, decide to either truncate the file during setup(), or append a header to it so that it is obvious when your sketch has restarted.

Why do you keep closing and re-opening the file?

What is it about the logged data that you think is 'garbled'? I see twice as many samples as you were expecting, perhaps because you've appended two sets of results to the same file, but I don't see any garbled data.

PeterH:
Don't call setup() from within setup().

If there's any doubt at all about whether you're creating to a new file or appending to an existing file, decide to either truncate the file during setup(), or append a header to it so that it is obvious when your sketch has restarted.

Why do you keep closing and re-opening the file?

What is it about the logged data that you think is 'garbled'? I see twice as many samples as you were expecting, perhaps because you've appended two sets of results to the same file, but I don't see any garbled data.

Thanks, already changed setup() to return.

We were using headers in setup() before but the same problem occurred: sometimes the headers were only imported once, sometimes twice, or imported somewhere else in the file.

As for the closing and reopening file thing, I decided to do that because for some reason when we weren't doing it before the output in the serial monitor was messed up and the file wasn't even created. After I added that, everything worked fine.

And yeah, I think I should change my wording because what I meant by garbled data was the two sets of data that were in the file when it was supposed to be only one set.

luisilva:
I believe that the problem is in the section of the barometer, because the 2 float number (from the tiltX and tiltY) are write right and the follow data is the data from the time. You have to had 15 numbers and you only have 11, so there are missing 4 the same number of the barometer section. I don't see the initialization of the barometer in the setup() is this right?

What about this? Do you get any conclusions about it?
Why don't you comment all the code of the loop() function but this section, and after do the opposite (comment only this section)? Is not normal that all the data shows well but this.

DrOmni:
We were using headers in setup() before but the same problem occurred: sometimes the headers were only imported once, sometimes twice, or imported somewhere else in the file.

That's quite an important clue and indicates that setup() is running more than once for a given file. So, either you have not started from a clean state and you're appending to an existing file, or your sketch is actually crashing and restarting and you haven't noticed. I see the current setup() prints a message about SD init each time it runs. That output would tell you for sure whether and when it was restarting.

My personal opinion on "SD logging":

Logging should be sent during program design to a serial output port, ideally the one that has the Arduino monitor connected (or a nice replacement such as Tetaterm.) After one is certain the program ius working correctly, an analysis should be conducted to determine if SD library and memory requirements can fit within the sketch proper without consuming too many resources; as the sketch was known to work correctly prior to adding in the SD lib & code.

If the SD resources are too demanding, then off-load logging to a separate uC hosting the SD interface. Offloading is simple, cheap, and will reduce stress and perhaps lengthen your life on Earth.
Sample logger:
http://www.hackster.io/rayburne/sd-card-serial-logger

Ray

luisilva:

luisilva:
I believe that the problem is in the section of the barometer, because the 2 float number (from the tiltX and tiltY) are write right and the follow data is the data from the time. You have to had 15 numbers and you only have 11, so there are missing 4 the same number of the barometer section. I don't see the initialization of the barometer in the setup() is this right?

What about this? Do you get any conclusions about it?
Why don't you comment all the code of the loop() function but this section, and after do the opposite (comment only this section)? Is not normal that all the data shows well but this.

Already did on the code end. Thanks! Didn't have our board with me during the weekend. Will try it first thing tomorrow!

PeterH:

DrOmni:
We were using headers in setup() before but the same problem occurred: sometimes the headers were only imported once, sometimes twice, or imported somewhere else in the file.

That's quite an important clue and indicates that setup() is running more than once for a given file. So, either you have not started from a clean state and you're appending to an existing file, or your sketch is actually crashing and restarting and you haven't noticed. I see the current setup() prints a message about SD init each time it runs. That output would tell you for sure whether and when it was restarting.

might be a problem with the file or the sd card then. Cuz when we were doing that I made it so that every time the header was imported it outputs a message into the serial log and a variable named ifHeader changes its value from 0 to 1. In loop(), I also made it so that it checks if the ifHeader var equals to 1. If it didn't, then it loops back to setup(). However every time I did it the serial log looked fine, and the ifHeader var always stayed 1 in loop(), and the problem still persisted...

Sigh, we have a tethered launch coming up tomorrow, presentation on Tuesday and real launch on Thursday. Gotta figure this out somehow :confused:

Loops back to setup()? That doesn't sound good at all.

GoForSmoke:
Loops back to setup()? That doesn't sound good at all.

My thinking was that if the headers weren't successfully imported, then there must've been some problems during setup() as that's where the headers were supposed to be importing.

Like I wrote a few posts ago, for the barometer is necessary a initialization.
The code of the example in the page of Adafuit, is what follows:

#include <Wire.h>
#include <Adafruit_BMP085.h>

/*************************************************** 
  This is an example for the BMP085 Barometric Pressure & Temp Sensor

  Designed specifically to work with the Adafruit BMP085 Breakout 
  ----> https://www.adafruit.com/products/391

  These displays use I2C to communicate, 2 pins are required to  
  interface
  Adafruit invests time and resources providing this open source code, 
  please support Adafruit and open-source hardware by purchasing 
  products from Adafruit!

  Written by Limor Fried/Ladyada for Adafruit Industries.  
  BSD license, all text above must be included in any redistribution
 ****************************************************/

// Connect VCC of the BMP085 sensor to 3.3V (NOT 5.0V!)
// Connect GND to Ground
// Connect SCL to i2c clock - on '168/'328 Arduino Uno/Duemilanove/etc thats Analog 5
// Connect SDA to i2c data - on '168/'328 Arduino Uno/Duemilanove/etc thats Analog 4
// EOC is not used, it signifies an end of conversion
// XCLR is a reset pin, also not used here

Adafruit_BMP085 bmp;
  
void setup() {
  Serial.begin(9600);
  if (!bmp.begin()) {
	Serial.println("Could not find a valid BMP085 sensor, check wiring!");
	while (1) {}
  }
}
  
void loop() {
    Serial.print("Temperature = ");
    Serial.print(bmp.readTemperature());
    Serial.println(" *C");
    
    Serial.print("Pressure = ");
    Serial.print(bmp.readPressure());
    Serial.println(" Pa");
    
    // Calculate altitude assuming 'standard' barometric
    // pressure of 1013.25 millibar = 101325 Pascal
    Serial.print("Altitude = ");
    Serial.print(bmp.readAltitude());
    Serial.println(" meters");

  // you can get a more precise measurement of altitude
  // if you know the current sea level pressure which will
  // vary with weather and such. If it is 1015 millibars
  // that is equal to 101500 Pascals.
    Serial.print("Real altitude = ");
    Serial.print(bmp.readAltitude(101500));
    Serial.println(" meters");
    
    Serial.println();
    delay(500);
}

Look to the lines in the setup() function :

  if (!bmp.begin()) {
	Serial.println("Could not find a valid BMP085 sensor, check wiring!");
	while (1) {}
  }

The meaning of this lines is, if the module can't be started stop the execution of the program.

luisilva:
Like I wrote a few posts ago, for the barometer is necessary a initialization.
The code of the example in the page of Adafuit, is what follows:

#include <Wire.h>

#include <Adafruit_BMP085.h>

/***************************************************
  This is an example for the BMP085 Barometric Pressure & Temp Sensor

Designed specifically to work with the Adafruit BMP085 Breakout
  ----> BMP085 Barometric Pressure/Temperature/Altitude Sensor- 5V ready : ID 391 : Adafruit Industries, Unique & fun DIY electronics and kits

These displays use I2C to communicate, 2 pins are required to 
  interface
  Adafruit invests time and resources providing this open source code,
  please support Adafruit and open-source hardware by purchasing
  products from Adafruit!

Written by Limor Fried/Ladyada for Adafruit Industries. 
  BSD license, all text above must be included in any redistribution
****************************************************/

// Connect VCC of the BMP085 sensor to 3.3V (NOT 5.0V!)
// Connect GND to Ground
// Connect SCL to i2c clock - on '168/'328 Arduino Uno/Duemilanove/etc thats Analog 5
// Connect SDA to i2c data - on '168/'328 Arduino Uno/Duemilanove/etc thats Analog 4
// EOC is not used, it signifies an end of conversion
// XCLR is a reset pin, also not used here

Adafruit_BMP085 bmp;
 
void setup() {
  Serial.begin(9600);
  if (!bmp.begin()) {
Serial.println("Could not find a valid BMP085 sensor, check wiring!");
while (1) {}
  }
}
 
void loop() {
    Serial.print("Temperature = ");
    Serial.print(bmp.readTemperature());
    Serial.println(" *C");
   
    Serial.print("Pressure = ");
    Serial.print(bmp.readPressure());
    Serial.println(" Pa");
   
    // Calculate altitude assuming 'standard' barometric
    // pressure of 1013.25 millibar = 101325 Pascal
    Serial.print("Altitude = ");
    Serial.print(bmp.readAltitude());
    Serial.println(" meters");

// you can get a more precise measurement of altitude
  // if you know the current sea level pressure which will
  // vary with weather and such. If it is 1015 millibars
  // that is equal to 101500 Pascals.
    Serial.print("Real altitude = ");
    Serial.print(bmp.readAltitude(101500));
    Serial.println(" meters");
   
    Serial.println();
    delay(500);
}




Look to the lines in the *setup()* function :



if (!bmp.begin()) {
Serial.println("Could not find a valid BMP085 sensor, check wiring!");
while (1) {}
  }




The meaning of this lines is, if the module can't be started stop the execution of the program.

That's what I did to my code :slight_smile: Thanks for the help!