Library for logging to SD card: SdCardLogHandlerRK

This question touches on multiple topics.
SdCardLogHandlerRK vs SerialLogHanlder:
Log handlers (of any flavour) provide a standardised interface for you to log messages irrespective of the actual data sink (aka target). For more background read here
The two LogHandlers you reference just use different sinks. One logs to USB Serial while the other logs to SD.

So if you want to use any of the Log.xxx() functions to dump your logs on an SD card you need to use the SdCardLogHandlerRK library (which is internally using the SdFat library to communicate with its desired data sink).
Log handlers also add some extra info to your data (e.g. timestamps, log scope, severity, …) which may not be desirable in your use case.

If you want to just read/write raw data (especially binary) from/to an SD card neither of the LogHandlers would help you with that. For that you should use the SdFat library directly (without any wrapper).

2 Likes

@ScruffR
Thank you, i will give that link a read. I wish to log numerical data (Not the raw binary sensor data). I will proceed as you advised. I was also trying to understand the difference between Log.info and the LogtoSD.info, i will read through the link to see if it explained there. Thanks, once again ScruffR.

The example you probably want is the 3-print example in SdCardLogHandlerRK.

#include "Particle.h"

#include "SdCardLogHandlerRK.h"

SYSTEM_THREAD(ENABLED);

// The SD card CS pin on the Adafruit AdaLogger FeatherWing is D5.
const int SD_CHIP_SELECT = D5;
SdFat sd;

SdCardPrintHandler printToCard(sd, SD_CHIP_SELECT, SPI_FULL_SPEED);

size_t counter = 0;

void setup() {
	Serial.begin();
}

void loop() {
	printToCard.printlnf("testing counter=%d", counter++);
	delay(1000);
}

With this example, any time you use printToCard.printlnf() it will print data into your log file. It automatically takes care of opening and closing the file, and rotating the file when it gets large.

2 Likes

Thank you @rickkas7
I will be using a 16GB micro SD, how to i set the limit of the log file to b larger then 1 MB?

Hello @rickkas7

I tried this program on my particle photon but the example does not compile. I have included the src files in the app as well from github:

The code results in a complile error with the following description:

sddatatest.ino:12:0: undefined reference to "SdCardPrintHandler::SdCardPrintHandler(SdFat&, unsigned char, particle::__SPISettings)"
error
sddatatest.ino:23:0: undefined reference to "SdCardPrintHandler::~SdCardPrintHandler()"

Here is the link for my app.

Please help.

You should double check the code you have got in your SdCardLogHandlerRK.cpp file.
This looks very much like you have got the contents of SdCardLogHandlerRK.h in there instead of the actual implementation sources.

BTW, that library is also available on Web IDE for direct import :wink:
https://build.particle.io/libs/SdCardLogHandlerRK/0.1.0
When using that you need to remove the explicit import of SdFat library since the SdCardLogHandlerRK lib already has the needed dependency set.

1 Like

:man_facepalming:t2:
i am sorry i should have checked it twice, i checked it before using the buffer.h and after i removed the buffer.cpp i did not check if the SDcardloghandler.cpp and .h were correct. Thank you ScruffR.

Hello @rickkas7

I adapted your program to log the BME280 sensor measurements. Here is the link for my program:

I also noticed that printing to the serial monitor does not work as usual (A serial print command i had in the Setup function did not display on the serial monitor and when the values stopped recording in the SD card, the serial monitor stopped printing the values).
Is it possible to print to the serial monitor if i am logging data to the SD card using the program you wrote here?

Thanks! Yes, you can have multiple log handlers. Here’s an example that uses both Serial and SD card loggers, and you choose which to log to using the categories feature.

Also is there a way to "sense" if the SD card is reaching full capacity?

@rickkas7
Hello Rick, another question... Is there a manner in which to detect if an SD card is connected?
I have tried to use the sd.cardErrorCode() function in the following manner:

if(SDActive==1){ //A push button in Blynk is pressed to activate data logging

  SaveToSDCard();
  
  //To detect if SD Card is connected
  Serial.print("sd.cardErrorCode(): "); 
    flag = sd.cardErrorCode(); // directly related to sd success/failure
    Serial.println(flag);

// If there's an error i.e. the SD is not connected
    if (flag) {
  
        Serial.printlnf("SD card not connected");
        Blynk.notify("SD card not connected");
        Blynk.virtualWrite(V14,0);    //Switch the push button off
        SDActive=0;                        //Deactivate the logging feature
        
    }
    else
    Serial.println("Data is being logged onto the SD card"); //If all is well

}
else{

Serial.printlnf("SD card option not activated"); //If the PB in Blynk is not pressed to activate data logging

}	 

This works fine accept that:
-Data has to be sent to the SD card first and if the SD card is not connected then only will it be realized that the SD is not connected.
-When the SD card is disconnected it takes about 6 seconds for the hardware to realize that it has been reconnected
Is there a more elegant manner in which to detect the connection of the SD card?

Usually SD card slots also feature a “card present” pin which you can use for that.

1 Like

Okay thanks ScruffR. Unfortunately for me, the module appears not to have such a pin. I guess what i have done will have to do.

