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.
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@5:5fd55c860c09, 2016-03-31 (annotated)
- Committer:
- joeata2wh
- Date:
- Thu Mar 31 22:05:19 2016 +0000
- Revision:
- 5:5fd55c860c09
- Parent:
- 3:ee7427ad4745
- Child:
- 7:3886935f9364
adding command listener support to data_log
Who changed what in which revision?
User | Revision | Line number | New 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 | 5:5fd55c860c09 | 13 | |
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 | 5:5fd55c860c09 | 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 | 5:5fd55c860c09 | 105 | struct DLOG *lgr = dlMake(&dlchip, dlbuff, 80, &pc); |
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 | 5:5fd55c860c09 | 110 | dlHelp(lgr); // send data log command help to pc. |
joeata2wh | 1:58f86fb1fb22 | 111 | #ifdef DO_LOG_ERASE |
joeata2wh | 2:74a52e5fd1ed | 112 | //Erase the log But only if you really want to loose the |
joeata2wh | 2:74a52e5fd1ed | 113 | // data |
joeata2wh | 2:74a52e5fd1ed | 114 | pc.printf("ERASE LOG"); |
joeata2wh | 2:74a52e5fd1ed | 115 | dlEraseLog(lgr); |
joeata2wh | 2:74a52e5fd1ed | 116 | pc.printf("afer erase logger nextWritePos=%ld", lgr->nextWritePos); |
joeata2wh | 1:58f86fb1fb22 | 117 | #endif |
joeata2wh | 1:58f86fb1fb22 | 118 | |
joeata2wh | 2:74a52e5fd1ed | 119 | #ifdef DO_SEND_MANUAL |
joeata2wh | 2:74a52e5fd1ed | 120 | // Manually Read first 1,000 bytes from the log |
joeata2wh | 2:74a52e5fd1ed | 121 | // could contain some garbage since it does not |
joeata2wh | 2:74a52e5fd1ed | 122 | // pay attention to current log length. |
joeata2wh | 2:74a52e5fd1ed | 123 | char rbuff[1001]; |
joeata2wh | 2:74a52e5fd1ed | 124 | memset(rbuff,1001,0); |
joeata2wh | 2:74a52e5fd1ed | 125 | dlchip.readStream(dlFirstLogEntry, rbuff, 1000); |
joeata2wh | 2:74a52e5fd1ed | 126 | pc.printf("first 1000 characters from log\r\n%s\r\n", rbuff); |
joeata2wh | 2:74a52e5fd1ed | 127 | #endif |
joeata2wh | 1:58f86fb1fb22 | 128 | |
joeata2wh | 2:74a52e5fd1ed | 129 | #ifdef DO_SEND_ALL |
joeata2wh | 2:74a52e5fd1ed | 130 | // Send Log Contents to Serial port |
joeata2wh | 2:74a52e5fd1ed | 131 | // sends from byte zero of the log |
joeata2wh | 2:74a52e5fd1ed | 132 | // to byte 100,000 or end of log which ever comes |
joeata2wh | 2:74a52e5fd1ed | 133 | // first. |
joeata2wh | 2:74a52e5fd1ed | 134 | pc.printf("\r\nSend the contents of log to PC automated way\r\n"); |
joeata2wh | 2:74a52e5fd1ed | 135 | dlReadSendAll(lgr, &pc); |
joeata2wh | 2:74a52e5fd1ed | 136 | pc.printf("\r\nDONE SEND ALL\r\n"); |
joeata2wh | 2:74a52e5fd1ed | 137 | #endif |
joeata2wh | 2:74a52e5fd1ed | 138 | |
joeata2wh | 2:74a52e5fd1ed | 139 | #ifdef DO_SEND_LAST |
joeata2wh | 2:74a52e5fd1ed | 140 | pc.printf("\r\nSend the last 900 bytes\r\n"); |
joeata2wh | 2:74a52e5fd1ed | 141 | dlReadSendLast(lgr, &pc, 50000); |
joeata2wh | 2:74a52e5fd1ed | 142 | pc.printf("\r\nDONE Send Last\r\n"); |
joeata2wh | 2:74a52e5fd1ed | 143 | #endif |
joeata2wh | 1:58f86fb1fb22 | 144 | |
joeata2wh | 1:58f86fb1fb22 | 145 | |
joeata2wh | 1:58f86fb1fb22 | 146 | // Record our record headers. These need to match the records we will |
joeata2wh | 1:58f86fb1fb22 | 147 | // write later. It is OK to change the defintion of the headers as the |
joeata2wh | 1:58f86fb1fb22 | 148 | // parser will use the correct defenition provided you always record the |
joeata2wh | 1:58f86fb1fb22 | 149 | // defenition before writing the log entries. |
joeata2wh | 1:58f86fb1fb22 | 150 | dlLog(lgr, "HEAD\tsensors", "waterTemp.f,oxygenLevel.f,solarStrength.f,waterSpeed.f"); |
joeata2wh | 1:58f86fb1fb22 | 151 | dlLog(lgr, "HEAD\ttstate", "currState.s,oxygenLevel.f"); |
joeata2wh | 1:58f86fb1fb22 | 152 | pc.printf("\r\nWrote record type headers\n"); |
joeata2wh | 1:58f86fb1fb22 | 153 | // by convention I record the record header every time the system starts. |
joeata2wh | 1:58f86fb1fb22 | 154 | // in future will have a fast way to retrieve active header for each record |
joeata2wh | 1:58f86fb1fb22 | 155 | // type so you can determine if new header is differnt that exisitng header and |
joeata2wh | 1:58f86fb1fb22 | 156 | // only record if they chanaged. |
joeata2wh | 1:58f86fb1fb22 | 157 | // The .f suffix is a hint to down stream parser to parse as a float. |
joeata2wh | 1:58f86fb1fb22 | 158 | // .f - float, .i - int or long, .s-string, .d - ISO date, .b - byte |
joeata2wh | 1:58f86fb1fb22 | 159 | // at the very least you should record a new HEAD record the first time |
joeata2wh | 1:58f86fb1fb22 | 160 | // a given record type is used and every time you add or change the fields or field ordering. |
joeata2wh | 1:58f86fb1fb22 | 161 | |
joeata2wh | 1:58f86fb1fb22 | 162 | generateSimulatedLog(lgr, 20, 10.0); |
joeata2wh | 1:58f86fb1fb22 | 163 | |
joeata2wh | 1:58f86fb1fb22 | 164 | while(1) { |
joeata2wh | 1:58f86fb1fb22 | 165 | |
joeata2wh | 1:58f86fb1fb22 | 166 | |
joeata2wh | 1:58f86fb1fb22 | 167 | } |
joeata2wh | 1:58f86fb1fb22 | 168 | |
joeata2wh | 3:ee7427ad4745 | 169 | |
joeata2wh | 1:58f86fb1fb22 | 170 | |
joeata2wh | 1:58f86fb1fb22 | 171 | } |