Troubleshooting SD Card Writing and ADXL345 Integration on Arduino Nano

Hi, I have a problem with my Arduino code for my Arduino Nano clone. I am making a device that measures different things and prints these in the console and saves them on a micro SD Card.

#include <TinyGPSPlus.h>

#include <SoftwareSerial.h>

#include <SD.h>

#include <Adafruit_BMP280.h>

#include <Wire.h>

//WIRING
//ARDUINO - MODULE
//----------------
//          GPS
//4         TX
//3         RX
//----------------
//          SD
//10        CS
//11        MOSI
//12        MISO
//13        SCK
//----------------
//          MQ-4
//A0        AO
//----------------
//          APC220
//----------------
//          BMP280
//3.3v      VCC !BELANGRIJK!
//A4        SDA
//A5        SCL
//----------------
//          Buzzer
//7         VCC
//GND       GND

//GPS
static const uint32_t GPSBaud = 9600;
static const int RXPin = 3, TXPin = 4;
TinyGPSPlus gps;
SoftwareSerial ss(RXPin, TXPin);

// SD Card
const int chipSelect = 10; // CS pin
File dataFile;

//MQ-4
#define gasPin A0

//BMP280
Adafruit_BMP280 bmp;
float pressureAtZero = 0;

//adxl345
int ADXL345 = 0x53;
float X_out, Y_out, Z_out;

void setup() {
  Serial.begin(9600);
  ss.begin(GPSBaud);

  pinMode(gasPin, INPUT);

  // Initialize SD Card
  if (!SD.begin(chipSelect)) {
    Serial.println(F("SD Card initialization failed!"));
    //while (1);
  }

  Wire.begin(); // Initiate the Wire library
  // Set ADXL345 in measuring mode
  Wire.beginTransmission(ADXL345); // Start communicating with the device
  Wire.write(0x2D); // Access/ talk to POWER_CTL Register - 0x2D
  // Enable measurement
  Wire.write(8); // (8dec -> 0000 1000 binary) Bit D3 High for measuring enable
  Wire.endTransmission();

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

  /* Default settings from datasheet.  */
  bmp.setSampling(Adafruit_BMP280::MODE_NORMAL, /* Operating Mode. */
    Adafruit_BMP280::SAMPLING_X2, /* Temp. oversampling */
    Adafruit_BMP280::SAMPLING_X16, /* Pressure oversampling */
    Adafruit_BMP280::FILTER_X16, /* Filtering. */
    Adafruit_BMP280::STANDBY_MS_500); /* Standby time. */
  pressureAtZero = bmp.readPressure() / 100;

  dataFile = SD.open("data.csv", FILE_WRITE);
  Serial.println("Opened");
  if (dataFile) {
    dataFile.println("Time;Date;Latitude;Longitude;Altitude_GPS;Speed;Satellites;Temperature;Pressure_HPa;Altitude_Baro;GasReading");
    dataFile.close();
    Serial.println("write succesfull");
  } else {
    Serial.println("failed");
  }
}

