Logger to USB stick running on KL25Z

Dependencies:   F401RE-USBHost FastAnalogIn RTC mbed tsi_sensor2 mbed-rtos2

Fork of ReadSendData by Stuart Scott

Homepage for kXX_logger(was kl25x_usb_logger)

2014Nov12 - Added TSI - only works for kl25z, and fails after 4287seconds

Tests sequences for freezing 4287seconds 4287 4287 - Changed to USB write update every two seconds and lasted 4283seconds. Added mbed TSI_sensor - kudos to mbed as it turns out to be complex to do capacitive touch sensing. Kinetis L series Cortex-M0 (kl25z) has simpler capacitive sensing hardware which mbed/TSI_sensor lib uses, but doesn't support Cortex-M4 versions. K20D is older more complex interface. TSS hasn't had an open source support by Freescale in the past however that has changed. Currently I found KDS1.1.1 has a Processor Expert TSSlib that can be imported - but I'm getting a weird compile/include error - and not found any examples of how to make it work.

2014Oct05 - Published simple kl25z_usb_logger

Testing: Version (2:f06fbf86887e) has run over 28days starting 2014Sept07, reading a sensor data every seconds and then on the USB stick open file, write record, close file

This is over 2,419,200seconds ( 28days*24hrs*3600seconds) The stick data reflected 2,447,633 seconds, but only had 1,776,366 data records. After 407,650 data records started missing updates - analysis: the file could not be opened and read to the end in the allocated 1 second, and therefore missed the next ADC deadline.

By the end of the test period with 1,776,366 records on the USB Stick, ever read then write was causing it to miss two/three ADC updates - ie taking longer than two seconds.

Analysis: this was an aggressive test, and normally would not be opening, writing, closing every 1 second. However big plus the file mechanism held up and the program continued to operate, with a graceful failure of a missed ADC readings. The output used JSON, which reads nicely but turns out not to be supported by Excel (2014?) I was using.

Conclusion: Overall the USB Mechanisms and file read is stable. However the USB stack used doesn't have an event based management for stick insertion and removal, or OTG power management . This KL25Z has been a very useful test to prove the configuration of clocks for USB Stack is possible and stable. The target processor being looked at is the MK20 and is similar in clocks, but slightly more comprehensive USB OTG Next step is to investigate the full Freescale Kinetis USB OTG Stack with power management on a custom K20 board, using Kinetis Design Studio, and very low power capability.

Example https://github.com/ErichStyger/mcuoneclipse/tree/master/Examples/FRDM-K20D50M/FRDM-K20_MSD_Host http://mcuoneclipse.com/2014/10/25/usb-cdc-with-the-frdm-k64f-finally/#comment-37727 - Commentary on Freescale USB versions

main.cpp

Committer:
neilh20
Date:
2015-05-19
Revision:
15:91ef840c442c
Parent:
13:729f5ae38c24

File content as of revision 15:91ef840c442c:

/* low power data logger. [FRDM-KL25Z]

Periodically read data from ADC and when present write it in JSON format to USB Stick

Copyright  http://www.apache.org/licenses/LICENSE-2.0
and all subsystems per their own licenses which are believed to be GPL 2.0

[c]Record an analoge value to an internal buffer 
[p]and when a USB stick is plugged in store the values on the USB stick in JSON format
   (currently requires USB stick at reset)
[c]Debug output the events as they happen.
[c]The ticker delay sets the sampling rate. 
[F]Date is set when user sets it, 
[c]or elapsed time used in the beginning 

[F]If OpenSDA USB power supplied, then flash LEDs to indicate activity as follows
[c]1) the Blue led when taking a sample
[c]2) the green led when delivering samples to the USB stick
[F]3)no led shall be on at same time for clear colours

[F] when not doing anything else sleep to conserve power.

 To modify this for your application, assuming a ratiometric input to the Vref=3.3V
 1) Determine physical pins ADC performed on and type of conversion,
 1) Determine the "multiplier" from the incoming 16bit ADC wrt to 3.3V to your units.
 2) Write the output code changing the JSON headers to define your headers, dataConversion and units
 
Thanks to other authors for setting up KL25z for 
* FastAnalogIn Library  mbed.org/users/Sissors/code/FastAnalogIn/
* USB Host mbed.org/users/va009039/code/F401RE-USBHost/
 
 [] 1st three character nomeclature.
[x] indicates implemented state of feature 
[c] - complete and unit tested
[p] - partial in progress or not unit tested
[f] - future idea

Testing: Basic tests performed, see attached file logger_tests.docx

Modifications to Hardware
1) FRDM-KL25z add J21 and R8 per the instruction manual for USB host

Issues: 
1) FRDM-KL25z hardware not designed to switch the USB power so saving power on a connected USB device.
Solution: Add switched 5V power.
2) FRDM-KL25z Hardware not designed for low power accurate wall time clock - wastes power keeping the 8Mhz going.
Solution: add an external low power 32KHz oscilator into hardware pin RTC_IN (An osc is the next step up from a raw Xtal)


*/

