Has anyone had success hooking up an SD card to the Photon and writing/reading data?


#21

Thanks for sharing the code. It worked for me. I’m using the photon to log accelometer and gyrsocope data. I’ve included the SD.fat library in my code. It shows up as #include <SdFat.h> in the code. However, I’m still not able to log any data into my SD card.


#22

What kind of debugging have you tried so far? I remember having some issues getting everything working right.


#23

I’m working on adding the ability to write to an SD card as part of a code used to log Accel/ gyro data. I tried combining your code with mine, and currently trying to get the two codes to work together by eliminating redundancies.


#24

I’m working on adding the ability to write to an SD card as part of a code used to log Accel/ gyro data. I tried combining your code with mine, and currently trying to get the two codes to work together by eliminating redundancies


#25

This line suggests you are targeting an old system version and use the legacy version of SdFat library.
We’d recommend moving on to the modern structure and more recent system versions.


#26

I am currently using the most recent version of the SDfat library, it is included in my LSM330 code. I checked my SD card using the trymefirst and it is connected properly. However, my code is failing to write my accel data to the SD card. Is there a command I’m missing that allows the photon to write a file to the SD card?


#27

Would someone please provide me with a response.


#28

@nasib94, a bit more debugging information would be useful. What IS your code able to do? If your take the SD code out of the timers and put it into loop() using millis() delays as timers, does it work there? Sprinkling some Serial.print() debug message would also help to figure what the code is doing.


#29

my code is able to output the USB data of the LSM330 sensor in addition to publishing the data to the console. I checked my memory card using the “trymefirst” code and the file was written to my SD card correctly. I guess what I am missing is the correct command to write my data to the SD card. The SDfat lib was included in the project as well.


#30

@nasib94, Software Timers have limited stack space. So, why don’t you try what I suggested:

If your take the SD code out of the timers and put it into loop() using millis() delays as timers, does it work there? Sprinkling some Serial.print() debug message would also help to figure what the code is doing.


#31

I’m also sampling at 1000 milli seconds right now with no ouput to the SD card. I included ----Serial.print("sd.cardErrorCode(): "); — in order to check for SD error. Attached is my code for reference:

 // This #include statement was automatically added by the Particle IDE.
#include <SdFat.h>

// This #include statement was automatically added by the Particle IDE.
//#include <sd-card-library-photon-compat.h>



// This #include statement was automatically added by the Particle IDE.
#include "LSM330.h"

#include <Wire.h>



LSM330 lsm;