void loop() {
  while (ss.available() > 0) {
    if (gps.encode(ss.read())) {

      //Wire.beginTransmission(ADXL345);
      //  Wire.write(0x32); // Start with register 0x32 (ACCEL_XOUT_H)
      //Wire.endTransmission(false);
      //Wire.requestFrom(ADXL345, 6, true); // Read 6 registers total, each axis value is stored in 2 registers
      //X_out = ( Wire.read()| Wire.read() << 8); // X-axis value
      //X_out = X_out/256; //For a range of +-2g, we need to divide the raw values by 256, according to the datasheet

      //Y_out = ( Wire.read()| Wire.read() << 8); // Y-axis value
      //Y_out = Y_out/256;
      //Z_out = ( Wire.read()| Wire.read() << 8); // Z-axis value
      //Z_out = (Z_out-56.32)/256;

      //char xyz[20];
      //xyz[0] = '\0';
      //snprintf(xyz,20,"%s;%s;%s;",String(X_out).c_str(),String(Y_out).c_str(),String(Z_out).c_str());

      char output[121];
      output[0] = '\0';
      snprintf(output, 121, "%s;%s;%s,%s;%s;%s;%s;%s;%s;%s;%s;", (gps.time.isValid() ? String(gps.time.value()).c_str() : "N/A"), //time
        (gps.date.isValid() ? String(gps.date.value()).c_str() : "N/A"), //date
        (gps.location.isValid() ? String(gps.location.lat(), 6).c_str() : "N/A"), //latitude
        (gps.location.isValid() ? String(gps.location.lng(), 6).c_str() : "N/A"), //longitude
        (gps.altitude.isValid() ? String(gps.altitude.value()).c_str() : "N/A"), //Altitude GPS
        (gps.speed.isValid() ? String(gps.speed.mps()).c_str() : "N/A"), //Speed
        (gps.satellites.isValid() ? String(gps.satellites.value()).c_str() : "N/A"), //Satellites
        String(bmp.readTemperature()).c_str(), //Temp
        String(bmp.readPressure() / 100).c_str(), //Pressure HPa
        String(bmp.readAltitude(pressureAtZero)).c_str(), //Altitude Baro
        String(analogRead(gasPin)).c_str()
      );
      //Serial.println(xyz);
      Serial.println(output);
      dataFile = SD.open("data.csv", FILE_WRITE);
      if (dataFile) {
        dataFile.println(output);
        dataFile.flush();
        dataFile.close();
        Serial.println("write succesfull");
      } else {
        Serial.println("error opening file");
      }
    }
  }
}

I have two problems:
The first one is that nothing is being written to the SD card.
The second problem is that when I uncomment the ADXL345 part, char xyz is being printed, but not the output for some reason. Also, when I try to print 'xyz' and 'output' in one Serial print line by doing 'Serial.println(String(xyz) + String(output));', a blank line is printed.
When I comment the ADXL345 part, the output is being printed.

I'm stuck here for a few days now, does anyone have suggestions?

The sketch is using 96% of the storage and 80% of RAM, could that have an influence?

80% ram uasage is a bit much but might work. Further be aware that the Nano is a 5V device; this means that the outputs (e.g. MOSI and SCK) are 5V signals and can damage your SD card. A SD module might or might not cater for that. Which SD module are you using?

If you remove the code for the SD card (replace with some serial prints) , do you get the expected results?

I can not really look at your code at the moment to advise better, sorry for that.

I am using the HW-125 micro sd card adapter. It's rated for 3 to 5v logic signals.
If I remove the SD card code the output is being printed, as expected:

11264400;220224;52.234434,6.623663;N/A;0.05;N/A;27.55;988.33;0.02;37;
11264500;220224;52.234434,6.623663;N/A;0.12;N/A;27.55;988.33;0.02;36;
11264500;220224;52.234434,6.623663;N/A;0.12;N/A;27.55;988.33;0.02;37;
11264500;220224;52.234434,6.623663;N/A;0.12;N/A;27.55;988.33;0.02;36;
11264500;220224;52.234434,6.623663;N/A;0.12;N/A;27.55;988.33;0.02;36;
11264600;220224;52.234434,6.623663;N/A;0.17;N/A;27.55;988.33;0.02;36;
11264600;220224;52.234434,6.623663;N/A;0.17;N/A;27.55;988.33;0.02;37;
11264600;220224;52.234434,6.623663;N/A;0.17;N/A;27.54;988.32;0.04;36;
11264600;220224;52.234434,6.623663;N/A;0.17;N/A;27.54;988.32;0.04;36;

The SD card module works with the read and write example from de SD.h library.

When I uncomment the ADXL345 part, I get this:

-0,34;-1,01;0,67
N/A;N/A;N/A,N/A;N/A;N/A;N/A;27.32;988.33;0,03;42;
-0,34;-1,01;0,68
N/A;N/A;N/A,N/A;N/A;N/A;N/A;27.32;988.33;0,03;42;
-0,32;-1,00;0,69

-0,35;-0,99;0,68

-0,36;-1,01;0,67

-0,34;-1,01;0,66

-0,34;-1,02;0,67

-0,33;-1,00;0,68

So it looks like that when the GPS data is valid, there is some error that causes the output to turn into a blank line.

