Go Down

Topic: Teensy 3.1 / Data Logging / SD Card / Latency / Non-Blocking SDFAT Library (Read 19571 times) previous topic - next topic

Koni

I am working on a logging device for paragliding, using a Teensy 3.1, a MS5637 pressure sensor and an SD card. The code I am using is as follows:

Code: [Select]
#include <SdFat.h>    // SD card library
#include <math.h>
#include <Wire.h>
const int chipSelect = 10;

#define ADDRESS 0x76 //define adress of MS5637 Barometer, infos from datasheet

// Create file system object
SdFat sd;

// Define text file for logging
ofstream logfile;

uint32_t D1 = 0;
uint32_t D2 = 0;
int64_t dT = 0;
int64_t TEMP = 0;
int64_t OFF = 0;
int64_t SENS = 0;
int64_t P = 0;
int64_t T2    = 0;
int64_t OFF2  = 0;
int64_t SENS2 = 0;
uint16_t C[7];

float temperature;
float pressure;
float altBaro;
const float sea_press = 1013.3;

String bufferString = "";
String SDbufferString = "";

uint64_t time = 0;
uint64_t timeOld = 0;




void setup() {

  Wire.begin();
  Serial.begin(115200);
 
  I2C0_F = 0x1A;   //set the I2C speed to 400KHz for Teensy 3.1

  delay(100);
  initBaro(ADDRESS);

  // initialize the SD card at SPI_FULL_SPEED
  if (!sd.begin(chipSelect, SPI_FULL_SPEED )) sd.initErrorHalt();

  // Create a new file in the current working directory
  // and generate a new name if the file already exists
  char fileName[13] = "LOGGER00.CSV";

  for (uint8_t i = 0; i < 150; i++)
  {
    fileName[6] = i/10 + '0';
    fileName[7] = i%10 + '0';
    if (sd.exists(fileName)) continue;
    logfile.open(fileName);
    break;
  }

  logfile << ("Time,Temperature,Pressure,AltitudeRaw") << flush;   //Write header to logfile
  timeOld = millis();
}



void loop() {

  time = millis();
  int dt = time - timeOld;
  timeOld = time;

  requestPressure();      // Request pressure conversion raw
  delay(20);              // OSR=8192, minimal delay ro read result is 17ms
  // Instead of delay(20) something useful can be done during this time, like reading an IMU
  readPressure();         // Read raw pressure

  requestTemperature();   // Request pressure conversion raw
  delay(20);              // OSR=8192, minimal delay ro read result is 17ms
  // Instead of delay(20) something useful can be done during this time, like reading an IMU
  readTemperature();      // Read raw pressure

  calculatePressureTemperature();
  altBaro = 44330 * (1.0 - pow(pressure /sea_press,0.1903));

  // make a string for assembling the data to log:
  String dataString = "";

  dataString += String("Time,");
  dataString += String(int(time));
  dataString += String(",");
  dataString += String("Temperature,");
  dataString += String(temperature);
  dataString += String(",");
  dataString += String("Pressure,");
  dataString += String(pressure);
  dataString += String(",");
  dataString += String("AltitudeBaro,");
  dataString += String(altBaro);
  dataString += String("\n");  //Create a new line on the SD card

  bufferString += dataString;
  int length = bufferString.length();

  if (length > 512)
  {

    SDbufferString = bufferString.substring(0,511);    //Create a string with 512 bytes for writing on the SD card.
    bufferString = bufferString.substring(511,length); //Remove the 512 bytes for the SD card from the main string.

    char charBuf[512];
    SDbufferString.toCharArray(charBuf,512);
    logfile << charBuf << flush;      //Write to SD Card 
  }

  Serial.print(int(dt));
  Serial.println();

}


void requestPressure()
{
  Wire.beginTransmission(ADDRESS);
  Wire.write(0x4A); //OSR=8192, minimal delay ro read result is 17ms
  Wire.endTransmission();
  //delay(20);
}