void setup(void)
{
    
     
    Serial.begin(9600);
    // The address can be changed making the option of connecting multiple devices
    lsm.getAddrX_LSM330(LSM330_DEFAULT_ADDRESS_ACCEL);          // 0x1E
    // lsm.getAddrX_LSM330(LSM330_ADDRESS_ACCEL_UPDATED);       // 0x1D

    lsm.getAddrG_LSM330(LSM330_DEFAULT_ADDRESS_GYRO);           // 0x6A
    // lsm.getAddrG_LSM330(LSM330_ADDRESS_GYRO_UPDATED);        // 0x6B

    // The Gyroscope's Output Data Rate and Bandwidth Selection and Full-Scale Selection,
    // Acceleration Data Rate Selection and Acceleration Full-Scale Selection,
    // can be changed via the following functions

    /*
    // Also some more parameters can be changed using the following functions
    // Gyroscope:
    // Power-Down Mode Enable, Gyroscope X, Y, Z Axes Enable,
    // Block Data Update, Big/Little Endian Data Selection, Full-Scale Selection,
    // SPI Serial Interface Mode Selection
    // Accelerometer:
    // Block Data Update for Acceleration Data
    // Acceleration X, Y, Z Axes Enable
    // Acceleration Anti-Alias Filter Bandwidth
    // Self-Test Mode Configuration, SPI Serial Interface Mode Selection
    // These functions can be modified and added here so that the parameters can be altered as per requirement
    */

    lsm.setAccelDataRate(ACCEL_DATARATE_3_125HZ);        // AODR (Hz): 3.125
    // lsm.setAccelDataRate(ACCEL_DATARATE_POWERDOWN);   // Power Down Mode
    // lsm.setAccelDataRate(ACCEL_DATARATE_6_25HZ);      // AODR (Hz): 6.25
    // lsm.setAccelDataRate(ACCEL_DATARATE_12_5HZ);      // AODR (Hz): 12.5
    // lsm.setAccelDataRate(ACCEL_DATARATE_25HZ);        // AODR (Hz): 25
    // lsm.setAccelDataRate(ACCEL_DATARATE_50HZ);        // AODR (Hz): 50
    // lsm.setAccelDataRate(ACCEL_DATARATE_100HZ);       // AODR (Hz): 100
    // lsm.setAccelDataRate(ACCEL_DATARATE_200HZ);       // AODR (Hz): 200
    // lsm.setAccelDataRate(ACCEL_DATARATE_400HZ);       // AODR (Hz): 400
    // lsm.setAccelDataRate(ACCEL_DATARATE_800HZ);       // AODR (Hz): 800
    // lsm.setAccelDataRate(ACCEL_DATARATE_1600HZ);      // AODR (Hz): 1600

    lsm.setAccelRange(ACCEL_RANGE_16G);          // ±16 g
    // lsm.setAccelRange(ACCEL_RANGE_2G);        // ±2 g
    // lsm.setAccelRange(ACCEL_RANGE_4G);        // ±4 g
    // lsm.setAccelRange(ACCEL_RANGE_6G);        // ±6 g
    // lsm.setAccelRange(ACCEL_RANGE_8G);        // ±8 g

    lsm.setGyroDataRate(GYRO_DATARATE_95_12_5);      // ODR (Hz): 95, Cutoff: 12.5
    // lsm.setGyroDataRate(GYRO_DATARATE_95_25);     // ODR (Hz): 95, Cutoff: 25
    // lsm.setGyroDataRate(GYRO_DATARATE_190_12_5);  // ODR (Hz): 190, Cutoff: 12.5
    // lsm.setGyroDataRate(GYRO_DATARATE_190_25);    // ODR (Hz): 190, Cutoff: 25
    // lsm.setGyroDataRate(GYRO_DATARATE_190_50);    // ODR (Hz): 190, Cutoff: 50
    // lsm.setGyroDataRate(GYRO_DATARATE_190_70);    // ODR (Hz): 190, Cutoff: 70
    // lsm.setGyroDataRate(GYRO_DATARATE_380_20);    // ODR (Hz): 380, Cutoff: 20
    // lsm.setGyroDataRate(GYRO_DATARATE_380_25);    // ODR (Hz): 380, Cutoff: 25
    // lsm.setGyroDataRate(GYRO_DATARATE_380_50);    // ODR (Hz): 380, Cutoff: 50
    // lsm.setGyroDataRate(GYRO_DATARATE_380_100);   // ODR (Hz): 380, Cutoff: 100
    // lsm.setGyroDataRate(GYRO_DATARATE_760_30);    // ODR (Hz): 760, Cutoff: 30
    // lsm.setGyroDataRate(GYRO_DATARATE_760_35);    // ODR (Hz): 760, Cutoff: 35
    // lsm.setGyroDataRate(GYRO_DATARATE_760_50);    // ODR (Hz): 760, Cutoff: 50
    // lsm.setGyroDataRate(GYRO_DATARATE_760_100);   // ODR (Hz): 760, Cutoff: 100

    lsm.setGyroScale(GYRO_SCALE_2000DPS);            // 2000 dps
    // lsm.setGyroScale(GYRO_SCALE_250DPS);          // 250 dps
    // lsm.setGyroScale(GYRO_SCALE_500DPS);          // 500 dps

    lsm.begin();
    delay(100);
}

