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.

Committer:
joeata2wh
Date:
Thu Mar 31 19:17:17 2016 +0000
Revision:
3:ee7427ad4745
Parent:
2:74a52e5fd1ed
Child:
5:5fd55c860c09
adding serial-command-listener

Who changed what in which revision?

UserRevisionLine numberNew contents of line
joeata2wh 0:0cfc06f9f18c 1 /* xj-data-log-test-and-exmple.c
joeata2wh 0:0cfc06f9f18c 2 Shows basic usage of dat_log library and
joeata2wh 0:0cfc06f9f18c 3 performs some basic tests.
joeata2wh 0:0cfc06f9f18c 4
joeata2wh 0:0cfc06f9f18c 5 By Joseph Ellsworth CTO of A2WH
joeata2wh 0:0cfc06f9f18c 6 Take a look at A2WH.com Producing Water from Air using Solar Energy
joeata2wh 0:0cfc06f9f18c 7 March-2016 License: https://developer.mbed.org/handbook/MIT-Licence
joeata2wh 0:0cfc06f9f18c 8 Please contact us http://a2wh.com for help with custom design projects.
joeata2wh 0:0cfc06f9f18c 9 */
joeata2wh 0:0cfc06f9f18c 10
joeata2wh 0:0cfc06f9f18c 11 #include "mbed.h"
joeata2wh 0:0cfc06f9f18c 12 #include "dataLog.h"
joeata2wh 3:ee7427ad4745 13 #include "serial-command.h"
joeata2wh 0:0cfc06f9f18c 14 const float adc_ref = 3.3f;
joeata2wh 0:0cfc06f9f18c 15 // SPI PINS
joeata2wh 0:0cfc06f9f18c 16 #define SPI1_SCK PA_5
joeata2wh 0:0cfc06f9f18c 17 #define SPI1_MISO PA_6
joeata2wh 0:0cfc06f9f18c 18 #define SPI1_MOSI PA_7
joeata2wh 0:0cfc06f9f18c 19
joeata2wh 0:0cfc06f9f18c 20 // SPI PINS for Data Log chip
joeata2wh 0:0cfc06f9f18c 21 #define dataLogMISOPin SPI1_MISO
joeata2wh 0:0cfc06f9f18c 22 #define dataLogMOSIPin SPI1_MOSI
joeata2wh 0:0cfc06f9f18c 23 #define dataLogCLKPin SPI1_SCK
joeata2wh 0:0cfc06f9f18c 24 #define dataLogSelectPin PC_12
joeata2wh 0:0cfc06f9f18c 25
joeata2wh 1:58f86fb1fb22 26 //#define DO_LOG_ERASE // uncomment to erase the existing log.
joeata2wh 2:74a52e5fd1ed 27 //#define DO_SEND_MANUAL // uncomment to send first 1000 bytes to pc on startup
joeata2wh 2:74a52e5fd1ed 28 //#define DO_SEND_ALL // uncomment to send entire log to PC on startup
joeata2wh 2:74a52e5fd1ed 29 #define DO_SEND_LAST // uncomment to sent last 900 bytes to pc on startup
joeata2wh 1:58f86fb1fb22 30
joeata2wh 0:0cfc06f9f18c 31 AnalogIn waterTempSense(A0);
joeata2wh 0:0cfc06f9f18c 32 AnalogIn oxygenLevelSense(A1);
joeata2wh 0:0cfc06f9f18c 33 AnalogIn solarStrengthSense(A2);
joeata2wh 0:0cfc06f9f18c 34 AnalogIn waterSpeedSense(A3);
joeata2wh 0:0cfc06f9f18c 35 // imagine for sake of example that we have
joeata2wh 0:0cfc06f9f18c 36 // circuitry that actually convert these Volt
joeata2wh 0:0cfc06f9f18c 37 // readings to useful readings.
joeata2wh 0:0cfc06f9f18c 38
joeata2wh 0:0cfc06f9f18c 39 DigitalOut led(LED1);
joeata2wh 1:58f86fb1fb22 40 Serial pc(USBTX, USBRX);
joeata2wh 0:0cfc06f9f18c 41
joeata2wh 1:58f86fb1fb22 42 // simulated process generating log entries
joeata2wh 1:58f86fb1fb22 43 // based on ADC readings.
joeata2wh 1:58f86fb1fb22 44 void generateSimulatedLog(struct DLOG *lgr, int numCycles, float sleepBetween) {
joeata2wh 1:58f86fb1fb22 45 char stateHappy[] = "happy";
joeata2wh 1:58f86fb1fb22 46 char stateSad[] = "sad";
joeata2wh 1:58f86fb1fb22 47 char stateDead[] = "dead fish";
joeata2wh 1:58f86fb1fb22 48 char *currState = stateHappy;
joeata2wh 1:58f86fb1fb22 49 char *lastState = 0;
joeata2wh 1:58f86fb1fb22 50 int cycleCnt;
joeata2wh 1:58f86fb1fb22 51
joeata2wh 1:58f86fb1fb22 52
joeata2wh 1:58f86fb1fb22 53 for(cycleCnt=0; cycleCnt < numCycles; cycleCnt++) {
joeata2wh 0:0cfc06f9f18c 54 float waterTemp = waterTempSense.read() * adc_ref;
joeata2wh 0:0cfc06f9f18c 55 float oxygenLevel = oxygenLevelSense.read() * adc_ref;
joeata2wh 0:0cfc06f9f18c 56 float solarStrength = solarStrengthSense.read() * adc_ref;
joeata2wh 0:0cfc06f9f18c 57 float waterSpeed = waterSpeedSense.read() * adc_ref;
joeata2wh 0:0cfc06f9f18c 58
joeata2wh 1:58f86fb1fb22 59 char tbuff[256];
joeata2wh 1:58f86fb1fb22 60 sprintf(tbuff, "%0.2f,%0.2f,%0.2f,%0.2f", waterTemp, oxygenLevel, solarStrength,waterSpeed);
joeata2wh 1:58f86fb1fb22 61 dlLog(lgr, "sensors",tbuff);
joeata2wh 1:58f86fb1fb22 62 // date and time are automatically recorded with the call.
joeata2wh 1:58f86fb1fb22 63 // time is only recorded to the second granularity and dates
joeata2wh 1:58f86fb1fb22 64 // are actually recorded as separate records when the date changes.
joeata2wh 1:58f86fb1fb22 65
joeata2wh 1:58f86fb1fb22 66 pc.printf("cycleCnt=%d logged %s\r\n", cycleCnt, tbuff);
joeata2wh 1:58f86fb1fb22 67
joeata2wh 1:58f86fb1fb22 68 currState = stateHappy;
joeata2wh 1:58f86fb1fb22 69 if (oxygenLevel < 20)
joeata2wh 1:58f86fb1fb22 70 currState = stateSad;
joeata2wh 1:58f86fb1fb22 71 if (oxygenLevel < 12)
joeata2wh 1:58f86fb1fb22 72 currState = stateDead;
joeata2wh 1:58f86fb1fb22 73
joeata2wh 1:58f86fb1fb22 74 if (currState != lastState) {
joeata2wh 1:58f86fb1fb22 75 // Only generate a new log if the current state has changed
joeata2wh 1:58f86fb1fb22 76 lastState = currState;
joeata2wh 1:58f86fb1fb22 77 sprintf(tbuff,"%s,%0.2f", currState, oxygenLevel);
joeata2wh 1:58f86fb1fb22 78 dlLog(lgr,"state", tbuff);
joeata2wh 1:58f86fb1fb22 79 pc.printf("cycleCnt=%d logged stateChange %s\r\n", cycleCnt, tbuff);
joeata2wh 0:0cfc06f9f18c 80 }
joeata2wh 1:58f86fb1fb22 81 // lgr will internally log time. Whenever the date changes
joeata2wh 1:58f86fb1fb22 82 // it will log a new date records.
joeata2wh 1:58f86fb1fb22 83 led = !led;
joeata2wh 1:58f86fb1fb22 84 wait(sleepBetween);
joeata2wh 0:0cfc06f9f18c 85 }
joeata2wh 0:0cfc06f9f18c 86 }
joeata2wh 1:58f86fb1fb22 87
joeata2wh 3:ee7427ad4745 88 // documentaion on supporting Serial Interupts
joeata2wh 3:ee7427ad4745 89 // https://developer.mbed.org/cookbook/Serial-Interrupts
joeata2wh 3:ee7427ad4745 90
joeata2wh 3:ee7427ad4745 91
joeata2wh 1:58f86fb1fb22 92 int main() {
joeata2wh 1:58f86fb1fb22 93 pc.baud(9600);
joeata2wh 1:58f86fb1fb22 94 pc.printf("\nData Logger Example example\n");
joeata2wh 1:58f86fb1fb22 95 set_time(1459380179);
joeata2wh 1:58f86fb1fb22 96
joeata2wh 1:58f86fb1fb22 97 // NOTE: You need to initialize the clock to some meaningful value
joeata2wh 1:58f86fb1fb22 98 // I use a clock chip but here is a manual setting
joeata2wh 1:58f86fb1fb22 99 time_t seconds = time(NULL);
joeata2wh 1:58f86fb1fb22 100 pc.printf("time set to %s\r\n", ctime(&seconds));
joeata2wh 1:58f86fb1fb22 101
joeata2wh 1:58f86fb1fb22 102 char dlbuff[81]; // buffer used internally by this instance of data log
joeata2wh 1:58f86fb1fb22 103 DataLogChipType dlchip(dataLogMOSIPin, dataLogMISOPin, dataLogCLKPin, dataLogSelectPin);
joeata2wh 1:58f86fb1fb22 104 pc.printf("\r\nInitialized Data Log Chip");
joeata2wh 1:58f86fb1fb22 105 struct DLOG *lgr = dlMake(&dlchip, dlbuff, 80);
joeata2wh 2:74a52e5fd1ed 106
joeata2wh 1:58f86fb1fb22 107 pc.printf("\r\nInitialized Logger\r\n");
joeata2wh 1:58f86fb1fb22 108 pc.printf("logger nextWritePos=%ld", lgr->nextWritePos);
joeata2wh 1:58f86fb1fb22 109
joeata2wh 1:58f86fb1fb22 110 #ifdef DO_LOG_ERASE
joeata2wh 2:74a52e5fd1ed 111 //Erase the log But only if you really want to loose the
joeata2wh 2:74a52e5fd1ed 112 // data
joeata2wh 2:74a52e5fd1ed 113 pc.printf("ERASE LOG");
joeata2wh 2:74a52e5fd1ed 114 dlEraseLog(lgr);
joeata2wh 2:74a52e5fd1ed 115 pc.printf("afer erase logger nextWritePos=%ld", lgr->nextWritePos);
joeata2wh 1:58f86fb1fb22 116 #endif
joeata2wh 1:58f86fb1fb22 117
joeata2wh 2:74a52e5fd1ed 118 #ifdef DO_SEND_MANUAL
joeata2wh 2:74a52e5fd1ed 119 // Manually Read first 1,000 bytes from the log
joeata2wh 2:74a52e5fd1ed 120 // could contain some garbage since it does not
joeata2wh 2:74a52e5fd1ed 121 // pay attention to current log length.
joeata2wh 2:74a52e5fd1ed 122 char rbuff[1001];
joeata2wh 2:74a52e5fd1ed 123 memset(rbuff,1001,0);
joeata2wh 2:74a52e5fd1ed 124 dlchip.readStream(dlFirstLogEntry, rbuff, 1000);
joeata2wh 2:74a52e5fd1ed 125 pc.printf("first 1000 characters from log\r\n%s\r\n", rbuff);
joeata2wh 2:74a52e5fd1ed 126 #endif
joeata2wh 1:58f86fb1fb22 127
joeata2wh 2:74a52e5fd1ed 128 #ifdef DO_SEND_ALL
joeata2wh 2:74a52e5fd1ed 129 // Send Log Contents to Serial port
joeata2wh 2:74a52e5fd1ed 130 // sends from byte zero of the log
joeata2wh 2:74a52e5fd1ed 131 // to byte 100,000 or end of log which ever comes
joeata2wh 2:74a52e5fd1ed 132 // first.
joeata2wh 2:74a52e5fd1ed 133 pc.printf("\r\nSend the contents of log to PC automated way\r\n");
joeata2wh 2:74a52e5fd1ed 134 dlReadSendAll(lgr, &pc);
joeata2wh 2:74a52e5fd1ed 135 pc.printf("\r\nDONE SEND ALL\r\n");
joeata2wh 2:74a52e5fd1ed 136 #endif
joeata2wh 2:74a52e5fd1ed 137
joeata2wh 2:74a52e5fd1ed 138 #ifdef DO_SEND_LAST
joeata2wh 2:74a52e5fd1ed 139 pc.printf("\r\nSend the last 900 bytes\r\n");
joeata2wh 2:74a52e5fd1ed 140 dlReadSendLast(lgr, &pc, 50000);
joeata2wh 2:74a52e5fd1ed 141 pc.printf("\r\nDONE Send Last\r\n");
joeata2wh 2:74a52e5fd1ed 142 #endif
joeata2wh 1:58f86fb1fb22 143
joeata2wh 1:58f86fb1fb22 144
joeata2wh 1:58f86fb1fb22 145 // Record our record headers. These need to match the records we will
joeata2wh 1:58f86fb1fb22 146 // write later. It is OK to change the defintion of the headers as the
joeata2wh 1:58f86fb1fb22 147 // parser will use the correct defenition provided you always record the
joeata2wh 1:58f86fb1fb22 148 // defenition before writing the log entries.
joeata2wh 1:58f86fb1fb22 149 dlLog(lgr, "HEAD\tsensors", "waterTemp.f,oxygenLevel.f,solarStrength.f,waterSpeed.f");
joeata2wh 1:58f86fb1fb22 150 dlLog(lgr, "HEAD\ttstate", "currState.s,oxygenLevel.f");
joeata2wh 1:58f86fb1fb22 151 pc.printf("\r\nWrote record type headers\n");
joeata2wh 1:58f86fb1fb22 152 // by convention I record the record header every time the system starts.
joeata2wh 1:58f86fb1fb22 153 // in future will have a fast way to retrieve active header for each record
joeata2wh 1:58f86fb1fb22 154 // type so you can determine if new header is differnt that exisitng header and
joeata2wh 1:58f86fb1fb22 155 // only record if they chanaged.
joeata2wh 1:58f86fb1fb22 156 // The .f suffix is a hint to down stream parser to parse as a float.
joeata2wh 1:58f86fb1fb22 157 // .f - float, .i - int or long, .s-string, .d - ISO date, .b - byte
joeata2wh 1:58f86fb1fb22 158 // at the very least you should record a new HEAD record the first time
joeata2wh 1:58f86fb1fb22 159 // a given record type is used and every time you add or change the fields or field ordering.
joeata2wh 1:58f86fb1fb22 160
joeata2wh 1:58f86fb1fb22 161 generateSimulatedLog(lgr, 20, 10.0);
joeata2wh 1:58f86fb1fb22 162
joeata2wh 1:58f86fb1fb22 163 while(1) {
joeata2wh 1:58f86fb1fb22 164
joeata2wh 1:58f86fb1fb22 165
joeata2wh 1:58f86fb1fb22 166 }
joeata2wh 1:58f86fb1fb22 167
joeata2wh 3:ee7427ad4745 168
joeata2wh 1:58f86fb1fb22 169
joeata2wh 1:58f86fb1fb22 170 }