void readPressure()
{
  Wire.beginTransmission(ADDRESS);
  Wire.write((byte) 0x00);
  Wire.endTransmission();

  Wire.beginTransmission(ADDRESS);
  Wire.requestFrom(ADDRESS, (int)3);
  if (Wire.available() >= 3)
  {
    D1 = Wire.read() * (unsigned long)65536 + Wire.read() * (unsigned long)256 + Wire.read();
  }
  Wire.endTransmission();
}


void requestTemperature()
{
  Wire.beginTransmission(ADDRESS);
  Wire.write(0x5A);  //OSR=8192, minimal delay ro read result is 17ms
  Wire.endTransmission();
  //delay(20);
}


void readTemperature()
{
  Wire.beginTransmission(ADDRESS);
  Wire.write((byte) 0x00);
  Wire.endTransmission();

  Wire.beginTransmission(ADDRESS);
  Wire.requestFrom(ADDRESS, (int)3);
  if (Wire.available() >= 3)
  {
    D2 = Wire.read() * (unsigned long)65536 + Wire.read() * (unsigned long)256 + Wire.read();
  }
  Wire.endTransmission();
}


void calculatePressureTemperature()
{
  OFF2 = 0;
  SENS2 = 0;

  dT=D2-C[5]*pow(2,8);
  TEMP=2000+(dT*C[6])/pow(2,23);
  OFF=C[2]*pow(2,17)+dT*C[4]/pow(2,6);
  SENS=C[1]*pow(2,16)+dT*C[3]/pow(2,7);

  if (TEMP < 2000) // if temperature lower than 20 Celsius
  {
    OFF2 =  61*pow((TEMP-2000),2)/pow(2,4);
    SENS2 = 29*pow((TEMP-2000),2)/pow(2,4);
  }

  TEMP=TEMP-T2;
  OFF=OFF-OFF2;
  SENS=SENS-SENS2;

  P=(((D1*SENS)/pow(2,21)-OFF)/pow(2,15));

  temperature=(float)TEMP/100;
  pressure=(float)P/100;

}


void initBaro(uint8_t address)
{
  Serial.println("Reading Calibration Data");

  Wire.beginTransmission(address);
  Wire.write(0x1E); // reset
  Wire.endTransmission();
  delay(10);


  for (int i = 0; i < 6  ; i++) {

    Wire.beginTransmission(address);
    Wire.write(0xA2 + (i * 2));
    Wire.endTransmission();

    Wire.beginTransmission(address);
    Wire.requestFrom(address, (uint8_t) 6);
    delay(1);
    if (Wire.available())
    {
      C[i + 1] = Wire.read() << 8 | Wire.read();
    }
    else {
      Serial.println("Error Reading Calibration Data"); // error reading the PROM or communicating with the device
    }

    Serial.print("C");
    Serial.print(i + 1);
    Serial.print(" ");
    Serial.println(C[i + 1]);
  }
  Serial.println();
}


The issue is now that it takes too long (and the dt time is not constant, varies between 41ms to 300ms) to write the data to the SD card, and that the code does nothing useful when the SD card is busy writing the data. The idea is to have a code that always needs the same time for the loop, and that there would no useful processor time wasted waiting for the SD card to write the data.

Ideally it would be great if the writing of the data to the SD card could be split into several actions:

Buffer the data that have to be written to the SD card in strings of 512 bytes.

Check if the SD card is ready to accept data. If not, continue directly with the main loop. But if it's possible to write data then this data should be sent to the SD card, without waiting that the SD card actually writes the data. Continue immediately after the write command with the main loop.

Check later on if the data has been written to the SD card in the loop. If the SD card is still busy writing data continue immediately with the main loop. Otherwise write another 512 bytes from the buffer.

Like this the loop would always take almost the same time to execute, but the data would only be sent to the SD card if that makes sense. The intervals where the data are written to the SD card would then be somewhat written randomly, but the code would not be slowed down due to the fact that data gets written to the SD card.