void loop(void)
{
    byte errorX, errorG;
    int8_t addressX, addressG;

    // LSM330 Accelerometer Address
    addressX = lsm.lsm_i2cAddressX;

    // LSM330 Gyroscope Address
    addressG = lsm.lsm_i2cAddressG;

    // The i2c_scanner uses the return value of
    // the Write.endTransmisstion to see if
    // a device did acknowledge to the address.
    Wire.beginTransmission(addressX);
    errorX = Wire.endTransmission();
    Wire.beginTransmission(addressG);
    errorG = Wire.endTransmission();
    if ((errorX == 0) && (errorG == 0))
    {
        Serial.println("Getting Linear Acceleration and Angular Rate Data Readings from LSM330");
        Serial.println(" ");
        // Set up the sensor for Accelerometer and Gyroscope
        lsm.setUpAccelerometer();
        lsm.setUpGyroscope();
        delay(50);
        // Display the results for Acceleration and Angular Rate Values
        lsm.Measure_Sensor();
        lsm.Measure_Accelerometer();
        lsm.Measure_Gyroscope();

      
         //   Particle.publish("Impulse", "1  ");
        //}
        //delay(500);
        
        //Serial.print("Linear Acceleration in Y-Axis: ");
        //Serial.println(lsm.lsm_accelData.Y);
        //Particle.publish("accelData.Y", String(lsm.lsm_accelData.Y/ 139));   // Divided over 139 to convert into m/s^2
        //String(calibY) = ("String(lsm.lsm_accelData.Y" / 139) ;     //acceleration in m/s^2 
        //Particle.publish("realY","String(calibY)" ); 
        
        
        //delay(500);
        
        Serial.print("Linear Acceleration in Z-Axis: ");
        Serial.println(lsm.lsm_accelData.Z/139);
        Particle.publish("accelData.Z", String(lsm.lsm_accelData.Z/139));
        delay(50);
        if  (   0 < (lsm.lsm_accelData.Z/139) < 25 ) 
        {
            Particle.publish("impulse","slow");
        }
        
         if  (  25<(lsm.lsm_accelData.Z/139)< 50 ) 
        {
            Particle.publish("impulse", "medium");
        }
        
        if  ((lsm.lsm_accelData.Z/139) > 50 ) 
        {
            Particle.publish("impulse", "fast");
        } 
        
        //Serial.println(" ");
        //Serial.println("    ***********     ");
        //Serial.println(" ");
        //delay(500);
        //Serial.print("Rotation in X-Axis: ");
        //Serial.println(lsm.lsm_gyroData.X);
        //Particle.publish("gyroData.X", String(lsm.lsm_gyroData.X));
        //delay(500);
        //Serial.print("Rotation in Y-Axis: ");
        //Serial.println(lsm.lsm_gyroData.Y);
        //Particle.publish("gyroData.Y", String(lsm.lsm_gyroData.Y));
        //delay(500);
        //Serial.print("Rotation in Z-Axis: ");
        //Serial.println(lsm.lsm_gyroData.Z);
        //delay(500);
        //Particle.publish("gyroData.Z", String(lsm.lsm_gyroData.Z));
        //Serial.println(" ");
        //Serial.println("        ***************************        ");
        //Serial.println(" ");
        //delay(1000);
    }
    else
    {
        Serial.println("LSM330 Disconnected!");
        Serial.println(" ");
        Serial.println("        ************        ");
        Serial.println(" ");
    }


    delay(100);
}


//This is a code to gewrite data to SD card and manage storae//////////////////////////

//// //// //// //// ////
//// microSD config ////
// Pick an SPI configuration.
// See SPI configuration section below (comments are for photon).
#define SPI_CONFIGURATION 1
//// //// //// //// ////
// Setup SPI configuration.
#if SPI_CONFIGURATION == 0
// Primary SPI with DMA
// SCK => A3, MISO => A4, MOSI => A5, SS => A2 (default)
SdFat sd;
const uint8_t chipSelect = SS;
#elif SPI_CONFIGURATION == 1
// Secondary SPI with DMA
// SCK => D4, MISO => D3, MOSI => D2, SS => D1
SdFat sd(1);
const uint8_t chipSelect = D1;
#elif SPI_CONFIGURATION == 2
// Primary SPI with Arduino SPI library style byte I/O.
// SCK => A3, MISO => A4, MOSI => A5, SS => A2 (default)
SdFatLibSpi sd;
const uint8_t chipSelect = SS;
#elif SPI_CONFIGURATION == 3
// Software SPI.  Use any digital pins.
// MISO => D5, MOSI => D6, SCK => D7, SS => D0
SdFatSoftSpi<D5, D6, D7> sd;
const uint8_t chipSelect = D0;
#endif  // SPI_CONFIGURATION
//// //// //// //// ////
//// //// //// //// ////


