Data log for logging enviornmental, process and system state to FRAM or EPROM chips that do not have support for a file system. Includes support for multiple record types, evolving record types, automatic date and time stamp and copying contents to serial. Will soon support journaling to micro SD file system. We always log to fram first for speed and power conserfaction then copy in bulk to SD cards when we have charging power available.

Dependencies:   data_log mbed

Flexible Data Logger Example Use and Test Code

Detailed Documentation

See home page for data log library https://developer.mbed.org/users/joeata2wh/code/data_log/

License

By Joseph Ellsworth CTO of A2WH Take a look at A2WH.com Producing Water from Air using Solar Energy March-2016 License: https://developer.mbed.org/handbook/MIT-Licence Please contact us http://a2wh.com for help with custom design projects.

main.cpp

Committer:
joeata2wh
Date:
2016-03-31
Revision:
3:ee7427ad4745
Parent:
2:74a52e5fd1ed
Child:
5:5fd55c860c09

File content as of revision 3:ee7427ad4745:

/* xj-data-log-test-and-exmple.c
  Shows basic usage of dat_log library and
  performs some basic tests. 
  
  By Joseph Ellsworth CTO of A2WH
  Take a look at A2WH.com Producing Water from Air using Solar Energy
  March-2016 License: https://developer.mbed.org/handbook/MIT-Licence 
  Please contact us http://a2wh.com for help with custom design projects.
*/
    
#include "mbed.h"
#include "dataLog.h"  
#include "serial-command.h"
const float adc_ref = 3.3f;
// SPI PINS 
#define SPI1_SCK PA_5
#define SPI1_MISO PA_6
#define SPI1_MOSI PA_7

// SPI PINS for Data Log chip
#define dataLogMISOPin  SPI1_MISO
#define dataLogMOSIPin  SPI1_MOSI
#define dataLogCLKPin   SPI1_SCK
#define dataLogSelectPin PC_12

//#define DO_LOG_ERASE  // uncomment to erase the existing log.
//#define DO_SEND_MANUAL  // uncomment to send first 1000 bytes to pc on startup
//#define DO_SEND_ALL // uncomment to send entire log to PC on startup
#define DO_SEND_LAST // uncomment to sent last 900 bytes to pc on startup

AnalogIn waterTempSense(A0);
AnalogIn oxygenLevelSense(A1);
AnalogIn solarStrengthSense(A2);
AnalogIn waterSpeedSense(A3);
// imagine for sake of example that we have
// circuitry that actually convert these Volt
// readings to useful readings.
 
DigitalOut led(LED1);
Serial pc(USBTX, USBRX);

// simulated process generating log entries
// based on ADC readings. 
void generateSimulatedLog(struct DLOG *lgr,  int numCycles, float sleepBetween) {
    char stateHappy[] = "happy";
    char stateSad[] = "sad";
    char stateDead[] = "dead fish";
    char *currState = stateHappy;
    char *lastState = 0;
    int cycleCnt;
   

    for(cycleCnt=0; cycleCnt < numCycles; cycleCnt++) {
        float waterTemp = waterTempSense.read() * adc_ref;
        float oxygenLevel = oxygenLevelSense.read() * adc_ref;
        float solarStrength = solarStrengthSense.read() * adc_ref;
        float waterSpeed = waterSpeedSense.read() * adc_ref;
    
        char tbuff[256];
        sprintf(tbuff, "%0.2f,%0.2f,%0.2f,%0.2f", waterTemp, oxygenLevel, solarStrength,waterSpeed);
        dlLog(lgr, "sensors",tbuff);
        // date and time are automatically recorded with the call.
        // time is only recorded to the second granularity and dates
        // are actually recorded as separate records when the date changes.

        pc.printf("cycleCnt=%d logged %s\r\n", cycleCnt, tbuff);
        
        currState = stateHappy;
        if (oxygenLevel < 20)
          currState = stateSad;
        if (oxygenLevel < 12)
          currState = stateDead;
          
        if (currState != lastState) {
          // Only generate a new log if the current state has changed
          lastState = currState;
          sprintf(tbuff,"%s,%0.2f", currState, oxygenLevel);
          dlLog(lgr,"state", tbuff);  
          pc.printf("cycleCnt=%d logged stateChange %s\r\n", cycleCnt, tbuff);  
        }
        // lgr will internally log time.  Whenever the date changes
        // it will log a new date records.     
        led = !led;    
        wait(sleepBetween);
    }
}

