I am a bit of a novice with the Arduino, and have had to learn a lot for my High Altitude balloon launch. I am getting ready to launch this, but for some reason, my datalogger, printing to an SD card, will work in the setup function but not the loop.
If someone could explain what is wrong with my code, and any improvements that could be made to it, it would be very helpful. I am using SPI, SD, I2C, Wire, Sparkfun BMP, and MPU6050 library.
#include <SPI.h>
#include <SD.h>
#include <math.h>
#define thermoPin 0
#include <SFE_BMP180.h>
SFE_BMP180 pressure;
#define ALTITUDE 87.8
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#include <Wire.h>
MPU6050 mpu;
#define OUTPUT_READABLE_YAWPITCHROLL
#define OUTPUT_READABLE_REALACCEL
#define analogInPin 3 // Analog input pin that the potentiometer is attached to
int sensorValue = 0; // value read from the sensor
// MPU control/status vars
bool dmpReady = false; // set true if DMP init was successful
uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU
uint8_t devStatus; // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize; // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount; // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer
// orientation/motion vars
Quaternion q; // [w, x, y, z] quaternion container
VectorInt16 aa; // [x, y, z] accel sensor measurements
VectorInt16 aaReal; // [x, y, z] gravity-free accel sensor measurements
VectorFloat gravity; // [x, y, z] gravity vector
float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector
volatile bool mpuInterrupt = false; // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
mpuInterrupt = true;
}
#define photocellPin 2
// the cell and 10K pulldown are connected to a9
int photocellReading; // the analog reading from the analog resistor divider
#define chipSelect 4
void setup() {
pinMode(A5, OUTPUT);
pinMode(A4, OUTPUT);
// Open dataFile communications and wait for port to open:
if (!SD.begin(chipSelect)) {
// don't do anything more:
return;
}
File dataFile = SD.open("datalog.txt", FILE_WRITE);
if(dataFile) {
// Initialize the sensor (it is important to get calibration values stored on the device).
if (pressure.begin())
dataFile.println("BMP180 init success");
else
{
dataFile.println("BMP180 init fail\n\n");
while(1); // Pause forever.
}
// join I2C bus (I2Cdev library doesn't do this automatically)
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
Wire.begin();
TWBR = 24; // 400kHz I2C clock (200kHz if CPU is 8MHz)
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
Fastwire::setup(400, true);
#endif
dataFile.println(F("Initializing I2C devices..."));
mpu.initialize();
dataFile.println(F("Testing device connections..."));
dataFile.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
// load and configure the DMP
dataFile.println(F("Initializing DMP..."));
devStatus = mpu.dmpInitialize();
mpu.setXGyroOffset(220);
mpu.setYGyroOffset(76);
mpu.setZGyroOffset(-85);
mpu.setZAccelOffset(1788); // 1688 factory default for my test chip
mpu.setDMPEnabled(true);
mpuIntStatus = mpu.getIntStatus();
dmpReady = true;
packetSize = mpu.dmpGetFIFOPacketSize();
dataFile.close();
}
}
void loop() {
// make a string for assembling the data to log:
int valF, valC;
valF = analogRead(thermoPin);
valC = analogRead(thermoPin);
File dataFile = SD.open("datalog.txt", FILE_WRITE);
// if the file is available, write to it:
if (dataFile) {
dataFile.print(F("Millis since Launch"));
dataFile.println(millis());
dataFile.print(F(", Analog Val."));
dataFile.print(valF);
photocellReading = analogRead(photocellPin);
dataFile.print(F(" Analog reading = "));
dataFile.println(photocellReading); // the raw analog reading
// We'll have a few threshholds, qualitatively determined
dataFile.println();
char status;
double T,P,p0,a;
dataFile.print(F("provided altitude: "));
dataFile.print(ALTITUDE,0);
dataFile.println(F(" meters, "));
status = pressure.startTemperature();
if (status != 0)
{
// Wait for the measurement to complete:
delay(status);
status = pressure.getTemperature(T);
if (status != 0)
{
// Print out the measurement:
dataFile.print(F("temperature: "));
dataFile.print(T,2);
dataFile.println(" deg F");
status = pressure.startPressure(3);
if (status != 0)
{
// Wait for the measurement to complete:
delay(status);
status = pressure.getPressure(P,T);
if (status != 0)
{
// Print out the measurement:
dataFile.print(F("absolute pressure: "));
dataFile.print(P,2);
dataFile.print(F(" mb, "));
p0 = pressure.sealevel(P,ALTITUDE); // we're at 87.08 meters (Home)
dataFile.print(F("relative (sea-level) pressure: "));
dataFile.print(p0,2);
dataFile.print(F(" mb, "));
dataFile.print(p0*0.0295333727,2);
dataFile.println(" inHg");
a = pressure.altitude(P,p0);
dataFile.print(F("computed altitude: "));
dataFile.print(a,0);
dataFile.print(F(" meters, "));
}
else dataFile.println("error retrieving pressure measurement\n");
}
else dataFile.println("error starting pressure measurement\n");
}
else dataFile.println("error retrieving temperature measurement\n");
}
else dataFile.println("error starting temperature measurement\n");
// if programming failed, don't try to do anything
if (!dmpReady) return;
// wait for MPU interrupt or extra packet(s) available
while (!mpuInterrupt && fifoCount < packetSize) {
}
// reset interrupt flag and get INT_STATUS byte
mpuInterrupt = false;
mpuIntStatus = mpu.getIntStatus();
// get current FIFO count
fifoCount = mpu.getFIFOCount();
// check for overflow (this should never happen unless our code is too inefficient)
if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
// reset so we can continue cleanly
mpu.resetFIFO();
dataFile.println(F("FIFO overflow!"));
// otherwise, check for DMP data ready interrupt (this should happen frequently)
} else if (mpuIntStatus & 0x02) {
// wait for correct available data length, should be a VERY short wait
while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
// read a packet from FIFO
mpu.getFIFOBytes(fifoBuffer, packetSize);
// track FIFO count here in case there is > 1 packet available
// (this lets us immediately read more without waiting for an interrupt)
fifoCount -= packetSize;
#ifdef OUTPUT_READABLE_YAWPITCHROLL
// display Euler angles in degrees
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
dataFile.print(F("ypr\t"));
dataFile.print(ypr[0] * 180/M_PI);
dataFile.print(F("\t"));
dataFile.print(ypr[1] * 180/M_PI);
dataFile.print(F("\t"));
dataFile.println(ypr[2] * 180/M_PI);
#endif
#ifdef OUTPUT_READABLE_REALACCEL
// display real acceleration, adjusted to remove gravity
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetAccel(&aa, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
dataFile.print(F("areal\t"));
dataFile.print(aaReal.x);
dataFile.print(F("\t"));
dataFile.print(aaReal.y);
dataFile.print(F("\t"));
dataFile.println(aaReal.z);
#endif
}
// read the analog in value:
sensorValue = analogRead(analogInPin);
// print the results to the dataFile monitor:
dataFile.print(F(" analog Val of OZONE= " ));
dataFile.println(sensorValue);
dataFile.close();
}
analogWrite(A4, 168);
delay(3000);
analogWrite(A4, 0);
delay(750);
analogWrite(A5, 0);
delay(250);
analogWrite(A5, 168);
delay(3000);
analogWrite(A4, 168);
delay(2000);
analogWrite(A4, 0);
delay(1000);
}
Possible problems (correct me if I'm wrong):
- I noticed that the MPU6050 stops transmitting if delays are put into it
EDIT: I have now tried the script but commented out the accelerometer (MPU6050) portion, and it successfully writes to the SD card. could someone explain why the accelerometer portion messes up the rest of the code?
1 Answer 1
I haven't followed all the details of your code, nor looked into all of the libraries being used, so you may need to ignore some of the following comments.
You write "my datalogger, printing to an sd card, will work in the setup function but not the loop". However, you don't say what you mean by "work", nor how you tell if it works.
It looks like your code in
loop()
uses the same filename as insetup()
. If so, there's no reason to write a file insetup()
because it will just be overwritten later. (If you have a reason, use a different file name.)Sequences like
status = pressure.startTemperature(); if (status != 0) {... delay(status); ... }
look suspect. The parameter to Arduino'sdelay()
function is a number of milliseconds, in anunsigned long
; is yourstartTemperature()
supposed to return a status that's usable as the number of milliseconds to delay?Your
status
is declared viachar status;
andchar
is a signed type. Ifstatus
is negative, the calldelay(status);
will first convertstatus
to a signedlong
by sign-extension. Then it will convert the signedlong
to anunsigned long
with the same bit pattern. This will ensure a delay of at least 7 weeks, which presumably is longer than you mean to wait. [Ref: See the "Conversions from Signed Integral Types" table in microsoft.com/en-us/library's Conversions from Signed Integral Types webpage.]To avoid those long and tedious sequences of
dataFile.print(...);
statements, you can#include Streaming.h
, which is a contributed library that adds some "syntactic sugar" to Arduino C. At compile time it converts C++-like<<
stream operators to.print()
calls, without increasing code size. If you don't haveStreaming.h
installed, install it viaStreaming5.zip
from arduiniana.org. Then, for example, in place ofdataFile.print(F("areal\t")); dataFile.print(aaReal.x); dataFile.print(F("\t")); dataFile.print(aaReal.y); dataFile.print(F("\t")); dataFile.println(aaReal.z);
write
dataFile << F("areal\t") << aaReal.x << '\t' << aaReal.y << '\t' << aaReal.z << endl;
- Whether using
Streaming.h
or not, writingF("\t")
instead of'\t'
uses half a dozen bytes of program memory where one will do, and uses the same amount or more of RAM. That is, while F() saves on RAM usage for long strings, it's of negative value for single characters and short strings.
Explore related questions
See similar questions with these tags.