#include "mbed.h"
//#include "rtos.h"
#include "rtc_api.h"
#include "FastAnalogIn.h"
#include "USBHostMSD.h"
#include "board_cust.h"


#if defined(TARGET_K20D50M)
#define TsiActive 0

#elif defined (TARGET_KL25Z) || defined (TARGET_KL46Z)
  #define TsiActive 1
  #include "tsi_sensor.h"
  //Cap Sensor B0=CH0, B1=CH6
  #define ELEC0 0
  #define ELEC1 6
  //FRDM_KL25x slider electrodes - TSI0_CH9=PTB16, CH10=B17
//  #define ELEC0 9
//  #define ELEC1 10
   TSIAnalogSlider tsi(ELEC0, ELEC1, 40);
   //TSIElectrode tsiElec0(ELEC0);
   //TSIElectrode tsiElec1(ELEC1);
   //#define TsiRead()  tsi.readDistance() - has problesm
   #define TsiRead()  tsi.readPercentage()
   
#elif defined (TARGET_KL05Z)
  #define TsiActive 0
  #include "TSISensor.h"

  #define ELEC0 9
  #define ELEC1 8
    TSIAnalogSlider tsi(ELEC0, ELEC1, 40);
    //#define TsiRead(void)  tsi.Distance()
#else
  #define TsiActive 0
  #error TARGET NOT DEFINED
#endif


//size of Customer ADC sample  buffer 
#define BuffSize 16  //16 to test, upto kl25z spare ram of about xxxx?, !2000, linker warns if too large, but also fails
#define SAMPLE_WIN 8 // Should be less than BuffSize
#if BuffSize < SAMPLE_WIN
#error Buffsize < SAMPLE_WIN
#endif 
//set the delay used by the ticker
const int sysTockInit_sec = 1; //Test 1, Real life 15minutes or 900sec

using namespace mbed;


//Device Init - per FRDM-KL25Z
DigitalOut ledRed(LED1); 
//DigitalOut ledGrn(LED2); 
PwmOut ledPwmGrn(LED2);
DigitalOut ledBlue(LED3); 
FastAnalogIn ainPinAA(PTC2); ///AnalogAin pin

Serial pc(USBTX, USBRX);

//Samples stored here - needs to be compact as can use a lot of internal ram
 unsigned short sample_u16[BuffSize]; //critical resource for number of samples
 unsigned short publish_u16[BuffSize]; //value published
 unsigned short sampleInst_raw, sampleAvg_raw;
float tsiSlider_dist;
uint32_t elec0p,electr[BuffSize];
//uint32_t elec1p;
 unsigned int time_[BuffSize];


 
 bool UsbPresent=0;
 bool warmBoot = rtc_isenabled(); 
 long lp2i =0;
 bool sysEventA=0; //Local event
 unsigned int tmrE_evt; //Elapsed
 Ticker tock; //periodic tick

     
#if defined(TARGET_K20D50M)
const char meProc[] = "K20D50M";
#elif defined(TARGET_KL25Z)
const char meProc[]= "KL25Z";
#else //#if defined(TARGET_KL46Z)||defined(TARGET_K64F) 
#error Undef processor
#endif //


 


//--------------------------------------------------------------
void tockEvent()
{
    tmrE_evt++;
    sysEventA=1;
}

