MAX32620HSP (MAXREFDES100) RPC Example for Graphical User Interface

Dependencies:   USBDevice

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