Please help - Getting ADXL345 accel's to log to an SD card.

Hi there,

For my final year university project I am looking into semi active suspension control for a mountain bike. I am trying to get 4 ADXL345 accelerometers to log to an SD card using an Arduino Uno and a Micro SD WIFI shield. I have working code for the 4 accel's and working code for the SD card. I am really struggling to combine these two so that the accel's log to the card.

I tried to upload the code but it exceeds the maximum Character limit. How can I get around this?

It seems that the card does not initialize with the accel's connected (pins 11, 12 and 13) but with them disconnected it does. This leads me to think that it is an issue with the SPI used to communicate with both the accle's and SD card. I have tried numerous things to try and make it work but none with any success. I'm really running out of time to get this to work.

Andy

There's no code and those are SPI pins.

I am sorry it is saying that the post with code is too large - exceeds the character limit?

Attach it in a Notepad or Word file or IDE *.ino file

Thank you Raschemmel, I have shortened the code by removing 3 of the sensors as this will also make it easier to read.

//Add the SPI library so we can communicate with the ADXL345 sensor
#include <SPI.h>
#include <SD.h>

const int chipSelect = 4;

//Assign the Chip Select signal to pin 10.
int CS1 = 10;
int CS2 = 9;
int CS3 = 8;
int CS4 = 7;

int Out;

int i=1;

int xg1, yg1, zg1;
int xg2, yg2, zg2;
int xg3, yg3, zg3;
int xg4, yg4, zg4;

File logfile;

//This buffer will hold values read from the ADXL345 registers.
char values_1[10];
char values_2[10];
char values_3[10];
char values_4[10];

//These variables will be used to hold the x,y and z axis accelerometer values.
int x1,y1,z1;
int x2,y2,z2;
int x3,y3,z3;
int x4,y4,z4;

//This is a list of some of the registers available on the ADXL345.
//To learn more about these and the rest of the registers on the ADXL345, read the datasheet!
char POWER_CTL_1 = 0x2D;	//Power Control Register
char DATA_FORMAT_1 = 0x31;
char DATAX0_1 = 0x32;	//X-Axis Data 0
char DATAX1_1 = 0x33;	//X-Axis Data 1
char DATAY0_1 = 0x34;	//Y-Axis Data 0
char DATAY1_1 = 0x35;	//Y-Axis Data 1
char DATAZ0_1 = 0x36;	//Z-Axis Data 0
char DATAZ1_1 = 0x37;	//Z-Axis Data 1
char XOFFSET_1 = 0x1E;   //X-Offset
char YOFFSET_1 = 0x1F;   //Y-Offset
char ZOFFSET_1 = 0x20;   //Z-Offset




void setup(){ 

  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.");




  //Initiate an SPI communication instance.
  SPI.begin();
  //Configure the SPI connection for the ADXL345.
  SPI.setDataMode(SPI_MODE3);
  //Create a serial connection to display the data on the terminal.


  //  Accel 1
  //Set up the Chip Select pin to be an output from the Arduino.
  pinMode(CS1, OUTPUT);
  //Before communication starts, the Chip Select pin needs to be set high.
  digitalWrite(CS1, HIGH);

  //Put the ADXL345 into +/- 4G range by writing the value 0x01 to the DATA_FORMAT register.
  writeRegister_1(DATA_FORMAT_1, 0x0B);
  //Put the ADXL345 into Measurement Mode by writing 0x08 to the POWER_CTL register.
  writeRegister_1(POWER_CTL_1, 0x08);  //Measurement mode 

  writeRegister_1(XOFFSET_1, -3);
  writeRegister_1(YOFFSET_1, 0);
  writeRegister_1(ZOFFSET_1, 15); 

}
void loop(){

  // Accel 1

  //Reading 6 bytes of data starting at register DATAX0 will retrieve the x,y and z acceleration values from the ADXL345.
  //The results of the read operation will get stored to the values[] buffer.
  readRegister_1(DATAX0_1, 6, values_1);

  //The ADXL345 gives 10-bit acceleration values, but they are stored as bytes (8-bits). To get the full value, two bytes must be combined for each axis.
  //The X value is stored in values[0] and values[1].
  x1 = ((int)values_1[1]<<8)|(int)values_1[0] & 0xFF;
  //The Y value is stored in values[2] and values[3].
  y1 = ((int)values_1[3]<<8)|(int)values_1[2] & 0xFF;
  //The Z value is stored in values[4] and values[5].
  z1 = ((int)values_1[5]<<8)|(int)values_1[4] & 0xFF;



  xg1 = x1 * 3.637;
  yg1 = y1 * 3.588;
  zg1 = z1 * 4.002;









  String dataString = "";

  //  // read three sensors and append to the string:
  //  for (int analogPin = 0; analogPin < 3; analogPin++) {
  //    int sensor = analogRead(analogPin);

  dataString += String(i);    
  dataString += ","; 
  dataString += ","; 
  dataString += ","; 
  dataString += String(xg1);    
  dataString += ",";  
  dataString += String(yg1);    
  dataString += ","; 
  dataString += String(zg1);




  // 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");
  } 

  i++;


  delay(200); 
}


