SD card

ShermanP:
I don't know how you would dedicate ram to these buffers, or address them, or read data from them for conversion to CSV, in C++, but I assume there is a way to do all that if you are a C++ jock. And actually, I would love to see an example of that code if anybody knows of one.

If you can save the data in an interrupt, then one relatively easy way would be:

Declare a struct with all the data for each log entry:

struct LogEntry
{
    float _myFloats[7];
    long _myLong;
    bool _myBool;
};

Create two arrays, each with enough entries so that you will have enough time to write out the data to the SD card. Since they'll be the same size, you can use a two dimensional array.

constexpr size_t MAX_LOG_MEM = 1024;  //  Or however much RAM you want to dedicate
constexpr size_t NUM_ENTRIES = MAX_LOG_MEM / 2 / sizeof LogEntry;
LogEntry myLogEntries[2][NUM_ENTRIES];  //  Maximum entries that fit under the total size

Or just set NUM_ENTRIES directly if you have a good idea of what it should be,

Use a couple of global variable to track which one you are filling, the offset, and when it is full. When one is full, start filling the other, and in the main loop start writing out the first to the SD card. Mark that one as empty when you are done. You could even have the interrupt throw away entries if it tries to switch and the other one isn't empy yet.

If you have problems with the buffers sometimes not emptying fast enough, you could use more buffers, but with fewer entries (for example, change the '2' above to '3', and you'd have three sets to work with. 'Most' of the time the writer keeps up, but when it hits a delay, there's an 'extra' buffer for the logger to use.)

This all assumes you can actually get you data in the interrupt handler - you may not be able to if you have to talk to another device, for example. I don't know enough about the 'yield()' functionality to know how to incorporate that.

BUT, before doing that, you should test if it would be fast enough overall. In 15 seconds you write out 1500 entries. So, write a quick test program that does ONLY that (use dummy data), and see how long it takes. If that test takes more than 15 seconds, then your sampling rate will be too fast.

Interrupts are nice except when you have a sensor and the SD card on the same SPI bus. You can't interrupt one to do the other.

yield(), as used by SdFat.h, can leave the SD card CS selected but it is never actively using the card. You can de-select CS and use the SPI bus while SdFat is waiting for the card.

Thanks very much for the code, MHotchin. I knew there had to be a way, and you provided it. I would just say that the sticking point with this, for the 328P at least, is that you only have 2K of ram to begin with. So two 512-byte buffers uses up half the ram. For a sketch of any complexity, that may be a problem. But it may be possible to get away with a good bit less than 512 bytes per buffer.

MorganS, even if you could get the software to de-select the SD card’s CS, you still have the problem that the standard microSD adapter with level-shifter I/C does not release MISO when CS is turned off. You have to do a bit of rewiring on the adapter to make that happen.

Also, my understanding is that once initiated, an SPI byte transfer will complete whether the processor has been interrupted or not. So the Loop code that’s writing to the SD would need to disable the data sampling interrupt beginning just before writing a byte to the output shift register, and ending just after reading a byte from the input shift register. That can work in assembler, but probably not so well in C++ unless you modify the SDFat library to do that.

Anyway, I think the OP now has several ways he could get this done, at least if his sensors don’t need SPI.

There are SD card modules that do not pass the MISO signal through the level shifter. The Adafruit micro SD module is one. See the schematic here.

ShermanP:
Also, my understanding is that once initiated, an SPI byte transfer will complete whether the processor has been interrupted or not. So the Loop code that's writing to the SD would need to disable the data sampling interrupt beginning just before writing a byte to the output shift register, and ending just after reading a byte from the input shift register. That can work in assembler, but probably not so well in C++ unless you modify the SDFat library to do that.

Anyway, I think the OP now has several ways he could get this done, at least if his sensors don't need SPI.

Granted, if no SPI sensors or two SPI buses, then there's no issue.

The issue is not byte transfers. Many SPI devices treat the CS assert as a 'transaction'. If you want to write a byte to an address (like a configuration register in a sensor), you have to send the address and then send the byte in the same transaction. You can't release CS and do something else in between those two bytes.