This is a mbed 5.2 Release

Dependencies:   USBDevice

Fork of mbed-os-test by Jerry Bradshaw

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers DataLoggingService.cpp Source File

DataLoggingService.cpp

00001 /*******************************************************************************
00002  * Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
00003  *
00004  * Permission is hereby granted, free of charge, to any person obtaining a
00005  * copy of this software and associated documentation files (the "Software"),
00006  * to deal in the Software without restriction, including without limitation
00007  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
00008  * and/or sell copies of the Software, and to permit persons to whom the
00009  * Software is furnished to do so, subject to the following conditions:
00010  *
00011  * The above copyright notice and this permission notice shall be included
00012  * in all copies or substantial portions of the Software.
00013  *
00014  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
00015  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00016  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
00017  * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
00018  * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
00019  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
00020  * OTHER DEALINGS IN THE SOFTWARE.
00021  *
00022  * Except as contained in this notice, the name of Maxim Integrated
00023  * Products, Inc. shall not be used except as stated in the Maxim Integrated
00024  * Products, Inc. Branding Policy.
00025  *
00026  * The mere transfer of this software does not imply any licenses
00027  * of trade secrets, proprietary technology, copyrights, patents,
00028  * trademarks, maskwork rights, or any other form of intellectual
00029  * property whatsoever. Maxim Integrated Products, Inc. retains all
00030  * ownership rights.
00031  *******************************************************************************
00032  */
00033 #include "mbed.h"
00034 #include "Logging.h"
00035 #include "Streaming.h"
00036 #include "RpcServer.h"
00037 #include "S25FS512.h"
00038 #include "BMP280.h"
00039 #include "PacketFifo.h"
00040 #include "DataLoggingService.h"
00041 #include "ServiceNonInterrupt.h"
00042 #include "HspLed.h"
00043 #include "MAX30001_helper.h"
00044 #include "MAX30101_helper.h"
00045 #include "StringInOut.h"
00046 #include "StringHelper.h"
00047 #include "Peripherals.h"
00048 #include "Device_Logging.h"
00049 
00050 /// BMP280 logging object reference
00051 extern Device_Logging *bmp280_Logging;
00052 /// MAX14720 instance 0 logging object reference
00053 extern Device_Logging *MAX30205_0_Logging;
00054 /// MAX14720 instance 1 logging object reference
00055 extern Device_Logging *MAX30205_1_Logging;
00056 
00057 #define PING_PONG_BUFFER_SIZE 512
00058 #define HALF_OF_PING_PONG_BUFFER_SIZE PING_PONG_BUFFER_SIZE / 2
00059 #define MISSION_DEFINITION_SIZE 4096
00060 
00061 eLoggingTrigger loggingTrigger;
00062 
00063 /// buffer where mission strings are stored
00064 char loggingMissionCmds[MISSION_DEFINITION_SIZE];
00065 /// This houses two 256 byte ram concatenated to act as a ping-pong
00066 uint8_t PingPong_SRAM[PING_PONG_BUFFER_SIZE];
00067 uint32_t buttonTrigger = 0;
00068 
00069 eLoggingOutput loggingOutput;
00070 // extern int bleStartCommand;
00071 bool volatile globalFlag;
00072 extern int highDataRate;
00073 static uint32_t currentPage;
00074 static uint32_t sramIndex;
00075 /// flag to indicate that sram buffer 0 is dirty and will need to be flushed
00076 static uint32_t sram_buffer_0_dirty;
00077 /// flag to indicate that sram buffer 1 is dirty and will need to be flushed
00078 static uint32_t sram_buffer_1_dirty;
00079 /// usb byte buffer for sending out a bulk transfer
00080 static uint8_t usb_block[64];
00081 /// running index used to accumulate bytes to send as a block via bulk transfer
00082 static uint16_t usb_block_index = 0;
00083 
00084 typedef enum {
00085   eStartEvent_NULL,
00086   eStartEvent_BLE,
00087   eStartEvent_BUTTON,
00088   eStartEvent_RPC_TO_USB,
00089   eStartEvent_RPC_TO_FLASH
00090 } eStartEvent;
00091 static eStartEvent startEvent;
00092 
00093 /**
00094 * @brief Sets a flag to start USB logging (streaming)
00095 */
00096 void LoggingService_StartLoggingUsb(void) {
00097   loggingTrigger = eTriggerLog_RPC_USB;
00098 }
00099 
00100 /**
00101 * @brief Sets a flag to start flash logging
00102 */
00103 void LoggingService_StartLoggingFlash(void) {
00104   loggingTrigger = eTriggerLog_RPC_FLASH;
00105 }
00106 
00107 /**
00108 * @brief Checks the various logging start condition
00109 * @return 1 if a start condition is true, 0 if there is no start condition
00110 */
00111 static bool _LoggingService_CheckStartCondition(void) {
00112   bool buttonPressed;
00113   buttonPressed = Peripherals::pushButton()->GetButtonFallState();
00114 
00115   // default not logging USB or flash
00116   loggingOutput = eLogToNothing;
00117   startEvent = eStartEvent_NULL;
00118   if (buttonPressed) {
00119     Peripherals::pushButton()->clearButtonFallState();
00120     // a falling state has been detected... wait for a fraction of a second and
00121     // re-read the pin
00122     //   only start datalogging if the pin was released within this wait time
00123     wait(0.75f);
00124     int buttonRead = Peripherals::pushButton()->Read();
00125     // if after a period of time the button is still pressed then get out
00126     if (buttonRead == 0)
00127       return 0;
00128     buttonTrigger = 0;
00129 
00130     loggingTrigger = eTriggerLog_BUTTON;
00131     loggingOutput = eLogToFlash;
00132     startEvent = eStartEvent_BUTTON;
00133     return true;
00134   }
00135   if (loggingTrigger == eTriggerLog_RPC_FLASH) {
00136     loggingOutput = eLogToFlash;
00137     startEvent = eStartEvent_RPC_TO_FLASH;
00138     return true;
00139   }
00140   if (Peripherals::hspBLE()->getStartDataLogging()) {
00141     loggingTrigger = eTriggerLog_BLE;
00142     loggingOutput = eLogToFlash;
00143     startEvent = eStartEvent_BLE;
00144     return true;
00145   }
00146   // check if start is from RPC call for USB streaming
00147   if (loggingTrigger == eTriggerLog_RPC_USB) {
00148     loggingOutput = eLogtoUsb;
00149     startEvent = eStartEvent_RPC_TO_USB;
00150     return true;
00151   }
00152   return false;
00153 }
00154 
00155 /**
00156 * @brief Read the mission string from flash into a buffer
00157 * @return false if a mission was not defined, true if mission was read and
00158 * buffered
00159 */
00160 static bool _LoggingService_ReadMissionFromFlash(void) {
00161   // get mission from flash
00162   Logging_ReadMissionFromFlash((uint8_t *)loggingMissionCmds);
00163   if (Logging_IsMissionDefined((uint8_t *)loggingMissionCmds) == 0) {
00164     return false;
00165   }
00166   printf(loggingMissionCmds);
00167   fflush(stdout);
00168   RPC_ProcessCmds(loggingMissionCmds);
00169   return true;
00170 }
00171 
00172 /**
00173 * @brief Process a RPC command that is pointed to.
00174 * @param cmd RPC string to process
00175 */
00176 void ProcessCmd(char *cmd) {
00177   char cmd_[256];
00178   char reply[512];
00179   strcpy(cmd_, cmd);
00180   RPC_call(cmd_, reply);
00181 }
00182 
00183 /**
00184 * @brief Buffer sensor fifo data in ram buffers, when a ram buffer is full (a
00185 * flash page worth of data is accumulated) then flash that buffer.
00186 *        A buffer ping pong method is used so that one buffer can be flashing as
00187 * the other buffer fills with sensor fifo data.
00188 * @param fifoData Sensor data taken from the fifo to be stored into flash
00189 */
00190 static void _LoggingServer_OutputToFlash(uint32_t fifoData) {
00191   uint32_t index;
00192   char str[128];
00193   uint8_t *ptr;
00194   //
00195   // Log To Flash
00196   //
00197   // i.e. there is data, read one 32-bit size data at a time.
00198   // put the fifo data into the ping-pong SRAM
00199   PingPong_SRAM[sramIndex++] = fifoData & 0xFF; // LSByte goes into index N
00200   PingPong_SRAM[sramIndex++] = (fifoData >> 8) & 0xFF;
00201   PingPong_SRAM[sramIndex++] = (fifoData >> 16) & 0xFF;
00202   PingPong_SRAM[sramIndex++] = (fifoData >> 24) & 0xFF; // MSByte goes into index N+3
00203 
00204   // flag this buffer as dirty
00205   if (sramIndex <= 256)
00206     sram_buffer_0_dirty = 1;
00207   else
00208     sram_buffer_1_dirty = 1;
00209 
00210   if (sramIndex == 256 ||
00211       sramIndex == 512) // Either Ping SRAM or Pong SRAM location is full
00212   {                     // therefore write to Flash
00213 
00214     index = sramIndex - 256;
00215     ptr = &PingPong_SRAM[index];
00216     sprintf(str, "currentPage=%d", currentPage);
00217     Peripherals::s25FS512()->writePage_Helper(currentPage, ptr, 0);
00218 
00219     // this page is no longer dirty
00220     if (index == 0)
00221       sram_buffer_0_dirty = 0;
00222     if (index == 256)
00223       sram_buffer_1_dirty = 0;
00224 
00225     currentPage++;
00226   }
00227   sramIndex = sramIndex % 512; // Wrap around the index
00228 }
00229 
00230 /**
00231 * @brief If flash ram buffers are flagged as dirty, flush to flash
00232 */
00233 static void _LoggingServer_WriteDirtySramBufferToFlash(void) {
00234   uint8_t *ptr = PingPong_SRAM;
00235   if (sram_buffer_0_dirty == 0 && sram_buffer_1_dirty == 0)
00236     return;
00237   if (sram_buffer_0_dirty == 1) {
00238     ptr += 0;
00239   }
00240   if (sram_buffer_1_dirty == 1) {
00241     ptr += 256;
00242   }
00243   printf("_LoggingServer_WriteDirtySramBufferToFlash:%d,%d\n",
00244          sram_buffer_0_dirty, sram_buffer_1_dirty);
00245   fflush(stdout);
00246   // s25fs512_WritePage_Helper(currentPage, ptr, 0);
00247   Peripherals::s25FS512()->writePage_Helper(currentPage, ptr, 0);
00248 }
00249 
00250 /**
00251 * @brief Initialize the USB block running index
00252 * @param fifoData Sensor data taken from the fifo to be sent out USB
00253 */
00254 static void _LoggingServer_OutputToCdcAcm(uint32_t fifoData) {
00255   uint8_t *ptr;
00256   uint8_t str[16];
00257   sprintf((char *)str, "%X ", fifoData);
00258   ptr = str;
00259   usb_block_index = 0;
00260   while (*ptr != 0) {
00261     usb_block[usb_block_index] = *ptr;
00262     ptr++;
00263     usb_block_index++;
00264   }
00265   Peripherals::usbSerial()->writeBlock(usb_block, usb_block_index);
00266 }
00267 
00268 /**
00269 * @brief Initialize the USB block running index
00270 */
00271 static void _LoggingServer_OutputToCdcAcm_Start(void) { usb_block_index = 0; }
00272 
00273 /**
00274 * @brief Buffer up fifoData from sensors, do a USB block transfer if buffer is
00275 * full
00276 * @param fifoData Sensor data taken from the fifo to be send out USB within a
00277 * bulk block transfer
00278 * @return Return the success status of the writeblock operation
00279 */
00280 static bool _LoggingServer_OutputToCdcAcm_Block(uint32_t fifoData) {
00281   uint8_t str[64];
00282   uint8_t *ptr;
00283   bool result;
00284   //
00285   // Log to CDCACM
00286   //
00287   result = true;
00288   sprintf((char *)str, "%X ", fifoData);
00289   ptr = str;
00290   while (*ptr != 0) {
00291     usb_block[usb_block_index] = *ptr;
00292     ptr++;
00293     usb_block_index++;
00294     if (usb_block_index >= 64) {
00295       result = Peripherals::usbSerial()->writeBlock(usb_block, 64);
00296       usb_block_index = 0;
00297     }
00298   }
00299   return result;
00300 }
00301 
00302 /**
00303 * @brief Output a full USB block via bulk transfer
00304 */
00305 static void _LoggingServer_OutputToCdcAcm_End(void) {
00306   if (usb_block_index == 0)
00307     return;
00308   Peripherals::usbSerial()->writeBlock(usb_block, usb_block_index - 1);
00309 }
00310 
00311 /**
00312 * @brief Blink LED pattern that indicates that the flash end boundary has been
00313 * reached
00314 */
00315 static void BlinkEndOfDatalogging(void) {
00316   // blink to signal end of logging
00317   Peripherals::hspLed()->pattern(0x55555555, 20);
00318   wait(2);
00319 }
00320 
00321 /**
00322 * @brief Reads the first data page of flash, if all FF's then the page is empty
00323 * @return 1 if the flash is empty as indicated by the first data page of the
00324 * flash, 0 if not
00325 */
00326 int isFlashEmpty(void) {
00327   int i;
00328   uint8_t data[256];
00329   int firstDataPage = Logging_GetLoggingStartPage();
00330   Peripherals::s25FS512()->readPages_Helper(firstDataPage, firstDataPage, data, 0);
00331   for (i = 0; i < 256; i++) {
00332     if (data[i] != 0xFF)
00333       return 0;
00334   }
00335   return 1;
00336 }
00337 
00338 /**
00339 * @brief Blink LED pattern that indicates that the flash is not empty and a new
00340 * flash logging session can not occur
00341 */
00342 void BlinkFlashNotEmpty(void) {
00343   Peripherals::hspLed()->pattern(0x55555555, 20);
00344   wait(1);
00345 }
00346 
00347 void ExecuteDefaultMission(void) {
00348   ProcessCmd("/MAX30001/CAL_InitStart 01 01 01 03 7FF 00");
00349   ProcessCmd("/MAX30001/ECG_InitStart 01 01 01 00 02 03 1F 0 00 00 01");
00350   ProcessCmd("/MAX30001/RtoR_InitStart 01 03 0F 00 03 01 00 00 01");
00351   ProcessCmd("/MAX30001/Rbias_FMSTR_Init 01 02 01 01 00");
00352   ProcessCmd("/LIS2DH/InitStart 02 01");
00353 }
00354 
00355 void LoggingService_Init(void) { loggingTrigger = eTriggerLog_NULL; }
00356 
00357 /**
00358 * @brief This routine checks to see if a USB or flash logging action needs to be taken
00359 *           The routine checks for a start condition via button press, USB command, or BLE command 
00360 *           Once one of these start conditions is present, the logging begins until stopped or memory is full
00361 * @return 1 if successful, 0 if error or logging was aborted and no logging occurred
00362 */
00363 uint8_t LoggingService_ServiceRoutine(void) {
00364   uint32_t fifoData;
00365   uint32_t endPage;
00366   USBSerial *usbSerial = Peripherals::usbSerial();
00367   // BMP280 *bmp280 = Peripherals::bmp280();
00368   bool buttonPressed;
00369   int packetBurstCount = 0;
00370   HspLed *hspLed = Peripherals::hspLed();
00371 
00372   sramIndex = 0;
00373   // only start logging if conditions exist
00374 
00375     if (_LoggingService_CheckStartCondition() == false) return 0;
00376     printf("Begin Logging...");
00377     if (startEvent == eStartEvent_NULL) printf("eStartEvent_NULL..."); 
00378     if (startEvent == eStartEvent_BLE) printf("eStartEvent_BLE..."); 
00379     if (startEvent == eStartEvent_BUTTON) printf("eStartEvent_BUTTON..."); 
00380     if (startEvent == eStartEvent_RPC_TO_USB) printf("eStartEvent_RPC_TO_USB..."); 
00381     if (startEvent == eStartEvent_RPC_TO_FLASH) printf("eStartEvent_RPC_TO_FLASH..."); 
00382     fflush(stdout);
00383 
00384   // start logging stuttered blink pattern
00385   hspLed->pattern(0xA0F3813, 20);
00386 
00387   if (startEvent == eStartEvent_RPC_TO_FLASH ||
00388       startEvent == eStartEvent_BUTTON) {
00389     // check to see if datalog already in flash... abort and force user to erase
00390     // flash if needed
00391     if (loggingOutput == eLogToFlash) {
00392       if (isFlashEmpty() == 0) {
00393         Logging_SetStart(false);
00394         // bleStartCommand = 0x00;
00395         BlinkFlashNotEmpty();
00396         hspLed->blink(1000);
00397         printf("Abort Logging, flash log exists. ");
00398         fflush(stdout);
00399         return 0;
00400       }
00401     }
00402   }
00403 
00404   if (startEvent == eStartEvent_BLE) {
00405     // check for mission in flash
00406     if (_LoggingService_ReadMissionFromFlash() == false) {
00407       // if there is no mission in flash then do a default mission for the sake
00408       // of ble Android app working "out-of-the-box" and stream RtoR and Accel
00409       printf("No Mission in Flash...ExecuteDefaultMission...");
00410       fflush(stdout);
00411       ExecuteDefaultMission();
00412       // do not log this data
00413       loggingOutput = eLogToNothing;
00414     } else {
00415       // there is a mission in flash check if there is already logged data
00416       if (isFlashEmpty() == 0) {
00417         // just do default mission
00418         printf("Logged Data Detected...ExecuteDefaultMission...");
00419         fflush(stdout);
00420         ExecuteDefaultMission();
00421         // do not log this data
00422         loggingOutput = eLogToNothing;
00423       } else {
00424         // flag that we are logging to flash
00425         loggingOutput = eLogToFlash;
00426       }
00427     }
00428   }
00429 
00430   // if we are logging to flash then read mission in flash
00431   if (loggingOutput == eLogToFlash) {
00432     if (_LoggingService_ReadMissionFromFlash() ==
00433         false) { // if there is no mission in flash then get out
00434       Logging_SetStart(false);
00435       Peripherals::hspLed()->pattern(0xC3C3C3C3, 20);
00436       wait(2);
00437       printf("Abort Logging, Mission does not exist. ");
00438       fflush(stdout);
00439       return 0;
00440     }
00441     currentPage = Logging_GetLoggingStartPage();
00442     endPage = Logging_GetLoggingEndPage();
00443   }
00444 
00445   MAX30001_Helper_SetupInterrupts();
00446   if (MAX30001_AnyStreamingSet() == 1) {
00447     MAX30001_Helper_StartSync();
00448   }
00449 
00450   SetDataLoggingStream(TRUE);
00451   ServiceNonInterrupt_Init();
00452   ServiceNonInterrupt_StartTimer();
00453 
00454   while (usbSerial->readable()) {
00455     usbSerial->_getc();
00456   }
00457   fifo_clear(GetUSBIncomingFifo()); // clear USB serial incoming fifo
00458   fifo_clear(GetStreamOutFifo());
00459 
00460   sram_buffer_0_dirty = 0;
00461   sram_buffer_1_dirty = 0;
00462 
00463 
00464     if (loggingOutput == eLogToNothing) printf("eLogToNothing..."); fflush(stdout);
00465     if (loggingOutput == eLogToFlash) printf("eLogToFlash..."); fflush(stdout);
00466     if (loggingOutput == eLogtoUsb) printf("eLogtoUsb..."); fflush(stdout);
00467     printf("highDataRate=%d...",highDataRate); fflush(stdout);
00468 
00469 
00470   Peripherals::timestampTimer()->reset();
00471   Peripherals::timestampTimer()->start();
00472 
00473   _LoggingServer_OutputToCdcAcm_Start();
00474   while (1) {
00475     if (loggingOutput == eLogToFlash) {
00476       // check if we are at the end of flash
00477       if (currentPage >= endPage) {
00478         BlinkEndOfDatalogging(); // blink for 3 seconds to signal end of logging
00479         break;
00480       }
00481     }
00482 
00483     if (startEvent == eStartEvent_BUTTON) {
00484       buttonPressed = Peripherals::pushButton()->GetButtonFallState();
00485       if (buttonPressed) {
00486         Peripherals::pushButton()->clearButtonFallState();
00487         // if there is a dirty sram buffer... flush it to flash
00488         _LoggingServer_WriteDirtySramBufferToFlash();
00489         BlinkEndOfDatalogging(); // blink for 3 seconds to signal end of logging
00490         break;
00491       }
00492     }
00493 
00494     if (loggingTrigger == eTriggerLog_BLE) {
00495       if (Peripherals::hspBLE()->getStartDataLogging() == false) {
00496         // if there is a dirty sram buffer... flush it to flash
00497         _LoggingServer_WriteDirtySramBufferToFlash();
00498         BlinkEndOfDatalogging(); // blink for 3 seconds to signal end of logging
00499         break;
00500       }
00501     }
00502 
00503     if (startEvent == eStartEvent_RPC_TO_USB ||
00504         startEvent == eStartEvent_RPC_TO_FLASH) {
00505       if (usbSerial->available()) {
00506         if (loggingOutput == eLogToFlash) {
00507           _LoggingServer_WriteDirtySramBufferToFlash();
00508         }
00509         wait(0.2f);
00510         while (usbSerial->available()) {
00511           usbSerial->_getc();
00512         }
00513         fifo_clear(GetUSBIncomingFifo()); // clear USB serial incoming fifo
00514         fifo_clear(GetStreamOutFifo());
00515         break;
00516       }
00517     }
00518 
00519     // check to see if data is available
00520     packetBurstCount = 0;
00521     while (PacketFifo_Empty() == 0) {
00522       if (packetBurstCount >= 100)
00523         break;
00524       fifoData = PacketFifo_GetUint32();
00525       if (loggingOutput == eLogToFlash) {
00526         _LoggingServer_OutputToFlash(fifoData);
00527       }
00528       if (loggingOutput == eLogtoUsb) {
00529         if (highDataRate == 0)
00530           _LoggingServer_OutputToCdcAcm(fifoData);
00531         else
00532           _LoggingServer_OutputToCdcAcm_Block(fifoData);
00533       }
00534       packetBurstCount++;
00535     }
00536 
00537     if (PacketFifo_Empty() != 0) {
00538       Peripherals::ble()->waitForEvent();
00539     }
00540     ServiceNonInterrupt_BMP280(bmp280_Logging);
00541     ServiceNonInterrupt_MAX30205(MAX30205_0_Logging,
00542                                  Peripherals::max30205_top(),
00543                                  PACKET_MAX30205_TEMP_TOP);
00544     ServiceNonInterrupt_MAX30205(MAX30205_1_Logging,
00545                                  Peripherals::max30205_bottom(),
00546                                  PACKET_MAX30205_TEMP_BOTTOM);
00547   }
00548   _LoggingServer_OutputToCdcAcm_End();
00549   printf("End Logging.\n");
00550   fflush(stdout);
00551 
00552   bmp280_Logging->stop();
00553   MAX30205_0_Logging->stop();
00554   MAX30205_1_Logging->stop();
00555   MAX30001_Helper_Stop(); // if any MAX30001 streams have been started, stop
00556                           // them
00557   MAX30101_Helper_Stop(); // if any MAX30101 streams have been started, stop
00558                           // them
00559   Peripherals::lis2dh()->stop();
00560   SetDataLoggingStream(FALSE);
00561   Peripherals::timestampTimer()->stop();
00562   hspLed->blink(1000);
00563   // default to non-usb packet speed optimizing
00564   highDataRate = 0;
00565   loggingTrigger = eTriggerLog_NULL;
00566   return 1;
00567 }