//This function will write a value to a register on the ADXL345.
//Parameters:
//  char registerAddress - The register to write a value to
//  char value - The value to be written to the specified register.
void writeRegister_1(char registerAddress_1, char value_1){
  //Set Chip Select pin low to signal the beginning of an SPI packet.
  digitalWrite(CS1, LOW);
  //Transfer the register address over SPI.
  SPI.transfer(registerAddress_1);
  //Transfer the desired register value over SPI.
  SPI.transfer(value_1);
  //Set the Chip Select pin high to signal the end of an SPI packet.
  digitalWrite(CS1, HIGH);
}


// Accel 1

//This function will read a certain number of registers starting from a specified address and store their values in a buffer.
//Parameters:
//  char registerAddress - The register addresse to start the read sequence from.
//  int numBytes - The number of registers that should be read.
//  char * values - A pointer to a buffer where the results of the operation should be stored.
void readRegister_1(char registerAddress_1, int numBytes_1, char * values_1){
  //Since we're performing a read operation, the most significant bit of the register address should be set.
  char address_1 = 0x80 | registerAddress_1;
  //If we're doing a multi-byte read, bit 6 needs to be set as well.
  if(numBytes_1 > 1)address_1 = address_1 | 0x40;

  //Set the Chip select pin low to start an SPI packet.
  digitalWrite(CS1, LOW);
  //Transfer the starting register address that needs to be read.
  SPI.transfer(address_1);
  //Continue to read registers until we've read the number specified, storing the results to the input buffer.
  for(int a=0; a<numBytes_1; a++){
    values_1[a] = SPI.transfer(0x00);
  }
  //Set the Chips Select pin high to end the SPI packet.
  digitalWrite(CS1, HIGH);
}

How many SPI devices do you have?
Do they all have chip select pins?

The 4 accelerometers and the SD card so 5 in total. Each as an individual CS pin. 10-8 for the Accels (chosen) and 4 for the SD (not changeable) the 4 accels are connected to pins 13, 12 and 11 as the ?common? pins.

I meant to say in the first post (its 1am in the morning for me) that according to the Datasheet, the SD communicates using mode0 and the accel's on mode3. Could this be the issue? I haven't and don't know how to account for this in the code.

Usualy the SD is 10.

I tried changing it to 10 but it would not work. I'm pretty sure it is 4 as it will log analog readings without the accel's

Code for just SD logging;

#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");
  } 
}

the SD communicates using mode0 and the accel's on mode3. Could this be the issue?

Yes
I can't find anything in the code that sets the mode. Is it programmable ?
Can you change it for the SD routines and change it back for the measurement function ? You know, Pro-Mini's cost less than a sandwich. Have you thought of adding a Pro-Mini ? Can you describe the block diagram of your system ?

Perhaps using I2C could help resolve some conflicts. The ADXL345 supports I2C so you could get it off the SPI bus, and it's rather easy to implement. Checkout Jeff Rowberg's i2cdevlib on github GitHub - jrowberg/i2cdevlib: I2C device library collection for AVR/Arduino or other C++-based MCUs

Thank's for the help guys, I think I have it working now although its pretty slow ~30Hz :frowning:

The problem was that the SD card SPI was mode 0 and the accels mode 3. Setting the mode to the correct one before calling the device fixed the problem.

Would you guys have any ideas on how to increase the sample rate?

Did you ever get this figured out? I'm working on something similar.

Is 40,000 samples per second fast enough ?
Read Reply#4 of this post :
http://forum.arduino.cc/index.php?topic=232914.msg1679019#msg1679019

I'm talking to the adxl345 via spi on CSpin 10 with the SD card on pin CS8. That example was reading from an analog sensor. I was hoping there was a working code example for me to play with until I understand it better.

Would you guys have any ideas on how to increase the sample rate?

So you mean of the ADXL345 ?
You mean you didn't read the datasheet , page-14 about the RATE CODE ? SHEEEESH !