If I interpret correctly, both the SD card module and the ADXL345 work fine by themselves, but they don't work together. If that's the case, then it sounds like you may be the victim of a circuit design flaw found in most microSD card modules. The module is supposed to release the MISO line when its CS line is not asserted so the other device can transmit on MISO. But the module doesn't do that. The solution is to solder a wire in place, and cut an existing trace. The fix is shown here:

https://forum.arduino.cc/t/gps-locator-gps-data-writing-to-sd-card/683771/2

Make sure your module looks like the one in the picture.

Edit: Sorry I didn't look at your code. It looks like you are using I2c for the ADXL. If that's the case, there should be no conflict even if MISO is not released.

Use of String objects will cause an AVR-based Arduino to crash unexpectedly. Strings are never necessary, and they take up (and corrupt) valuable memory, so if you want this program to run reliably, get rid of all the Strings, replacing them with simple Serial.print() statements.

Thanks, I will try that tomorrow, but when I use the 'output' part and the SD part, a blank line is printed in the console. Without the SD part, it is printed. In the link you send, the thread starter has data being printed in the console, but by me it is not working.

When I try to use the GPS part, ADXL345 part and the SD card module together it's a complete mess in the console, with half printed strings etc.

I have an APC220 module which I eventually will wire to pin 0 and 1, so that everything that is printed in the console is send via the APC220. The problem is that you can only send one thing per second, so multiple Serial.print() statements will not work. I already tried that and then I got a few lines and then nothing until I pressed the reset button.

Maybe I will buy a Nano Every, which has more memory and RAM, could that be a solution?

I can't imagine why not. What does "send one thing per second" mean?

Using the String objects certainly will not work in the long run, on an AVR-based Arduino.

Well if you only have one device using SPI (the SD module), then the design flaw shouldn't be the cause of your problems. The module should work fine if it's the only SPI device. It just doesn't play well with others.

I have tried to do more Serial.print() statements in this code:

#include <TinyGPSPlus.h>
#include <SoftwareSerial.h>
#include <SD.h>
#include  <Adafruit_BMP280.h>
#include <Wire.h> 

// GPS
static const int RXPin = 3, TXPin = 4;

//WIRING
//ARDUINO - MODULE
//----------------
//          GPS
//4         TX
//3         RX
//----------------
//          SD
//10        CS
//11        MOSI
//12        MISO
//13        SCK
//----------------
//          MQ-4
//A0        AO
//----------------
//          APC220
//----------------
//          BMP280
//3.3v      VCC !BELANGRIJK!
//A4        SDA
//A5        SCL
//----------------
//          Buzzer
//7         VCC
//GND       GND


//GPS
static const uint32_t GPSBaud = 9600;
TinyGPSPlus gps;
SoftwareSerial ss(RXPin, TXPin);

// SD Card
const int chipSelect = 10; // CS pin
File dataFile;

//MQ-4
#define gasPin A0
int gasValue;

//BMP280
Adafruit_BMP280 bmp;
float pressureAtZero = 0;

//adxl345
int ADXL345 = 0x53;
float X_out, Y_out, Z_out;

void setup() {
  Serial.begin(9600);
  ss.begin(GPSBaud);

  pinMode(gasPin, INPUT);

  // Initialize SD Card
  if (!SD.begin(chipSelect)) {
    Serial.println(F("SD Card initialization failed!"));
    //while (1);
  }
  
  Wire.begin(); // Initiate the Wire library
  // Set ADXL345 in measuring mode
  Wire.beginTransmission(ADXL345); // Start communicating with the device 
  Wire.write(0x2D); // Access/ talk to POWER_CTL Register - 0x2D
  // Enable measurement
  Wire.write(8); // (8dec -> 0000 1000 binary) Bit D3 High for measuring enable 
  Wire.endTransmission();

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

  /* Default settings from datasheet.  */
  bmp.setSampling(Adafruit_BMP280::MODE_NORMAL,     /* Operating Mode. */
                  Adafruit_BMP280::SAMPLING_X2,     /* Temp. oversampling */
                  Adafruit_BMP280::SAMPLING_X16,    /* Pressure oversampling */
                  Adafruit_BMP280::FILTER_X16,      /* Filtering. */
                  Adafruit_BMP280::STANDBY_MS_500);  /* Standby time. */
  pressureAtZero = bmp.readPressure()/100;
}

