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 Sep 15 18:33:56 2016 +0000
Revision:
7:3886935f9364
Parent:
5:5fd55c860c09
This version works with STM Nucleo F303K8 but SB16,SB18 must be removed to allow I2C and SPI to work simultaneously on that board

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 7:3886935f9364 9
joeata2wh 7:3886935f9364 10 When using F303K8 and I2C
joeata2wh 7:3886935f9364 11 SB16 and SB18 must be removed because
joeata2wh 7:3886935f9364 12 they link D4 to A4 and D5 to A5
joeata2wh 7:3886935f9364 13 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
joeata2wh 7:3886935f9364 14
joeata2wh 7:3886935f9364 15 FRAM Chip Interface MB85RS2MTPH-G-JNE1
joeata2wh 7:3886935f9364 16 3 MBit FRAM. Also works with WinBond W25Q80BV and Windbond 8MB chips.
joeata2wh 7:3886935f9364 17
joeata2wh 7:3886935f9364 18 #1 - Chip Select
joeata2wh 7:3886935f9364 19 #2 - DO - Data Out hook to MISO
joeata2wh 7:3886935f9364 20 #3 - WP - Write Protect - connect GND
joeata2wh 7:3886935f9364 21 #4 - GND
joeata2wh 7:3886935f9364 22 #5 - DI - Data In - hook to MOSI
joeata2wh 7:3886935f9364 23 #6 - CLk- Clock - Hook to SPI_SCLK
joeata2wh 7:3886935f9364 24 #7 - HD - Hold - connect VCC 3.3V
joeata2wh 7:3886935f9364 25 #8 - VCC - Connect VCC 3.3V
joeata2wh 7:3886935f9364 26
joeata2wh 7:3886935f9364 27 See Also: http://www.instructables.com/id/How-to-Design-with-Discrete-SPI-Flash-Memory/step2/The-WinBond-Device-Interface/
joeata2wh 7:3886935f9364 28
joeata2wh 7:3886935f9364 29 If system seems to return garbage after writting test data
joeata2wh 7:3886935f9364 30 then one or more pins are most likely linked with a solder bridge
joeata2wh 7:3886935f9364 31 check the users guide for the board you are using.
joeata2wh 0:0cfc06f9f18c 32 */
joeata2wh 0:0cfc06f9f18c 33
joeata2wh 0:0cfc06f9f18c 34 #include "mbed.h"
joeata2wh 0:0cfc06f9f18c 35 #include "dataLog.h"
joeata2wh 5:5fd55c860c09 36
joeata2wh 0:0cfc06f9f18c 37 const float adc_ref = 3.3f;
joeata2wh 0:0cfc06f9f18c 38 // SPI PINS
joeata2wh 0:0cfc06f9f18c 39 #define SPI1_SCK PA_5
joeata2wh 0:0cfc06f9f18c 40 #define SPI1_MISO PA_6
joeata2wh 0:0cfc06f9f18c 41 #define SPI1_MOSI PA_7
joeata2wh 0:0cfc06f9f18c 42
joeata2wh 0:0cfc06f9f18c 43 // SPI PINS for Data Log chip
joeata2wh 0:0cfc06f9f18c 44 #define dataLogMISOPin SPI1_MISO
joeata2wh 0:0cfc06f9f18c 45 #define dataLogMOSIPin SPI1_MOSI
joeata2wh 0:0cfc06f9f18c 46 #define dataLogCLKPin SPI1_SCK
joeata2wh 7:3886935f9364 47 #define dataLogSelectPin PA_4
joeata2wh 0:0cfc06f9f18c 48
joeata2wh 1:58f86fb1fb22 49 //#define DO_LOG_ERASE // uncomment to erase the existing log.
joeata2wh 2:74a52e5fd1ed 50 //#define DO_SEND_MANUAL // uncomment to send first 1000 bytes to pc on startup
joeata2wh 2:74a52e5fd1ed 51 //#define DO_SEND_ALL // uncomment to send entire log to PC on startup
joeata2wh 7:3886935f9364 52 //#define DO_SEND_LAST // uncomment to send last 900 bytes to pc on startup
joeata2wh 7:3886935f9364 53 //#define TEST_BASIC_READ_WRITE // uncomment to erase chip and test basic read write interface
joeata2wh 1:58f86fb1fb22 54
joeata2wh 0:0cfc06f9f18c 55 AnalogIn waterTempSense(A0);
joeata2wh 0:0cfc06f9f18c 56 AnalogIn oxygenLevelSense(A1);
joeata2wh 0:0cfc06f9f18c 57 AnalogIn solarStrengthSense(A2);
joeata2wh 7:3886935f9364 58
joeata2wh 0:0cfc06f9f18c 59 // imagine for sake of example that we have
joeata2wh 0:0cfc06f9f18c 60 // circuitry that actually convert these Volt
joeata2wh 0:0cfc06f9f18c 61 // readings to useful readings.
joeata2wh 0:0cfc06f9f18c 62
joeata2wh 0:0cfc06f9f18c 63 DigitalOut led(LED1);
joeata2wh 1:58f86fb1fb22 64 Serial pc(USBTX, USBRX);
joeata2wh 0:0cfc06f9f18c 65
joeata2wh 1:58f86fb1fb22 66 // simulated process generating log entries
joeata2wh 1:58f86fb1fb22 67 // based on ADC readings.
joeata2wh 1:58f86fb1fb22 68 void generateSimulatedLog(struct DLOG *lgr, int numCycles, float sleepBetween) {
joeata2wh 1:58f86fb1fb22 69 char stateHappy[] = "happy";
joeata2wh 1:58f86fb1fb22 70 char stateSad[] = "sad";
joeata2wh 1:58f86fb1fb22 71 char stateDead[] = "dead fish";
joeata2wh 1:58f86fb1fb22 72 char *currState = stateHappy;
joeata2wh 1:58f86fb1fb22 73 char *lastState = 0;
joeata2wh 1:58f86fb1fb22 74 int cycleCnt;
joeata2wh 1:58f86fb1fb22 75
joeata2wh 1:58f86fb1fb22 76
joeata2wh 1:58f86fb1fb22 77 for(cycleCnt=0; cycleCnt < numCycles; cycleCnt++) {
joeata2wh 0:0cfc06f9f18c 78 float waterTemp = waterTempSense.read() * adc_ref;
joeata2wh 0:0cfc06f9f18c 79 float oxygenLevel = oxygenLevelSense.read() * adc_ref;
joeata2wh 0:0cfc06f9f18c 80 float solarStrength = solarStrengthSense.read() * adc_ref;
joeata2wh 7:3886935f9364 81 //float waterSpeed = waterSpeedSense.read() * adc_ref;
joeata2wh 0:0cfc06f9f18c 82
joeata2wh 1:58f86fb1fb22 83 char tbuff[256];
joeata2wh 7:3886935f9364 84 sprintf(tbuff, "%0.2f,%0.2f,%0.2f", waterTemp, oxygenLevel, solarStrength);
joeata2wh 1:58f86fb1fb22 85 dlLog(lgr, "sensors",tbuff);
joeata2wh 1:58f86fb1fb22 86 // date and time are automatically recorded with the call.
joeata2wh 1:58f86fb1fb22 87 // time is only recorded to the second granularity and dates
joeata2wh 1:58f86fb1fb22 88 // are actually recorded as separate records when the date changes.
joeata2wh 1:58f86fb1fb22 89
joeata2wh 1:58f86fb1fb22 90 pc.printf("cycleCnt=%d logged %s\r\n", cycleCnt, tbuff);
joeata2wh 1:58f86fb1fb22 91
joeata2wh 1:58f86fb1fb22 92 currState = stateHappy;
joeata2wh 1:58f86fb1fb22 93 if (oxygenLevel < 20)
joeata2wh 1:58f86fb1fb22 94 currState = stateSad;
joeata2wh 1:58f86fb1fb22 95 if (oxygenLevel < 12)
joeata2wh 1:58f86fb1fb22 96 currState = stateDead;
joeata2wh 1:58f86fb1fb22 97
joeata2wh 1:58f86fb1fb22 98 if (currState != lastState) {
joeata2wh 1:58f86fb1fb22 99 // Only generate a new log if the current state has changed
joeata2wh 1:58f86fb1fb22 100 lastState = currState;
joeata2wh 1:58f86fb1fb22 101 sprintf(tbuff,"%s,%0.2f", currState, oxygenLevel);
joeata2wh 1:58f86fb1fb22 102 dlLog(lgr,"state", tbuff);
joeata2wh 1:58f86fb1fb22 103 pc.printf("cycleCnt=%d logged stateChange %s\r\n", cycleCnt, tbuff);
joeata2wh 0:0cfc06f9f18c 104 }
joeata2wh 1:58f86fb1fb22 105 // lgr will internally log time. Whenever the date changes
joeata2wh 1:58f86fb1fb22 106 // it will log a new date records.
joeata2wh 1:58f86fb1fb22 107 led = !led;
joeata2wh 1:58f86fb1fb22 108 wait(sleepBetween);
joeata2wh 0:0cfc06f9f18c 109 }
joeata2wh 0:0cfc06f9f18c 110 }
joeata2wh 1:58f86fb1fb22 111
joeata2wh 3:ee7427ad4745 112 // documentaion on supporting Serial Interupts
joeata2wh 3:ee7427ad4745 113 // https://developer.mbed.org/cookbook/Serial-Interrupts
joeata2wh 3:ee7427ad4745 114
joeata2wh 3:ee7427ad4745 115
joeata2wh 1:58f86fb1fb22 116 int main() {
joeata2wh 1:58f86fb1fb22 117 pc.baud(9600);
joeata2wh 1:58f86fb1fb22 118 pc.printf("\nData Logger Example example\n");
joeata2wh 1:58f86fb1fb22 119 set_time(1459380179);
joeata2wh 1:58f86fb1fb22 120
joeata2wh 1:58f86fb1fb22 121 // NOTE: You need to initialize the clock to some meaningful value
joeata2wh 1:58f86fb1fb22 122 // I use a clock chip but here is a manual setting
joeata2wh 1:58f86fb1fb22 123 time_t seconds = time(NULL);
joeata2wh 1:58f86fb1fb22 124 pc.printf("time set to %s\r\n", ctime(&seconds));
joeata2wh 1:58f86fb1fb22 125
joeata2wh 1:58f86fb1fb22 126 char dlbuff[81]; // buffer used internally by this instance of data log
joeata2wh 1:58f86fb1fb22 127 DataLogChipType dlchip(dataLogMOSIPin, dataLogMISOPin, dataLogCLKPin, dataLogSelectPin);
joeata2wh 1:58f86fb1fb22 128 pc.printf("\r\nInitialized Data Log Chip");
joeata2wh 7:3886935f9364 129
joeata2wh 2:74a52e5fd1ed 130
joeata2wh 7:3886935f9364 131 #ifdef TEST_BASIC_READ_WRITE
joeata2wh 7:3886935f9364 132 printf("start basic write test\r\n");
joeata2wh 7:3886935f9364 133 printf("chipErase\r\n");
joeata2wh 7:3886935f9364 134 dlchip.chipErase();
joeata2wh 7:3886935f9364 135 memset(dlbuff,0,30);
joeata2wh 7:3886935f9364 136 memcpy(dlbuff, "I am a test\r\n\r\n\r\n",17);
joeata2wh 7:3886935f9364 137 dlchip.writeStream(100,dlbuff, 17);
joeata2wh 7:3886935f9364 138 memset(dlbuff,0,30);
joeata2wh 7:3886935f9364 139 dlchip.readStream(100, dlbuff, 17);
joeata2wh 7:3886935f9364 140 printf("read buffer =%s\r\n", dlbuff);
joeata2wh 7:3886935f9364 141 #endif
joeata2wh 7:3886935f9364 142
joeata2wh 7:3886935f9364 143 struct DLOG *lgr = dlMake(&dlchip, dlbuff, 80, &pc);
joeata2wh 1:58f86fb1fb22 144 pc.printf("\r\nInitialized Logger\r\n");
joeata2wh 7:3886935f9364 145 pc.printf("logger nextWritePos=%ld", lgr->nextWritePos);
joeata2wh 1:58f86fb1fb22 146
joeata2wh 5:5fd55c860c09 147 dlHelp(lgr); // send data log command help to pc.
joeata2wh 1:58f86fb1fb22 148 #ifdef DO_LOG_ERASE
joeata2wh 2:74a52e5fd1ed 149 //Erase the log But only if you really want to loose the
joeata2wh 2:74a52e5fd1ed 150 // data
joeata2wh 2:74a52e5fd1ed 151 pc.printf("ERASE LOG");
joeata2wh 2:74a52e5fd1ed 152 dlEraseLog(lgr);
joeata2wh 2:74a52e5fd1ed 153 pc.printf("afer erase logger nextWritePos=%ld", lgr->nextWritePos);
joeata2wh 1:58f86fb1fb22 154 #endif
joeata2wh 1:58f86fb1fb22 155
joeata2wh 2:74a52e5fd1ed 156 #ifdef DO_SEND_MANUAL
joeata2wh 2:74a52e5fd1ed 157 // Manually Read first 1,000 bytes from the log
joeata2wh 2:74a52e5fd1ed 158 // could contain some garbage since it does not
joeata2wh 2:74a52e5fd1ed 159 // pay attention to current log length.
joeata2wh 2:74a52e5fd1ed 160 char rbuff[1001];
joeata2wh 2:74a52e5fd1ed 161 memset(rbuff,1001,0);
joeata2wh 2:74a52e5fd1ed 162 dlchip.readStream(dlFirstLogEntry, rbuff, 1000);
joeata2wh 2:74a52e5fd1ed 163 pc.printf("first 1000 characters from log\r\n%s\r\n", rbuff);
joeata2wh 2:74a52e5fd1ed 164 #endif
joeata2wh 1:58f86fb1fb22 165
joeata2wh 2:74a52e5fd1ed 166 #ifdef DO_SEND_ALL
joeata2wh 2:74a52e5fd1ed 167 // Send Log Contents to Serial port
joeata2wh 2:74a52e5fd1ed 168 // sends from byte zero of the log
joeata2wh 2:74a52e5fd1ed 169 // to byte 100,000 or end of log which ever comes
joeata2wh 2:74a52e5fd1ed 170 // first.
joeata2wh 2:74a52e5fd1ed 171 pc.printf("\r\nSend the contents of log to PC automated way\r\n");
joeata2wh 2:74a52e5fd1ed 172 dlReadSendAll(lgr, &pc);
joeata2wh 2:74a52e5fd1ed 173 pc.printf("\r\nDONE SEND ALL\r\n");
joeata2wh 2:74a52e5fd1ed 174 #endif
joeata2wh 2:74a52e5fd1ed 175
joeata2wh 2:74a52e5fd1ed 176 #ifdef DO_SEND_LAST
joeata2wh 2:74a52e5fd1ed 177 pc.printf("\r\nSend the last 900 bytes\r\n");
joeata2wh 2:74a52e5fd1ed 178 dlReadSendLast(lgr, &pc, 50000);
joeata2wh 2:74a52e5fd1ed 179 pc.printf("\r\nDONE Send Last\r\n");
joeata2wh 2:74a52e5fd1ed 180 #endif
joeata2wh 1:58f86fb1fb22 181
joeata2wh 1:58f86fb1fb22 182
joeata2wh 1:58f86fb1fb22 183 // Record our record headers. These need to match the records we will
joeata2wh 1:58f86fb1fb22 184 // write later. It is OK to change the defintion of the headers as the
joeata2wh 1:58f86fb1fb22 185 // parser will use the correct defenition provided you always record the
joeata2wh 1:58f86fb1fb22 186 // defenition before writing the log entries.
joeata2wh 1:58f86fb1fb22 187 dlLog(lgr, "HEAD\tsensors", "waterTemp.f,oxygenLevel.f,solarStrength.f,waterSpeed.f");
joeata2wh 1:58f86fb1fb22 188 dlLog(lgr, "HEAD\ttstate", "currState.s,oxygenLevel.f");
joeata2wh 1:58f86fb1fb22 189 pc.printf("\r\nWrote record type headers\n");
joeata2wh 1:58f86fb1fb22 190 // by convention I record the record header every time the system starts.
joeata2wh 1:58f86fb1fb22 191 // in future will have a fast way to retrieve active header for each record
joeata2wh 1:58f86fb1fb22 192 // type so you can determine if new header is differnt that exisitng header and
joeata2wh 1:58f86fb1fb22 193 // only record if they chanaged.
joeata2wh 1:58f86fb1fb22 194 // The .f suffix is a hint to down stream parser to parse as a float.
joeata2wh 1:58f86fb1fb22 195 // .f - float, .i - int or long, .s-string, .d - ISO date, .b - byte
joeata2wh 1:58f86fb1fb22 196 // at the very least you should record a new HEAD record the first time
joeata2wh 1:58f86fb1fb22 197 // a given record type is used and every time you add or change the fields or field ordering.
joeata2wh 1:58f86fb1fb22 198
joeata2wh 1:58f86fb1fb22 199 generateSimulatedLog(lgr, 20, 10.0);
joeata2wh 1:58f86fb1fb22 200
joeata2wh 1:58f86fb1fb22 201 while(1) {
joeata2wh 1:58f86fb1fb22 202
joeata2wh 1:58f86fb1fb22 203
joeata2wh 1:58f86fb1fb22 204 }
joeata2wh 1:58f86fb1fb22 205
joeata2wh 3:ee7427ad4745 206
joeata2wh 1:58f86fb1fb22 207
joeata2wh 1:58f86fb1fb22 208 }