// documentaion on supporting Serial Interupts 
// https://developer.mbed.org/cookbook/Serial-Interrupts


int main() {
    pc.baud(9600);
    pc.printf("\nData Logger Example example\n");
    set_time(1459380179);
    
    // NOTE: You need to initialize the clock to some meaningful value
    //  I use a clock chip but here is a manual setting 
    time_t seconds = time(NULL);
    pc.printf("time set to %s\r\n", ctime(&seconds));
    
    char dlbuff[81]; // buffer used internally by this instance of data log
    DataLogChipType dlchip(dataLogMOSIPin, dataLogMISOPin, dataLogCLKPin, dataLogSelectPin);
    pc.printf("\r\nInitialized Data Log Chip");
    struct DLOG *lgr = dlMake(&dlchip, dlbuff, 80);    
    
    pc.printf("\r\nInitialized Logger\r\n");    
    pc.printf("logger nextWritePos=%ld", lgr->nextWritePos);
    
    #ifdef DO_LOG_ERASE
      //Erase the log But only if you really want to loose the
      // data
      pc.printf("ERASE LOG");
      dlEraseLog(lgr);
      pc.printf("afer erase logger nextWritePos=%ld", lgr->nextWritePos);
    #endif 
    
    #ifdef DO_SEND_MANUAL
      // Manually Read first 1,000 bytes from the log
      // could contain some garbage since it does not
      // pay attention to current log length.
      char rbuff[1001];
      memset(rbuff,1001,0);
      dlchip.readStream(dlFirstLogEntry, rbuff, 1000);
      pc.printf("first 1000 characters from log\r\n%s\r\n", rbuff);
    #endif
    
    #ifdef DO_SEND_ALL
      // Send Log Contents to Serial port
      // sends from byte zero of the log
      // to byte 100,000 or end of log which ever comes 
      // first. 
      pc.printf("\r\nSend the contents of log to PC automated way\r\n");
      dlReadSendAll(lgr, &pc);
      pc.printf("\r\nDONE SEND ALL\r\n");
    #endif
    
    #ifdef DO_SEND_LAST
      pc.printf("\r\nSend the last 900 bytes\r\n");
      dlReadSendLast(lgr, &pc, 50000);
      pc.printf("\r\nDONE Send Last\r\n");
    #endif
    
    
    // Record our record headers.  These need to match the records we will
    // write later.  It is OK to change the defintion of the headers as the 
    // parser will use the correct defenition provided you always record the
    // defenition before writing the log entries.
    dlLog(lgr, "HEAD\tsensors", "waterTemp.f,oxygenLevel.f,solarStrength.f,waterSpeed.f");
    dlLog(lgr, "HEAD\ttstate", "currState.s,oxygenLevel.f");
    pc.printf("\r\nWrote record type headers\n");
    // by convention I record the record header every time the system starts.
    // in future will have a fast way to retrieve active header for each record
    // type so you can determine if new header is differnt that exisitng header and
    // only record if they chanaged. 
    // The .f suffix is a hint to down stream parser to parse as a float.
    //  .f - float,  .i - int or long, .s-string,  .d - ISO date, .b - byte
    // at the very least you should record a new HEAD record the first time 
    // a given record type is used and every time you add or change the fields or field ordering.
    
    generateSimulatedLog(lgr, 20, 10.0);
    
    while(1) {
        
        
    }
  
  
    
}