Have you got a link or datasheet for that SD module?

On the SdCardPrintHandler (or SdCardLogHandler) object, call, for example:

if (printToCard.getLastBeginResult()) {
    // Card is present
}
1 Like

There was no datasheet but this is where I purchased the SD card from:

Thanks @rickkas7 i will give this a try

I think you are mistaken.
Look at the image shown on the site you linked to


Markt the description of the CD pin!

2 Likes

Apologies @ScruffR. i posted the wrong link. This is the one i have ordered now. The link below is the one i am prototyping with that does not have the card detect pin:
https://www.diyelectronics.co.za/store/memory/512-micro-sd-card-module.html

Thank you @ScruffR ScruffR

2 posts were merged into an existing topic: Using both I2C and Canbus on Photon

@rickkas7 Should there be any concern with having two different SdCardLogHandlers (writing to two different directories) and reading/writing other files on the SD card (and a serial log handler)?

I’ve written a test program and it seems to be stable (so far).

The only caveat being:

  • You must write a new line or data >= BUF_SIZE to each handler or else getLastBeginResult() will never return true
    • getLastBeginResult() returns lastBeginResult, which is only set by scanCard(), which is private
    • Which is only called by writeBuf(), which is also private, which itself is only called by write() when it encounters a newline or bufOffset >= BUF_SIZE (so can’t trick it by printing “”)

Simple test program:

#include <SPI.h>
#include "SdFat.h"
#include "sdios.h"
#include "SdCardLogHandlerRK.h"

SYSTEM_THREAD(ENABLED);

const int SD_CHIP_SELECT = SS;

SdFat sd;
SdFile someFile;

SdCardLogHandler<2048> sdLogHandlerOne(sd, SD_CHIP_SELECT, SPI_FULL_SPEED,
                                       LOG_LEVEL_ERROR,
                                       {{"app.snap", LOG_LEVEL_WARN}});

SdCardLogHandler<2048> sdLogHandlerTwo(sd, SD_CHIP_SELECT, SPI_FULL_SPEED,
                                       LOG_LEVEL_NONE,
                                       {{"app.crackle", LOG_LEVEL_ERROR}});

SerialLogHandler serLogHandler(LOG_LEVEL_NONE,
                                  {{"app.pop", LOG_LEVEL_TRACE}});

STARTUP(sdLogHandlerOne.withLogsDirName("logDirOne").withNoSerialLogging();
        sdLogHandlerTwo.withLogsDirName("logDirTwo").withNoSerialLogging(););

Logger sdLoggerOne("app.snap");
Logger sdLoggerTwo("app.crackle");
Logger serialLogger("app.pop");

void setup() {
    Serial.begin(9600);
    while (!Serial.isConnected()) {
        delay(50);
    }
    serialLogger.trace("Setting up sd card handlers");
    sdLogHandlerOne.setup();
    sdLogHandlerTwo.setup();
    serialLogger.trace("End of setup");
}

void loop() {
    static unsigned long loopCounter = 0;
    
    loopCounter++;

    sdLoggerOne.warn("foo - %lu", loopCounter);
    sdLogHandlerTwo.printlnf("bar - %lu", loopCounter);

    if (sdLogHandlerOne.getLastBeginResult() && sdLogHandlerTwo.getLastBeginResult()) {
        if (someFile.open("someFile.txt", FILE_WRITE)) {
            someFile.println("Some data");
            someFile.close();
            serialLogger.trace("Have written data to file");
        }
    }


    sdLogHandlerOne.loop();
    sdLogHandlerTwo.loop();
    
    delay(100);
    
    if (loopCounter >= 10) {  //No reason to fill up the card, so prep for next upload
        System.dfu();
    }
}


@rickkas7 @prtclusr I’m hoping you can give me some help with writing data to an SD card. Most recently I tried the code @prtclusr posted but with the same result; nothing appears to get written to the SD card. We are using a custom printed circuit board with a Particle E-Series board added to it. We have 2 micro USB ports that we are using. Currently we have one that we use as the serial port, then the other that we have a micro SD card adapter/reader plugged into.

What I think is happening is that the board thinks both micro SD ports should act as a serial port, so it freaks out when I’m trying to run the serial monitor and write to the SD card. So I tried flashing the code to the board, disconnecting the serial cable, then plugging in the SD card reader. Next, I pressed the reset button on the board to re-start the program, but the board just sits there breathing cyan. After 30ish minutes I unplugged the SD card/reader and plugged it into the computer and there was no data written there.

The end goal with this whole project is to send the location and 2 different temperatures from sensors we have attached to it every few hours. So far I’ve gotten a program to check if there is cellular service every few hours and capture the location and temperatures. At that point, it needs to transmit the location and temperatures if there is service. If there isn’t cellular service, then it needs to capture that same data, store and save it, then transmit all of that data the next time it gets service. Any help you can offer would be greatly appreciated!

A little more info, this is the first project I’ve really ever tried with Particle and am getting extremely overwhelmed with all the information available, but not knowing what to do with it. So if you could just keep responses simple, that would be fantastic.