OK, I'm making some headway. It samples and writes to the SD card in 8ms intervals, or about 120 writes per second. It saves the time in Milliseconds, x axis, y axis, z axis, and also converts to G's for the x,y, and z.
The files it saves have a weird naming convention at the end for some unknown reason. All you have to do is change the .0*T to .txt or .csv and you can open it in a comma delineated spread sheet.
Here's the working code. Do with it as you please. It is just the sample code for the spark fun shields that has been mutilated, and sewn back together with tid bits from other code found around the net. I hope this helps other people just starting out like me. Also, I'm sure it's not the most efficient code, so if someone wants to play with and refine it, I'm sure others in the community would appreciate your efforts.

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  Sonny Schade   04/15/2014
//  Accelerometer Project using SparkFun ADXL345 and SD Data Logger Breakout boards
//  Both Sheilds communicating over SPI
//  SD Card uses Pin 8 by default for SS/CS Pin (Spark Fun Board)
//  ADXL345 using Pin 10 for CS pin but you can use another digital pin if you choose
//  
//  This code was created by mutilating and sewing together code from sample code and other people's projects 
//  so feel free to do the same to this one, I hope this helps someone starting out.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// Include the relevant libraries provided with the Arduino
#include <SPI.h>
#include <SD.h>

// place holder for a large number
unsigned long time; // called it "time"

//Factors to change the data output by the accelerometer from raw data to G forces
double g=0.0039; //+/- 2g Make sure this matches the writeRegister Data Format on line 91!
//double g=0.0078; //+/- 4g
//double g=0.016;  //+/- 8g
//double g=0.031; //+/- 16g

#define SYNC_INTERVAL 10000 // mills between calls to flush() - to write data to the card
uint32_t syncTime = 0; // time of last sync()

// Assign the ADXL345 CS pin to Pin 10 ** make this whatever digital pin you chose to use**
int CS=10;

//This is a list of some of the registers available on the ADXL345.
//To learn more about these and the rest of the registers on the ADXL345, read the datasheet!
char POWER_CTL = 0x2D;    //Power Control Register
char DATA_FORMAT = 0x31;
char DATAX0 = 0x32;    //X-Axis Data 0
char DATAX1 = 0x33;    //X-Axis Data 1
char DATAY0 = 0x34;    //Y-Axis Data 0
char DATAY1 = 0x35;    //Y-Axis Data 1
char DATAZ0 = 0x36;    //Z-Axis Data 0
char DATAZ1 = 0x37;    //Z-Axis Data 1
char BW_RATE = 0x2C;    //Sampling Rate

//This buffer will hold values read from the ADXL345 registers.
char values[10];
//Initalize variables
int x,y,z;
int i;
double xg,yg,zg;
double totals[3];
File myFile;

void setup(){
  //Initiate an SPI communication instance.
  SPI.begin();
  Serial.begin(38400);   
  
  //Set up the Chip Select pin for the SD card to be an output from the Arduino.
  pinMode(8, OUTPUT);
  //Set chip select for ADXL345 to be output from Arduino.
  pinMode(CS, OUTPUT);

  //Set the CS Pin for the accelerometer high so that the ADXL345 and SD card won't be trying to talk at same time.
  digitalWrite(CS, HIGH);

  //Initialize the SD card
  if (!SD.begin(8)) { // the (8) is the CS pin you chose to use, on my sheild that pin is hardwired to "8"
    return;
  }
  // create a new file 
  // starts with Crash00, and next one would be Crash01 
  char filename[] = "Crash.txt";
  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
      myFile = SD.open(filename, FILE_WRITE);
      break;  // leave the loop!
    }
  }
   
  //Print header to file
  myFile.println("Time in MilliSeconds,X_Axis,Y_Axis,Z_Axis, X_G's,Y_G's,Z_G's");

  //Configure the SPI connection for the ADXL345.
  SPI.setDataMode(SPI_MODE3);
  //Before communication starts, the Chip Select pin needs to be set high.
  digitalWrite(CS, HIGH);

  //Put the ADXL345 into +/- 4G range by writing the value 0x01 to the DATA_FORMAT register.
  writeRegister(DATA_FORMAT, 0x00);//0x00 +/- 2G, 0x01 +/- 4G. 0x10 +/- 8G,0x11 +/-16G
  //Put the ADXL345 into Measurement Mode by writing 0x08 to the POWER_CTL register.
  writeRegister(POWER_CTL, 0x08);  //Measurement mode 
  //Try and change sampling rate
  writeRegister(BW_RATE, 0x0D);
}