It doesn't matter if it takes time to write to the card, but it would be very useful if the Arduino or Teensy could do something else while the card is writing the data, instead of waiting for the SD card until the write process has finished. If such an approach is feasible then it might take much less processor time to write the data. And if the data is written not in constant time steps this doesen't matter, it's important that the correct data is written to the SD card, not when that data is written.

A non-blocking SD library or SDFAT library would solve that problem.

I am quite new to programming, so help to solve that problem would greatly be appreciated.

el_supremo

Don't send me technical questions via Private Message.

Koni

The SD card latency problem / code blocking  is not Teensy related, its the same problem with Arduinos. Thats why I posted in this forum, but also at http://forum.pjrc.com/forum.php. Teensy 3.1  has more memory than an Arduino Uno, so buffering might be easier to realize with a Teensy. But a non-locking SDFAT library might be also solve many SD card related problems with Arduinos.

fat16lib

Code: [Select]
logfile << charBuf << flush;      //Write to SD Card  
Writing single 512 byte blocks to files on modern SD cards will always have long unpredictable write delays.  The basic flash write size is not 512 bytes.

Modern SD cards have a Recording Unit (RU) that is a multiple of 16 KB.  High performance cards may have 32 KB or larger RUs.

When a 512 byte block is forced to the card, a new RU must be written.  Existing data must be copied to the new RU by the card controller.  This could mean 32 KB of data is written to flash each time you do a flush.  This soon exhausts the free erased RUs and the card will block while it erased an AU of flash which may be several MB.

Quote
Check if the SD card is ready to accept data.

There is no command to do this.  Any new features that could help are not available in SPI mode.  SPI mode is very limiting and only marginally supported in modern cards.

There are several ways to deal with this problem.  You can use an RTOS to buffer sensor data in a high priority thread and write to the SD in a low priority thread.  See the SD logger example in NilRTOS https://code.google.com/p/rtoslibs/downloads/list.

I wrote SdFat which is also the base for SD.h.  I have no plans to add new features to avoid this problem.  Writing to a file system using SPI mode with a 512 byte cache buffer will have long delays with modern SD cards.

I am experimenting with SDIO using 32 KB buffers on an STM32F407.  I can write at over 5 MB/sec with no busy delays.  I can't port this code to Arduino.

Edit: I can write at over 20 MB/sec with only small, less than 1 ms, occasional busy delays using 4-bit SDIO.

JChristensen


Writing single 512 byte blocks to files on modern SD cards will always have long unpredictable write delays.  The basic flash write size is not 512 bytes.


This would also be true of any smaller buffer size?

fat16lib

Quote
This would also be true of any smaller buffer size?


SPI mode performance of the new generation of SD cards varies greatly and is not very predictable.  Manufactures only include SPI mode since the SD spec requires it.  Sometimes larger buffers reduce the frequency of long latency write but not the time.  In other cases the time is reduced.

New cards are optimized for phones and tablets in 4-bit SDIO mode.  There is little correlation between SPI performance with small writes and large writes in SDIO mode. 

I don't think there is a future in optimizing SPI mode performance for fast data logging. 

I am buying  development boards with SDIO mode SD sockets.  The SDIO controller on these boards have CRC and a fast DMA mode.

Koni

Increased the buffer to 8 KB, with a buffer of 16 KB the code does not execute. But there is still the problem that writing to the SC card blocks the code.

What about the idea to first open a file on the SD card (T). Then pre-erase a block of 8 KB on the SD card in the next loop (T+1). Send 8 KB from the buffer to the SD card in the following loop  (T+2) , but in a way that the code is not blocked. Then a few loops later (T + 10) close the SD file. If there is not a lot of data to be written, maybe every 120 loops in my case, then it should be possible to log the data correctly, without delaying the main code, even when the SD card has sometimes a big latency.


fat16lib