// variable initalizations
FatFile globalAccelFile;
String globalAccelFileName;
String accelData; 
bool flag; // flag checks whether certain events were successful or not
double accelMagnitude; 
int fileTracker = 1; 
int globalAccelFileTracker = 1;

// checks the accelerometer x y and z analog values, which are connected to A0, A1, and A2, respectively. 
void checkAccel() {
    globalAccelFileName = "globalAccel" + String(globalAccelFileTracker) + ".txt";
    globalAccelFile.open(globalAccelFileName, O_RDWR | O_CREAT | O_AT_END);
    
    accelMagnitude = floor(sqrt(pow(analogRead(A0),2) + pow(analogRead(A1),2) + pow(analogRead(A2),2)) + 0.5); 
    accelData += String(accelMagnitude).remove(4); 
    
    // maximum String length is 622 bytes. Let's just be safe and say 616 so we don't overrun. 
    if (accelData.length() > 616) {
        accelData += " " + Time.timeStr(); 
        globalAccelFile.write(accelData);
        accelData = "";
    }
    
    if (accelData.length() <= 616) { 
        accelData += "\n"; // each data point is on its own line
    }
    
    if (globalAccelFile.fileSize() > 1000000) // if the accel file exceeds 1MB, on to the next one
    {
        globalAccelFileTracker++;
    }
    
    globalAccelFile.close(); // don't forget to close the file, otherwise you run the risk of corrupting the file and card. 
}


// Find out if the SD card is cooperating. Good idea to call this before attempting to do anything major 
// with the SD Card. This function is a little leaky - some SD errors can get by without this function picking them up. 
void checkSdError() { 
    Serial.print("sd.cardErrorCode(): "); 
    flag = sd.cardErrorCode(); // directly related to sd success/failure
    Serial.println(flag);
    Serial.print("sd.cardErrorData(): "); // for additional debugging
    Serial.println(sd.cardErrorData());
    
    // If there's an error, write high to D7. You can plug in an LED at D7 to check the SD status whenever you want. 
    if (!flag) {
        digitalWrite(D7, LOW);
    }
    
    else {
        digitalWrite(D7, HIGH);
    }
} 


// software Timers are preferable to delays, so the code can run concurrently, not sequentially. Along these lines, it's probably 
// a good idea to enable system threading using SYSTEM_THREAD(ENABLED); 
Timer CheckAccelTimer(50, checkAccel); // sample accelerometer at 20 Hz
Timer CheckSdErrorTimer(1000, checkSdError); // checks for SD card errors every 1 second

//void setup() {
    // Particle.connect(); // must manually call Particle.connect() if system_mode is semi_automatic
    // Time.zone(-7); // changes time zone. Does not adjust for DST, must manually change.
   // Serial.begin(115200); // debugging purposes
    //sd.begin(chipSelect, SPI_FULL_SPEED); // init at full speed for best performanceo
    //pinMode(D7, OUTPUT); // initalize D7 as an output for use by checkSdError
    //Time.setTime(1473379200); // set time to start on September 9 2016 00:00:00 GMT (UNIX time = 1473379200, or seconds since
    // the UNIX epoch, 1 January 1970)
    
    // Start the timers
    //CheckAccelTimer.start();
    //CheckSdErrorTimer.start();
//}

// keep the loop empty - it's better to use software timers (as I did above) so that you can nest events, 
// instead of running your code line by line (which is what happens in setup and loop). 
//void loop() { 

//}

#32

If you don’t want people to get lost in that code, you may want to tidy it up a bit before posting.
All these comments and scattered declarations don’t make it easy to follow.


#33

#include <SdFat.h>

//#include <sd-card-library-photon-compat.h>

#include “LSM330.h”

#include <Wire.h>

LSM330 lsm;

void setup(void)
{

Serial.begin(9600);
// The address can be changed making the option of connecting multiple devices
lsm.getAddrX_LSM330(LSM330_DEFAULT_ADDRESS_ACCEL);          // 0x1E
// lsm.getAddrX_LSM330(LSM330_ADDRESS_ACCEL_UPDATED);       // 0x1D

lsm.getAddrG_LSM330(LSM330_DEFAULT_ADDRESS_GYRO);           // 0x6A
// lsm.getAddrG_LSM330(LSM330_ADDRESS_GYRO_UPDATED);        // 0x6B

// The Gyroscope's Output Data Rate and Bandwidth Selection and Full-Scale Selection,
// Acceleration Data Rate Selection and Acceleration Full-Scale Selection,
// can be changed via the following functions



lsm.setAccelDataRate(ACCEL_DATARATE_3_125HZ);        // AODR (Hz): 3.125

lsm.setAccelRange(ACCEL_RANGE_16G);          // ±16 g


lsm.setGyroDataRate(GYRO_DATARATE_95_12_5);      // ODR (Hz): 95, Cutoff: 12.5


lsm.setGyroScale(GYRO_SCALE_2000DPS);            // 2000 dps


lsm.begin();
delay(100);

}

void loop(void)
{
byte errorX, errorG;
int8_t addressX, addressG;

// LSM330 Accelerometer Address
addressX = lsm.lsm_i2cAddressX;

// LSM330 Gyroscope Address
addressG = lsm.lsm_i2cAddressG;

// The i2c_scanner uses the return value of
// the Write.endTransmisstion to see if
// a device did acknowledge to the address.
Wire.beginTransmission(addressX);
errorX = Wire.endTransmission();
Wire.beginTransmission(addressG);
errorG = Wire.endTransmission();
if ((errorX == 0) && (errorG == 0))
{
    Serial.println("Getting Linear Acceleration and Angular Rate Data Readings from LSM330");
    Serial.println(" ");
    // Set up the sensor for Accelerometer and Gyroscope
    lsm.setUpAccelerometer();
    lsm.setUpGyroscope();
    delay(50);
    // Display the results for Acceleration and Angular Rate Values
    lsm.Measure_Sensor();
    lsm.Measure_Accelerometer();
    lsm.Measure_Gyroscope();

  
 
    
    
    //delay(500);
    
    Serial.print("Linear Acceleration in Z-Axis: ");
    Serial.println(lsm.lsm_accelData.Z/139);
    Particle.publish("accelData.Z", String(lsm.lsm_accelData.Z/139));
    delay(50);
    if  (   0 &lt; (lsm.lsm_accelData.Z/139) &lt; 25 ) 
    {
        Particle.publish("impulse","slow");
    }
    
     if  (  25&lt;(lsm.lsm_accelData.Z/139)&lt; 50 ) 
    {
        Particle.publish("impulse", "medium");
    }
    
    if  ((lsm.lsm_accelData.Z/139) &gt; 50 ) 
    {
        Particle.publish("impulse", "fast");
    } 
    
 
}
else
{
    Serial.println("LSM330 Disconnected!");
    Serial.println(" ");
    Serial.println("        ************        ");
    Serial.println(" ");
}


delay(100);

}

//This is a code to gewrite data to SD card and manage storae//////////////////////////