void loop() {

  Wire.beginTransmission(ADXL345);
  Wire.write(0x32); // Start with register 0x32 (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(ADXL345, 6, true); // Read 6 registers total, each axis value is stored in 2 registers
  X_out = ( Wire.read()| Wire.read() << 8); // X-axis value
  X_out = X_out/256; //For a range of +-2g, we need to divide the raw values by 256, according to the datasheet
  Y_out = ( Wire.read()| Wire.read() << 8); // Y-axis value
  Y_out = Y_out/256;
  Z_out = ( Wire.read()| Wire.read() << 8); // Z-axis value
  Z_out = (Z_out-56.32)/256;

  Serial.print("Xa= ");
  Serial.print(X_out);
  Serial.print("   Ya= ");
  Serial.print(Y_out);
  Serial.print("   Za= ");
  Serial.println(Z_out);

  gasValue = analogRead(gasPin);
  //displayInfo();
  while (ss.available() > 0) {
    if (gps.encode(ss.read())) {
      displayInfo();
      Serial.print(F(" Gas: "));
      Serial.println(gasValue);
      // Write GPS data to SD Card
      writeToFile();
    }
  }

  if (millis() > 5000 && gps.charsProcessed() < 10) {
    Serial.println(F("No GPS detected: check wiring."));
    //while(true);
  }
  delay(550)
}

  void displayInfo() {
  Serial.print(F("Location: ")); 
  if (gps.location.isValid()) {
    Serial.print(gps.location.lat(), 6);
    Serial.print(F(","));
    Serial.print(gps.location.lng(), 6);
  } else {
    Serial.print(F("INVALID"));
  }

  Serial.print(F("  Date/Time: "));
  if (gps.date.isValid()) {
    Serial.print(gps.date.month());
    Serial.print(F("/"));
    Serial.print(gps.date.day());
    Serial.print(F("/"));
    Serial.print(gps.date.year());
  } else {
    Serial.print(F("INVALID"));
  }

  Serial.print(F(" "));
  if (gps.time.isValid()) {
    if (gps.time.hour() < 10) Serial.print(F("0"));
    Serial.print(gps.time.hour());
    Serial.print(F(":"));
    if (gps.time.minute() < 10) Serial.print(F("0"));
    Serial.print(gps.time.minute());
    Serial.print(F(":"));
    if (gps.time.second() < 10) Serial.print(F("0"));
    Serial.print(gps.time.second());
    Serial.print(F("."));
    if (gps.time.centisecond() < 10) Serial.print(F("0"));
    Serial.print(gps.time.centisecond());
  } else {
    Serial.print(F("INVALID"));
  }

  Serial.print(F(" Temperature: "));
  Serial.print(bmp.readTemperature());
  Serial.print("*C");

  Serial.print(F(" Pressure: "));
  Serial.print(bmp.readPressure()/100);  //displaying the Pressure in hPa, you can change the unit
  Serial.print(" hPa");

  Serial.print(F(" Altitude: "));
  Serial.print(bmp.readAltitude(pressureAtZero));  //The "1019.66" is the pressure(hPa) at sea level in day in your region
  Serial.print(" m");
}

