Proivdes data log data structure for FRAM, EPROM chip with functions to read chip and send back on serial data string.

Dependencies:   W25Q80BV multi-serial-command-listener

Dependents:   xj-data-log-test-and-example

Data Logging Data structure

Both Read and write seem to be working fine but testing has been limited.

Motivation

I needed a flexible data log structure that could tolerate evolving data structures as I discovered more things that needed to be measured. I also wanted something that is mostly human readable while remaining sufficiently concise to make efficient use of expensive storage resources.

I found it challenging to track everything needed to perform after the fact analysis we need to improve our state machine. In addition what I wanted to measure changed with time and I needed a robust way to log this data so we could analyze it latter. without breaking or converting all the old data. A self describing data format like JSON or XML would work but FRAM is expensive so I wanted something flexible but still concise.

I am working on A2WH which is a electronic controller for a sophisticated product that balances many sensors, battery charging from photo voltaic panels, controlling speed of many different fans, humidity and environmental data. Our main challenge is we never have enough battery power to run everything so we have to make decisions about what to run in an effort to produce the maximum amount of water from the available solar power resource. Our 2nd challenge is that balancing system actions such as increasing or decreasing fan speeds is driven by a complex internal prediction model that attempts balance many competing thermodynamic requirements. To get all this right requires substantial after the fact analysis and that requires logging a large amount of evolving data.

Design Notes

See: data-log-read.me.txt in the same project

Sample Use and Basic Test

Serial Command Interface

COMMANDS
  readall= send entire contents of log
  readlast 999
     999 = number of bytes from tail of log to retrieve
  tread 333 444
     333 = starting offset to start reading log
     444 = number of bytes to retrieve from log
  erase = erase log and start a new one
  help  = display this help

Other Chips

For legacy reasons I am using the library for "W25Q80BV.h" simply because I started with it. The actual FRAM chip I am using is 2 MBit FRAM MB85RS2MTPH-G-JNE I also tested it with SRAM 23LCV1024-I/P

Simplifying Design Decision

I made a simplifying assumption that every-time we generate a log entry I record the offset of the next write at a specific location in the chip. This works and is fast but it causes lots of updates against a single location. I prefer FRAM because this would rapidly fatigue FLASH chips like the W25Q80BV. Storing this pointer data in the CPU has the same fatigue problem.

Another other option would be to store this offset and our other critical configuration data in the clock chip but it is susceptible to loosing power and loosing this critical data.

One reason I don't log directly to the micro-sd is for the same fatigue problem but it is mostly for power management.

The FRAM chip provides adequate durability and data retention through power outage. The power outage retention is critical because the A2WH systems can be buried under feet of snow in the winter and solar panels do not provide much recharge under that condition.

One design option I have considered but not yet implemented is using a much smaller FRAM chip critical configuration data and rapid update data and then log directly to a larger and less expensive FLASH chip .

Journaling to micro-SD

I latter decided to add features to allow after the fact copying of the data to micro-sd cards to obtain larger log storage without soldering in more chips. I found the micro-sd consume quite a lot of power so I still want to log direct to the FRAM then copy to the micro-sd when I have surplus power available. Still thinking about consolidation tactics to allow re-use of FRAM after the data has been copied ot micro-sd.