This won't work.
Quote
What about the idea to first open a file on the SD card (T). Then pre-erase a block of 8 KB on the SD card in the next loop (T+1). Send 8 KB from the buffer to the SD card in the following loop  (T+2) , but in a way that the code is not blocked. Then a few loops later (T + 10) close the SD file.



Use an RTOS, that will work.
Quote
There are several ways to deal with this problem.  You can use an RTOS to buffer sensor data in a high priority thread and write to the SD in a low priority thread.  See the SD logger example in NilRTOS https://code.google.com/p/rtoslibs/downloads/list.

JChristensen


Quote
This would also be true of any smaller buffer size?


SPI mode performance of the new generation of SD cards varies greatly and is not very predictable.  Manufactures only include SPI mode since the SD spec requires it.  Sometimes larger buffers reduce the frequency of long latency write but not the time.  In other cases the time is reduced.

New cards are optimized for phones and tablets in 4-bit SDIO mode.  There is little correlation between SPI performance with small writes and large writes in SDIO mode. 

I don't think there is a future in optimizing SPI mode performance for fast data logging. 

I am buying  development boards with SDIO mode SD sockets.  The SDIO controller on these boards have CRC and a fast DMA mode.


Thanks for the reply, I sure do appreciate your library and the effort and expertise you've put into it.

Koni

To use an RTOS might work, but right now that is far too complex for my programming skills.

From http://elm-chan.org/docs/mmc/mmc_e.html it looks like it is possible to eliminate waste time:

Quote
Single Block Write

The Single Block Write writes a block to the card. After a CMD24 is accepted, the host controller sends a data packet to the card. The packet format is same as block read operations. Most cards cannot change write block size and it is fixed to 512. The CRC field can have any fixed value unless the CRC function is enabled. The card responds a Data Response immediataly following the data packet from the host. The Data Response trails a busy flag and host controller must wait until the card goes ready.
In principle of the SPI mode, the CS signal must be kept asserted during a transaction. However there is an exception to this rule. When the card is busy, the host controller can deassert CS to release SPI bus for any other SPI devices. The card will drive DO low again when reselected during internal process is in progress. Therefore a preceding busy check, check if card is busy prior to each command and data packet, instead of post wait can eliminate waste wait time.
In addition the internal write process is initiated a byte after the data response, this means eight clocks are required to initiate internal write operation. The state of CS signal during the eight clocks can be either low or high, so that it can be done by bus release process described below.


So basically it would be sufficient to check if the card is busy in checking if DO is low, and only send commands and / or data packets to the card when DO is high, and continue with the code after a data packet was sent and the data response signal was received, without waiting for the SD card and DO to go high again, as described above. Then the latency problem might be almost eliminated.

fat16lib

Quote
So basically it would be sufficient to check if the card is busy in checking if DO is low, and only send commands and / or data packets to the card when DO is high


Won't work.  Please read the following carefully.

When you write to a file, a large number of SD commands and operations operations are required.  There is no way you could check for busy before executing this statement.
Code: [Select]
logfile << charBuf << flush;
This statement may require a cluster to be allocated.  That requires reading FAT blocks until a free cluster is found.  Updating the FAT block in memory, writing it back to the SD, then updating the backup FAT.  Even if you write 512 bytes, the data may cross a block so there could be read of a block, copy part of the data to that block, write the block back, then write a second block.  Then the flush requires reading the directory entry for the file, updating the directory entry and writing it back.

The SdFat library does check for busy before every write since the write would fail otherwise.  The problem here is that the card won't indicate busy until you send the write command.  Then you must wait before sending the data packet. If you write a RU which may be 16 KB, there may be 32 busy periods.

SdFat can't check for busy before reads.  The card sends a stream of busy tokens for read and finally sends a start data token.   

No reasonable SD library can do what you want.  Chan's FatFS does not have a non-blocking mode.  FatFS is widely use and the latency problem is handled by using FatFS in an RTOS.

