Teensy 3.1: ADC Measurement, SD logging, problem with Sample Rate?

Status
Not open for further replies.
Thanks for the update and now it makes sense. I have started to use github now, so makes like easier with syncs and stuff......So chopsticks, if you want to make sure no samples are being missed now, if you use micros() you should see it advance 4000us every sample. The way you had it above, it would not populate the array if the there was a comparison error, meaning you would miss samples. However with the new changes, all should be good and you should be close to getting it all working.....now you just need to figure out the binary to csv conversion stuff and you should be golden. Thanks again Pedvide for all your help.
 
Here is the code from SDfat, with my best guess at how it might work for you (you will have to also get bufferedwriter.h, which converts your binary to values you want). Again, I have no clue if this will work....or if you want to use this method.

It should not be a problem to use this method. I have implemented your code, although i need to make use of bufferedwriter.h, but where can i find this? It is not available in the sdfat library tho? Or am i using the wrong sdfat lib? >.< The one i'm using is the 'SdFatBeta20121203'? I've somewhere seen a newer version but could not find it.

I'm going to get busy with the analog logger. I'll update my progress! :)


UPDATE:
(1)
my code:
Code:
/*
ECG measurement Project:
- Sensing two ADC values,
- Analog files being logged on sd card
    Time  A-B  A  B
*/

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


//Define ADC Inputs
#define ADC_0 0
#define ADC_1 1

//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[] = FILE_BASE_NAME "000.BIN"; //[13]

ADC *adc = new ADC(); // adc object
ADC::Sync_result result;
int ADCvalue_0 = ADC_ERROR_VALUE;
int ADCvalue_1 = ADC_ERROR_VALUE;
float myArray [250][3] = {}; //2d matrix array
float SDBuffer [128];
int count = 0;
uint32_t bn = 1; //block number
int value0;
int value1;

//Interval Timer
IntervalTimer timer0;
char c=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) {
    //sendError(3,1);
  }
  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)) {
    if (!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
  
  //Set ADC (Averaging and Resolution)  
  adc->setAveraging(32); // set number of averages
  adc->setResolution(12); // set bits of resolution
    // it can be ADC_VERY_LOW_SPEED, ADC_LOW_SPEED, ADC_MED_SPEED, ADC_HIGH_SPEED_16BITS, ADC_HIGH_SPEED or ADC_VERY_HIGH_SPEED
  //try low/med speed since load cell has high impedence
  adc->setConversionSpeed(ADC_HIGH_SPEED); // change the conversion speed
  // it can be ADC_VERY_LOW_SPEED, ADC_LOW_SPEED, ADC_MED_SPEED, ADC_HIGH_SPEED or ADC_VERY_HIGH_SPEED
  adc->setSamplingSpeed(ADC_HIGH_SPEED); // change the sampling speed
  
  adc->setAveraging(32, ADC_1); // set number of averages
  adc->setResolution(12, ADC_1); // set bits of resolution
  adc->setConversionSpeed(ADC_HIGH_SPEED, ADC_1); // change the conversion speed
  adc->setSamplingSpeed(ADC_HIGH_SPEED, ADC_1); // change the sampling speed
  // always call the compare functions after changing the resolution!
   // adc->enableCompare(1.0/3.3*adc->getMaxValue(), 0, ADC_0); // measurement will be ready if value < 1.0V
   // adc->enableCompareRange(1.0*adc->getMaxValue(ADC_1)/3.3, 2.0*adc->getMaxValue(ADC_1)/3.3, 0, 1, ADC_1); // ready if value lies out of [1.0,2.0] V

    
  // 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, 400); //start timer 4000=250sps
    } else if(c=='s') { // stop recording
        stopRecording();
        
    } 
  }
 delay(1);

}

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

void timerCallback0(){
      result = adc->analogSynchronizedRead(readPin0, readPin1);
     float ADCvalue_0 = result.result_adc0*3.3/adc->adc0->getMaxValue();
     float ADCvalue_1 = result.result_adc1*3.3/adc->adc1->getMaxValue();
  if (count < 128) {
  //&& (result.result_adc0 !=ADC_ERROR_VALUE) && (result.result_adc1 !=ADC_ERROR_VALUE)) {
    SDBuffer[count] = micros();
    SDBuffer[count+1] = ADCvalue_0-ADCvalue_1; 
    SDBuffer[count+2] = ADCvalue_0;
    SDBuffer[count+3] = ADCvalue_1;
    
// to Serial 
//        Serial.print("Time: ");
        Serial.print(SDBuffer[count]),-2;
        Serial.print("\t"); 
//        Serial.print("A-B: ");
        Serial.print((SDBuffer[count+1]),DEC);  
        Serial.print("\t");  
//        Serial.print("A: ");  
        Serial.print((SDBuffer[count+2]),DEC);
        Serial.print("\t");  
//        Serial.print("B: ");
        Serial.println((SDBuffer[count+3]),DEC);   
        
        count = count + 4;//move index 4 positions for next values
  }
 
  if (count== 128){
   noInterrupts(); 
   memcpy(pCache, &SDBuffer, 512);
       if (!sd.card()->writeData((uint8_t*)pCache)) ;
    bn++; //increment block number
    count = 0; 
    memset(pCache, '0', 512); 
      interrupts();
  }
       
}

//------------------------------------------------------------------------------
// convert binary file to text file
void binaryToText() {
  uint8_t lastPct;
  int buf[4];
  uint32_t t = 0;
  uint32_t syncCluster = 0;
  SdFile binFile;
  SdFile textFile;
  BufferedWriter bw;
  
  if (!binFile.open(binFile, O_READ)) {
    error("open binary");
  }
  // create a new binFile
  char name[13];
  strcpy_P(name, PSTR("DATA00.TXT"));
  for (uint8_t n = 0; n < 100; n++) {
    name[4] = '0' + n / 10;
    name[5] = '0' + n % 10;
    if (textFile.open(textFile, O_WRITE | O_CREAT | O_EXCL)) break;
  }
  if (!textFile.isOpen()) error("open textFile");
  PgmPrint("Writing: ");
  Serial.println(name);
  
  bw.init(&textFile);
  
  while (!Serial.available() && binFile.read(&buf, 16) == 16) {
    uint16_t i;

    for (i = 0; i < 4; i++) {
      bw.putNum(buf[i]);
      bw.putCRLF();
    }
    
    if (textFile.curCluster() != syncCluster) {
      bw.writeBuf();
      textFile.sync();
      syncCluster = textFile.curCluster();
    }
    if ((millis() -t) > 1000) {
      uint8_t pct = binFile.curPosition() / (binFile.fileSize()/100);
      if (pct != lastPct) {
        t = millis();
        lastPct = pct;
        Serial.print(pct, DEC);
        Serial.println('%');
      }
    }
    if (Serial.available()) break;
  }
  bw.writeBuf();
  textFile.close();
  PgmPrintln("Done");
}

errors:
Code:
  This report would have more information with
  "Show verbose output during compilation"
  enabled in File > Preferences.
Arduino: 1.0.5-r2 (Windows NT (unknown)), Board: "Teensy 3.1"
measuringdatatesting.ino: In function 'void binaryToText()':
measuringdatatesting:245: error: invalid conversion from 'uint8_t {aka unsigned char}' to 'const char*' [-fpermissive]
In file included from C:\Arduino\libraries\SdFat/SdFile.h:24:0,
                 from C:\Arduino\libraries\SdFat/SdFat.h:35,
                 from measuringdatatesting.ino:12:
C:\Arduino\libraries\SdFat/SdBaseFile.h:543:8: error:   initializing argument 2 of 'bool SdBaseFile::open(SdBaseFile&, const char*)' [-fpermissive]
measuringdatatesting:250: error: 'name' was not declared in this scope
measuringdatatesting:254: error: invalid conversion from 'int' to 'const char*' [-fpermissive]
In file included from C:\Arduino\libraries\SdFat/SdFile.h:24:0,
                 from C:\Arduino\libraries\SdFat/SdFat.h:35,
                 from measuringdatatesting.ino:12:
C:\Arduino\libraries\SdFat/SdBaseFile.h:543:8: error:   initializing argument 2 of 'bool SdBaseFile::open(SdBaseFile&, const char*)' [-fpermissive]
measuringdatatesting:266: error: call of overloaded 'putNum(int&)' is ambiguous
measuringdatatesting.ino:266:23: note: candidates are:
In file included from measuringdatatesting.ino:14:0:
C:\Arduino\libraries\BufferedWriter/BufferedWriter.h:53:8: note: void BufferedWriter::putNum(int8_t)
C:\Arduino\libraries\BufferedWriter/BufferedWriter.h:54:8: note: void BufferedWriter::putNum(uint8_t)
C:\Arduino\libraries\BufferedWriter/BufferedWriter.h:55:8: note: void BufferedWriter::putNum(int16_t)
C:\Arduino\libraries\BufferedWriter/BufferedWriter.h:56:8: note: void BufferedWriter::putNum(uint16_t)
C:\Arduino\libraries\BufferedWriter/BufferedWriter.h:57:8: note: void BufferedWriter::putNum(int32_t)
C:\Arduino\libraries\BufferedWriter/BufferedWriter.h:58:8: note: void BufferedWriter::putNum(uint32_t)
Code:
 
Last edited:
http://forum.arduino.cc/index.php?PHPSESSID=41flfffn7c9lkfuuvgqopqu1o5&topic=228549.0
Here is the link to the arduino forum. The bufferedwriter is part of his program. I have never tried it, so I hope you get it working.

I'll check it out! thanks for the link!

I'm going to try implementing the bintocsv part into my current code, as far I see, some adjustments are needed...

If I succeed to get it working properly, I will make a basic template for people who want to measure analog signals as well and trying to log on an sd card. Maybe creating some kind of library file where only the analog signals needs to get called, and functions like writetosd etc without having lots of coding in your .ino file.

I'll just see how far i can get! :)

If any have some suggestions, just let me know :)
 
Last edited:
Question, I'm logging binary files now, but i expect to have zero's and one's. my logfile.bin gave me this:
Code:
KÁ«•? 5@~|a?”K—Ê??kD @~|a?4K¬¼Š?ëzû?~|a?Ô$K´@‹?óþû?~|a?t4KL&‹?#Êû?®Ga?DK‰_…?Èö?~|a?´SKºbx?œïì?~|a?TcK$Ík?9¿æ?N±a?ôrKöNo?"€è?N±a?”‚KˆJu?S˜ë?æa?4’Kæ“q?š¢é?N±a?Ô¡K¾Ob?† â?N±a?t±Kê3X?
Ý?æa?ÁK¼µ[?íÍÞ?æa?³ÐK>©`?bá?îb?SàKž?`?F-á?îb?ôïKæa?† â?îb?”ÿK½i?øëå?îb?4Kú5y?t¨í?îb?ÔK ø??—ó?îb?t.K^f?¦Àð?îb?>K¦Àp?Êmé?îb?´MKD?j?™Uæ?îb?T]K8os?Åê?îb?ôlKŽ1?>¦ð?îb?”|KÏ€?F*ñ?îb?3ŒK9¼v?ü…ì?¿Ob?Ô›K
ñv?ü…ì?îb?t«K	¹ƒ?€Æô?îb?»K5çŒ?¬ôý?îb?´ÊK¯V‘?ß$@æa?TÚK·Ú‘?ãf@æa?ôéKï)’?K?@N±a?”ùKq—?û@N±a?4	KK#›?ùý@N±a?ÔKÃø˜?µè@N±a?t(K¿^’?³›@N±a?8KV÷Ž?ýÏÿ?N±a?´GKG<‘?w
@N±a?TWKñv•?˜@~|a?ôfK9Ζ?<Æ@~|a?”vKWD’?K?@~|a?4†Kgÿ??Ó^ @~|a?Ô•KyT”?\‰@~|a?t¥KÃø˜??Û@~|a?µKѳ–?¹@~|a?´ÄKÿä??Ó^ @N±a?SÔKÅHŒ?ý?~|a?óãK74??;y @~|a?”óK±£”?ø°@~|a?4KqГ?$:@®Ga?ÔKNsŽ??1ÿ?~|a?t"K$ß‹?c?ü?~|a?2KÇ•??* @~|a?´AK©”?ôn@~|a?TQK—“?ëê@~|a?ô`K*	Ž?ìÇþ?~|a?”pK´@‹?‹äû?®Ga?4€K¨Ž?]fÿ?~|a?Ô?Kï)’?t@~|a?tŸKß!‘?ð @~|a?¯KŒù‹?Ë·ü?~|a?´¾KS]ˆ?’ù?~|a?TÎKD¢Š?Fû?®Ga?ôÝKuº??L^þ?®Ga?”íK¼Ä‹?û‚ü?~|a?4ýKÁ®…? mö?~|a?ÔK(|‚?g:ó?~|a?sKqÓƒ?°‘ô?~|a?,K‰_…?Èö?~|a?´;KIŒ„?ˆJõ?~|a?TKKo†€?®Dñ?~|a?ôZKÚrz?î?N±a?”jK¬ô}?ýÒï?N±a?4zK¯Y??V2ò?N±a?Ô‰Kl€?®Dñ?N±a?t™Kºbx?
í?N±a?©KHwt?³.ë?æa?´¸K:	z?¬÷í?æa?TÈKÏ€?Þñ?æa?ô×KŒä{?Uåî?æa?“çKVån?"€è?îb?4÷Kðge?§ã?æa?ÔKðge?oÁã?îb?tKâ¬g?èãä?îb?&Kîb?îâ?îb?´5K:ÂV?”nÜ?îb?TEKfóO?*Ù?îb?ôTKø¡R?s^Ú?îb?”dKˆPU?»µÛ?îb?4tK4ÛL?{×?îb?ÔƒK:È6?,WÌ?æa?t“K>h?–Á¿?îb?£K
>ø°‚?îb?´²K éõ=l€?îb?TÂKP½¡=˜Rv?îb?ôÑK`pž<qg?îb?”áK@¼ž?`?¿Ob?4ñK€¨í»nt`?¿Ob?Ô K ¨í;+d?¿Ob?tK pž;oÁc??„b? K@¼nt`??„b?´/K€@Ó»>©`?¿Ob?T?K€Ø¸»Þa??„b?ôNK@p=Ã6l?¿Ob?”^Kp[j=F*q??„b?4nKP>2=t¨m??„b?Ô}K@Ö=ól??„b?s?KØ1·=šŸy?_¹b??KŒC>¹*…??„b?´¬KT‹5>²ó‡??„b?T¼KŒ©>yW„??„b?ôËKD<>8„ƒ?_¹b?”ÛK$ÓK>¬¼Š??„b?4ëK⾇>˜“?¿Ob?ÔúK¦…?>±£”??„b?t
Kt[j>NsŽ?¿Ob?K,T\>e²Œ?¿Ob?´)KÆâ’>‘à•?¿Ob?T9KvÆ>`Å¢?¿Ob?ôHKÍ>àk¤?¿Ob?“XK2”©>쌛?¿Ob?4hKî&¢>›±™?¿Ob?ÓwKŽÑå>«¶ª??„b?t‡K¯S!?·ÑÁ?¿Ob?—K8{3?ËÊ?îb?´¦KEæ½?Þy@îb?T¶K)½Å?Pe@îb?ôÅKùñÅ?„r@æa?”ÕKùñÅ?„r@æa?4åKùñÅ?Pe@N±a?ÓôKùñÅ?Pe@N±a?sKùñÅ?Pe@N±a?KaÆ?Pe@~|a?´#K™[Æ?„r@Þa?T3KÉ&Æ?Pe@®Ga?ôBK1AÆ?Pe@Þa?”RKvÆ?„r@Þ`?4bK™[Æ?„r@Þa?ÔqK™[Æ?„r@Þa?t?K1AÆ?Pe@Þa?‘KvÆ?„r@Þ`?³*K1AÆ?Pe@Þa?T°K™[Æ?Pe@Þ`?ô¿K1AÆ?Pe@Þa?”ÏK™[Æ?„r@Þa?4ßK1AÆ?Pe@Þa?ÔîK1AÆ?Pe@Þa?sþK™[Æ?Pe@Þ`?K1AÆ?Pe@Þa?´K™[Æ?Pe@Þ`?T-K™[Æ?Pe@Þ`?ô<K™[Æ?Pe@Þ`?“LKvÆ?„r@Þ`?4\K1AÆ?Pe@Þa?ÔkK™[Æ?Pe@Þ`?t{KvÆ?„r@Þ`?‹KvÆ?Pe@>©`?´šKvÆ?„r@Þ`?TªKi?Æ?„r@>©`?ô¹KvÆ?„r@Þ`?”ÉK™[Æ?Pe@Þ`?3ÙKvÆ?„r@Þ`?ÔèKvÆ?„r@Þ`?tøKi?Æ?„r@>©`?KvÆ?„r@Þ`?´KvÆ?„r@Þ`?T'Ki?Æ?„r@>©`?ô6K™[Æ?Pe@Þ`?”FKvÆ?„r@Þ`?4VK™[Æ?Pe@Þ`?ÔeKvÆ?„r@Þ`?suK™[Æ?Pe@Þ`?…Ki?Æ?„r@>©`?´”KvÆ?„r@Þ`?T¤KvÆ?„r@Þ`?ô³KvÆ?„r@Þ`?”ÃKvÆ?„r@Þ`?4ÓKi?Æ?„r@>©`?ÔâKvÆ?„r@Þ`?tòK™[Æ?„r@Þa?KvÆ?„r@Þ`?´KvÆ?„r@Þ`?S!K™[Æ?Pe@Þ`?ô0KvÆ?„r@Þ`?”@KvÆ?Pe@>©`?4PK™[Æ?„r@Þa?Ô_Ki?Æ?„r@>©`?soK™[Æ?Pe@Þ`?Kò½¸?„r@+N|?´ŽKvÆ?Pe@>©`?TžKvÆ?„r@Þ`?ô*K™[Æ?Pe@Þ`?“½KvÆ?Pe@>©`?4ÍKšd–?„r@n€*?ÔÜK?ŠÂ?Pe@"€h?tìKѪÆ?„r@nt`?üKvÆ?Pe@>©`?´KˆSÅ?Pe@/îb?TK0 ƒ?Pe@pʳ?ô*KW>²?Pe@IŒ„?”:K9ÅÆ?„r@ž?`?4JKvÆ?Pe@>©`?ÔYK‘×Å?„r@îb?tiKúý…?„r@ç°?yKMm®?„r@»wˆ?´ˆKvÆ?Pe@>©`?T˜KѪÆ?„r@nt`?ô§KnzÀ?Pe@d*l?”·KÀa‚?„r@Hƒ´?4ÇKMm®?„r@»wˆ?ÓÖKѪÆ?„r@nt`?sæKi?Æ?Pe@nt`?öKѪÆ?„r@nt`?´KXD’?„r@°*¤?TKa¶?X@×*€?ô$Ki?Æ?Pe@nt`?”4KѪÆ?„r@nt`?3DKi?Æ?Pe@nt`?ÔSK?µ*?„r@É/–?scK¿UÂ?Pe@Âéh?sKvÆ?Pe@>©`?³‚KvÆ?Pe@>©`?T’KvÆ?Pe@>©`?ô¡K…±?Pe@!E…?”±KùñÅ?„r@æa?4ÁKi?Æ?„r@>©`?ÔÐKvÆ?Pe@>©`?tàKi?Æ?„r@>©`?ðKi?Æ?„r@>©`?´ÿKvÆ?Pe@>©`?TKvÆ?Pe@>©`?ôKi?Æ?„r@>©`?”.KvÆ?Pe@>©`?3>K1AÆ?Pe@Þa?ÔMKi?Æ?„r@>©`?t]KvÆ?„r@Þ`?mKvÆ?„r@Þ`?´|KvÆ?„r@Þ`?TŒK 9Å?„r@ÏWc?ó›KvÆ?„r@Þ`?”«KvÆ?„r@Þ`?4»KvÆ?Pe@>©`?ÔÊKi?Æ?„r@>©`?tÚKvÆ?„r@Þ`?êK™[Æ?Pe@Þ`?´ùK™[Æ?Pe@Þ`?T	K™[Æ?Pe@Þ`?ôKvÆ?Pe@>©`?”(K™[Æ?Pe@Þ`?48KvÆ?„r@Þ`?ÔGKvÆ?„r@Þ`?tWK™[Æ?Pe@Þ`?gK™[Æ?Pe@Þ`?´vKvÆ?„r@Þ`?T†KvÆ?„r@Þ`?ó•KvÆ?„r@Þ`?”¥K™[Æ?Pe@Þ`?4µKvÆ?„r@Þ`?ÔÄKW;Â?Pe@’i?tÔKvÆ?„r@Þ`?äKvÆ?Pe@>©`?´óKvÆ?„r@Þ`?TKvÆ?„r@Þ`?óKðmÅ?„r@/îb?”"K™[Æ?Pe@Þ`?42K™[Æ?„r@Þa?ÔAKvÆ?„r@Þ`?tQKvÆ?„r@Þ`?aKvÆ?„r@Þ`?´pKvÆ?Pe@>©`?T€KvÆ?„r@Þ`?ô?K™[Æ?„r@Þa?”ŸKvÆ?„r@Þ`?4¯Ki?Æ?„r@>©`?Ô¾K1AÆ?Pe@Þa?tÎKvÆ?Pe@>©`?ÞK™[Æ?Pe@Þ`?´íK™[Æ?„r@Þa?TýK™[Æ?Pe@Þ`?óK™[Æ?Pe@Þ`?”K™[Æ?Pe@Þ`?4,K™[Æ?Pe@Þ`?Ô;K™[Æ?„r@Þa?tKK™[Æ?„r@Þa?[KvÆ?Pe@>©`?´jK™[Æ?Pe@Þ`?TzK™[Æ?Pe@Þ`?ô‰K1AÆ?Pe@Þa?“™K™[Æ?Pe@Þ`?4©K™[Æ?Pe@Þ`?Ó¸K™[Æ?Pe@Þ`?tÈK™[Æ?Pe@Þ`?ØK™[Æ?Pe@Þ`?´çK<b½?Pe@ÇÐr?T÷K1AÆ?Pe@Þa?ó K™[Æ?Pe@Þ`?“ K™[Æ?Pe@Þ`?3& K™[Æ?Pe@Þ`?Ó5 K 2“?Pe@*˜£?tE Ka¶?Pe@?»€?U KvÆ?Pe@>©`?´d KvÆ?Pe@>©`?Tt KvÆ?Pe@>©`?ôƒ Kôœ?Pe@¬¹š?”“ K†¹¾?X@eío?4£ Ki?Æ?„r@>©`?Ô² KvÆ?Pe@>©`?t KѪÆ?„r@nt`?Ò KH†¤?„r@À^’?´á K÷¤Â?Pe@RKh?Tñ KѪÆ?„r@nt`?ô !K™[Æ?Pe@Þ`?”!Ki?Æ?Pe@nt`?4 !K
š*?Pe@“0‰?Ô/!K€ÏÄ?Pe@?öc?s?!Ki?Æ?„r@>©`?O!KvÆ?Pe@>©`?´^!Ki?Æ?„r@>©`?Tn!K's²?„r@áq„?ô}!KèéÄ?„r@?öc?”?!KvÆ?Pe@>©`?4?!Ki?Æ?„r@>©`?Ô¬!Ki?Æ?„r@>©`?t¼!K8~£?„r@Ðf“?Ì!K|èº?Pe@IÄw?´Û!Ki?Æ?„r@>©`?Të!Ki?Æ?„r@>©`?ôú!Ki?Æ?„r@>©`?”
"K?·?Pe@“~?4"KùñÅ?Pe@N±a?Ô)"Ki?Æ?Pe@nt`?t9"Ki?Æ?Pe@nt`?I"Ki?Æ?„r@>©`?´X"KvÆ?Pe@>©`?Th"KvÆ?Pe@>©`?ów"K™[Æ?Pe@Þ`?”‡"K™[Æ?Pe@Þ`?4—"Ki?Æ?„r@>©`?Ô¦"KvÆ?„r@Þ`?s¶"K™[Æ?Pe@Þ`?Æ"K™[Æ?„r@Þa?´Õ"Ki?Æ?„r@>©`?Tå"KvÆ?„r@Þ`?óô"K™[Æ?Pe@Þ`?”#K™[Æ?Pe@Þ`?3#K™[Æ?Pe@Þ`?Ô##KvÆ?„r@Þ`?t3#K1AÆ?Pe@Þa?C#Ki?Æ?„r@>©`?´R#K™[Æ?Pe@Þ`?Tb#KvÆ?Pe@>©`?ôq#K1AÆ?Pe@Þa?”?#K™[Æ?Pe@Þ`?4‘#K™[Æ?Pe@Þ`?Ó*#K™[Æ?Pe@Þ`?t°#K1AÆ?Pe@Þa?À#K™[Æ?Pe@Þ`?´Ï#K1AÆ?Pe@Þa?Tß#K™[Æ?Pe@Þ`?ôî#K1AÆ?Pe@Þa?”þ#K™[Æ?Pe@Þ`?4$K™[Æ?Pe@Þ`?Ô$K™[Æ?Pe@Þ`?t-$KÉ&Æ?X@Þa?=$K™[Æ?Pe@Þ`?´L$KvÆ?„r@Þ`?S\$K1AÆ?Pe@Þa?ôk$K™[Æ?„r@Þa?”{$K™[Æ?„r@Þa?4‹$K™[Æ?Pe@Þ`?Óš$K™[Æ?Pe@Þ`?sª$K™[Æ?Pe@Þ`?º$K™[Æ?Pe@Þ`?´É$K™[Æ?„r@Þa?TÙ$K1AÆ?Pe@Þa?ôè$KvÆ?„r@Þ`?”ø$KvÆ?„r@Þ`?4%K™[Æ?„r@Þa?Ô%K¤|½?Pe@÷›r?t'%K 9Å?„r@ÏWc?7%KvÆ?„r@Þ`?´F%K™[Æ?Pe@Þ`?TV%KvÆ?„r@Þ`?óe%Kfö¿?„r@DÝm?”u%KÉ&Æ?„r@~|a?4…%K™[Æ?Pe@Þ`?Ô”%K™[Æ?Pe@Þ`?t¤%KvÆ?Pe@>©`?´%K¾¿?Pe@Ńo?´Ã%KðmÅ?Pe@_¹b?TÓ%K™[Æ?Pe@Þ`?ôâ%Ki?Æ?„r@>©`?”ò%KvÆ?Pe@>©`?4&K?ŠÂ?Pe@"€h?Ô&K™[Æ?Pe@Þ`?t!&K™[Æ?Pe@Þ`?1&KvÆ?Pe@>©`?´@&KvÆ?Pe@>©`?TP&K¾?Pe@_q?ô_&K@üÃ?Pe@Àœe?”o&KvÆ?Pe@>©`?4&K™[Æ?Pe@Þ`?ÓŽ&KpÇÃ?Pe@`f?tž&Kfù¯?„r@¢ë†?®&K
ý¶?Pe@-›?´½&K™[Æ?Pe@Þ`?TÍ&Ki?Æ?„r@>©`?ôÜ&K솻?Pe@h‡v?”ì&K²í§?Pe@îÜŽ?4ü&Kfù¯?„r@¢ë†?Ó'KÀ¢Å?Pe@¿Ob?t'Ki?Æ?Pe@nt`?+'KØä³?„r@0 ƒ?´:'KfüŸ?X@Ò³–?TJ'K¨?Pe@†ÂŽ?ôY'K¾¿?X@õNo?“i'K1Ä?Pe@ 3e?4y'K=e*?Pe@ce‰?Ôˆ'Kcb™?Pe@=h??s˜'Kß¡?Pe@Á«•?¨'K*
¹?Pe@ëz{?´·'KVî¾?Pe@•¸o?TÇ'Kºq¨?Pe@æXŽ?ôÖ'K@”?Pe@`Å¢?”æ'KÄEœ?Pe@Ü„š?4ö'KQµ?„r@·Ý??Ô(KüŽ¼?Pe@Gwt?t(Kr§?Pe@.°??%(KpГ?„r@˜£?´4(K\+œ?X@Ü„š?TD(KQµ?„r@·Ý??ôS(KüŽ¼?Pe@Gwt?”c(K²í§?„r@V÷Ž?4s(K©”?Pe@÷ª¢?Ó‚(K\+œ?Pe@DŸš?t’(K4´?Pe@?–‚?¢(KüŽ¼?Pe@Gwt?´±(Kºq¨?Pe@æXŽ?TÁ(K¹'•?Pe@碡?óÐ(Ku·??Pe@+™?”à(K)Àµ?Pe@w
??3ð(KüŽ¼?Pe@Gwt?Ôÿ(KŠ¦¨?„r@~>Ž?t)Kñv•?„r@n¡?)K¥ž?„r@ê?˜?´.)Kâµ·?Pe@|)~?T>)K¦ÉÀ?Pe@ól?óM)KÍƬ?„r@;Š?”])KL#›?„r@¼Á›?4m)KØç£?Pe@Èâ’?Ô|)KÌü?„r@wBt?tŒ)KxKÄ?Pe@Pþd?œ)Kç°?„r@ùý…?´«)K¶Šž?„r@RZ˜?T»)KJÓ§?„r@¾??ôÊ)K?ŠÂ?„r@ò´h?“Ú)KvÆ?Pe@>©`?4ê)K¦ÉÀ?Pe@ól?Óù)Ka¶?Pe@?»€?t	*K(½Å?„r@¿Ob?*KѪÆ?„r@nt`?´(*KѪÆ?„r@nt`?T8*KvÆ?Pe@>©`?ôG*KvÆ?„r@Þ`?”W*KvÆ?„r@Þ`?4g*Ki?Æ?„r@>©`?Ôv*KvÆ?„r@Þ`?t†*Ki?Æ?„r@>©`?–*KvÆ?„r@Þ`?´¥*Ki?Æ?„r@>©`?Tµ*K™[Æ?Pe@Þ`?ôÄ*Ki?Æ?„r@>©`?“Ô*K™[Æ?Pe@Þ`?4ä*K)Àµ?Pe@w
??Ôó*KÓú¹?Pe@šŸy?t+K™[Æ?Pe@Þ`?+K™[Æ?Pe@Þ`?³"+K£/º?X@*y?T2+K³£?Pe@˜“?ôA+KûD©?Pe@¥…??”Q+K'pÂ?Pe@ò´h?4a+K™[Æ?X@>©`?Ôp+Kï±?Pe@?Û„?t€+Kä›?„r@$Ü›??+K·×¡?Pe@éò”?´Ÿ+Kκ?X@IÄw?T¯+K?ŠÂ?Pe@"€h?ô¾+K¬¶ª?Pe@ôŒ?”Î+K¶“?Pe@˜£?4Þ+KX›?„r@쌛?Ôí+Ka¶?X@×*€?tý+KöW¿?Pe@Uån?
,K:˦?Pe@fÿ??´,Km6??X@Ëy©?S,,K8?“?Pe@hI£?ô;,K®?„r@óƈ?”K,K€Ò´?„r@ˆ‚?4[,KL#›?„r@¼Á›?Ôj,Kéõ„?Pe@·Ô±?sz,K¥…??Pe@ûD©?Š,K"Œ¨?Pe@~>Ž?´™,K¢®?Pe@ƒ(ˆ?T©,K8?“?Pe@hI£?ô¸,KªZw?„r@³7»?”È,KA„?X@÷§²?4Ø,Kö]Ÿ?Pe@ªl—?Ôç,K⸧?„r@&,??t÷,KEï??„r@Ãõ¨?-K$Ík?Pe@äÀ?³-K¬ô}?„r@²ê·?S&-Kä›?Pe@¼Á›?ô5-KI†¤?Pe@WD’?”E-KÕP??Pe@Ëy©?4U-K¦Àp?„r@µ„¾?Ôd-KÙíƒ?Pe@Çܲ?tt-Kß¡?Pe@Á«•?„-K„o«?Pe@[‹?´“-Kéò”?„r@ò¡?T£-Kt??„r@ñpµ?ô²-K„u‹?Pe@U«?”Â-Ka¦?Pe@?¸??4Ò-KÍƬ?Pe@ÓŠ?Ôá-K*›“?„r@hI£?tñ-K*y?„r@sdº?.KhOƒ?Pe@8{³?´.Ku·??Pe@+™?S .K±*¤?Pe@ï)’?ó/.KÅHŒ?Pe@Û?ª?“?.KB?j?Pe@‚Á?4O.Kü‚|?„r@Š£¸?Ô^.KË|™?„r@=h??tn.K¯S¡?Pe@ñv•?~.K+‰?„r@ÝÎ*?´?.K 3e?„r@xKÄ?S?.KXu?Pe@ô
¼?ô¬.Kr—?„r@–ÇŸ?”¼.K†¿ž?Pe@˜?4Ì.KÁ®…?Pe@ß±?ÔÛ.Kž?`?„r@9ÅÆ?të.KF*q?„r@åO¾?û.K¾”?P

This does not seem correct....

As far i see i need to convert binary to csv/text, but this isn't binary language... What could cause this?
 
Last edited:
Update:
(1) In the binarytosd part, there is a line 'block_t buf' which buffer is this? in the example of turtle9er, is not used something like block_t.... so I wonder... :)

The link u send me provides an analogbinlogger made for arduino uno, it's a lot of work to get it worked with Teensy as far i see now. :)

(2) Current code, which has the binarytotext implemented which 'turtle9er' provides me... although it is converting the bin to text, the output file is filled with zero's. Is this because of the issue with the binary output isn't correct?

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

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


//Define ADC Inputs
#define ADC_0 0
#define ADC_1 1

//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[] = FILE_BASE_NAME "000.BIN"; //[13]

ADC *adc = new ADC(); // adc object
ADC::Sync_result result;
int ADCvalue_0 = ADC_ERROR_VALUE;
int ADCvalue_1 = ADC_ERROR_VALUE;
float myArray [250][3] = {}; //2d matrix array
float SDBuffer [128];
int count = 0;
uint32_t bn = 1; //block number
int value0;
int value1;

//Interval Timer
IntervalTimer timer0;
char c=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) {
    //sendError(3,1);
  }
  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)) {
    if (!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
  
  //Set ADC (Averaging and Resolution)  
  adc->setAveraging(32); // set number of averages
  adc->setResolution(12); // set bits of resolution
    // it can be ADC_VERY_LOW_SPEED, ADC_LOW_SPEED, ADC_MED_SPEED, ADC_HIGH_SPEED_16BITS, ADC_HIGH_SPEED or ADC_VERY_HIGH_SPEED
  //try low/med speed since load cell has high impedence
  adc->setConversionSpeed(ADC_HIGH_SPEED); // change the conversion speed
  // it can be ADC_VERY_LOW_SPEED, ADC_LOW_SPEED, ADC_MED_SPEED, ADC_HIGH_SPEED or ADC_VERY_HIGH_SPEED
  adc->setSamplingSpeed(ADC_HIGH_SPEED); // change the sampling speed
  
  adc->setAveraging(32, ADC_1); // set number of averages
  adc->setResolution(12, ADC_1); // set bits of resolution
  adc->setConversionSpeed(ADC_HIGH_SPEED, ADC_1); // change the conversion speed
  adc->setSamplingSpeed(ADC_HIGH_SPEED, ADC_1); // change the sampling speed
  // always call the compare functions after changing the resolution!
   // adc->enableCompare(1.0/3.3*adc->getMaxValue(), 0, ADC_0); // measurement will be ready if value < 1.0V
   // adc->enableCompareRange(1.0*adc->getMaxValue(ADC_1)/3.3, 2.0*adc->getMaxValue(ADC_1)/3.3, 0, 1, ADC_1); // ready if value lies out of [1.0,2.0] V

    
  // 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() {
  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, 4000); //start timer 4000=250sps
    } else if(c=='s') { // stop recording
        stopRecording();
    } 
    } else if(c=='c') { // stop recording
        binaryToText();
        }
        
    } 
  
 delay(1);

}


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

void timerCallback0(){
      result = adc->analogSynchronizedRead(readPin0, readPin1);
     float ADCvalue_0 = result.result_adc0*3.3/adc->adc0->getMaxValue();
     float ADCvalue_1 = result.result_adc1*3.3/adc->adc1->getMaxValue();
   
  if (count < 128) {
  //&& (result.result_adc0 !=ADC_ERROR_VALUE) && (result.result_adc1 !=ADC_ERROR_VALUE)) {
    SDBuffer[count] = micros();
    SDBuffer[count+1] = ADCvalue_0-ADCvalue_1; 
    SDBuffer[count+2] = ADCvalue_0;
    SDBuffer[count+3] = ADCvalue_1;
    
// to Serial 
//        Serial.print("Time: ");
        Serial.print(SDBuffer[count]),-2;
        Serial.print("\t"); 
//        Serial.print("A-B: ");
        Serial.print((SDBuffer[count+1]),DEC);  
        Serial.print("\t");  
//        Serial.print("A: ");  
        Serial.print((SDBuffer[count+2]),DEC);
        Serial.print("\t");  
//        Serial.print("B: ");
        Serial.println((SDBuffer[count+3]),DEC); 
        
 /* // to SD 
//        Serial.print("Time: ");
        myFile.print(SDBuffer[count]),-2;
        myFile.print("\t"); 
//        Serial.print("A-B: ");
        myFile.print((SDBuffer[count+1]),DEC);  
        myFile.print("\t");  
//        Serial.print("A: ");  
        myFile.print((SDBuffer[count+2]),DEC);
        myFile.print("\t");  
//        Serial.print("B: ");
        myFile.println((SDBuffer[count+3]),DEC);   
  
  */      
        count = count + 4;//move index 4 positions for next values
  }
 
  if (count== 128){
   noInterrupts(); 
   memcpy(pCache, &SDBuffer, 512);
       if (!sd.card()->writeData((uint8_t*)pCache)) ;
    bn++; //increment block number
    count = 0; 
    memset(pCache, '0', 512); 
      interrupts();
  }
       
}

//------------------------------------------------------------------------------
// convert binary file to text file
void binaryToText() {
  uint8_t lastPct;
  int buf[4];
  uint32_t t = 0;
  uint32_t syncCluster = 0;
  SdFile binFile;
  SdFile textFile;
  BufferedWriter bw;
  
  if (!binFile.open("TMP_LOG.bin", O_READ)) {
    error("open binary");
  }
  // create a new binFile
  char name[13];
  strcpy_P(name, PSTR("DATA000.TXT"));
  for (uint8_t n = 0; n < 100; n++) {
    name[4] = '0' + n / 10;
    name[5] = '0' + n % 10;
    if (textFile.open(name, O_WRITE | O_CREAT | O_EXCL)) break;
  }
  if (!textFile.isOpen()) error("open textFile");
  PgmPrint("Writing: ");
  Serial.println(name);
  
  bw.init(&textFile);
  
  while (!Serial.available() && binFile.read(&buf, 16) == 16) {
    uint16_t i;

    for (i = 0; i < 4; i++) {
      bw.putNum(buf[i]);
      bw.putCRLF();
    }
    
    if (textFile.curCluster() != syncCluster) {
      bw.writeBuf();
      textFile.sync();  
      syncCluster = textFile.curCluster();
    }
    if ((millis() -t) > 1000) {
      uint8_t pct = binFile.curPosition() / (binFile.fileSize()/100);
      if (pct != lastPct) {
        t = millis();
        lastPct = pct;
        Serial.print(pct, DEC);
        Serial.println('%');
      }
    }
    if (Serial.available()) break;
  }
  bw.writeBuf();
  textFile.close();
  PgmPrintln("Done");
}
//------------------
 
Last edited:
Hi,

Well first, when I mean 0 and 1, I mean the true bits. You need a program called "Binary Viewer", search for it and download it, you can then open the file and it will show the written blocks of data. You can then tell it what type of data you expect ie: 1byte, 2 byte, 4 byte. I know the fast logger won't work since for arduino uno, but it has some stuff you can use to make your program work. The block_t_buf is a structure that contained more information than just the analog readings. SDfat uses a lot of buffers to prevent overrun and if that does happen, he keeps track of that....plus he also keeps track of the samples so he knows how many values to read (in case the 512 byte block of data is not full before it writes). I think you want 16byte segments, since you have 4 data points that are 4 bytes each 4x4=16. So it would read the 4 values, put them in a line and then do a line return. I wish I had more time to play around with the binary to csv, but I have a manuscript to finish and a poster to finish for a conference. The first thing I would do is get the binary viewer program, then you can see how your data is layed out....then try to get the binary to csv part working with a couple seconds of data collection. (top tip: for now just write the raw ADC values to the binary file, so when you go to look at the file you can see the values ie 0-4096...if you convert to float, that program above won't show the values correctly).
Seems you are on the right track, however you just need to learn the basics, which is pretty much what I had to do a few months ago ie: bits, bytes, binary, because once you understand the data structure, it will make more sense.
Also, the link for the analog logger is his newest program, I think I used his older logger (search the web for it). So his binary to csv is a little different between the two programs, not much, but follow his example and change it to fit your data vs his data.

Update:
Try this code...think I understand it now....also remember it makes a comma delimited file, not tab like you originally wanted....i'm sure you could easily change that.

Update2: So this code does work, I tried it. The only issue was when they create the csv file name, they forgot the ., so it wouldn't open the file. I update it and now works, your SD card will now have a binary file and a csv file. You might even be able to update the code so that after the conversion, you could delete the file, which would be this code.
if (sd.exists(binName)) {
if (!sd.remove(binName)) Serial.println("put error msg here if you want); //print error if you can't delete file
}
Also, for a 125kb file, it took 1.87 seconds to convert. So for you data collection at 250samples per second, that is a data collection of 2:08 minutes. So, now you should be ready to go and collect to your hearts content.
Code:
void binaryToCsv() {
  uint8_t lastPct = 0;
  int buf[128];
  //metadata_t* pm;
  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"));
  //pm = (metadata_t*)&buf;
  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 ie:128 samples
    for (i = 0; i < 128; i++) {
      bw.putNum(buf[i]);
      if((i + 1)%4) { //since 4 data points, it put a comma between 3 points and on the 4th,send a carrige return
        bw.putChar(',');
      } else {
        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"));
}
 
Last edited:
That does more then the code I had and been busy with. So far i was testing with my idea, i only got zero's..

Although i'm now using your code, I got this:
Code:
Time,A-B,A,B
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0
0,0,0,0

There are no values? Is it something in my coding? I assume that you've used the same code I did? Or how have you been testing this? Can you upload your library which has been used? Anyhow, I'm one step closer! :)
I'm going busy with it now. I actually don't know if the problem is the adc reading, because when i'm measuring, i see the correct values by the serial monitor. But it's written to the SD card as binary, after the measurement has stopped it isbeing converted back to csv.

I'll post updates later this day.

Current code has implemented the binary to .csv script, although I made some changes to put it in .txt, I hope that doesn't mind? As far I see it is not a problem... Anyway, the strings are separated by a "tab" instead of a ",". Even when using simple values such as 1,2,3,4 instead of analog measurements. it did not convert the right values back. converted file still contains zero's.

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

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

//Define ADC Inputs
#define ADC_0 0
#define ADC_1 1

//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;

#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"; //[13]

ADC *adc = new ADC(); // adc object
ADC::Sync_result result;
float ADCvalue_0 = ADC_ERROR_VALUE;
float ADCvalue_1 = ADC_ERROR_VALUE;
float SDBuffer [128];
int count = 0;
uint32_t bn = 1; //block number

//Interval Timer
IntervalTimer timer0;
char c=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) {
    //sendError(3,1);
  }
  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)) {
    if (!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
  
  //Set ADC (Averaging and Resolution)  
  adc->setAveraging(32); // set number of averages
  adc->setResolution(12); // set bits of resolution
    // it can be ADC_VERY_LOW_SPEED, ADC_LOW_SPEED, ADC_MED_SPEED, ADC_HIGH_SPEED_16BITS, ADC_HIGH_SPEED or ADC_VERY_HIGH_SPEED
  //try low/med speed since load cell has high impedence
  adc->setConversionSpeed(ADC_HIGH_SPEED); // change the conversion speed
  // it can be ADC_VERY_LOW_SPEED, ADC_LOW_SPEED, ADC_MED_SPEED, ADC_HIGH_SPEED or ADC_VERY_HIGH_SPEED
  adc->setSamplingSpeed(ADC_HIGH_SPEED); // change the sampling speed
  
  adc->setAveraging(32, ADC_1); // set number of averages
  adc->setResolution(12, ADC_1); // set bits of resolution
  adc->setConversionSpeed(ADC_HIGH_SPEED, ADC_1); // change the conversion speed
  adc->setSamplingSpeed(ADC_HIGH_SPEED, ADC_1); // change the sampling speed
  // always call the compare functions after changing the resolution!
   // adc->enableCompare(1.0/3.3*adc->getMaxValue(), 0, ADC_0); // measurement will be ready if value < 1.0V
   // adc->enableCompareRange(1.0*adc->getMaxValue(ADC_1)/3.3, 2.0*adc->getMaxValue(ADC_1)/3.3, 0, 1, ADC_1); // ready if value lies out of [1.0,2.0] V

    
  // 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() {
  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, 200000); //start timer 4000=250sps
    } else if(c=='s') { // stop recording
        stopRecording();
        delay(100);
        binaryToTXT();

    } 
    } else if(c=='c') { // stop recording
        binaryToTXT();
        }
      
 delay(1);

}


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

void timerCallback0(){
      result = adc->analogSynchronizedRead(readPin0, readPin1);
     float ADCvalue_0 = result.result_adc0*3.3/adc->adc0->getMaxValue();
     float ADCvalue_1 = result.result_adc1*3.3/adc->adc1->getMaxValue();
   
  if (count < 128) {
  //&& (result.result_adc0 !=ADC_ERROR_VALUE) && (result.result_adc1 !=ADC_ERROR_VALUE)) {
    SDBuffer[count] = micros();
    SDBuffer[count+1] = ADCvalue_0-ADCvalue_1; 
    SDBuffer[count+2] = ADCvalue_0;
    SDBuffer[count+3] = ADCvalue_1;
    
// to Serial 
//        Serial.print("Time: ");
        Serial.print(SDBuffer[count]),DEC;
        Serial.print("\t"); 
//        Serial.print("A-B: ");
        Serial.print((SDBuffer[count+1]),DEC);  
        Serial.print("\t");  
//        Serial.print("A: ");  
        Serial.print((SDBuffer[count+2]),DEC);
        Serial.print("\t");  
//        Serial.print("B: ");
        Serial.println((SDBuffer[count+3]),DEC);  
        count = count + 4;//move index 4 positions for next values
  }
 
  if (count== 128){
   noInterrupts(); 
   memcpy(pCache, &SDBuffer, 512);
       if (!sd.card()->writeData((uint8_t*)pCache)) ;
    bn++; //increment block number
    count = 0; 
    memset(pCache, '0', 512); 
      interrupts();
  }
       
}

//------------------------------------------------------------------------------
void binaryToTXT() {
  uint8_t lastPct = 0;
  int buf[128];
  //metadata_t* pm;
  uint32_t t0 = millis();
  uint32_t syncCluster = 0;
  SdFile txtFile;
  char txtName[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 txtFile
  strcpy(txtName, binName);
  strcpy_P(&txtName[BASE_NAME_SIZE + 3], PSTR(".txt"));

  if (!txtFile.open(txtName, O_WRITE | O_CREAT | O_TRUNC)) {
    error("open txtFile failed");  
  }
  Serial.println();
  Serial.print(F("Writing: "));
  Serial.print(txtName);
  Serial.println(F(" - type any character to stop"));
  //pm = (metadata_t*)&buf;
  bw.init(&txtFile);
  bw.putStr_P(PSTR("Time\t")); //Write header at top of file
  bw.putStr_P(PSTR("A-B\t"));
  bw.putStr_P(PSTR("A\t"));
  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 ie:128 samples
    for (i = 0; i < 128; i++) {
      bw.putNum(buf[i]);
      if((i + 1)%4) { //since 4 data points, it put a comma between 3 points and on the 4th,send a carrige return
        bw.putChar('\t');
      } else {
        bw.putCRLF();
      }
    }
    if (txtFile.curCluster() != syncCluster) {
      bw.writeBuf();
      txtFile.sync();
      syncCluster = txtFile.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();
  txtFile.close();
  Serial.print(F("Done: "));
  Serial.print(0.001*(millis() - t0));
  Serial.println(F(" Seconds"));
}
 
Last edited:
Well your binarytoTXT has to go before you create the temp file...since that is in the stopRecording(), then the new file will be used to create your TXT file since it is called before you try to create the TXT file. So move that down into the stopRecording right before createFile();, and that should work now. I am not sure why the temp file is used, I just go with it since it works. Code below worked for me, but maybe you have to move it under the rename function.
With your updates you should be able to use txt instead of csv.
binarytoTXT();
if (!myFile.rename(sd.vwd(), binName)) ;
createFile();
 
Question, I'm logging binary files now, but i expect to have zero's and one's.

You do. However, you do not have a sequence of "0" characters and "1" characters. You have a sequence of bytes, each containing 8 bits which are zero or one. What you are seeing is those bytes interpreted as character codes.
 
Well your binarytoTXT has to go before you create the temp file...since that is in the stopRecording(), then the new file will be used to create your TXT file since it is called before you try to create the TXT file. So move that down into the stopRecording right before createFile();, and that should work now. I am not sure why the temp file is used, I just go with it since it works. Code below worked for me, but maybe you have to move it under the rename function.
With your updates you should be able to use txt instead of csv.
binarytoTXT();
if (!myFile.rename(sd.vwd(), binName)) ;
createFile();

I've changed it now, and I am getting some results!
Although, for testing i'm using as input: "micros,1,2,3" instead of the sensor signals

The output in the text file are:
Code:
Time	A-B	A	B
1240771400	1073741824	1077936128	1082130432
1240803400	1073741824	1077936128	1082130432
1240835400	1073741824	1077936128	1082130432
1240867400	1073741824	1077936128	1082130432
1240899400	1073741824	1077936128	1082130432
1240931400	1073741824	1077936128	1082130432
1240963400	1073741824	1077936128	1082130432
1240995400	1073741824	1077936128	1082130432
1241027400	1073741824	1077936128	1082130432

This aint the time or '1,2,3'??
 
Last edited:
You do. However, you do not have a sequence of "0" characters and "1" characters. You have a sequence of bytes, each containing 8 bits which are zero or one. What you are seeing is those bytes interpreted as character codes.

Thanks for explanations! :)
 
How are you putting 1,2,3 in? Are they floats? There is some issue with the values since it must not be putting the right number of bits and bytes. Make all your values float and see if that works. This might be beyond my knowledge. I open all my binary files directly into labview and I can easily tell it if the values are float.
void BufferedWriter::putNum(float value, uint8_t prec), this is what is called in the bufferedwritter program, so maybe you need to replace bw.putNum(buf); with bw.putNum(buf, #); where # is the units of precision you want. Also noticed buf is an int, change that to a float and see if that helps.
Also Change SDBuffer to float. Here is the updated code and I added some comments so you can see the changes. This works for me.
Code:
/*
ECG measurement Project:
- Sensing two ADC values,
- Analog files being logged on sd card
    Time  A-B  A  B
*/

//Including Libraries
#include <ADC.h>
#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 "CHOP" //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";

ADC *adc = new ADC(); // adc object
ADC::Sync_result result;
float ADCvalue_0 = ADC_ERROR_VALUE;
float ADCvalue_1 = ADC_ERROR_VALUE;
float SDBuffer [128];
int count = 0;
uint32_t bn = 1; //block number
int startTime; //to reset milli count at each recording

//Interval Timer
IntervalTimer timer0;
char c=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)) {
    if (!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
  
  //Set ADC (Averaging and Resolution)  
  adc->setAveraging(32); // set number of averages
  adc->setResolution(12); // set bits of resolution
    // it can be ADC_VERY_LOW_SPEED, ADC_LOW_SPEED, ADC_MED_SPEED, ADC_HIGH_SPEED_16BITS, ADC_HIGH_SPEED or ADC_VERY_HIGH_SPEED
  //try low/med speed since load cell has high impedence
  adc->setConversionSpeed(ADC_HIGH_SPEED); // change the conversion speed
  // it can be ADC_VERY_LOW_SPEED, ADC_LOW_SPEED, ADC_MED_SPEED, ADC_HIGH_SPEED or ADC_VERY_HIGH_SPEED
  adc->setSamplingSpeed(ADC_HIGH_SPEED); // change the sampling speed
 //adc->enableCompare(1.0/3.3*adc->getMaxValue(ADC_0), 0, ADC_0); // measurement will be ready if value < 1.0V
  // adc->enableCompareRange(1.0*adc->getMaxValue(ADC_0)/3.3, 2.0*adc->getMaxValue(ADC_0)/3.3, 0, 1, ADC_0); // ready if value lies out of [1.0,2.0] V
 
  adc->setAveraging(32, ADC_1); // set number of averages
  adc->setResolution(12, ADC_1); // set bits of resolution
  adc->setConversionSpeed(ADC_HIGH_SPEED, ADC_1); // change the conversion speed
  adc->setSamplingSpeed(ADC_HIGH_SPEED, ADC_1); // change the sampling speed
  // always call the compare functions after changing the resolution!
   // adc->enableCompare(1.0/3.3*adc->getMaxValue(ADC_1), 0, ADC_1); // measurement will be ready if value < 1.0V
   // adc->enableCompareRange(1.0*adc->getMaxValue(ADC_1)/3.3, 2.0*adc->getMaxValue(ADC_1)/3.3, 0, 1, ADC_1); // ready if value lies out of [1.0,2.0] V

    
  // 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, 40000); //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(){
      result = adc->analogSynchronizedRead(readPin0, readPin1);
     float ADCvalue_0 = result.result_adc0*3.3/adc->adc0->getMaxValue();
     float ADCvalue_1 = result.result_adc1*3.3/adc->adc1->getMaxValue();
  if (count < 128){ //since using float values, 128x4=512bytes
  //Conversion issue fixed, so don't need code commented out below
   // && (result.result_adc0 !=ADC_ERROR_VALUE) && (result.result_adc1 !=ADC_ERROR_VALUE) 
    SDBuffer[count] = millis()-startTime; //if you don't get a start time, then will be time since program started
    SDBuffer[count+1] = ADCvalue_0-ADCvalue_1; 
    SDBuffer[count+2] = ADCvalue_0;
    SDBuffer[count+3] = ADCvalue_1;
        Serial.print("Time: ");
        Serial.print(SDBuffer[count]);
        Serial.print("\t"); 
        Serial.print("A-B: ");
        Serial.print(SDBuffer[count+1],DEC);  
        Serial.print("\t");  
        Serial.print("A: ");  
        Serial.print(SDBuffer[count+2],DEC);
        Serial.print("\t");  
        Serial.print("B: ");
        Serial.println(SDBuffer[count+3],DEC); 
    count = count + 4;//move index 4 positions for next values
  }
 
  if (count== 128){
   noInterrupts(); 
   memcpy(pCache, &SDBuffer, 512);
       if (!sd.card()->writeData((uint8_t*)pCache)) ;
    bn++; //increment block number
    count = 0; 
    memset(pCache, '0', 512); 
      interrupts();
  }
       
 
}
void binaryToCsv() {
  uint8_t lastPct = 0;
  float buf[128]; //changed to float
  //metadata_t* pm;
  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();
  // 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"));
  //pm = (metadata_t*)&buf;
  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 ie:128 samples
    for (i = 0; i < 128; i++) {
      bw.putNum(buf[i],4); //Change 4 to what ever level of decimals you want
      if((i + 1)%4) { //since 4 data points, it put a comma between 3 points and on the 4th,send a carrige return
        bw.putChar(',');
      } else {
        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"));
}
 
Last edited:
Thanks for the effort Turtle9er, it seems to work as far I see now, especially the starttime.

It seems that float buf did the trick. It was an int, but i'm using floating numbers.

Future improvements will be the replacement of inputs (record and stop record) with buttons.
For now, I'll tweak the current code. There are still improvements possible on my side.
I'll give an update here when i'm interfering another problem.

I'll give lots of credits to you ;
 
Well it was very similar to my program, which I took from SDfat, so he deserves all the credit. My program already uses a button and I actually just figure out how to put it to sleep between recordings, since mine runs on batteries. I just used the bounce library for the button and it works a treat. Let me know if you want it.
 
Well it was very similar to my program, which I took from SDfat, so he deserves all the credit. My program already uses a button and I actually just figure out how to put it to sleep between recordings, since mine runs on batteries. I just used the bounce library for the button and it works a treat. Let me know if you want it.

That actually sounds cool! I'm wired to an MicroUSB cable, haha. see my attachment of my developed protoboard which has been used for measurement (to give somewhat a impression :) )
 
Last edited:
That looks a lot more advance. What is the purpose of this board, to just measure ECG.....have you thought about doing EMG also, I'm doing my PhD in sport performance and I do a lot of stuff with EMG.
 
That looks a lot more advance. What is the purpose of this board, to just measure ECG.....have you thought about doing EMG also, I'm doing my PhD in sport performance and I do a lot of stuff with EMG.

Purpose of this board is to measure analog signals from two sensors, these signals are preamplified. There's an analog differential amplifier with the two sensor inputs (which also forms A-B signal), the differential signal can be measured by Teensy aswell, with the use of levelshifters. Also, there is used an analog driven right leg circuit, and the option for digital (with a digital potentiometer - AD5363, for the ones who are interested). There are several connectors for external power supply, and BNC connectors for the output signals (for using oscilloscope's).

An USB to I2C module, used to control the Digital Potentiometer with LabVIEW i.e.). And of course the Teensy itself with a sd-adapter and RTC (which i'm not using at the moment).

I haven't really thought about EMG, because that was not my goal for measuring. But i quess it should be possible with this board aswell. What I know about EMG is detecting the respiration, it can be measured by making use of the ECG... the respiration per minute can be calculated by using the peaks of every heartbeat, anyhow respiration can also be analyzed by using filtering between 0-5Hz tho? Just let me know if I could help you with that! :)
 
Last edited:
Is there a reason why i can only log 64 seconds? I did a measurement of 5 minutes, but only the first 64 seconds has been measured.. It's the half of 128 byte and 1/8 from 512 byte
Is it even possible to measure longer then 10 minutes for example?

Also, I have added a button function with 'start' and 'stop now. :) Seems to work!
 
You need to change the block size to fit your needs. Right now it is set at 500 blocks, which is 256000 bytes, you are collecting 4 samples every 40ms, and each sample is 4 bytes, therefore you are collecting 16bytes every 40ms.

This means every second you saving 4000 bytes. 256000/4000=64seconds.

So just make the block size fit your needs, in the end the file is truncated down anyways, but you don't want to make it crazy bit, since then it will take longer to process.

To make your file smaller, you could not do the A-B math, and do that during the binarytoTXT conversion. Also, if after lots of testing your millis timer is always correct and samples are never missed, then you could add that in later.

I also noticed one issue with how the binary to text works, you have millis as a float, so if you want precision on your voltage values, you will also get these decimals on your millis. Meaning, 40ms now becomes 40.0000. So you can either divide you time by 1000 to get it into a smaller value ie: 40ms now becomes 0.040. Or you might be able to do this in your code

Code:
    for (i = 0; i < 128; i+4) { //increment i by 4 every iteration
      bw.putNum(buf[i]); //don't want decimals for millis counter
      bw.putChar('\t');
      bw.putNum(buf[i+1],6); //eg: want 6 decimals for voltage readings
      bw.putChar('\t');
      bw.putNum(buf[i+2],6); 
      bw.putChar('\t');
      bw.putNum(buf[i+3],6); 
        bw.putCRLF();
    }

Not pretty, but is should work.....maybe
ps - EMG is to do with all muscle activity, not just respiration.
 
Last edited:
Status
Not open for further replies.
Back
Top