micrologger.pde
/* Datalogger
Eric Ayars
July '10
This Arduino program controls and reads three axes from an MMA7260 3-axis
accelerometer, and writes the data to a micro-SD card.
It uses the Fat16 library by Will Greiman. Will, if you're reading this, my
hat is off to you. The deeper I get into the library the more impressed I
am by the quality of the work you did. Very nice code!
Errors will be given on the LED.
Here are those errors, in flashes:
2,1 "card.init"
2,2 "Fat16::init"
3,1 "file.open() error"
3,2 "file.close() error"
4,1 "error when trying to write header"
4,2 "error when trying to write data"
4,3 "file.sync() error"
*/
/* User-selected values
The following definitions can be set before sending the code to the
Arduino, and change the datalogger's behavior.
LOG_INTERVAL is the time in milliseconds between samples.
GAIN is the user-selectable range on the MMA7260 accelerometer, and
should be set to one of {1, 2, 4, 6}. The range for 1 is actually
1.5, if you care about that...
The default value (if you set something wrong) is 6.
DEBUG should be set to 0 for optimal data-collection speed. If set to
1, the Arduino sends verbose messages to the serial port about
everything it is doing.
CONVERT_VALUES should be set to 1 to have the Arduino convert raw
A/D integers into float SI values of acceleration. Set it to 0 if
you would prefer the data file to contain raw A/D values.
TIME_OR_BUTTON changes the stop condition for data collection. If set
to 1, the Arduino stops after the number of milliseconds set by
COLLECTION_MILLIS. If set to 0, it stops on a second button press.
COLLECTION_MILLIS applies only if TIME_OR_BUTTON is set to 1, and is
the number of milliseconds to collect data.
*/
#define LOG_INTERVAL 100
#define GAIN 1
#define DEBUG 0
#define CONVERT_VALUES 1
#define TIME_OR_BUTTON 0
#define COLLECTION_MILLIS 10000
// Definitions below this point should probably only be changed if you
// know what you're doing.
#define LONG_INTERVAL 500 // number of mills to wait, for a longish time
#define SHORT_INTERVAL 100 // number of mills to wait, short time.
#define BUTTON 4 // BUTTON pin
#define LED 6 // LED pin
#define GS1 7 // Gain select 1 pin
#define GS2 8 // Gain select 2 pin
#define SLP 9 // Sleep pin
#define X_ACCEL 2 // x input
#define Y_ACCEL 1 // y input
#define Z_ACCEL 0 // z input
#include <Fat16.h>
#include <Fat16util.h>
unsigned long int logTime = 0; // time of last log event
unsigned long int currentTime = 0; // Now, with startTime subtracted
unsigned long int startTime = 0; // When did we start collection?
int x, y, z; // data storage
float conversionFactor;
SdCard card; // 'card' is of type 'SdCard'.
Fat16 file; // 'file' is of type 'Fat16'.
boolean lastButtonState = HIGH;
boolean buttonPressed(byte button) {
// Checks to see if the button was pressed since last check.
boolean currentButtonState = digitalRead(button);
boolean state;
if (lastButtonState && !currentButtonState) {
// Button was high, is now low. The button has been pressed.
state = true;
} else {
// Not pressed.
state = false;
}
lastButtonState = currentButtonState;
#if DEBUG
Serial.println("buttonPressed()");
#endif // DEBUG
return state;
}
void flashLED(byte count, int interval) {
// Just what it says: it flashes the LED 'count' times for
// the given on/off interval each time.
byte j;
#if DEBUG
Serial.print("flashLED(");
Serial.print(count, DEC);
Serial.print(",");
Serial.print(interval, DEC);
Serial.println(")");
#endif //DEBUG
for (j=0; j<count; j++) {
digitalWrite(LED, HIGH);
delay(interval);
digitalWrite(LED, LOW);
delay(interval);
}
}
void sendError(byte errorGroup, byte errorItem) {
// Flashes the LED count times, then pauses, then repeats.
// Used to tell the user what the problem is.
#if DEBUG
Serial.print("sendError(");
Serial.print(errorGroup, DEC);
Serial.print(", ");
Serial.print(errorItem, DEC);
Serial.println(")");
#endif //DEBUG
while (true) {
flashLED(errorGroup, SHORT_INTERVAL);
delay(LONG_INTERVAL/2);
flashLED(errorItem, SHORT_INTERVAL);
delay(LONG_INTERVAL);
}
}
#if CONVERT_VALUES // This function is only needed if you're converting.
float convert(int ADValue) {
// Converts integer A/D values to float, depending on the value of
// conversionFactor.
float gValue = float(ADValue - 512)*conversionFactor;
return gValue;
}
#endif // CONVERT_VALUE
void setup(void) {
pinMode(LED, OUTPUT);
pinMode(BUTTON, INPUT);
pinMode(GS1, OUTPUT);
pinMode(GS2, OUTPUT);
pinMode(SLP, OUTPUT);
// Initialize the SD card or send the error
if (!card.init()) sendError(2,1);
// Locate the Fat16 volume or send the error
if (!Fat16::init(&card)) sendError(2,2);
// Set gain pins
// The MMA7260 has two pins that set the gain range
// 0 0 -> 1.5 g
// 0 1 -> 2 g
// 1 0 -> 4 g
// 1 1 -> 6 g
switch (GAIN) {
case 1:
digitalWrite(GS1, 0);
digitalWrite(GS2, 0);
conversionFactor = 0.0394775;
break;
case 2:
digitalWrite(GS1, 1);
digitalWrite(GS2, 0);
conversionFactor = 0.0526367;
break;
case 4:
digitalWrite(GS1, 0);
digitalWrite(GS2, 1);
conversionFactor = 0.1052734;
break;
default: // 6 g range is default.
digitalWrite(GS1, 1);
digitalWrite(GS2, 1);
conversionFactor = 0.1579101;
}
// Start asleep, to save power.
digitalWrite(SLP, LOW);
#if DEBUG
Serial.begin(9600);
Serial.println("Got through setup()");
#endif //DEBUG
} // end setup()
/*
* Main Loop
*/
void loop(void) {
// Start by waiting for the "go" button.
#if DEBUG
Serial.println("loop, wait for button");
#endif // DEBUG
while (!buttonPressed(BUTTON)) {
// Button is not pressed, so just sit here slowly flashing.
flashLED(1, LONG_INTERVAL);
}
/*
* Button has been pressed now, so open/create the file.
* The "create a new file" code below is directly from
* Will Greiman's Fat16 examples folder. Sweet code!
*/
char name[] = "LOGGER00.TXT";
for (int j=0;j<100;j++) {
name[6] = j/10 + '0';
name[7] = j%10 + '0';
if (file.open(name, O_CREAT | O_EXCL | O_WRITE)) break;
}
/* Here's how that works:
O_WRITE = 0x02 = 00000010
O_CREAT = 0x10 = 00010000
O_EXCL = 0x20 = 00100000
So (O_WRITE | O_CREAT | O_EXCL) = 00110010. This set of flags goes
to the file.open() routine, and it's written so that if both
O_CREAT and O_EXCL are set and the file already exists, then
file.open() returns false.
The for loop tries sequential LOGGERxx.CSV filenames until it finds
one that doesn't exist, in which case it returns true and the break
statement stops the loop. So each time you take data, it writes
to a new filename.
*/
#if DEBUG
for (byte j=0;j<12;j++) {
Serial.print(name[j]);
}
Serial.println();
#endif // DEBUG
if (!file.isOpen()) sendError(3,1);
// Now write the file header
file.writeError = false;
#if CONVERT_VALUES
file.println("xyz values in m/s/s");
#endif //CONVERT_VALUES
file.println("t (s)\tx\ty\tz");
#if DEBUG
Serial.println("t (s)\tx\ty\tz");
#endif //DEBUG
if (file.writeError || !file.sync()) sendError(4,1);
// File is open and header written, so give a countdown.
for (int j=5;j>0;j--) {
flashLED(j, SHORT_INTERVAL);
delay(LONG_INTERVAL);
}
// And we're live! Turn on the LED, wake up the accelerometer.
digitalWrite(LED, HIGH);
digitalWrite(SLP, HIGH);
/*
* Here's the data collection. There are two ways of deciding when to
* stop: "fixed time" (TIME_OR_BUTTON = 1) or "collect until button"
* (TIME_OR_BUTTON = 0). So start with some compiler directives that
* put in the right bit of while stop-code for our situation.
*/
startTime = millis();
#if TIME_OR_BUTTON
while (millis()-startTime < COLLECTION_MILLIS) {
// collect data for a set time.
#else
while (!buttonPressed(BUTTON)) {
// keep going until button is pressed.
#endif //TIME_OR_BUTTON
// This next bit waits around until the current time is an
// exact multiple of LOG_INTERVAL.
do {
logTime = millis();
} while (logTime % LOG_INTERVAL);
// Now is a good time to log data, so...
currentTime = logTime - startTime;
x = analogRead(X_ACCEL);
y = analogRead(Y_ACCEL);
z = analogRead(Z_ACCEL);
file.print((float(currentTime) / 1000.0));
file.print("\t");
#if CONVERT_VALUES // We're saving converted values instead of raw, so...
file.print(convert(x));
file.print("\t");
file.print(convert(y));
file.print("\t");
file.println(convert(z));
#else // Here's code for saving raw values
file.print(x);
file.print("\t");
file.print(y);
file.print("\t");
file.println(z);
#endif // CONVERT_VALUES
#if DEBUG
Serial.print(currentTime / 1000);
Serial.print(".");
Serial.print(currentTime % 1000);
Serial.print("\t");
Serial.print(x);
Serial.print("\t");
Serial.print(y);
Serial.print("\t");
Serial.println(z);
#endif // DEBUG
// Any problem writing?
if (file.writeError) sendError(4,2);
} // end of whichever while loop is put in by the compiler.
// Close file, put accelerometer back to sleep.
if (!file.close()) sendError(3,2);
#if DEBUG
Serial.println("close()");
#endif //DEBUG
digitalWrite(LED, LOW);
digitalWrite(SLP, LOW);
} // end loop()
Generated by GNU enscript 1.6.4.