For ExFat you need SdFat-beta
Thanks - will try.
For ExFat you need SdFat-beta
if ((newIMUData == 1 && rtk.upDated == true)) {
Ok going through the examples seems everything is based on a timer call back. Question is if I just want to log data every 200ms based on the data available test in a loop:
how would I modify the say the ASCII example?Code:if ((newIMUData == 1 && rtk.upDated == true)) {
EDIT: @defragster, recognize the if test
// The ISR handler for an interval timer must be at global scope,
// and not in the DataLogger object scope. THUS, This function to
// transfer back to the object so that the buffering can use all
// the datalogger private variables.
void LoggerISR(void) { // call the data logger collection ISR handler
[B]// mydl.TimerChore();[/B]
}
// this function is called from the global level interval timer ISR.
// It has access to alll the class variables and will call the collector function.
void TimerChore(void);
collectionrate = crate;
mystatus.collectionrate = crate;
@defragster -- seems to hang as soon as it trys to write the first pack. May have to do something else
Odd - that is set in StartLogger() {collectPtr = chunkPtrs[collectchunk];} - and should not care about how the mydl.TimerChore(); gets called as that is expected to be in user code so it is hands off on the _isr the timer triggers.
Did that code make it to github? Not sure if I have the same/latest version as you - and can't see your edits from here
Opps - I thought that was just posted for throwback fun - didn't realize that is where the datalogger usage was
#include <MTP.h>
#include <Storage.h>
#include <usb1_mtp.h>
#include <TinyGPS.h>
#include <DataLogger.h>
#include <CMDHandler.h>
#include "MTP.h"
const int admarkpin = 12;
const int gpspwrpin = 2;
// Add stuff for MTP
#define DO_DEBUG 1
MTPStorage_SD storage;
MTPD mtpd(&storage);
TinyGPS mygps;
SdFs *fsptr;
// instantiate a datalogger object
DataLogger mydl;
CMDHandler mycmds;
bool MTPLoopOK = true; // start up with MTP Active---no loop only when logging
bool autostarted = false;
#define GPSON digitalWriteFast(gpspwrpin, HIGH);
#define GPSOFF digitalWriteFast(gpspwrpin, LOW);
#define LEDON digitalWriteFast(ledpin, HIGH);
#define LEDOFF digitalWriteFast(ledpin, LOW);
const char compileTime [] = "GPS Logger Compiled on " __DATE__ " " __TIME__;
const char *autoname = "GPS";
const char *extype = "002";
void LoggerISR(void) { // call the data logger collection ISR handler
mydl.TimerChore();
}
#define SAMPLERATE 100
#define SAVEINTERVAL 1 // seconds between records
// use our own buffer on the T3.6
#define MAXBUFFER 20000
uint8_t mybuffer[MAXBUFFER];
char logfilename[64];
uint32_t gpsChars = 0;
bool echoflag = false;
bool logging = false;
uint64_t fsreserved;
elapsedMillis el_autostart;
// string input for buffer 16 bytes long
// not all bytes are filled, thus the length is used
struct gpsstring{
uint8_t len;
char sdata[15];
};
// processed data storage structure
struct datrec {
uint32_t unixtime, ltime;
int32_t lat, lon;
float felev, fcourse, fspeed;
float fhdop;
};
#define AUTODELAY 300000 // 5 minutes
void setup() {
uint32_t bufflen;
TLoggerStat *tsp;
pinMode(ledpin, OUTPUT);
pinMode(gpspwrpin, OUTPUT);
Serial.begin(9600);
Serial1.begin(9600);
delay(500);
Serial.print("\n\nGPS Data Logger ");
Serial.println(compileTime);
mydl.SetDBPrint(false); // turn on debug output
fsptr = mydl.InitStorage();// try starting SD Card and file system
if (fsptr == NULL) {
// initializing SD Card failed
fastBlink();
}
// now try to initialize buffer. Check that request falls within
// size of local buffer or if it will fit on heap
bufflen = mydl.InitializeBuffer(sizeof(gpsstring), SAMPLERATE, 1200, mybuffer);
if ((bufflen == 0) || (bufflen > MAXBUFFER)) {
Serial.println("Not enough buffer space! Reduce buffer time or sample rate.");
fastBlink();
}
LEDON
delay(1000); // Need a delay while SDFat checks free clusers for first time
tsp = mydl.GetStatus();
LEDOFF
Serial.printf("Free Space: %6.2f\n", (float)(tsp->spaceavailable) / (1024.0 * 1024.0));
Serial.println("Calculating reserved space.");
fsreserved = tsp->spaceavailable / 20; //reserve the last 5% of space available;
StartMTP();
// Now attach our customized callback functions
mydl.AttachCollector(&myCollector); // specify our collector callback function
mydl.AttachWriter(&myBinaryWriter); // logger saves processed binary data you generat
mydl.AttachDisplay(&myBinaryDisplay, 20000); // display written data once per 5 seconds
GPSON
mydl.AutoFile(autoname, extype, 6);
delay(200);
el_autostart = 0;
InitCommands();
}
void InitCommands(void){
mycmds.AddCommand(&StartLogging,"SL",0);
mycmds.AddCommand(&QuitLogging, "QL",0);
mycmds.AddCommand(&ShowStatus, "SS",0);
mycmds.AddCommand(&EchoFlag, "EF",0);
mycmds.AddCommand(&Directory, "DI",0);
}
void EchoFlag(void *cmdline){
echoflag = !echoflag;
Serial.println();
}
void Directory(void *cmdline){
mydl.ShowDirectory();
}
void loop() {
// put your main code here, to run repeatedly:
TLoggerStat *tsp;
do{
tsp = mydl.GetStatus();
delay(1);
} while(tsp->spaceavailable == 0);
if(logging)mydl.CheckLogger(); // check for data to write to SD at regular intervals
if ((tsp->spaceavailable < fsreserved) && logging) { //we are near end of card
Serial.println("Halting logging. Getting near end of SD Card.");
Serial.printf("Space Available = %6.1f Space reserved = %6.1f\n",
(float)tsp->spaceavailable / (1024.0 * 1024.0), (float)fsreserved / (1024.0 * 1024.0));
QuitLogging(NULL);
ShowStatus(NULL);
logging = false;
}
if(mycmds.CheckCommandInput()){
el_autostart = 0; // reset autostart timer
}
if(MTPLoopOK){
mtpd.loop();
}
if(!autostarted){
if(el_autostart > AUTODELAY){
StartLogging(NULL);
}
}
asm("WFI\n");
}
void StartMTP(void){
Serial.println("Starting MTP Responder");
usb_mtp_configure();
if(!Storage_init(fsptr)) {
Serial.println("Could not initialize MTP Storage!");
fastBlink();
} else MTPLoopOK = true;
}
void StartLogging(void *cmdline) {
TLoggerStat *tsp;
tsp = mydl.GetStatus();
Serial.println("Starting Logger.");
logging = true;
autostarted = true;
MTPLoopOK = false;
mydl.MakeFileName(autoname, extype);
strncpy(logfilename, tsp->filename, strlen(tsp->filename));
mydl.StartLogger(logfilename, 1000); // sync once per second
Serial.print("\n");
}
void QuitLogging(void *cmdline) {
Serial.println("Stopping Logger.");
mydl.StopLogger();
logging = false;
MTPLoopOK = true;
}
// blink at 500Hz forever to signal unrecoverable error
void fastBlink(void) {
while (1) {
LEDON
delay(100);
LEDOFF
delay(100);
}
}
// can be called before, during, and after logging.
void ShowStatus(void *cmdline) {
TLoggerStat *tsp;
tsp = mydl.GetStatus();
Serial.println("\nLogger Status:");
Serial.printf("Bytes Written: %lu\n", tsp->byteswritten);
Serial.printf("Collection time: %lu seconds\n", tsp->collectionend - tsp->collectionstart);
Serial.printf("Max Collection delay: %lu microseconds\n", tsp->maxcdelay);
Serial.printf("Average Write Time: %6.3f milliseconds\n", tsp->avgwritetime / 1000.0);
Serial.printf("Maximum Write Time: %6.3f milliseconds\n\n", tsp->maxwritetime / 1000.0);
Serial.printf("GPS Characters processed: %lu\n", gpsChars);
}
/***************************************************
Callback function to handle user-defined collection
and logging.
******************************************************/
// called from the datalogger timer handler
// The collector simply collects GPS input characters
// and processes them through mygps;
// the binary data strucure isn't used at this point
void myCollector( void* vdp) {
uint8_t len;
struct gpsstring *gstp;
gstp = (struct gpsstring *)(vdp);
len = Serial1.available();
if(len > 15) len = 15;
gstp->len = len;
len = Serial1.readBytes(&gstp->sdata[0], len);
}
// called from the datalogger CheckLoggger function
void myBinaryDisplay( void* vdp) {
struct datrec *dp;
dp = (struct datrec *)vdp;
if (!logging) return;
Serial.printf("Lat: %10.6f Lon: %12.6f elev: %6.1f HDOP: %4.2f Sats: %u\n",
(double)dp->lat/1e6, (double)dp->lon/1e6, dp->felev, dp->fhdop,mygps.satellites());
}
/************************************************
* struct datrec {
uint32_t unixtime, ltime;
uint32_t flat, flon;
float felev, fcourse, fspeed;
float fhdop;
};
***************************************************/
// Used to write processed GPS binary data. Input to function is
// a pointer to a gpsstr record. We process the string input
// through mygps. This gets called once for each string buffered
// at the 100Hz rate.
// A binary record is saved every saveinterval seconds
uint16_t myBinaryWriter(void *bdp, void* rdp) {
static uint16_t rcounter = 0;
static struct datrec dr;
char** sptr;
sptr = (char**) rdp;
uint32_t age, ldate;
uint16_t i, rval;
struct gpsstring *gsptr;
gsptr = (struct gpsstring *)bdp;
// first process the input string at bdp
for(i= 0; i< gsptr->len; i++) mygps.encode(gsptr->sdata[i]);
if(echoflag)Serial.write(gsptr->sdata, gsptr->len);
gpsChars += gsptr->len;
rcounter++;
if(rcounter >= (SAMPLERATE * SAVEINTERVAL)){
rcounter = 0;
mygps.get_datetime(&ldate, &dr.ltime, &age);
mygps.get_position(&dr.lat, &dr.lon, &age);
dr.unixtime = now();
dr.felev = mygps.f_altitude()/0.3048;
dr.fcourse = mygps.f_course();
dr.fspeed = mygps.f_speed_mph();
dr.fhdop = (float)mygps.hdop()/100.0;
*sptr = (char *)&dr;
rval = sizeof(dr);
} else {
*sptr = NULL;
rval = 0;// return nothing until end of interval
}
return rval;
}
#include <MTP.h>
#include <Storage.h>
#include <usb1_mtp.h>
#include <DataLogger.h>
#include <CMDHandler.h>
#include <ADC.h>
const int admarkpin = 12;
// Add stuff for MTP
#define DO_DEBUG 0
MTPStorage_SD storage;
MTPD mtpd(&storage);
SdFs *fsptr;
// instantiate a datalogger object
DataLogger mydl;
CMDHandler mycmds;
bool MTPLoopOK = true; // start up with MTP Active---no loop only when logging
bool autostarted = false;
#define LEDON digitalWriteFast(ledpin, HIGH);
#define LEDOFF digitalWriteFast(ledpin, LOW);
const char compileTime [] = " Compiled on " __DATE__ " " __TIME__;
const char *autoname = "Solar";
const char *extype = "005";
void LoggerISR(void) { // call the data logger collection ISR handler
mydl.TimerChore();
}
#define SAMPLERATE 1000
#define SAVEINTERVAL 2 // seconds between records
// use our own buffer on the T3.6
// rawdat is 16 bytes and we need at least 1200
#define MAXBUFFER 24000
uint8_t mybuffer[MAXBUFFER];
char logfilename[64];
bool logging = false;
uint64_t fsreserved;
elapsedMillis el_autostart;
// raw data for buffer 16 bytes long
struct rawdat {
uint32_t icount;
float ival, vs, vt;
};
// processed data storage structure
struct datrec {
uint32_t unixtime;
float mAvg, mPeak, vsAvg, vtAvg;
};
// instantiate a new ADC object
ADC *adc = new ADC(); // adc object;
#define AUTODELAY 300000 // 5 minutes
void setup() {
uint32_t bufflen;
TLoggerStat *tsp;
pinMode(ledpin, OUTPUT);
pinMode(A3, INPUT); // A3 is voltage at teensy
pinMode(A2, INPUT); // A2 is solar/battery voltage
pinMode(A0, INPUT); // a0 is current sensor output
Serial.begin(9600);
delay(500);
Serial.print("\n\nSolar Data Logger ");
Serial.println(compileTime);
mydl.SetDBPrint(false); // turn on debug output
fsptr = mydl.InitStorage();// try starting SD Card and file system
if (fsptr == NULL) {
// initializing SD Card failed
fastBlink();
}
// now try to initialize buffer. Check that request falls within
// size of local buffer or if it will fit on heap
bufflen = mydl.InitializeBuffer(sizeof(rawdat), SAMPLERATE, 1200, mybuffer);
if ((bufflen == 0) || (bufflen > MAXBUFFER)) {
Serial.println("Not enough buffer space! Reduce buffer time or sample rate.");
fastBlink();
}
adc->adc0->setAveraging(1 ); // set number of averages
adc->adc0->setResolution(12); // set bits of resolution
adc->adc0->setReference(ADC_REFERENCE::REF_1V2); // use the 1.2V reference
adc->adc0->setConversionSpeed(ADC_CONVERSION_SPEED::MED_SPEED); // change the conversion speed
adc->adc0->setSamplingSpeed(ADC_SAMPLING_SPEED::MED_SPEED); // change the sampling speed
LEDON
delay(1000); // Need a delay while SDFat checks free clusers for first time
tsp = mydl.GetStatus();
LEDOFF
Serial.printf("Free Space: %6.2f\n", (float)(tsp->spaceavailable) / (1024.0 * 1024.0));
Serial.println("Calculating reserved space.");
fsreserved = tsp->spaceavailable / 20; //reserve the last 5% of space available;
StartMTP();
// Now attach our customized callback functions
mydl.AttachCollector(&myCollector); // specify our collector callback function
mydl.AttachWriter(&myBinaryWriter); // logger saves processed binary data you generate
mydl.AttachDisplay(&myBinaryDisplay, 2000); // display written data once per 2 seconds
mydl.AutoFile(autoname, extype, 6);
delay(200);
el_autostart = 0;
InitCommands();
}
void InitCommands(void) {
mycmds.AddCommand(&StartLogging, "SL", 0);
mycmds.AddCommand(&QuitLogging, "QL", 0);
mycmds.AddCommand(&ShowStatus, "SS", 0);
mycmds.AddCommand(&Directory, "DI", 0);
}
void Directory(void *cmdline) {
mydl.ShowDirectory();
}
void loop() {
// put your main code here, to run repeatedly:
TLoggerStat *tsp;
do {
tsp = mydl.GetStatus();
delay(1);
} while (tsp->spaceavailable == 0);
if (logging)mydl.CheckLogger(); // check for data to write to SD at regular intervals
if ((tsp->spaceavailable < fsreserved) && logging) { //we are near end of card
Serial.println("Halting logging. Getting near end of SD Card.");
Serial.printf("Space Available = %6.1f Space reserved = %6.1f\n",
(float)tsp->spaceavailable / (1024.0 * 1024.0), (float)fsreserved / (1024.0 * 1024.0));
QuitLogging(NULL);
ShowStatus(NULL);
logging = false;
}
if (mycmds.CheckCommandInput()) {
el_autostart = 0; // reset autostart timer
}
if (MTPLoopOK) {
mtpd.loop();
}
if (!autostarted) {
if (el_autostart > AUTODELAY) {
StartLogging(NULL);
}
}
//asm("WFI\n");
}
void StartMTP(void) {
Serial.println("Starting MTP Responder");
usb_mtp_configure();
if (!Storage_init(fsptr)) {
Serial.println("Could not initialize MTP Storage!");
fastBlink();
} else MTPLoopOK = true;
}
void StartLogging(void *cmdline) {
TLoggerStat *tsp;
tsp = mydl.GetStatus();
Serial.println("Starting Logger.");
logging = true;
autostarted = true;
MTPLoopOK = false;
mydl.MakeFileName(autoname, extype);
strncpy(logfilename, tsp->filename, strlen(tsp->filename));
mydl.StartLogger(logfilename, 1000); // sync once per second
Serial.print("\n");
}
void QuitLogging(void *cmdline) {
Serial.println("Stopping Logger.");
mydl.StopLogger();
logging = false;
MTPLoopOK = true;
}
// blink at 500Hz forever to signal unrecoverable error
void fastBlink(void) {
while (1) {
LEDON
delay(100);
LEDOFF
delay(100);
}
}
// can be called before, during, and after logging.
void ShowStatus(void *cmdline) {
TLoggerStat *tsp;
tsp = mydl.GetStatus();
Serial.println("\nLogger Status:");
Serial.printf("Bytes Written: %lu\n", tsp->byteswritten);
Serial.printf("Collection time: %lu seconds\n", tsp->collectionend - tsp->collectionstart);
Serial.printf("Max Collection delay: %lu microseconds\n", tsp->maxcdelay);
Serial.printf("Average Write Time: %6.3f milliseconds\n", tsp->avgwritetime / 1000.0);
Serial.printf("Maximum Write Time: %6.3f milliseconds\n\n", tsp->maxwritetime / 1000.0);
}
/***************************************************
Callback function to handle user-defined collection
and buffering
struct rawdat{
uint32_t spare
float ival, vs, vt;
};
******************************************************/
// called from the datalogger timer handler
// The collector simply collects three adc inputs
// and saves them in a rawdat record
void myCollector( void* vdp) {
struct rawdat *rp;
rp = (struct rawdat *)vdp;
uint16_t adval;
float vadc, ival;
adval = (uint16_t)adc->adc0->analogRead(A0); // current sensor
vadc = 5.0 * 1.2 * (float)adval / 4096.0; // parallel resistance is 4K
ival = 500.0 * vadc; // 500mA/volt
rp->ival = ival;
adval = (uint16_t)adc->adc0->analogRead(A2); // SOlar Volts
vadc = 5.0 * 1.2 * (float)adval / 4096.0;
rp->vs = vadc;
adval = (uint16_t)adc->adc0->analogRead(A3); // T3 Power Volts
vadc = 5.0 * 1.2 * (float)adval / 4096.0;
rp->vt = vadc;
}
char *TString(time_t tm) {
static char dstr[12];
// sprintf(dstr,"%02u/%02u/%04u ",month(tm),day(tm),year(tm));
sprintf(dstr, "%02u:%02u:%02u", hour(tm), minute(tm), second(tm));
return dstr;
}
// called from the datalogger CheckLoggger function
void myBinaryDisplay( void* vdp) {
struct datrec *dp;
dp = (struct datrec *)vdp;
if (!logging) return;
Serial.printf("%s\t", TString(dp->unixtime));
Serial.printf("%4.1f\t%4.1f\t%4.3f\t%4.3f\n", dp->mAvg, dp->mPeak, dp->vsAvg, dp->vtAvg);
}
/************************************************
struct datrec {
uint32_t unixtime,
float mAvg, mPeak,vsAvg, vtAvg;
};
***************************************************/
// Used to write processed processed binary data. Input to function is
// a pointer to a rawdat record.
// This gets called once for each rawdat buffered at the 1000Hz rate.
// A binary record is saved every saveinterval seconds
uint16_t myBinaryWriter(void *bdp, void* rdp) {
static uint16_t rcounter = 0;
uint16_t rval;
static struct datrec dr;
struct rawdat *rp;
rp = (struct rawdat *)bdp;
static float isum, vssum, vtsum, ipeak;
char** sptr;
sptr = (char**) rdp;
// add data to sums and check peaks
isum += rp->ival;
if (rp->ival > ipeak) ipeak = rp->ival;
vssum += rp->vs;
vtsum += rp->vt;
rcounter++;
if (rcounter >= (SAMPLERATE * SAVEINTERVAL)) {
dr.mPeak = ipeak;
dr.mAvg = isum / rcounter;
dr.vsAvg = vssum / rcounter;
dr.vtAvg = vtsum / rcounter;
dr.unixtime = now();
ipeak = 0.0;
isum = 0.0;
vssum = 0.0;
vtsum = 0.0;
*sptr = (char *)&dr;
rval = sizeof(dr);
rcounter = 0;
} else {
*sptr = NULL;
rval = 0;// return nothing until end of interval
}
return rval;
}
/************************************************
Time Display Functions for 16x2 display
* *************************************************/
char *DTGString(time_t tm) {
static char dstr[32];
sprintf(dstr, "%02u/%02u/%04u ", month(tm), day(tm), year(tm));
sprintf(&dstr[17], "%02u:%02u:%02u ", hour(tm), minute(tm), second(tm));
return dstr;
}
// nee to make sure this works as intended
bufferneeded = buffneeded64/ 1000;
if(buffneeded64 != ((uint64_t)bufferneeded*1000)){ // calculation will overflow in uint32_t
if (dbprint)iosptr->printf("Possible overflow in buffer size calculation using %lu?\n", bufferneeded);
return 0;
}
// nee to make sure this works as intended
if(buffneeded64 > 0x0FFFFFF00){ // calculation will overflow in uint32_t
if (dbprint)iosptr->println("Possible overflow in buffer size calculation");
return 0;
}
if ((newIMUData == 1 && rtk.upDated == true))
if ((newIMUData == 1 && rtk.upDated == true)) {
newIMUData = 0;
getIMU(); //Filter updated to get Yaw Pitch and Roll
dp->iTOW = rtk.iTOW*2.7778E-07;
dp->tempIMU = IMU.getTemperature_C();
dp->fixType = rtk.fixType;
dp->lat1 = rtk.lat1;
dp->lon1 = rtk.lon1;
dp->hMSL = rtk.hMSL;
dp->velN = rtk.velN;
dp->velE = rtk.velE;
dp->velD = rtk.velD;
dp->pitch = ypr[1];
dp->roll = ypr[2];
dp->yaw = ypr[0];
bool DataLogger::OpenDataFile(const char *filename) {
Serial.print("Opening FIle "); Serial.println(filename);
if (! dataFile.open(filename, O_WRITE | O_CREAT | O_TRUNC)) {
Serial.println("Failed to open File"); Serial.flush();
if (dbprint)iosptr->println("Open File failed");
return false;
}
void LoggerISR(void) __attribute__ ((weak));
void LoggerISR(void) { // when not using Timer this satisfies the linker, but if user fails to define with timer will hide the issue
}
bool StartLogger(const char*filename, uint16_t syncInterval, [B]bool useTimer=true[/B]);
bool DataLogger::StartLogger(const char *filename, uint16_t syncInterval, [B]bool useTimer[/B] )
// ...
if ( useTimer) dlTimer.begin(&LoggerISR, 1000000 / collectionrate); // start at collectionrate
Adding the OnDemand [ useTimer=false below } method of logging seems it would be a useful and important decision - it is basically there - but the _isr() timer driven logging may not always be proper.
Suppose the GPS goes offline when in power saving mode or no reception - or if the GPS message of 5 or 10 Hz happens to get out of sync with the Teensy Timer - it would be much better to just have the Teensy OnDemand log the GPS message as received - when it is valid and changing.
Again - not sure the code I have is the most current - but in reading it the obvious changes should generally be covered - in general:
Code:void LoggerISR(void) __attribute__ ((weak)); void LoggerISR(void) { // when not using Timer this satisfies the linker, but if user fails to define with timer will hide the issue } bool StartLogger(const char*filename, uint16_t syncInterval, [B]bool useTimer=true[/B]); bool DataLogger::StartLogger(const char *filename, uint16_t syncInterval, [B]bool useTimer[/B] ) // ... if ( useTimer) dlTimer.begin(&LoggerISR, 1000000 / collectionrate); // start at collectionrate
Makes sense for an on demand application to me. The issue I am having may be associated that I am trying to use an external card reader on SPI and SDIO.