graydetroit
Well-known member
I was discussing the topic of reading/writing project data structures from the SD card while at the same time playing audio files which are buffered from the SD card in this thread.
I am able to do non-blocking reads of other project data from the SD card while the SD buffered audio is playing, I do this by keeping some variables outside the main loop() which track the total data read from the SD each time the main loop iterates, whether the reading is complete or not, etc. The code reads data from files in chunks of 40kb.
Here is some pseudo code of how it works:
So this works fine, but if I try a similar approach for _writing_ data to the SD while SD audio playback is happening, the SD card access becomes like frozen it seems. The serial output comes to a crawl and the program behaves very strangely after until I reboot. Here is similar pseudo code but for the writing (which doesn't work).
I'm thinking something in
Thank you!
I am able to do non-blocking reads of other project data from the SD card while the SD buffered audio is playing, I do this by keeping some variables outside the main loop() which track the total data read from the SD each time the main loop iterates, whether the reading is complete or not, etc. The code reads data from files in chunks of 40kb.
Here is some pseudo code of how it works:
C++:
#define ASYNC_IO_BUFFER_SIZE 500 * 80
typedef struct {
char bigBuffer[512 * 800];
} STUFF;
EXTMEM STUFF stuff;
File asyncReadFile;
bool shouldLoad = false;
bool asyncFileReadComplete = false;
uint32_t asyncReadFileTotalRead = 0;
SdFs sd;
bool ready = false;
void setup() {
SPI.setMOSI(SDCARD_MOSI_PIN);
SPI.setSCK(SDCARD_SCK_PIN);
if (!(SD.begin(SDCARD_CS_PIN)))
{
return false;
}
// Initialize SDFat
if (!sd.begin(SD_CONFIG)) {
return false;
}
ready = true;
}
bool sdBusy() { return ready ? sd.card()->isBusy() : false; }
bool readFileBufferedAsync(std::string filename, void *buf, size_t size)
{
if(sdBusy()) {
Serial.println("SD CARD BUSY, CANNOT ASYNC READ!");
return true;
}
Serial.printf("expected size: %d for file: %s", size, filename.c_str());
asyncReadFile = SD.open(filename.c_str(), FILE_READ);
if (!asyncReadFile || !asyncReadFile.available())
{
Serial.printf("Failed to open file for reading: %s\n", filename.c_str());
return false;
}
Serial.printf(" asyncReadFile available: %d", asyncReadFile.available());
size_t bufferSize = ASYNC_IO_BUFFER_SIZE;
int8_t *index = (int8_t*)buf;
uint32_t chunkSize = min(bufferSize, size - asyncReadFileTotalRead);
asyncReadFileTotalRead += asyncReadFile.readBytes((char *)index, chunkSize);
Serial.printf(" asyncReadFileTotalRead: %d, chunkSize: %d\n", asyncReadFileTotalRead, chunkSize);
if (asyncReadFileTotalRead == size) {
asyncFileReadComplete = true;
asyncReadFileTotalRead = 0;
asyncReadFile.close();
Serial.printf("done reading file %s!\n", filename.c_str());
return true;
}
asyncReadFile.close();
return true;
}
// then in the main loop...
void update() {
if (shouldLoad && asyncFileReadComplete == false) {
readFileBufferedAsync("/big_buffer.bin", (byte *)&stuff, sizeof(stuff));
} else if (shouldLoad && asyncFileReadComplete) {
shouldLoad = false;
asyncFileReadComplete = false;
Serial.println("done loading!");
}
}
So this works fine, but if I try a similar approach for _writing_ data to the SD while SD audio playback is happening, the SD card access becomes like frozen it seems. The serial output comes to a crawl and the program behaves very strangely after until I reboot. Here is similar pseudo code but for the writing (which doesn't work).
C++:
#define ASYNC_IO_BUFFER_SIZE 500 * 80
typedef struct {
char bigBuffer[512 * 800];
} STUFF;
EXTMEM STUFF stuff;
byte writeBuffer[ASYNC_IO_BUFFER_SIZE];
File asyncWriteFile;
bool shouldSave = false;
bool asyncFileWriteComplete = false;
uint32_t remaining = 0;
uint32_t offset = 0;
SdFs sd;
bool ready = false;
void setup() {
SPI.setMOSI(SDCARD_MOSI_PIN);
SPI.setSCK(SDCARD_SCK_PIN);
if (!(SD.begin(SDCARD_CS_PIN)))
{
return false;
}
// Initialize SDFat
if (!sd.begin(SD_CONFIG)) {
return false;
}
ready = true;
remaining = sizeof(stuff);
}
bool sdBusy() { return ready ? sd.card()->isBusy() : false; }
bool writeFileBufferedAsync(std::string filename, const byte *data)
{
if(sdBusy()) {
Serial.println("SD CARD BUSY, CANNOT ASYNC READ!");
return true;
}
asyncWriteFile = SD.open(filename.c_str(), FILE_WRITE);
if (remaining > 0)
{
size_t chunkSize = min(ASYNC_IO_BUFFER_SIZE, remaining);
memcpy(writeBuffer, data + offset, chunkSize);
size_t bytesWritten = file.write(writeBuffer, chunkSize);
offset += chunkSize;
remaining -= bytesWritten;
Serial.printf("remaining: %d\n", remaining);
}
if (remaining <= 0) {
asyncFileWriteComplete = true;
}
asyncWriteFile.close();
return true;
}
// then in the main loop...
void update() {
if (shouldSave && asyncFileWriteComplete == false) {
writeFileBufferedAsync("/big_buffer.bin", (byte *)&stuff);
} else if (shouldSave && asyncFileWriteComplete) {
shouldSave = false;
asyncFileWriteComplete = false;
remaining = 0;
offset = 0;
Serial.println("done saving!");
}
I'm thinking something in
writeFileBufferedAsync
is wrong, but I'm not sure where. The code could just be completely wrong, I'm not used to writing structs into a file in chunks like this, so any help here is appreciated. If it's merely a limitation of the write speeds being slower vs read speeds being higher, I will have to find another workaround, because concurrent SD playback and read/write access is what I intended to do for my project.Thank you!
Last edited: