Library for writing rotating logs to SD card on the Particle Photon and Electron
Switch branches/tags
Nothing to show
Clone or download

README.md

SdCardLogHandlerRK

Library for writing rotating logs to SD card on the Particle Photon and Electron

The full browsable API documentation is here.

The official location for this library is: https://github.com/rickkas7/SdCardLogHandlerRK.

It's in the Particle community libraries as: SdCardLogHandlerRK.

Hardware

You'll need a SD card reader, presumably a Micro SD card reader. Make sure you get one that's compatible with the 3.3V logic levels used on the Photon and Electron. Some readers designed for the Arduino expect the 5V logic levels used by the original Arduino. I use this one from Sparkfun but there are others.

Photon

Electron

The SD card reader connects via the SPI interface. There are two SPI interfaces on the Photon and Electron and you can use either. Note that each SPI device must have a separate SS (sometimes called CS) pin. While it's listed in the table below as A2 (or D5), you can use any GPIO pin.

Primary SPI (SPI object)

  • A2 SS
  • A3 SCK
  • A4 MISO
  • A5 MOSI

Secondary SPI (SPI1 object)

  • D5 SS
  • D4 SCK
  • D3 MISO
  • D2 MOSI

In the picture above:

Device SPI Name SD Reader Color
A2 SS /CS Yellow
A3 SCK SCK Orange
A4 MISO DO Blue
A5 MOSI DI Green
3V3 VCC Red
GND GND Black
CD

Using the library

This library uses the Logging API that was added in system firmware 0.6.0.

For example:

Log.trace("Low level debugging message");
Log.info("This is info message");
Log.warn("This is warning message");
Log.error("This is error message");

Log.info("System version: %s", System.version().c_str());

It does not remap Serial, so if you're using Serial.print for logging, you should switch to using Log.info instead.

The library creates a directory (default: "logs") at the top level of the SD card and stores the log files in that. Each log file is a .txt file 6-digit number beginning with 000001.txt.

The default log file is 1 MB (1000000 bytes), but you can reconfigure this. The actual size will be slightly larger than that, as the log is rotated when it exceeds the limit, and the file will always contain a whole log entry.

When rotated, the default is to keep 10 log files, but this is configurable. The oldest is deleted when the maximum number is reached.

By default, SdCardLogHandler writes to Serial as well, like SerialLogHandler. This can be reconfigured.

This is the example program:

#include "Particle.h"

#include "SdFat.h"
#include "SdCardLogHandlerRK.h"

SYSTEM_THREAD(ENABLED);

const int SD_CHIP_SELECT = A2;
SdFat sd;

SdCardLogHandler logHandler(sd, SD_CHIP_SELECT, SPI_FULL_SPEED);

size_t counter = 0;

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

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

Initialize the SdFat library

You normally initialize the SdFat library by creating a global object for it. For example:

SdFat sd;

If you are using SPI1 (the D pins) instead, use:

SdFat sd(1);

Note that you should not call sd.begin() in setup! The begin call is done by the SdCardLogHandler because it needs to be done earlier than setup, and also every time the card is ejected.

If you need to check for a successful call to begin in your code, instead using logHandler.getLastBeginResult(). You might do this before using the SD card in your code. If there is no SD card inserted, the result will be false.

Initialize the SdCardLogHandler

Normally you initialize the SdCardLogHandler by creating a global object like this:

SdCardLogHandler logHandler(sd, SD_CHIP_SELECT, SPI_FULL_SPEED);
  • sd is the SdFat object, as described in the previous section
  • SD_CHIP_SELECT is the pin connected to the CS/SS pin. In the code above, SD_CHIP_SELECT is set to A2.
  • SPI_FULL_SPEED determines the speed to use, either full or SPI_HALF_SPEED.

There are also two optional parameters:

SdCardLogHandler logHandler(sd, SD_CHIP_SELECT, SPI_FULL_SPEED, LOG_LEVEL_TRACE);

The next optional parameter allows you to set the logging level.

  • LOG_LEVEL_ALL : special value that can be used to enable logging of all messages
  • LOG_LEVEL_TRACE : verbose output for debugging purposes
  • LOG_LEVEL_INFO : regular information messages
  • LOG_LEVEL_WARN : warnings and non-critical errors
  • LOG_LEVEL_ERROR : error messages
  • LOG_LEVEL_NONE : special value that can be used to disable logging of any messages

And finally, you can use logging categories as well to set the level for certain categories:

SdCardLogHandler logHandler(sd, SD_CHIP_SELECT, SPI_FULL_SPEED, LOG_LEVEL_INFO, {
	{ "app.network", LOG_LEVEL_TRACE } 
});

That example defaults to INFO but app.network events would be at TRACE level.

Options

There are also options available to control how logging is done. For example, if you want to change the default log file size to (approximately) 50000 bytes:

SdCardLogHandler logHandler(sd, SD_CHIP_SELECT, SPI_FULL_SPEED, LOG_LEVEL_TRACE);
STARTUP(logHandler.withDesiredFileSize(50000));

You can chain these together fluent-style to change multiple settings:

SdCardLogHandler logHandler(sd, SD_CHIP_SELECT, SPI_FULL_SPEED, LOG_LEVEL_TRACE);
STARTUP(logHandler.withDesiredFileSize(50000).withMaxFilesToKeep(5));

By default, SdCardLogHandler writes the log entries to Serial like SerialLogHandler. You can turn this off by using:

SdCardLogHandler logHandler(sd, SD_CHIP_SELECT, SPI_FULL_SPEED, LOG_LEVEL_TRACE);
STARTUP(logHandler.withNoSerialLogging());

Or if you want to log to Serial1 (or another port) instead:

SdCardLogHandler logHandler(sd, SD_CHIP_SELECT, SPI_FULL_SPEED, LOG_LEVEL_TRACE);
STARTUP(logHandler.withWriteToStream(&Serial1);

Normally, a sync operation is done after each log entry, which maximizes the chance the the log entry will be written out to SD card successfully. However, this will slow down operation if you do a lot of logging. You can have the SdFat library manage its own sync, which happens about every 512 bytes of log messages, by turning off sync after every entry:

SdCardLogHandler logHandler(sd, SD_CHIP_SELECT, SPI_FULL_SPEED, LOG_LEVEL_TRACE);
STARTUP(logHandler.withSyncEveryEntry(false);

Finally, when the SD card is ejected, a check for being inserted is only done at most every 10 seconds. This is because there's a timeout operation that makes the operation slow, and doing it on every log entry would make the performance very bad. You can change this interval by using:

SdCardLogHandler logHandler(sd, SD_CHIP_SELECT, SPI_FULL_SPEED, LOG_LEVEL_TRACE);
STARTUP(logHandler.withCardCheckPeriod(30000);

The value is in milliseconds; that changes the value from 10 seconds to 30 seconds.