//--------------------------------------------------------------
/* Process output Header to USB, 
return !0 if valid USB present
*/
 bool outputHeaderUsb(FILE* fpUsb) {
    // JSON {"headings":{"time":{"elapsed":"seconds"},  "adcN":"normalized_to_1.0", "adcV":"Volts"}}
    
    if (!UsbPresent) 
    {
      if (1/*ToDo msd.connect()*/) //Attempt to find a device
      {//USB Event: newly connected so attempt write header
        if (NULL !=(fpUsb = fopen("/usbAa/test1.txt", "a"))) 
        {
            pc.printf("USB File test1.txt opened.\n\r");
            fprintf(fpUsb,"{\"headings\":{\"time\":{\"elapsed\":\"seconds\"},  \"adcN\":\"normalized_to_1.0\", \"adcV\":\"Volts\"}"
#if TsiActive
             "\"tsi\":\"[0-40]\""
#endif
            "}}\n");
            UsbPresent=1;
            fclose(fpUsb); fpUsb=NULL;
        } else {
            //Can't find a file for some reason. Could be USB full or failed software
            pc.printf(" Error: USB File not opened.\n\r");
            UsbPresent=0;
        }
      } //else //Can't find USB device
    } else {
        // UsbPresent and assume headers written
        if (1/*ToDo !msd.connected()*/) 
        {
          pc.printf("  USB Flash drive removed.\n\r");
          UsbPresent=0;
        }
    }
    
    return UsbPresent;
}
//--------------------------------------------------------------
 void outputHeaders(void) {
    //ToDo - outputHeaderUsb(*fp);
    pc.printf("  Time(S)   depthR(norm) Volts Idx %d\n\r",(unsigned int) rtc_read());
}//outputHeaders



//--------------------------------------------------------------
/* Manage the write to USB stick
[FUT] detect USB stick and if not present write it when inserted

JSON output json.org
{key:value, key:value, key:value}
{time:[elapsed:<secs 0-2**32>, epoch2014:<secs 0-2**32>,wall:[date:<>,clock:<>],],
 adcNorm:<float number 0.0-1.0>, 
 adcV:<Volts 0-VRef>
 */
bool manageUsbWrite(FILE* fpUsb) {
#define ADC_NORMALIZE (1.0f/65535.0f)
#define ADC_NORM_VREF 3.3
    float data_n=( ((float)sample_u16[lp2i]) * ADC_NORMALIZE);
    bool writeUsb=0;
    //Future redesign to use USBHALHost::wait_attach()
    if (outputHeaderUsb(fpUsb)) 
    {
      if (NULL !=(fpUsb = fopen("/usbAa/test1.txt", "a"))) 
      {  
#if TsiActive
    //del pc.printf("%10d  %5.4f (%6.4fV) %i\n\r",time_[lp2i],data_n,(ADC_NORM_VREF*data_n),lp2i);
          fprintf(fpUsb, "{\"time\":{\"elapsed\":%10d},\"adcN\":  %5.4f, \"adcV\":%6.4f, \"e0\":%i }\n",time_[lp2i],
          data_n,(ADC_NORM_VREF*data_n), 
          elec0p);
#else
           fprintf(fpUsb, "{\"time\":{\"elapsed\":%10d},\"adcN\":  %5.4f, \"adcV\":%6.4f}\n",time_[lp2i],data_n,(ADC_NORM_VREF*data_n));
#endif
           fclose(fpUsb); fpUsb=NULL;
           writeUsb=1;
       } else {
           //Potential Error
           UsbPresent=0; //Can't say its dissappeared as will cause USB find problem.
           pc.printf("  Error: USB unexpected can't write!!\n\r");
           //writeUsb=0;
       }
    } 
    return writeUsb;
 } 