//// //// //// //// ////
//// microSD config ////
// Pick an SPI configuration.
// See SPI configuration section below (comments are for photon).
#define SPI_CONFIGURATION 1
//// //// //// //// ////
// Setup SPI configuration.
#if SPI_CONFIGURATION == 0
// Primary SPI with DMA
// SCK => A3, MISO => A4, MOSI => A5, SS => A2 (default)
SdFat sd;
const uint8_t chipSelect = SS;
#elif SPI_CONFIGURATION == 1
// Secondary SPI with DMA
// SCK => D4, MISO => D3, MOSI => D2, SS => D1
SdFat sd(1);
const uint8_t chipSelect = D1;
#elif SPI_CONFIGURATION == 2
// Primary SPI with Arduino SPI library style byte I/O.
// SCK => A3, MISO => A4, MOSI => A5, SS => A2 (default)
SdFatLibSpi sd;
const uint8_t chipSelect = SS;
#elif SPI_CONFIGURATION == 3
// Software SPI. Use any digital pins.
// MISO => D5, MOSI => D6, SCK => D7, SS => D0
SdFatSoftSpi<D5, D6, D7> sd;
const uint8_t chipSelect = D0;
#endif // SPI_CONFIGURATION
//// //// //// //// ////
//// //// //// //// ////

// variable initalizations
FatFile globalAccelFile;
String globalAccelFileName;
String accelData;
bool flag; // flag checks whether certain events were successful or not
double accelMagnitude;
int fileTracker = 1;
int globalAccelFileTracker = 1;

// checks the accelerometer x y and z analog values, which are connected to A0, A1, and A2, respectively.
void checkAccel() {
globalAccelFileName = “globalAccel” + String(globalAccelFileTracker) + “.txt”;
globalAccelFile.open(globalAccelFileName, O_RDWR | O_CREAT | O_AT_END);

accelMagnitude = floor(sqrt(pow(analogRead(A0),2) + pow(analogRead(A1),2) + pow(analogRead(A2),2)) + 0.5); 
accelData += String(accelMagnitude).remove(4); 

// maximum String length is 622 bytes. Let's just be safe and say 616 so we don't overrun. 
if (accelData.length() &gt; 616) {
    accelData += " " + Time.timeStr(); 
    globalAccelFile.write(accelData);
    accelData = "";
}

if (accelData.length() &lt;= 616) { 
    accelData += "\n"; // each data point is on its own line
}

if (globalAccelFile.fileSize() &gt; 1000000) // if the accel file exceeds 1MB, on to the next one
{
    globalAccelFileTracker++;
}

globalAccelFile.close(); // don't forget to close the file, otherwise you run the risk of corrupting the file and card. 

}

// Find out if the SD card is cooperating. Good idea to call this before attempting to do anything major
// with the SD Card. This function is a little leaky - some SD errors can get by without this function picking them up.
void checkSdError() {
Serial.print("sd.cardErrorCode(): ");
flag = sd.cardErrorCode(); // directly related to sd success/failure
Serial.println(flag);
Serial.print("sd.cardErrorData(): "); // for additional debugging
Serial.println(sd.cardErrorData());

// If there's an error, write high to D7. You can plug in an LED at D7 to check the SD status whenever you want. 
if (!flag) {
    digitalWrite(D7, LOW);
}

else {
    digitalWrite(D7, HIGH);
}

}

// software Timers are preferable to delays, so the code can run concurrently, not sequentially. Along these lines, it’s probably
// a good idea to enable system threading using SYSTEM_THREAD(ENABLED);
Timer CheckAccelTimer(50, checkAccel); // sample accelerometer at 20 Hz
Timer CheckSdErrorTimer(1000, checkSdError); // checks for SD card errors every 1 second

//void setup() {
// Particle.connect(); // must manually call Particle.connect() if system_mode is semi_automatic
// Time.zone(-7); // changes time zone. Does not adjust for DST, must manually change.
// Serial.begin(115200); // debugging purposes
//sd.begin(chipSelect, SPI_FULL_SPEED); // init at full speed for best performanceo
//pinMode(D7, OUTPUT); // initalize D7 as an output for use by checkSdError
//Time.setTime(1473379200); // set time to start on September 9 2016 00:00:00 GMT (UNIX time = 1473379200, or seconds since


#34

I attached a cleaner version of the code and eliminated all the unnecessary comments.