I also have a new replacement for SdFat that I use in RTOSs like ChibiOS and FreeRTOS.  The problem is simpler on ARM processors with more features than Teensy.  You can use SDIO and write a driver that sleeps while the SDIO controller does all the busy checking for a large read or write.  Other threads run and no CPU time is lost in busy checks.

There is another possibility.  You can create a large continuous file and write it with low level SD operations.  You must still buffer data but the check of MISO should work. 

This program used one large multiple block write and avoids most busy periods. It can log data much faster than your requirement on an Uno http://forum.arduino.cc/index.php?topic=228549.0 .
Quote
I did a reliability test logging five analog pins at 5,000 samples per second.  This is an ADC rate of 25,000 values per second.  I logged 512 MB of data without dropping any values.





Koni

Thanks for the quick response  fat16lib. It looks like I am stuck with my problem. The example from http://forum.arduino.cc/index.php?topic=228549.0  does not work with Teensy 3.1. But I need a Teensy 3.1 for the processing power for the filtering of the data using a Kalman Filter.

My last hope is your hint to use a large continuous file and write it with low level SD operations. Is it possible to use a CSV file as  the large continuous file? If yes then this might work, since buffering data is not a problem with Teensy. It's really just writing data to the SD card what I want, with the lowest latency possible. Nothing else.

fat16lib

The example http://forum.arduino.cc/index.php?topic=228549.0 shows how to use a per-allocated contiguous file.  Study it and use a similar method on Teensy.

Are you a lazy student trying to get others to think for you?

I won't be replying to any more of your questions since the examples I pointed out have the basic solution.


Koni

No I am not a lazy student. Just a newbie in programming, began playing around with Arduino about a year ago.

Koni

The hint with the Analog Logger helped a lot, and also the information from http://forum.pjrc.com/threads/25920-Teensy-3-1-ADC-Measurement-SD-logging-problem-with-Sample-Rate.

Code: [Select]
/*
ECG measurement Project:
- Sensing two ADC values,
- Analog files being logged on sd card
Time  A-B  A  B
*/

//Including Libraries
#include <string.h>
#include <Time.h>  
#include <SdFat.h>
#include <SdFatUtil.h>
#include <BufferedWriter.h>
SdFat sd;
SdFile myFile;

//Teensy 3.1 has the LED on pin 13
const int ledPin = 13;
const int readPin0 = A2;  //ADC read A2
const int readPin1 = A3;  //ADC read A3
const int chipSelect = SS;
//char filename[] = "00000000.txt";
#define SD_CHIP_SELECT  10  // SD chip select pin
#define error(s) sd.errorHalt_P(PSTR(s))
#define FILE_BASE_NAME "LOG" //Can be max size of 5 character, XXXXX000.BIN DOS 8.3 format
#define TMP_FILE_NAME "TMP_LOG.BIN"
const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1;
const uint32_t FILE_BLOCK_COUNT = 500UL; //500=250kb 1 block=512bytes
const uint32_t ERASE_SIZE = 262144L;
uint32_t bgnBlock, endBlock;
uint8_t*  pCache;
char binName[13] = FILE_BASE_NAME "000.BIN";

// Define text file for logging
ofstream myfile;

float SDBuffer [128];
int count = 0;
uint32_t bn = 1; //block number
int startTime; //to reset milli count at each recording

String dataString = "";
String SDbufferString = "";

//Interval Timer
IntervalTimer timer0;
char c=0;

uint64_t time = 0;

//------------------------------------------------------------------------------
/*
* User provided date time callback function.
* See SdFile::dateTimeCallback() for usage.
*/
void dateTime(uint16_t* date, uint16_t* time) {
 // User gets date and time from GPS or real-time
 // clock in real callback function
 time_t now();
 // return date using FAT_DATE macro to format fields
 *date = FAT_DATE(year(), month(), day());

 // return time using FAT_TIME macro to format fields
 *time = FAT_TIME(hour(), minute(), second());
}