void writeToFile() {
  // Open the file in write mode
  dataFile = SD.open("data.txt", FILE_WRITE);

  // If the file is open, write GPS data to it
  if (dataFile) {
    dataFile.print(F("Location: ")); 
  if (gps.location.isValid()) {
    dataFile.print(gps.location.lat(), 6);
    dataFile.print(F(","));
    dataFile.print(gps.location.lng(), 6);
  } else {
    dataFile.print(F("INVALID"));
  }

  dataFile.print(F("  Date/Time: "));
  if (gps.date.isValid()) {
    dataFile.print(gps.date.month());
    dataFile.print(F("/"));
    dataFile.print(gps.date.day());
    dataFile.print(F("/"));
    dataFile.print(gps.date.year());
  } else {
    dataFile.print(F("INVALID"));
  }

  dataFile.print(F(" "));
  if (gps.time.isValid()) {
    if (gps.time.hour() < 10) dataFile.print(F("0"));
    dataFile.print(gps.time.hour());
    dataFile.print(F(":"));
    if (gps.time.minute() < 10) dataFile.print(F("0"));
    dataFile.print(gps.time.minute());
    dataFile.print(F(":"));
    if (gps.time.second() < 10) dataFile.print(F("0"));
    dataFile.print(gps.time.second());
    dataFile.print(F("."));
    if (gps.time.centisecond() < 10) dataFile.print(F("0"));
    dataFile.print(gps.time.centisecond());
  } else {
    dataFile.print(F("INVALID"));
  }
      dataFile.print(F(" Gas: "));
      dataFile.println(gasValue);

   dataFile.print(F(" Temperature: "));
  dataFile.print(bmp.readTemperature());
  dataFile.print("*C");

  dataFile.print(F(" Pressure: "));
  dataFile.print(bmp.readPressure()/100);  //displaying the Pressure in hPa, you can change the unit
  dataFile.print(" hPa");

  dataFile.print(F(" Altitude: "));
  dataFile.print(bmp.readAltitude(1019.66));  //The "1019.66" is the pressure(hPa) at sea level in day in your region
  dataFile.print(" m");
  
    dataFile.close();
  } else {
    Serial.println(F("Error opening data.txt"));
  }
}

This worked well in the Serial Monitor, but when I connected the APC220, I got a few lines and then it stopped, when I then pressed the reset button, I again got only a few lines. But when I tried a simple code that just sends a simple number once per second, it works perfectly.

In this code, the SD module also worked. But I don't have the ADXL345 module in this code yet.

The SD card module is the only SPI device, the BMP280 and ADXL345 are I2C and the GPS and APC220 are serial. The MQ-4 sensor is analog.

I have soldered the wire and cut the trace, but now I get the 'SD Card initialization failed!' error. The ReadWrite example from SD.h still works fine.

I added some test Serial.println() statements:

#include <TinyGPSPlus.h>

#include <SoftwareSerial.h>

#include <SD.h>

#include <Adafruit_BMP280.h>

#include <Wire.h>

//WIRING
//ARDUINO - MODULE
//----------------
//          GPS
//4         TX
//3         RX
//----------------
//          SD
//10        CS
//11        MOSI
//12        MISO
//13        SCK
//----------------
//          MQ-4
//A0        AO
//----------------
//          APC220
//----------------
//          BMP280
//3.3v      VCC !BELANGRIJK!
//A4        SDA
//A5        SCL
//----------------
//          Buzzer
//7         VCC
//GND       GND

//GPS
static const uint32_t GPSBaud = 9600;
static const int RXPin = 3, TXPin = 4;
TinyGPSPlus gps;
SoftwareSerial ss(RXPin, TXPin);

// SD Card
const int chipSelect = 10; // CS pin
File dataFile;

//MQ-4
#define gasPin A0

//BMP280
Adafruit_BMP280 bmp;
float pressureAtZero = 0;

//adxl345
int ADXL345 = 0x53;
float X_out, Y_out, Z_out;

void setup() {
  Serial.begin(9600);
  ss.begin(GPSBaud);

  pinMode(gasPin, INPUT);

  // Initialize SD Card
  if (!SD.begin(chipSelect)) {
    Serial.println(F("SD Card initialization failed!"));
    //while (1);
  }
  Serial.println("test");
  Wire.begin(); // Initiate the Wire library
  // Set ADXL345 in measuring mode
  Wire.beginTransmission(ADXL345); // Start communicating with the device
  Wire.write(0x2D); // Access/ talk to POWER_CTL Register - 0x2D
  // Enable measurement
  Wire.write(8); // (8dec -> 0000 1000 binary) Bit D3 High for measuring enable
  Wire.endTransmission();
Serial.println("test1");
  if (!bmp.begin(0x76)) {
    Serial.println(F("Could not find a valid BMP280 sensor,  check wiring!"));
    //while (1);
  }else{
    Serial.println("succes");
  }

  /* Default settings from datasheet.  */
  bmp.setSampling(Adafruit_BMP280::MODE_NORMAL, /* Operating Mode. */
    Adafruit_BMP280::SAMPLING_X2, /* Temp. oversampling */
    Adafruit_BMP280::SAMPLING_X16, /* Pressure oversampling */
    Adafruit_BMP280::FILTER_X16, /* Filtering. */
    Adafruit_BMP280::STANDBY_MS_500); /* Standby time. */
  pressureAtZero = bmp.readPressure() / 100;
Serial.println("test2");
  dataFile = SD.open("data.csv", FILE_WRITE);
  Serial.println("Opened");
  if (dataFile) {
    dataFile.println("Time;Date;Latitude;Longitude;Altitude_GPS;Speed;Satellites;Temperature;Pressure_HPa;Altitude_Baro;GasReading");
    dataFile.close();
    Serial.println("write succesfull");
  } else {
    Serial.println("failed");
  }
  Serial.println("test3");
}