void loop(){

  //Count Milliseconds from time Arduino powered on
  time = millis();
  
  //Reading 6 bytes of data starting at register DATAX0 will retrieve the x,y and z acceleration values from the ADXL345.
  //The results of the read operation will get stored to the values[] buffer.
  readRegister(DATAX0, 6, values);

  //The ADXL345 gives 10-bit acceleration values, but they are stored as bytes (8-bits). To get the full value, two bytes must be combined for each axis.
  //The X value is stored in values[0] and values[1].
  x = ((int)values[1]<<8)|(int)values[0];
  //The Y value is stored in values[2] and values[3].
  y = ((int)values[3]<<8)|(int)values[2];
  //The Z value is stored in values[4] and values[5].
  z = ((int)values[5]<<8)|(int)values[4];

  //Convert raw data to g values
  xg=x*g;
  yg=y*g;
  zg=z*g;

  //Store g values in an array
  totals[0] = xg;
  totals[1] = yg;
  totals[2] = zg;

   // log time
    myFile.print(time);
    myFile.print(',');
   
  //Log accelerations
    myFile.print(x);
    myFile.print(',');
    myFile.print(y);
    myFile.print(',');
    myFile.print(z);
    myFile.print(',');
  for (i = 0; i < 3; i = i + 1) {
    myFile.print(totals[i]);
    myFile.print(',');
    // Open the serial monitor to see if it's working, select a baud rate of 38400, only displays milliseconds and G's
    Serial.print(time);
    Serial.print(',');
    Serial.print(totals[i]);
    Serial.print(',');
  }
  myFile.println();
  Serial.println();
 
  // Now we write data to disk! Don't sync too often - requires 2048 bytes of I/O to SD card
  // which uses a bunch of power and takes time
  if ((millis() - syncTime) < SYNC_INTERVAL) return;
  syncTime = millis();

  // flush the system
  myFile.flush();
 // delay to help clear some of the noise
 // delayMicroseconds(2);

}   //END OF LOOP!!!!!

//This function will write a value to a register on the ADXL345.
//Parameters:
//  char registerAddress - The register to write a value to
//  char value - The value to be written to the specified register.
void writeRegister(char registerAddress, char value){
  //Set Chip Select pin low to signal the beginning of an SPI packet.
  digitalWrite(CS, LOW);
  //Transfer the register address over SPI.
  SPI.transfer(registerAddress);
  //Transfer the desired register value over SPI.
  SPI.transfer(value);
  //Set the Chip Select pin high to signal the end of an SPI packet.
  digitalWrite(CS, HIGH);
}

//This function will read a certain number of registers starting from a specified address and store their values in a buffer.
//Parameters:
//  char registerAddress - The register addresse to start the read sequence from.
//  int numBytes - The number of registers that should be read.
//  char * values - A pointer to a buffer where the results of the operation should be stored.
void readRegister(char registerAddress, int numBytes, char * values){
  //Since we're performing a read operation, the most significant bit of the register address should be set.
  char address = 0x80 | registerAddress;
  //If we're doing a multi-byte read, bit 6 needs to be set as well.
  if(numBytes > 1)address = address | 0x40;

  //Set the Chip select pin low to start an SPI packet.
  digitalWrite(CS, LOW);
  //Transfer the starting register address that needs to be read.
  SPI.transfer(address);
  //Continue to read registers until we've read the number specified, storing the results to the input buffer.
  for(int i=0; i<numBytes; i++){
    values[i] = SPI.transfer(0x00);
  }
  //Set the Chips Select pin high to end the SPI packet.
  digitalWrite(CS, HIGH);
}

Would you guys have any ideas on how to increase the sample rate?
So you mean of the ADXL345 ?
You mean you didn't read the datasheet , page-14 about the RATE CODE ? SHEEEESH !

You never commented on my above comment. Did you or did you not read that section and use the RATE CODE to increase the
sample rate. I am surprised you had nothing at all to say about that. Doesn't that sound relevant to your question ?

Sorry, I think you have me confused with the original poster. I just saw this post yesterday.I wasn't asking about data speed, I was just asking if the original poster had working code, for the two spi devices. But I have them talking now, and figured I'd post the working code for other newbies to play with. I spent two days searching the forums, and for some reason most of the posts either end with people saying " OK it works now" or " I will do what your said , thanks" and then they never post their code once they have it working, which is really annoying. Since I finally got my two spi devices to talk and record, I figured I'd post the code for people like me, looking for a working frame work to build upon.
But since you mention it, do you have some ideas of making it faster?

But since you mention it, do you have some ideas of making it faster?

You mean besides changing the rate code to a higher value ?