void createFile(){

 bn=1; //reset block count
 count=0;
 // Find unused file name.
 if (BASE_NAME_SIZE > 5) {
 }
 while (sd.exists(binName)) {
   if (binName[BASE_NAME_SIZE + 2] != '9') {
     binName[BASE_NAME_SIZE + 2]++; //changed from +1 since now 0-999 instead of 0-99
   }
   else {
     binName[BASE_NAME_SIZE + 2] = '0';
     //binName[BASE_NAME_SIZE + 1]++;
     if (binName[BASE_NAME_SIZE+1] == '9') {
       binName[BASE_NAME_SIZE + 1] = '0';
       binName[BASE_NAME_SIZE ]++;
     }
     else{
       if (binName[BASE_NAME_SIZE] == '9') ;
       binName[BASE_NAME_SIZE + 1]++;
     }
   }

   // Serial.println(binName);
   // delete old log file
   if (sd.exists(TMP_FILE_NAME)) {
     sd.remove(TMP_FILE_NAME);
   }

 }


 // create new file
 myFile.close();
 if (!myFile.createContiguous(sd.vwd(),
 TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT));
 // get address of file on SD
 if (!myFile.contiguousRange(&bgnBlock, &endBlock));
 // use SdFat's internal buffer
 pCache = (uint8_t*)sd.vol()->cacheClear();
 if (pCache == 0);
 memset(pCache, '0', 512);
 // flash erase all data in file
 uint32_t bgnErase = bgnBlock;
 uint32_t endErase;
 while (bgnErase < endBlock) {
   endErase = bgnErase + ERASE_SIZE;
   if (endErase > endBlock) endErase = endBlock;
   if (!sd.card()->erase(bgnErase, endErase)) ;
   bgnErase = endErase + 1;
 }
}

time_t getTeensy3Time()
{
 return Teensy3Clock.get();
}

void setup() {
 Serial.begin(115200);
 setSyncProvider(getTeensy3Time);
 SdFile::dateTimeCallback(dateTime);
 pinMode(ledPin, OUTPUT);
 pinMode(readPin0, INPUT);  //Read analog signal pin1
 pinMode(readPin1, INPUT);  //Read analog signal pin2  

 // Initialize SdFat or print a detailed error message and halt
 // Use half speed like the native library.
 // change to SPI_FULL_SPEED for more performance.
 if (!sd.begin(chipSelect, SPI_FULL_SPEED)) sd.initErrorHalt();

 createFile();  
}

void loop() {
 //not sure how you start/stop data collection
 //Example using serial input
 if (Serial.available()) {
   c = Serial.read();
   if(c=='r') { // Start recording
     if (!sd.card()->writeStart(bgnBlock, FILE_BLOCK_COUNT))Serial.println("Recording") ;
     //digitalWrite(LED,ledValue);//Turn LED on to indicate recording if you want
     timer0.begin(timerCallback0, 20000); //start timer 4000=250sps
     startTime=millis();
   }
   else if(c=='s') { // stop recording
     stopRecording();  
   }

 }
 delay(1);

}

void stopRecording(){
 timer0.end(); //Turn off timer to stop SD card write
 if (!sd.card()->writeStop())Serial.println("Stopped");
 // Truncate file if recording stopped early.
 if (bn != FILE_BLOCK_COUNT) {    
   if (!myFile.truncate(512L * bn)) ;
 }
 binaryToCsv();
 if (!myFile.rename(sd.vwd(), binName)) ;
 createFile();
}