//--------------------------------------------------------------
/* Manage the sample
- store it 
- and write it to USB stick
[FUT] detect USB stick and if not present write it when inserted

JSON output json.org
{key:value, key:value, key:value}
{time:[elapsed:<secs 0-2**32>, epoch2014:<secs 0-2**32>,wall:[date:<>,clock:<>],],
 adcNorm:<float number 0.0-1.0>, 
 adcV:<Volts 0-VRef>}
*/
void manageSample() {
    float data_n;
    int i_lp,idx;
    int publishInst_raw;
    bool noisey=0;

    /* WaterDepth eTape sensor is noisey - average input for noise and compare against changes by capactive sensor
    * Average published sensor and check if new reading changes by more than 1/300 of fsd if it does check against capacitive sensor and see if it is trending same direction
    */
    publishInst_raw = sample_u16[lp2i]; //over X previous
    sampleInst_raw  = sample_u16[lp2i]; //over X previous
    elec0p = electr[lp2i];
    for (i_lp =1; i_lp< SAMPLE_WIN; i_lp++) {
        idx = lp2i-i_lp;
        if (idx <0) idx+= BuffSize;
        publishInst_raw += publish_u16[i_lp]; //over X previous
        sampleInst_raw += sample_u16[i_lp]; //over X previous
        elec0p += electr[i_lp];
    }
    publishInst_raw /= SAMPLE_WIN;
    sampleInst_raw /= SAMPLE_WIN;
    elec0p /= SAMPLE_WIN;
#if 0 //future
    #define SAMPLES_ABS_CNT 5
    if (SAMPLES_ABS_CNT < abs(sampleInst_raw - publishInst_raw) )  {
        //Need to check if this is noise
        //if ( abs(abs(elec0p) - electr[lp21]);
        noisey = 1;
        }
#endif //0
    data_n=sampleInst_raw * ADC_NORMALIZE;
    pc.printf("\n\r%10d  %5.4f (%6.4fV) %i %i N%i %i",time_[lp2i],data_n,(ADC_NORM_VREF*data_n),
    electr[lp2i],elec0p, noisey,
    lp2i);

    //ledGrn = !(lp2i&0x01);//!ledGrn; syncs with blue led flashing    
    //ledPwmGrn = ((lp2i&0x01) ? (1.0-? ? ?) : 1.0 ); //LEDGrn=1 off, =0 On
    if (lp2i&0x01) {//Only write every two times
        if (1/*ToDo==manageUsbWrite(*fp)*/) {
            ledBlue = !ledBlue; //(lp2i&0x03);
        } else {ledBlue =1;}//off
    }

}//end manageSample

//--------------------------------------------------------------
/* Manage the sample
- take an ADC sample and 
*/
void manageAdcIn(void){
    //Take samples and store in buffer
    if (++lp2i >= BuffSize) lp2i=0;
    sample_u16[lp2i]=ainPinAA.read_u16();
    time_[lp2i]=(unsigned int)tmrE_evt; //TODO - replace with wall time
#if TsiActive
    tsiSlider_dist = TsiRead();
    electr[lp2i]= tsi.getDelta0();
#endif //TsiActive
    
    //elec1p = tsi.getDelta1();    
    //tsiSlider_dist = (float)tsi.readPercentage(); 
       
 }//manageAdcIn
 
 //--------------------------------------------------------------
 void UsbHostMsd_task(void const *) {
     USBHostMSD  msd("usbAa"); //defines the file system access
    int i = 0;
    
    while(1) {
        
        // try to connect a MSD device
        while(!msd.connect()) {
            //Thread::wait(500);
        }
        
        // in a loop, append a file
        // if the device is disconnected, we try to connect it again
        while(1) {
            
            // append a file
            FILE * fp = fopen("/usb/test1.txt", "a");
        
            if (fp != NULL) {
                fprintf(fp, "Hello fun SD Card World: %d!\r\n", i++);
                printf("Goodbye World!\r\n");
                fclose(fp);
            } else {
                printf("FILE == NULL\r\n");
            }
            
            //Thread::wait(500);
        
            // if device disconnected, try to connect again
            if (!msd.connected())
                break;
        }
            
    }
}
/* Input parser from usb - but how large is buffer
  char buffer[128];    
  pc.gets(buffer, 4);
  pc.printf("I got '%s'\n", buffer);  
*/
//--------------------------------------------------------------
int main() {

//TODO: check RCM_SRS0 &RS1
    warmBoot = rtc_isenabled(); 
    if (!warmBoot) {
        //TODO figure out how to manage wall time
        rtc_init(); //Assumes external clock - TODO: options int32Khz  RTC_CLKIN ext32KhzXtal 
        rtc_write(0x0); //Init to some default;
    }

    ledRed =0;//ON
    //ledGrn =1; //off
    ledPwmGrn =1.0; //off
    ledBlue = 1; //on    
    
    //Thread msdTask(UsbHostMsd_task, NULL, osPriorityNormal, 1024 * 4);
    
    wait_ms(5000); 
    //Thread::wait(2000);
    ledRed =1;//Off
    //ledGrn =1; //off
    ledPwmGrn =1.0; //off
    ledBlue = 0; //on
    pc.baud(115200);//Opt 57600

    pc.printf("\n\r%s Logger v0.1 \n\r Sample [time=%dsec, size=%d] Clock=%d\n\r",meProc, sysTockInit_sec,BuffSize,SystemCoreClock);

    outputHeaders();

    tock.attach(&tockEvent,sysTockInit_sec); 

    while (true) {        
     if(sysEventA)
     {
       sysEventA=0;
       manageAdcIn();
       manageSample();
      }//else 
      // TODO go into power down till next event
    }
   
}