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-09-15
Revision:
7:3886935f9364
Parent:
5:5fd55c860c09

File content as of revision 7:3886935f9364:

/* 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.
  
  When using F303K8 and I2C 
    SB16 and SB18 must be removed because
    they link D4 to A4 and D5 to A5
    PG #12 http://www.st.com/content/ccc/resource/technical/document/user_manual/e3/0e/88/05/e8/74/43/a0/DM00231744.pdf/files/DM00231744.pdf/jcr:content/translations/en.DM00231744.pdf    
   
    FRAM Chip Interface MB85RS2MTPH-G-JNE1 
        3 MBit FRAM.  Also works with WinBond W25Q80BV and Windbond 8MB chips.
        
    #1 - Chip Select 
    #2 - DO - Data Out hook to MISO
    #3 - WP - Write  Protect - connect GND
    #4 - GND
    #5 - DI - Data In - hook to MOSI
    #6 - CLk- Clock - Hook to SPI_SCLK
    #7 - HD - Hold - connect VCC 3.3V
    #8 - VCC - Connect VCC 3.3V
    
    See Also: http://www.instructables.com/id/How-to-Design-with-Discrete-SPI-Flash-Memory/step2/The-WinBond-Device-Interface/
    
    If system seems to return garbage after writting test data 
    then one or more pins are most likely linked with a solder bridge
    check the users guide for the board you are using. 
*/
    
#include "mbed.h"
#include "dataLog.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 PA_4

//#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 send last 900 bytes to pc on startup
//#define TEST_BASIC_READ_WRITE // uncomment to erase chip and test basic read write interface

AnalogIn waterTempSense(A0);
AnalogIn oxygenLevelSense(A1);
AnalogIn solarStrengthSense(A2);

// 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", waterTemp, oxygenLevel, solarStrength);
        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");
    
    
    #ifdef TEST_BASIC_READ_WRITE  
    printf("start basic write test\r\n");
    printf("chipErase\r\n");
    dlchip.chipErase(); 
    memset(dlbuff,0,30);   
    memcpy(dlbuff, "I am a test\r\n\r\n\r\n",17);
    dlchip.writeStream(100,dlbuff, 17);
    memset(dlbuff,0,30);
    dlchip.readStream(100, dlbuff, 17);
    printf("read buffer =%s\r\n", dlbuff);
    #endif

    struct DLOG *lgr = dlMake(&dlchip, dlbuff, 80, &pc);    
    pc.printf("\r\nInitialized Logger\r\n");    
    pc.printf("logger nextWritePos=%ld", lgr->nextWritePos);    
    
    dlHelp(lgr); // send data log command help to pc. 
    #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) {
        
        
    }
  
  
    
}