void timerCallback0(){

 time = millis();

 float ADCvalue_0 = analogRead(A2)*3.3/1024;
 float ADCvalue_1 = analogRead(A3)*3.3/1024;

 Serial.print("Time: ");
 Serial.print(int(time));
 Serial.print("\t");
 Serial.print("A-B: ");
 Serial.print(ADCvalue_0-ADCvalue_1,3);  
 Serial.print("\t");  
 Serial.print("A: ");  
 Serial.print(ADCvalue_0,3);
 Serial.print("\t");  
 Serial.print("B: ");
 Serial.print(ADCvalue_1,3);
 Serial.print("\t");
 Serial.println("Additional_Data... ");  

 //Serial.println(int(time));

 dataString += String("Time: ");
 dataString += String(int(time));
 dataString += String("\t");
 dataString += String("A-B: ");
 dataString += String(ADCvalue_0-ADCvalue_1,3);  
 dataString += String("\t");
 dataString += String("A: ");
 dataString += String(ADCvalue_0,3);
 dataString += String("\t");  
 dataString += String("B: ");
 dataString += String(ADCvalue_1,3);
 dataString += String("\t");  
 dataString += String("Additional_Data... ");
 dataString += String("\n");  //Create a new line on the SD card

 int length = dataString.length();

 if (length > 512)
 {
   noInterrupts();

   SDbufferString = dataString.substring(0,511);    //Create a string with 512 bytes for writing on the SD card.
   dataString = dataString.substring(511,length);   //Remove the 512 bytes for the SD card from the main string.

   //char charBuf[512];
   //SDbufferString.toCharArray(charBuf,512);
   //memcpy(pCache, &charBuf, 512);
   
   memcpy(pCache, &SDbufferString, 512);
   
   if (!sd.card()->writeData((uint8_t*)pCache)) ;  //Here is the problem, probably the wrong data type get's written to the SD Card

   bn++; //increment block number
   count = 0;
   memset(pCache, '0', 512);
   interrupts();
 }


}

void binaryToCsv() {
 uint8_t lastPct = 0;
 int buf[512];
 uint32_t t0 = millis();
 uint32_t syncCluster = 0;
 SdFile csvFile;
 char csvName[13];
 BufferedWriter bw;

 if (!myFile.isOpen()) {
   Serial.println(F("No current binary file"));
   return;
 }
 myFile.rewind();
 //if (!binFile.read(&buf , 512) == 512) error("Read metadata failed");
 // create a new csvFile
 strcpy(csvName, binName);
 strcpy_P(&csvName[BASE_NAME_SIZE + 3], PSTR(".CSV"));

 if (!csvFile.open(csvName, O_WRITE | O_CREAT | O_TRUNC)) {
   error("open csvFile failed");  
 }
 Serial.println();
 Serial.print(F("Writing: "));
 Serial.print(csvName);
 Serial.println(F(" - type any character to stop"));
 bw.init(&csvFile);
 bw.putStr_P(PSTR("Time,")); //Write header at top of file
 bw.putStr_P(PSTR("A-B,"));
 bw.putStr_P(PSTR("A,"));
 bw.putStr_P(PSTR("B"));
 bw.putCRLF();

 uint32_t tPct = millis();
 while (!Serial.available() && myFile.read(&buf, 512) == 512) {
   uint16_t i;
   //read 512 bytes of data here i.e. 512 samples
   for (i = 0; i < 512; i++) {
     bw.putChar(buf[i]);
     //bw.putStr(buf[i]);  //Invalid conversion from "int" to "char*" (-fpermissive), does not compile
   }

   //bw.putCRLF();


   if (csvFile.curCluster() != syncCluster) {
     bw.writeBuf();
     csvFile.sync();
     syncCluster = csvFile.curCluster();
   }
   //looks like code to print out % done
   if ((millis() - tPct) > 1000) {
     uint8_t pct = myFile.curPosition()/(myFile.fileSize()/100);
     if (pct != lastPct) {
       tPct = millis();
       lastPct = pct;
       Serial.print(pct, DEC);
       Serial.println('%');
     }
   }
   if (Serial.available()) break;
 }
 bw.writeBuf();
 csvFile.close();
 Serial.print(F("Done: "));
 Serial.print(0.001*(millis() - t0));
 Serial.println(F(" Seconds"));
}


Looks like it's "only" a data conversion problem. The logging itself seems to work, BIN files get created, and I didn't observe any latencies. But data in the csv file is just garbage because the data is not fed with the correct format / variable to the SD card.

How can I solve this problem?

Go Up