Future

  • Support fast indexing by date to only pull back log entries between two dates.
  • Record most recent record headers for each record types where they are fast to access so we can send them with the data when only sending back portions of the data.
  • Support wrap around use of data log to re-use storage on chip.
  • Copy Data to micro SD card and consolidate FRAM chip for re-use.

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:
Wed Apr 13 04:15:43 2016 +0000
Revision:
11:bf816d33be80
Parent:
10:0d1d96ba7279
Added Python parser to convert DLOG format captured from readall command in serial port into CSV format for use in excel an R.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
joeata2wh 1:b2e12bf6b4aa 1 /* DataLog.h - Data logger for logging enviornmental
joeata2wh 1:b2e12bf6b4aa 2 data to EPROM, SRAM or FRAM chip without a file system
joeata2wh 3:5550814cc21c 3 inteface. Supports multiple and evolving records types
joeata2wh 3:5550814cc21c 4 and trnsport back to serial
joeata2wh 3:5550814cc21c 5
joeata2wh 3:5550814cc21c 6 See data-log-text.txt for detailed design and layout notes
joeata2wh 3:5550814cc21c 7 See: xj-data-log-test-and-example.c for example use.
joeata2wh 4:fa5bbe31a039 8 https://developer.mbed.org/users/joeata2wh/code/xj-data-log-test-and-example/wiki/Homepage
joeata2wh 4:fa5bbe31a039 9
joeata2wh 1:b2e12bf6b4aa 10 By Joseph Ellsworth CTO of A2WH
joeata2wh 1:b2e12bf6b4aa 11 Take a look at A2WH.com Producing Water from Air using Solar Energy
joeata2wh 1:b2e12bf6b4aa 12 March-2016 License: https://developer.mbed.org/handbook/MIT-Licence
joeata2wh 1:b2e12bf6b4aa 13 Please contact us http://a2wh.com for help with custom design projects.
joeata2wh 3:5550814cc21c 14
joeata2wh 1:b2e12bf6b4aa 15 Before you complain about not using proper C++ classes, I intend to
joeata2wh 1:b2e12bf6b4aa 16 port the entire A2WH project to PSoc where I may or may not be able to
joeata2wh 1:b2e12bf6b4aa 17 use the full set of C++ features. I am using techniques that should be
joeata2wh 1:b2e12bf6b4aa 18 easier to port to a ANSI C enviornment. You may think this is a wierd
joeata2wh 1:b2e12bf6b4aa 19 decision but the PSoC enviornments gives me transparant support for
joeata2wh 1:b2e12bf6b4aa 20 differential ADC and very low power analog comparators that remain active
joeata2wh 1:b2e12bf6b4aa 21 when CPU is in deep sleep and which can wake the CPU up from deep sleep
joeata2wh 1:b2e12bf6b4aa 22 which makes very low power designs easier. mBed is still weak for this
joeata2wh 1:b2e12bf6b4aa 23 kind of advanced peripherial support.
joeata2wh 1:b2e12bf6b4aa 24
joeata2wh 1:b2e12bf6b4aa 25 */
joeata2wh 1:b2e12bf6b4aa 26 #ifndef DataLog_H
joeata2wh 1:b2e12bf6b4aa 27 #define DataLog_H
joeata2wh 1:b2e12bf6b4aa 28 #include "mbed.h"
joeata2wh 6:5368ef5fced0 29 #include "multi-serial-command-listener.h"
joeata2wh 3:5550814cc21c 30
joeata2wh 1:b2e12bf6b4aa 31 #include "W25Q80BV.h" // Note: We are using this library because we started with it but
joeata2wh 1:b2e12bf6b4aa 32 // the actual chip we are using is a 2 MBit FRAM MB85RS2MTPH-G-JNE
joeata2wh 1:b2e12bf6b4aa 33 // also tested with SRAM 23LCV1024-I/P Prefer SRAM or FRAM because
joeata2wh 1:b2e12bf6b4aa 34 // we made a simplifying assumption that we could write next log
joeata2wh 1:b2e12bf6b4aa 35 // address to the same position over and over. Without wear leveling
joeata2wh 1:b2e12bf6b4aa 36 // this could rapidly wear out a e-prom chip. Could have written
joeata2wh 1:b2e12bf6b4aa 37 // this to the clock chip which uses SRAM but FRAM has such high write
joeata2wh 1:b2e12bf6b4aa 38 // durability that we don't have to.
joeata2wh 1:b2e12bf6b4aa 39
joeata2wh 1:b2e12bf6b4aa 40 #define DataLogChipType W25Q80BV
joeata2wh 1:b2e12bf6b4aa 41
joeata2wh 1:b2e12bf6b4aa 42 // TODO: Add the SDIO link here to copy data from dlog chip to
joeata2wh 1:b2e12bf6b4aa 43 // SD card when available.
joeata2wh 1:b2e12bf6b4aa 44
joeata2wh 2:8d06af2f1fcc 45
joeata2wh 1:b2e12bf6b4aa 46 const long dlChipMaxAddr = 250000; // 2 mBit 2000000 / 8
joeata2wh 1:b2e12bf6b4aa 47 #define dlChipFullErr -2
joeata2wh 1:b2e12bf6b4aa 48 #define dlAddressTooLarge -3
joeata2wh 1:b2e12bf6b4aa 49 #define dlMaxOperationSize -4;
joeata2wh 1:b2e12bf6b4aa 50 // Chip DLog Chip Memory Layout
joeata2wh 9:8b257bf173f0 51 const long dlAddrInitByte = 15000; // data before this is assumed to be used for system config variables
joeata2wh 4:fa5bbe31a039 52 const char dlInitByteValue = 214;
joeata2wh 1:b2e12bf6b4aa 53 const int dlMaxReadWriteSize = 32000; // limit imposed by streaming interface for the chip
joeata2wh 5:286459acee56 54 const long dlAddrNextWritePos = dlAddrInitByte + 1;
joeata2wh 4:fa5bbe31a039 55 const long dlNextWritePosSize = 4;
joeata2wh 4:fa5bbe31a039 56 const long dlAddrCurrYDay = dlAddrNextWritePos + dlNextWritePosSize;
joeata2wh 4:fa5bbe31a039 57 const long dlCurrYDaySize = 2;
joeata2wh 4:fa5bbe31a039 58 const long dlAddrHeaders = dlAddrCurrYDay + dlCurrYDaySize ;
joeata2wh 1:b2e12bf6b4aa 59 const long dlHeadersLen = 256;
joeata2wh 5:286459acee56 60 const long dlAddrDateIndex = dlAddrHeaders + dlHeadersLen + 1;
joeata2wh 5:286459acee56 61 const long dlDateIndexLen = 1000;
joeata2wh 5:286459acee56 62 const long dlAddrFirstLogEntry= dlAddrDateIndex + dlDateIndexLen + 1;
joeata2wh 1:b2e12bf6b4aa 63 const char dlEmpty[] = {0,0,0,0,0,0,0};
joeata2wh 5:286459acee56 64 const long dlMaxLogSize = dlChipMaxAddr - dlAddrFirstLogEntry;
joeata2wh 1:b2e12bf6b4aa 65 #define MIN(X,Y) X <? Y
joeata2wh 1:b2e12bf6b4aa 66 #define MAX(X,Y) X >? Y
joeata2wh 1:b2e12bf6b4aa 67
joeata2wh 1:b2e12bf6b4aa 68 struct DLOG {
joeata2wh 1:b2e12bf6b4aa 69 DataLogChipType *chip;
joeata2wh 1:b2e12bf6b4aa 70 long nextWritePos;
joeata2wh 1:b2e12bf6b4aa 71 int currYDay; // tm_yday from gmtime only log date date when date changes
joeata2wh 1:b2e12bf6b4aa 72 char *buff;
joeata2wh 1:b2e12bf6b4aa 73 int buffLen;
joeata2wh 6:5368ef5fced0 74 Serial *sio;
joeata2wh 6:5368ef5fced0 75 struct SCMD *cmdProc;
joeata2wh 6:5368ef5fced0 76
joeata2wh 1:b2e12bf6b4aa 77 };
joeata2wh 1:b2e12bf6b4aa 78
joeata2wh 8:f73745332754 79 //-- Utility functions for exchanging data with Memory Chip
joeata2wh 8:f73745332754 80 void ecWriteByte(DataLogChipType *chip, long addr, char tin){
joeata2wh 8:f73745332754 81 chip->writeStream(addr, (char *) &tin, sizeof(char));
joeata2wh 8:f73745332754 82 }
joeata2wh 8:f73745332754 83
joeata2wh 8:f73745332754 84 void ecWriteInt(DataLogChipType *chip, long addr, int tin){
joeata2wh 8:f73745332754 85 chip->writeStream(addr, (char *) &tin, sizeof(int));
joeata2wh 8:f73745332754 86 }
joeata2wh 8:f73745332754 87
joeata2wh 8:f73745332754 88 void ecWriteLong(DataLogChipType *chip, long addr, long tin){
joeata2wh 8:f73745332754 89 chip->writeStream(addr, (char *) &tin, sizeof(long));
joeata2wh 8:f73745332754 90 }
joeata2wh 8:f73745332754 91
joeata2wh 8:f73745332754 92 void ecWriteFloat(DataLogChipType *chip, long addr, float tin){
joeata2wh 8:f73745332754 93 chip->writeStream(addr, (char *) &tin, sizeof(float));
joeata2wh 8:f73745332754 94 }
joeata2wh 8:f73745332754 95
joeata2wh 8:f73745332754 96 void ecWriteStr(DataLogChipType *chip, long addr, char *tin){
joeata2wh 8:f73745332754 97 int slen = strlen(tin);
joeata2wh 8:f73745332754 98 chip->writeStream(addr, tin, slen+1);
joeata2wh 8:f73745332754 99 }
joeata2wh 8:f73745332754 100
joeata2wh 8:f73745332754 101 char ecReadByte(DataLogChipType *chip, long addr){
joeata2wh 8:f73745332754 102 char tval;
joeata2wh 8:f73745332754 103 chip->readStream(addr, (char *) &tval, sizeof(char));
joeata2wh 8:f73745332754 104 return tval;
joeata2wh 8:f73745332754 105 }
joeata2wh 8:f73745332754 106
joeata2wh 8:f73745332754 107 int ecReadInt(DataLogChipType *chip, long addr, int tin){
joeata2wh 8:f73745332754 108 int tval;
joeata2wh 8:f73745332754 109 chip->readStream(addr, (char *) &tval, sizeof(int));
joeata2wh 8:f73745332754 110 return tval;
joeata2wh 8:f73745332754 111 }
joeata2wh 8:f73745332754 112
joeata2wh 8:f73745332754 113 long ecReadLong(DataLogChipType *chip, long addr, long tin){
joeata2wh 8:f73745332754 114 long tval;
joeata2wh 8:f73745332754 115 chip->readStream(addr, (char *) &tval, sizeof(long));
joeata2wh 8:f73745332754 116 return tval;
joeata2wh 8:f73745332754 117 }
joeata2wh 8:f73745332754 118
joeata2wh 8:f73745332754 119 float ecReadFloat(DataLogChipType *chip, long addr, float tin){
joeata2wh 8:f73745332754 120 float tval;
joeata2wh 8:f73745332754 121 chip->readStream(addr, (char *) &tval, sizeof(float));
joeata2wh 8:f73745332754 122 return tval;
joeata2wh 8:f73745332754 123 }
joeata2wh 8:f73745332754 124
joeata2wh 8:f73745332754 125
joeata2wh 8:f73745332754 126 // -- BEGIN DATA LOG --
joeata2wh 8:f73745332754 127
joeata2wh 6:5368ef5fced0 128 void dlCommandProc(char *cmd, void *dwrk);
joeata2wh 6:5368ef5fced0 129
joeata2wh 5:286459acee56 130 long dlLen(struct DLOG *wrk) {
joeata2wh 5:286459acee56 131 return wrk->nextWritePos - dlAddrFirstLogEntry;
joeata2wh 5:286459acee56 132 }
joeata2wh 1:b2e12bf6b4aa 133 // save the current nextWritePos to the chip so we hav it
joeata2wh 1:b2e12bf6b4aa 134 // just in case of a reboot
joeata2wh 1:b2e12bf6b4aa 135 void dlSaveNextWritePos(struct DLOG *wrk) {
joeata2wh 4:fa5bbe31a039 136 wrk->chip->writeStream(dlAddrNextWritePos,(char *) &wrk->nextWritePos,dlNextWritePosSize); // write next write postion
joeata2wh 1:b2e12bf6b4aa 137 }
joeata2wh 1:b2e12bf6b4aa 138
joeata2wh 1:b2e12bf6b4aa 139 long dlReadNextWritePos(struct DLOG *wrk) {
joeata2wh 4:fa5bbe31a039 140 wrk->chip->readStream(dlAddrNextWritePos, (char *) &wrk->nextWritePos, dlNextWritePosSize);
joeata2wh 4:fa5bbe31a039 141 //printf("dlReadNextWritePos wrk->nextWritePos=%ld\n\r",wrk->nextWritePos);
joeata2wh 1:b2e12bf6b4aa 142 return wrk->nextWritePos;
joeata2wh 1:b2e12bf6b4aa 143 }
joeata2wh 1:b2e12bf6b4aa 144
joeata2wh 1:b2e12bf6b4aa 145 int dlReadCurrYDay(struct DLOG *wrk) {
joeata2wh 5:286459acee56 146 wrk->chip->readStream(dlAddrCurrYDay, (char *) &wrk->currYDay, dlCurrYDaySize);
joeata2wh 4:fa5bbe31a039 147 //printf("dlReadCurrYDay wrk->currYDay=%d\r\n", wrk->currYDay);
joeata2wh 1:b2e12bf6b4aa 148 return wrk->currYDay;
joeata2wh 1:b2e12bf6b4aa 149 }
joeata2wh 1:b2e12bf6b4aa 150
joeata2wh 1:b2e12bf6b4aa 151 void dlUpdateCurrDate(struct DLOG *wrk, int newYDay) {
joeata2wh 1:b2e12bf6b4aa 152 wrk->currYDay = newYDay;
joeata2wh 4:fa5bbe31a039 153 wrk->chip->writeStream(dlAddrCurrYDay,(char *) &wrk->currYDay, dlCurrYDaySize); // write next write postion
joeata2wh 4:fa5bbe31a039 154 }
joeata2wh 4:fa5bbe31a039 155
joeata2wh 4:fa5bbe31a039 156 // Erase Log from Chip but do not touch
joeata2wh 4:fa5bbe31a039 157 // data on chip outside of log space.
joeata2wh 4:fa5bbe31a039 158 void dlEraseLog(struct DLOG *wrk) {
joeata2wh 4:fa5bbe31a039 159 //printf("dlEraseLogStart\r\n");
joeata2wh 5:286459acee56 160 wrk->nextWritePos = dlAddrFirstLogEntry;
joeata2wh 4:fa5bbe31a039 161 wrk->currYDay = -99;
joeata2wh 5:286459acee56 162 memset(wrk->buff,0,wrk->buffLen);
joeata2wh 4:fa5bbe31a039 163 wrk->chip->writeStream(dlAddrInitByte, (char *) &dlInitByteValue,1); // write init byte
joeata2wh 5:286459acee56 164 wrk->chip->writeStream(dlAddrNextWritePos,(char *) &wrk->nextWritePos,dlNextWritePosSize); // reset the next write postion
joeata2wh 5:286459acee56 165 wrk->chip->writeStream(dlAddrCurrYDay, (char *) &wrk->currYDay, dlCurrYDaySize); // Reset the currYDay
joeata2wh 4:fa5bbe31a039 166 wrk->chip->writeStream(dlAddrHeaders,wrk->buff,MIN(wrk->buffLen,dlHeadersLen)); // nulls over the header region
joeata2wh 5:286459acee56 167 wrk->chip->writeStream(dlAddrDateIndex,wrk->buff,MIN(wrk->buffLen,dlDateIndexLen)); // nulls over the header region
joeata2wh 4:fa5bbe31a039 168 wrk->chip->writeStream(wrk->nextWritePos,wrk->buff,wrk->buffLen); // nulls first of the log
joeata2wh 4:fa5bbe31a039 169 //printf("dlEraseLogDone\r\n");
joeata2wh 1:b2e12bf6b4aa 170 }
joeata2wh 1:b2e12bf6b4aa 171
joeata2wh 1:b2e12bf6b4aa 172 // New data log chip detected write data to initialize it.
joeata2wh 1:b2e12bf6b4aa 173 long dlInitializeChip(struct DLOG *wrk) {
joeata2wh 4:fa5bbe31a039 174 dlEraseLog(wrk);
joeata2wh 1:b2e12bf6b4aa 175 return wrk->nextWritePos;
joeata2wh 1:b2e12bf6b4aa 176 }
joeata2wh 1:b2e12bf6b4aa 177
joeata2wh 1:b2e12bf6b4aa 178
joeata2wh 4:fa5bbe31a039 179
joeata2wh 1:b2e12bf6b4aa 180 /* read a initialization byte from chip. If the byte
joeata2wh 1:b2e12bf6b4aa 181 doesn't contain the expected value then write one
joeata2wh 1:b2e12bf6b4aa 182 and assume that we are starting our log ad the beginning */
joeata2wh 1:b2e12bf6b4aa 183 long dlCheckChipInit(struct DLOG *wrk){
joeata2wh 1:b2e12bf6b4aa 184 wrk->buff[0] = 0;
joeata2wh 4:fa5bbe31a039 185 wrk->chip->readStream(dlAddrInitByte, wrk->buff, 1);
joeata2wh 1:b2e12bf6b4aa 186 if (wrk->buff[0] != dlInitByteValue)
joeata2wh 4:fa5bbe31a039 187 {
joeata2wh 4:fa5bbe31a039 188 //printf("Found empty chip running init");
joeata2wh 1:b2e12bf6b4aa 189 return dlInitializeChip(wrk);
joeata2wh 4:fa5bbe31a039 190 }
joeata2wh 1:b2e12bf6b4aa 191 else {
joeata2wh 4:fa5bbe31a039 192 //printf("Found existing log\r\n");
joeata2wh 1:b2e12bf6b4aa 193 dlReadCurrYDay(wrk);
joeata2wh 1:b2e12bf6b4aa 194 return dlReadNextWritePos(wrk);
joeata2wh 1:b2e12bf6b4aa 195 }
joeata2wh 1:b2e12bf6b4aa 196 }
joeata2wh 1:b2e12bf6b4aa 197
joeata2wh 1:b2e12bf6b4aa 198
joeata2wh 6:5368ef5fced0 199
joeata2wh 1:b2e12bf6b4aa 200 // make and instance of our dlog structure fill it in
joeata2wh 1:b2e12bf6b4aa 201 // in and load any current data such as next write postion
joeata2wh 1:b2e12bf6b4aa 202 // already loaded in the chip.
joeata2wh 6:5368ef5fced0 203 struct DLOG *dlMake(DataLogChipType *dataLogMem, char *buff, short buffLen, Serial *sio) {
joeata2wh 1:b2e12bf6b4aa 204 struct DLOG *tout = (struct DLOG *) malloc(sizeof(struct DLOG));
joeata2wh 1:b2e12bf6b4aa 205 tout->chip = dataLogMem;
joeata2wh 5:286459acee56 206 tout->nextWritePos = dlAddrFirstLogEntry;
joeata2wh 1:b2e12bf6b4aa 207 tout->buff = buff;
joeata2wh 1:b2e12bf6b4aa 208 tout->buffLen = buffLen;
joeata2wh 6:5368ef5fced0 209 tout->sio = sio;
joeata2wh 6:5368ef5fced0 210 tout->cmdProc = scMake(sio, dlCommandProc, tout) ;
joeata2wh 1:b2e12bf6b4aa 211 dlCheckChipInit(tout);
joeata2wh 1:b2e12bf6b4aa 212 return tout;
joeata2wh 1:b2e12bf6b4aa 213 }
joeata2wh 1:b2e12bf6b4aa 214
joeata2wh 4:fa5bbe31a039 215
joeata2wh 1:b2e12bf6b4aa 216 // writes log stream entry to chip and updates the next write
joeata2wh 1:b2e12bf6b4aa 217 // position. Also adds a null terminator to data on chip
joeata2wh 1:b2e12bf6b4aa 218 // log entries should not contain null characters because we
joeata2wh 1:b2e12bf6b4aa 219 // eventually plant to delay flush of nextWritePos and use
joeata2wh 1:b2e12bf6b4aa 220 // scan forware to find the end when a crash occurs.
joeata2wh 1:b2e12bf6b4aa 221 // returns -2 if the write request would go beyond chip size.
joeata2wh 3:5550814cc21c 222 long dlWrite(struct DLOG *wrk, char *aStr) {
joeata2wh 1:b2e12bf6b4aa 223 int slen = strlen(aStr);
joeata2wh 1:b2e12bf6b4aa 224 if ((wrk->nextWritePos + slen) >= dlChipMaxAddr) {
joeata2wh 1:b2e12bf6b4aa 225 return dlChipFullErr;
joeata2wh 1:b2e12bf6b4aa 226 }
joeata2wh 4:fa5bbe31a039 227 wrk->chip->writeStream(wrk->nextWritePos, aStr,slen);
joeata2wh 1:b2e12bf6b4aa 228 wrk->nextWritePos += slen;
joeata2wh 1:b2e12bf6b4aa 229 dlSaveNextWritePos(wrk); // WARNING THIS IS THE LINE THAT WILL KILL EPROM CHIPS
joeata2wh 1:b2e12bf6b4aa 230 // with over-write fatigue.
joeata2wh 1:b2e12bf6b4aa 231 // TODO: add err check read first and last bytes.
joeata2wh 1:b2e12bf6b4aa 232 // compare to what was written.
joeata2wh 1:b2e12bf6b4aa 233 return wrk->nextWritePos;
joeata2wh 1:b2e12bf6b4aa 234 }
joeata2wh 4:fa5bbe31a039 235 char dlLFEmpty[] = "\n\000";
joeata2wh 4:fa5bbe31a039 236 //record log file entry with time stamp and header
joeata2wh 3:5550814cc21c 237 long dlLog(struct DLOG *wrk, char *recType, char *str) {
joeata2wh 4:fa5bbe31a039 238
joeata2wh 4:fa5bbe31a039 239 time_t seconds;
joeata2wh 4:fa5bbe31a039 240 time(&seconds);
joeata2wh 4:fa5bbe31a039 241 //printf("dlLog ctime(&seconds)=%s\r\n", ctime(&seconds));
joeata2wh 4:fa5bbe31a039 242 struct tm *ptm = localtime( &seconds );
joeata2wh 5:286459acee56 243 //printf("dlLog asctime=%s\r\n", asctime(ptm));
joeata2wh 5:286459acee56 244 //printf("dlLog ptm->tm_yday=%d\r\n", ptm->tm_yday );
joeata2wh 1:b2e12bf6b4aa 245 if (ptm->tm_yday != wrk->currYDay) {
joeata2wh 4:fa5bbe31a039 246 int year = 1900 + ptm->tm_year;
joeata2wh 4:fa5bbe31a039 247 int month= ptm->tm_mon + 1;
joeata2wh 4:fa5bbe31a039 248
joeata2wh 1:b2e12bf6b4aa 249 dlUpdateCurrDate(wrk, ptm->tm_yday);
joeata2wh 4:fa5bbe31a039 250 sprintf(wrk->buff,"\n00:00:00 DATE\t%04d-%02d-%02d", year, month, ptm->tm_mday);
joeata2wh 3:5550814cc21c 251 dlWrite(wrk, wrk->buff);
joeata2wh 5:286459acee56 252 //printf("update date dateRec=%s\r\n", wrk->buff);
joeata2wh 1:b2e12bf6b4aa 253 }
joeata2wh 4:fa5bbe31a039 254
joeata2wh 4:fa5bbe31a039 255 memset(wrk->buff,wrk->buffLen,0);
joeata2wh 4:fa5bbe31a039 256 sprintf(wrk->buff,"\n%02d:%02d:%02d %s\t", ptm->tm_hour, ptm->tm_min, ptm->tm_sec, recType);
joeata2wh 4:fa5bbe31a039 257 dlWrite(wrk, wrk->buff);
joeata2wh 4:fa5bbe31a039 258 //dlWrite(wrk, dlLFEmpty); // add terminating lineFeed
joeata2wh 5:286459acee56 259 //printf ("recHead=%s\r\n", wrk->buff);
joeata2wh 4:fa5bbe31a039 260 return dlWrite(wrk, str);
joeata2wh 1:b2e12bf6b4aa 261 }
joeata2wh 1:b2e12bf6b4aa 262
joeata2wh 1:b2e12bf6b4aa 263 // read a block of bytes from log starting at offset
joeata2wh 1:b2e12bf6b4aa 264 // for len bytes placed in buffer. If offset is >
joeata2wh 1:b2e12bf6b4aa 265 // log size then return dlAddressTooLarge if len would
joeata2wh 1:b2e12bf6b4aa 266 // be greate than log size only return that available.
joeata2wh 3:5550814cc21c 267 long dlRead(struct DLOG *wrk, char *buff, long offset, int len) {
joeata2wh 5:286459acee56 268 long addr = dlAddrFirstLogEntry + offset;
joeata2wh 1:b2e12bf6b4aa 269 if ((addr + len) >= dlChipMaxAddr)
joeata2wh 1:b2e12bf6b4aa 270 return dlAddressTooLarge;
joeata2wh 1:b2e12bf6b4aa 271 wrk->chip->readStream(offset, buff, len);
joeata2wh 1:b2e12bf6b4aa 272 return 1;
joeata2wh 1:b2e12bf6b4aa 273 }
joeata2wh 1:b2e12bf6b4aa 274
joeata2wh 3:5550814cc21c 275 long dlReadSend(struct DLOG *wrk, Serial *dest, long offset, long len) {
joeata2wh 5:286459acee56 276 long addr = dlAddrFirstLogEntry + offset;
joeata2wh 1:b2e12bf6b4aa 277 long maxAddr = MIN(addr + len, dlChipMaxAddr); // no overflow past end of chip
joeata2wh 1:b2e12bf6b4aa 278 maxAddr = MIN(maxAddr, wrk->nextWritePos); // no overlow pas end of log
joeata2wh 4:fa5bbe31a039 279 int chunkSize = wrk->buffLen -1;
joeata2wh 5:286459acee56 280 long endAdd = MIN(addr + len, maxAddr);
joeata2wh 5:286459acee56 281 //printf("\r\ndlReadSend addr=%ld offset=%ld len=%ld maxAddr=%ld chunkSize=%d endAdd=%ld nextWritePos=%ld\r\n",
joeata2wh 5:286459acee56 282 // addr, offset, len, maxAddr, chunkSize, endAdd, wrk->nextWritePos);
joeata2wh 1:b2e12bf6b4aa 283 if (addr >= maxAddr)
joeata2wh 1:b2e12bf6b4aa 284 return dlAddressTooLarge;
joeata2wh 5:286459acee56 285 int chunkCnt=0;
joeata2wh 1:b2e12bf6b4aa 286 do {
joeata2wh 5:286459acee56 287 memset(wrk->buff, wrk->buffLen, 0);
joeata2wh 1:b2e12bf6b4aa 288 if (addr + chunkSize > endAdd)
joeata2wh 1:b2e12bf6b4aa 289 chunkSize = endAdd - addr;
joeata2wh 5:286459acee56 290 //printf("\r\n\tdlReadSend addr=%ld endAdd=%ld chunkSize=%ld chunkCnt=%d\r\n", addr, endAdd, chunkSize, chunkCnt);
joeata2wh 1:b2e12bf6b4aa 291 wrk->chip->readStream(addr, wrk->buff, chunkSize);
joeata2wh 5:286459acee56 292 wrk->buff[chunkSize] = 0;
joeata2wh 1:b2e12bf6b4aa 293 dest->printf("%s", wrk->buff);
joeata2wh 5:286459acee56 294 int tlen = strlen(wrk->buff);
joeata2wh 5:286459acee56 295 //printf("\r\n\tdlReadSend tlen=%d\r\n", tlen);
joeata2wh 1:b2e12bf6b4aa 296 addr += chunkSize;
joeata2wh 10:0d1d96ba7279 297 wait(0.06);
joeata2wh 5:286459acee56 298 chunkCnt++;
joeata2wh 1:b2e12bf6b4aa 299 } while (addr < endAdd);
joeata2wh 5:286459acee56 300 return 1;
joeata2wh 1:b2e12bf6b4aa 301 }
joeata2wh 1:b2e12bf6b4aa 302
joeata2wh 5:286459acee56 303 long dlReadSendAll(struct DLOG *wrk, Serial *dest) {
joeata2wh 10:0d1d96ba7279 304 time_t sec = time(NULL);
joeata2wh 10:0d1d96ba7279 305 dest->printf("\r\n\r\nDataLog READ ALL %s logSize=%d\r\n", ctime(&sec),wrk->nextWritePos);
joeata2wh 10:0d1d96ba7279 306 long res = dlReadSend(wrk, dest, 0, wrk->nextWritePos);
joeata2wh 10:0d1d96ba7279 307 dest->printf("\r\n\r\nDataLog FINISHED SEND ALL res=%d\r\n\r\n", res);
joeata2wh 10:0d1d96ba7279 308 return res;
joeata2wh 5:286459acee56 309 }
joeata2wh 5:286459acee56 310
joeata2wh 5:286459acee56 311 long dlReadSendLast(struct DLOG *wrk, Serial *dest, long numSend) {
joeata2wh 10:0d1d96ba7279 312 time_t sec = time(NULL);
joeata2wh 10:0d1d96ba7279 313 dest->printf("\r\n\r\nDataLog Read Send Last %s numSend=%d \r\n", ctime(&sec), numSend);
joeata2wh 5:286459acee56 314 long logLen = dlLen(wrk);
joeata2wh 5:286459acee56 315 long calcLen = MIN(numSend, logLen);
joeata2wh 5:286459acee56 316 long startPos = logLen - calcLen;
joeata2wh 5:286459acee56 317 startPos = MAX(0, startPos);
joeata2wh 9:8b257bf173f0 318 dest->printf("\r\n\r\nRead Send Last numSend=%d logLen=%d, calcLen=%d startPos=%d\r\n",
joeata2wh 9:8b257bf173f0 319 numSend, logLen, calcLen, startPos);
joeata2wh 5:286459acee56 320 //printf("dlReadSendLast logLen=%ld startPos=%ld calcLen=%ld nextWritePos=%ld\r\n ", logLen, startPos,calcLen, wrk->nextWritePos);
joeata2wh 10:0d1d96ba7279 321 long res = dlReadSend(wrk, dest, startPos, calcLen);
joeata2wh 10:0d1d96ba7279 322 printf ("\r\n\r\nfinished sendlast res=%d\r\n", res);
joeata2wh 10:0d1d96ba7279 323 return res;
joeata2wh 5:286459acee56 324 }
joeata2wh 1:b2e12bf6b4aa 325
joeata2wh 6:5368ef5fced0 326
joeata2wh 6:5368ef5fced0 327 void dlHelp(struct DLOG *wrk) {
joeata2wh 6:5368ef5fced0 328 Serial *pc = wrk->sio;
joeata2wh 9:8b257bf173f0 329 pc->printf("\r\n\r\nDataLog Help \r\n");
joeata2wh 6:5368ef5fced0 330 pc->printf("\r\nCOMMANDS\r\n");
joeata2wh 6:5368ef5fced0 331 pc->printf(" readall= send entire contents of log\r\n");
joeata2wh 6:5368ef5fced0 332 pc->printf(" readlast 999\r\n");
joeata2wh 6:5368ef5fced0 333 pc->printf(" 999 = number of bytes from tail of log to retrieve\r\n");
joeata2wh 6:5368ef5fced0 334 pc->printf(" tread 333 444\r\n");
joeata2wh 6:5368ef5fced0 335 pc->printf(" 333 = starting offset to start reading log\r\n");
joeata2wh 6:5368ef5fced0 336 pc->printf(" 444 = number of bytes to retrieve from log\r\n");
joeata2wh 6:5368ef5fced0 337 pc->printf(" erase = erase log and start a new one\r\n");
joeata2wh 6:5368ef5fced0 338 pc->printf(" help = display this help\r\n");
joeata2wh 6:5368ef5fced0 339 }
joeata2wh 6:5368ef5fced0 340
joeata2wh 6:5368ef5fced0 341 // recieve callback from serial console
joeata2wh 6:5368ef5fced0 342 // parse those commands and take approapriate
joeata2wh 6:5368ef5fced0 343 // actions like sending back part of the data
joeata2wh 6:5368ef5fced0 344 // erasing the log etc.
joeata2wh 6:5368ef5fced0 345 void dlCommandProc(char *cmd, void *dwrk) {
joeata2wh 6:5368ef5fced0 346 if (dwrk == NULL) return;
joeata2wh 6:5368ef5fced0 347 struct DLOG *wrk = (struct DLOG *) dwrk;
joeata2wh 6:5368ef5fced0 348 char cmdPrefix[10];
joeata2wh 6:5368ef5fced0 349 long part2 = 0;
joeata2wh 6:5368ef5fced0 350 long part3 = 0;
joeata2wh 6:5368ef5fced0 351 sscanf (cmd,"%s %ld %ld",cmdPrefix, &part2, &part3);
joeata2wh 9:8b257bf173f0 352 wrk->sio->printf("cmd=%s cmdPref=%s part2=%ld part3=%ld\r\n", cmd, cmdPrefix, part2, part3);
joeata2wh 6:5368ef5fced0 353
joeata2wh 6:5368ef5fced0 354 if (strcmp(cmdPrefix,"readall") == 0) {
joeata2wh 6:5368ef5fced0 355 dlReadSendAll(wrk, wrk->sio);
joeata2wh 6:5368ef5fced0 356 }
joeata2wh 6:5368ef5fced0 357 else if (strcmp(cmdPrefix, "readlast") == 0) {
joeata2wh 6:5368ef5fced0 358 dlReadSendLast(wrk, wrk->sio, part2);
joeata2wh 6:5368ef5fced0 359 }
joeata2wh 6:5368ef5fced0 360 else if (strcmp(cmdPrefix, "read") == 0) {
joeata2wh 6:5368ef5fced0 361 dlReadSend(wrk, wrk->sio, part2, part3);
joeata2wh 6:5368ef5fced0 362 }
joeata2wh 6:5368ef5fced0 363 else if (strcmp(cmdPrefix, "erase") == 0) {
joeata2wh 9:8b257bf173f0 364 wrk->sio->printf("\r\n\r\nDataLog Erase \r\n");
joeata2wh 6:5368ef5fced0 365 dlEraseLog(wrk);
joeata2wh 6:5368ef5fced0 366 }
joeata2wh 9:8b257bf173f0 367 else if (strcmp(cmdPrefix, "help") == 0) {
joeata2wh 6:5368ef5fced0 368 dlHelp(wrk);
joeata2wh 6:5368ef5fced0 369 }
joeata2wh 6:5368ef5fced0 370 else {
joeata2wh 6:5368ef5fced0 371 wrk->sio->printf("ERR no matching command cmd=%s\r\n", cmd); // no command matched
joeata2wh 6:5368ef5fced0 372 dlHelp(wrk);
joeata2wh 6:5368ef5fced0 373 }
joeata2wh 6:5368ef5fced0 374 }
joeata2wh 6:5368ef5fced0 375
joeata2wh 1:b2e12bf6b4aa 376 #endif