void loop() {
  Serial.println("loop");
  while (ss.available() > 0) {
    if (gps.encode(ss.read())) {

      //Wire.beginTransmission(ADXL345);
      //  Wire.write(0x32); // Start with register 0x32 (ACCEL_XOUT_H)
      //Wire.endTransmission(false);
      //Wire.requestFrom(ADXL345, 6, true); // Read 6 registers total, each axis value is stored in 2 registers
      //X_out = ( Wire.read()| Wire.read() << 8); // X-axis value
      //X_out = X_out/256; //For a range of +-2g, we need to divide the raw values by 256, according to the datasheet

      //Y_out = ( Wire.read()| Wire.read() << 8); // Y-axis value
      //Y_out = Y_out/256;
      //Z_out = ( Wire.read()| Wire.read() << 8); // Z-axis value
      //Z_out = (Z_out-56.32)/256;

      //char xyz[20];
      //xyz[0] = '\0';
      //snprintf(xyz,20,"%s;%s;%s;",String(X_out).c_str(),String(Y_out).c_str(),String(Z_out).c_str());

      char output[121];
      output[0] = '\0';
      snprintf(output, 121, "%s;%s;%s,%s;%s;%s;%s;%s;%s;%s;%s;", (gps.time.isValid() ? String(gps.time.value()).c_str() : "N/A"), //time
        (gps.date.isValid() ? String(gps.date.value()).c_str() : "N/A"), //date
        (gps.location.isValid() ? String(gps.location.lat(), 6).c_str() : "N/A"), //latitude
        (gps.location.isValid() ? String(gps.location.lng(), 6).c_str() : "N/A"), //longitude
        (gps.altitude.isValid() ? String(gps.altitude.value()).c_str() : "N/A"), //Altitude GPS
        (gps.speed.isValid() ? String(gps.speed.mps()).c_str() : "N/A"), //Speed
        (gps.satellites.isValid() ? String(gps.satellites.value()).c_str() : "N/A"), //Satellites
        String(bmp.readTemperature()).c_str(), //Temp
        String(bmp.readPressure() / 100).c_str(), //Pressure HPa
        String(bmp.readAltitude(pressureAtZero)).c_str(), //Altitude Baro
        String(analogRead(gasPin)).c_str()
      );
      //Serial.println(xyz);
      Serial.println(output);
      dataFile = SD.open("data.csv", FILE_WRITE);
      if (dataFile) {
        dataFile.println(output);
        dataFile.flush();
        dataFile.close();
        Serial.println("write succesfull");
      } else {
        Serial.println("error opening file");
      }
    }
  }
}

In the Serial Monitor I get:

SD Card initialization failed!
test
test1

So I think te problem is in this statement: bmp.begin(0x76) because I don't get neither succes and Could not find a valid BMP280 sensor, check wiring!. So it looks like the program is stuck in the statement bmp.begin(0x76). I don't know why, because in the examples from the Adafruit_BMP280 library it works as it should.

Have you tried running I2Cscanner.ino with all devices connected? You should get 0x76 and maybe 0x77, but nothing else.

Yes, I did. I got 0x76 and 0x53 which is from the ADXL345 accelerometer. But that is not the cause, because when I remove all the code from the ADXL345, I still get only this in the Serial Monitor:

SD Card initialization failed!
test
test1

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.