/****************************************************************************/
/* Copyright 2011 MBARI.                                                    */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
#include <p24fxxxx.h>
#include <stdio.h>
#include <string.h>

#include "tasks.h"
#include "analog.h"
#include "serial.h"
#include "ex_rtc.h"
#include "data_flash.h"
#include "sys_timer.h"
#include "sys_defs.h"

/* timelapse scheduler states */
#define TL_SCHED_WAITING        1
#define TL_SCHED_RUNNING        2
#define TL_SCHED_STOPPED        3

/* image capture states */
#define TL_IMAGE_START          1
#define TL_IMAGE_POWER_1_WAIT   2
#define TL_IMAGE_POWER_2_WAIT   3
#define TL_IMAGE_PHOTO_WRITE    4
#define TL_IMAGE_PHOTO_WAIT     5
#define TL_IMAGE_LANC_OFF       6
#define TL_IMAGE_POWER_DOWN     7
#define TL_IMAGE_READY          8
             
/* video capture states */
#define TL_VIDEO_START          1
#define TL_VIDEO_POWER_1_WAIT   2
#define TL_VIDEO_POWER_2_WAIT   3
#define TL_VIDEO_RECORD         4
#define TL_VIDEO_RECORD_WAIT    5
#define TL_VIDEO_RECORD_STOP    6
#define TL_VIDEO_LANC_OFF       7
#define TL_VIDEO_POWER_DOWN     8
#define TL_VIDEO_READY          9

/* battery monitor states */
#define TL_B_MON_INIT           1
#define TL_B_MON_LOW            2
#define TL_B_MON_NORMAL         3
#define TL_B_MON_STOPPED        4

/* battery monitor time out */
#define TL_LOW_BATT_TIME        30000

/* camera delay times in ms */
#define TL_CAM_POWER_UP_1        5000
#define TL_CAM_POWER_UP_2       20000

#define TL_PHOTO_WRITE_TIME     20000
#define TL_VIDEO_STOP_TIME      10000

#define TL_CAM_POWER_DOWN       10000

/* time conversion helpers */
#define MIN_TO_SEC  60L
#define SEC_TO_MS   1000L
#define MIN_TO_MS   60000L

/* time lapse timers and state variables */
static unsigned long timeLapseSchedWait = 0;
static int timeLapseSchedState = TL_SCHED_WAITING;

static Timer timeLapseImageTimer;
static int timeLapseImageState = TL_IMAGE_READY;

static Timer timeLapseVideoTimer;
static int timeLapseVideoState = TL_VIDEO_READY;

static Timer timeLapseBattMonTimer;
static int timeLapseBattMonState = TL_B_MON_INIT;

/* time lapse config variables */
static int tskCaptureMode = CAPTURE_MODE_DEF;
static unsigned long tskStartOfDeploy = START_DEPLOY_DEF;
static unsigned long tskLenOfDeploy = LEN_DEPLOY_DEF;
static unsigned long tskSampleInterval = SAMPLE_INT_DEF;
static unsigned long tskLenOfVideo = VIDEO_LEN_DEF;
static unsigned long tskBattLevel = BATT_LEVEL_DEF;

/* time lapse local funcs */
void timeLapseSchedTask();
void timeLapseImageTask();
void timeLapseVideoTask();
void timeLapseBattMonTask();

/* debug mode variables and funcs */
int debugMode = 0;
static char msg_buff[64];
void debugPrint(char* msg);

void tskInit()
{
    /* create all time lapse timers */
    tmrCreate(&timeLapseImageTimer);
    tmrCreate(&timeLapseVideoTimer);
    tmrCreate(&timeLapseBattMonTimer);

    /* initialize the sched wait variable to the deployment start delay */
    timeLapseSchedWait = (tskStartOfDeploy * MIN_TO_SEC);
}

void tskExecute(unsigned int tasks)
{
    if ( tasks & TMR_TASK )
        tmrTask();

    if ( tasks & SCHED_TASK )
        Nop(); /* do nothing for now */
    
    if ( tasks & TIME_LAPSE_TASK )
        timeLapseSchedTask();
}

void tskShowStatus()
{
    /* scheduler status */
    serPutString(SER_CONSOLE, "*** TIME LAPSE SCHED TASK ***\r\n");

    serPutString(SER_CONSOLE, "timeLapseSchedState = ");

    switch ( timeLapseSchedState )
    {
        case TL_SCHED_WAITING: serPutString(SER_CONSOLE, "TL_SCHED_WAITING"); 
                               break;
        case TL_SCHED_RUNNING: serPutString(SER_CONSOLE, "TL_SCHED_RUNNING"); 
                               break;
        case TL_SCHED_STOPPED: serPutString(SER_CONSOLE, "TL_SCHED_STOPPED"); 
                               break;
        default: serPutString(SER_CONSOLE, "UNKOWN");
    }
    
    serPutString(SER_CONSOLE, "\r\n");

    sprintf(msg_buff, "timeLapseSchedWait = %lu SECS\r\n", timeLapseSchedWait);
    serPutString(SER_CONSOLE, msg_buff);

    sprintf(msg_buff, "tmrGetSeconds()    = %lu SECS\r\n", tmrGetSeconds());
    serPutString(SER_CONSOLE, msg_buff);
    
    /* calc remaining time before state change */
    if ( timeLapseSchedState != TL_SCHED_STOPPED)
    {
        sprintf(msg_buff, "Time Remaining     = %lu SECS\r\n", 
                (timeLapseSchedWait - tmrGetSeconds()));
        serPutString(SER_CONSOLE, msg_buff);
    }

    /* image task status */
    serPutString(SER_CONSOLE, "\r\n*** TIME LAPSE IMAGE TASK ***\r\n");

    serPutString(SER_CONSOLE, "timeLapseImageState = ");
    tskImageCaptureStateStr(msg_buff);
    serPutString(SER_CONSOLE, msg_buff);
    serPutString(SER_CONSOLE, "\r\n");

    sprintf(msg_buff, "timeLapseImageTimer = %lu ms\r\n", 
            tmrRead(&timeLapseImageTimer));
    serPutString(SER_CONSOLE, msg_buff);

    /* video task status */
    serPutString(SER_CONSOLE, "\r\n*** TIME LAPSE VIDEO TASK ***\r\n");
    
    serPutString(SER_CONSOLE, "timeLapseVideoState = ");
    tskVideoCaptureStateStr(msg_buff);
    serPutString(SER_CONSOLE, msg_buff);
    serPutString(SER_CONSOLE, "\r\n");

    sprintf(msg_buff, "timeLapseVideoTimer = %lu ms\r\n", 
            tmrRead(&timeLapseVideoTimer));
    serPutString(SER_CONSOLE, msg_buff);

    /* battery task status */
    serPutString(SER_CONSOLE, "\r\n*** TIME LAPSE B_MON TASK ***\r\n");
    
    serPutString(SER_CONSOLE, "timeLapseBattMonState = ");

    switch ( timeLapseBattMonState )
    {
        case TL_B_MON_INIT:     serPutString(SER_CONSOLE, "TL_B_MON_INIT"); 
                                break;
        case TL_B_MON_LOW:      serPutString(SER_CONSOLE, "TL_B_MON_LOW"); 
                                break;
        case TL_B_MON_NORMAL:   serPutString(SER_CONSOLE, "TL_B_MON_NORMAL"); 
                                break;
        case TL_B_MON_STOPPED:  serPutString(SER_CONSOLE, "TL_B_MON_STOPPED"); 
                                break;
        default: serPutString(SER_CONSOLE, "UNKOWN");
    }
    
    serPutString(SER_CONSOLE, "\r\n");

    sprintf(msg_buff, "timeLapseBattMonTimer = %lu ms\r\n", 
            tmrRead(&timeLapseBattMonTimer));
    serPutString(SER_CONSOLE, msg_buff);

    sprintf(msg_buff, "batteryVolts = %u mV\r\n", tskBattVolt());
    serPutString(SER_CONSOLE, msg_buff);
}


/******************************************************   
*******************************************************   
     
     Y V =   X cnts
    ---------------
    10 V = 232 cnts
    20 V = 465 cnts
    
    M_VOLT_CAL = (Y2 - Y1) / (X2 - X1)
    
    B_VOLT_CAL = Y2 -  M_VOLT_CAL(X2)

******************************************************   
******************************************************/

#define M_VOLT_CAL  0.042918
#define B_VOLT_CAL  0.042918

unsigned int tskBattVolt()
{
    unsigned int millivolt;
    double cnts_f = (double)anaGetSample(ANA_BATT);

    /* apply slope and offest */
    cnts_f *= M_VOLT_CAL;
    cnts_f += B_VOLT_CAL;

    /* convert to millivolts*/
    cnts_f *= 1000.0;

    millivolt = (unsigned int)cnts_f;

    return millivolt;
}

int tskStartImageCapture()
{
    debugPrint("tskStartImageCapture() [ ");
    rtcTimeDateStr(msg_buff);
    debugPrint(msg_buff);
    debugPrint("]\r\n");

    if ( tskImageCaptureReady() && tskVideoCaptureReady() )
    {
        debugPrint("timeLapseImageState = TL_IMAGE_START\r\n");
        timeLapseImageState = TL_IMAGE_START;
        return TRUE;
    }

    if ( !tskImageCaptureReady() )
    {
        debugPrint("ERR: timeLapseImageState = ");
        tskImageCaptureStateStr(msg_buff);
        debugPrint(msg_buff);
        debugPrint("\r\n");
    }

    if ( !tskVideoCaptureReady() )
    {
        debugPrint("ERR: timeLapseVideoState = ");
        tskVideoCaptureStateStr(msg_buff);
        debugPrint(msg_buff);
        debugPrint("\r\n");
    }

    return FALSE;
}

int tskStartVideoCapture()
{
    debugPrint("tskStartVideoCapture() [ ");
    rtcTimeDateStr(msg_buff);
    debugPrint(msg_buff);
    debugPrint("]\r\n");

    if ( tskImageCaptureReady() && tskVideoCaptureReady() )
    {
        debugPrint("timeLapseVideoState = TL_VIDEO_START\r\n");
        timeLapseVideoState = TL_VIDEO_START;
        return TRUE;
    }

    if ( !tskImageCaptureReady() )
    {
        debugPrint("ERR: timeLapseImageState = ");
        tskImageCaptureStateStr(msg_buff);
        debugPrint(msg_buff);
        debugPrint("\r\n");
    }

    if ( !tskVideoCaptureReady() )
    {
        debugPrint("ERR: timeLapseVideoState = ");
        tskVideoCaptureStateStr(msg_buff);
        debugPrint(msg_buff);
        debugPrint("\r\n");
    }

    return FALSE;
}

int tskImageCaptureReady()
{
    if ( timeLapseImageState == TL_IMAGE_READY )
        return TRUE;

    return FALSE;
}

int tskVideoCaptureReady()
{
    if ( timeLapseVideoState == TL_VIDEO_READY )
        return TRUE;

    return FALSE;
}

void tskImageCaptureStateStr(char* s)
{
    switch ( timeLapseImageState )
    {
        case TL_IMAGE_START:        strcpy(s, "TL_IMAGE_START"); break;
        case TL_IMAGE_POWER_1_WAIT: strcpy(s, "TL_IMAGE_POWER_1_WAIT"); break;
        case TL_IMAGE_POWER_2_WAIT: strcpy(s, "TL_IMAGE_POWER_2_WAIT"); break;
        case TL_IMAGE_PHOTO_WRITE:  strcpy(s, "TL_IMAGE_PHOTO_WRITE"); break;
        case TL_IMAGE_PHOTO_WAIT:   strcpy(s, "TL_IMAGE_PHOTO_WAIT"); break;
        case TL_IMAGE_LANC_OFF:     strcpy(s, "TL_IMAGE_LANC_OFF"); break;
        case TL_IMAGE_POWER_DOWN:   strcpy(s, "TL_IMAGE_POWER_DOWN"); break;
        case TL_IMAGE_READY:        strcpy(s, "TL_IMAGE_READY"); break;
        default: strcpy(s, "TL_IMAGE_UNKNOWN");
    }

    return;
}

void tskVideoCaptureStateStr(char* s)
{
    switch ( timeLapseVideoState )
    {
        case TL_VIDEO_START:        strcpy(s, "TL_VIDEO_START"); break;
        case TL_VIDEO_POWER_1_WAIT: strcpy(s, "TL_VIDEO_POWER_1_WAIT"); break;
        case TL_VIDEO_POWER_2_WAIT: strcpy(s, "TL_VIDEO_POWER_2_WAIT"); break;
        case TL_VIDEO_RECORD:       strcpy(s, "TL_VIDEO_RECORD"); break;
        case TL_VIDEO_RECORD_WAIT:  strcpy(s, "TL_VIDEO_RECORD_WAIT"); break;
        case TL_VIDEO_RECORD_STOP:  strcpy(s, "TL_VIDEO_RECORD_STOP"); break;
        case TL_VIDEO_LANC_OFF:     strcpy(s, "TL_VIDEO_LANC_OFF"); break;
        case TL_VIDEO_POWER_DOWN:   strcpy(s, "TL_VIDEO_POWER_DOWN"); break;
        case TL_VIDEO_READY:        strcpy(s, "TL_VIDEO_READY"); break;
        default: strcpy(s, "TL_VIDEO_UNKNOWN");
    }

    return;
}

int tskGetCaptureMode()
{
    return tskCaptureMode;
}

int tskSetCaptureMode(int mode)
{
    tskCaptureMode = mode;

    /* if the requested capture mode is unknown, set it to the default */
    switch ( tskCaptureMode )
    {
        case CAPTURE_MODE_OFF: break;
        case CAPTURE_MODE_IMG: break;
        case CAPTURE_MODE_VID: break;
        default: tskCaptureMode = CAPTURE_MODE_DEF; 
    }
    
    return 0;
}

unsigned long tskGetStartDeploy()
{
    return tskStartOfDeploy;
}

int tskSetStartDeploy(unsigned long start)
{
    tskStartOfDeploy = start;

    /* make sure the param is within bounds */
    if ( tskStartOfDeploy < START_DEPLOY_MIN )
        tskStartOfDeploy = START_DEPLOY_MIN; 
         
    if ( tskStartOfDeploy > START_DEPLOY_MAX )
        tskStartOfDeploy = START_DEPLOY_MAX;
         
    return 0;
}

unsigned long tskGetLenDeploy()
{
    return tskLenOfDeploy;
}

int tskSetLenDeploy(unsigned long length)
{
    tskLenOfDeploy = length;
    
    /* make sure the param is within bounds */
    if ( tskLenOfDeploy < LEN_DEPLOY_MIN )
        tskLenOfDeploy = LEN_DEPLOY_MIN; 
         
    if ( tskLenOfDeploy > LEN_DEPLOY_MAX )
        tskLenOfDeploy = LEN_DEPLOY_MAX;
    
    return 0;
}

unsigned long tskGetSampleInt()
{
    return tskSampleInterval;
}

int tskSetSampleInt(unsigned long interval)
{
    tskSampleInterval = interval;
    
    /* make sure the param is within bounds */
    if ( tskSampleInterval < SAMPLE_INT_MIN )
        tskSampleInterval = SAMPLE_INT_MIN; 
         
    if ( tskSampleInterval > SAMPLE_INT_MAX )
        tskSampleInterval = SAMPLE_INT_MAX;
    
    return 0;
}

unsigned long tskGetVidLen()
{
    return tskLenOfVideo;
}

int tskSetVidLen(unsigned long length)
{
    tskLenOfVideo = length;
    
    /* make sure the param is within bounds */
    if ( tskLenOfVideo < VIDEO_LEN_MIN )
        tskLenOfVideo = VIDEO_LEN_MIN; 
         
    if ( tskLenOfVideo > VIDEO_LEN_MAX )
        tskLenOfVideo = VIDEO_LEN_MAX;
    
    return 0;
}

unsigned long tskGetBattLevel()
{
    return tskBattLevel;
}

int tskSetBattLevel(unsigned long level)
{
    tskBattLevel = level;
    
    /* make sure the param is within bounds */
    if ( tskBattLevel < BATT_LEVEL_MIN )
        tskBattLevel = BATT_LEVEL_MIN; 
         
    if ( tskBattLevel > BATT_LEVEL_MAX )
        tskBattLevel = BATT_LEVEL_MAX;
    
    return 0;
}

/******************************** The Tasks *********************************/
void timeLapseSchedTask()
{
    /* run through both image and video tasks */
    timeLapseImageTask();
    timeLapseVideoTask();
    timeLapseBattMonTask();

/* There may be a problem with drift between the CPU clock and 
the RTC.  In order to eliminate this drift you could call 
rtcDateToSecs(...) and use that in place of tmrGetSeconds().  
The RTC should also be more accurate than the CPU clock */
    
    /* wait for deployment to start */
    if ( timeLapseSchedState == TL_SCHED_WAITING )
    {
        if ( tmrGetSeconds() < timeLapseSchedWait )
            return;

        /* initialize tskWaitTime and switch to run mode */
        timeLapseSchedWait = tmrGetSeconds();
        timeLapseSchedState = TL_SCHED_RUNNING;
    }
    
    if ( timeLapseSchedState == TL_SCHED_RUNNING )
    {
        /* if you've gone passed the deployment lenght switch states */
        if ( tmrGetSeconds() > (tskLenOfDeploy * MIN_TO_SEC) )
            timeLapseSchedState = TL_SCHED_STOPPED;

        /* wait for next image or video */
        if ( tmrGetSeconds() < timeLapseSchedWait )
            return;
            
        /* increment wait time by sample interval */
        timeLapseSchedWait += (tskSampleInterval * MIN_TO_SEC);

        /* start img capture if image mode */
        if ( tskCaptureMode == CAPTURE_MODE_IMG )
            tskStartImageCapture();

        /* start vid capture if video mode */
        if ( tskCaptureMode == CAPTURE_MODE_VID )
            tskStartVideoCapture();

        /* display capture mode off if in debug */
        if ( tskCaptureMode == CAPTURE_MODE_OFF )
        {
            debugPrint("CAPTURE_MODE_OFF [ ");
            rtcTimeDateStr(msg_buff);
            debugPrint(msg_buff);
            debugPrint("]\r\n");
        }

    }

    if ( timeLapseSchedState == TL_SCHED_STOPPED )
    {
        Nop(); /* do nothing for now */
    }

    return;
}

void grabLancPrompt()
{
    serRxFlush(SER_SPARE);
    serTxFlush(SER_SPARE);
    serPutByte(SER_SPARE, '\r');
    tmrDelayMs(100);
    serRxFlush(SER_SPARE);
    serTxFlush(SER_SPARE);
}

void timeLapseImageTask()
{
    /* if you're done with the image get out */
    if ( timeLapseImageState == TL_IMAGE_READY )
        return;

    if ( timeLapseImageState == TL_IMAGE_START )
    {
        /***/
        /* switch on FET */
        /***/
        _LATF0 = 1;
        tmrStart(&timeLapseImageTimer);
        tmrClear(&timeLapseImageTimer);
        timeLapseImageState = TL_IMAGE_POWER_1_WAIT;
        debugPrint("TL_IMAGE_POWER_1_WAIT\r\n");
    }

    if ( timeLapseImageState == TL_IMAGE_POWER_1_WAIT )
    {
        /***/
        /* wait for FET power */
        /***/
        if ( tmrRead(&timeLapseImageTimer) < TL_CAM_POWER_UP_1 )
            return;

        /***/
        /* send power up LANC command */
        /***/
        grabLancPrompt();
        serPutString(SER_SPARE, "ATSP\r");
        tmrClear(&timeLapseImageTimer);
        
        timeLapseImageState = TL_IMAGE_POWER_2_WAIT;
        debugPrint("TL_IMAGE_POWER_2_WAIT\r\n");
    }


    if ( timeLapseImageState == TL_IMAGE_POWER_2_WAIT )
    {
        /***/
        /* wait for power up */
        /***/
        if ( tmrRead(&timeLapseImageTimer) < TL_CAM_POWER_UP_2 )
            return;

        timeLapseImageState = TL_IMAGE_PHOTO_WRITE;
        debugPrint("TL_IMAGE_PHOTO_WRITE\r\n");
    }

    if ( timeLapseImageState == TL_IMAGE_PHOTO_WRITE )
    {
        /* turn on lights here */
        _LATB12 = 1;
        Nop();
        _LATB13 = 1;
        Nop();

        /***/
        /* send photo LANC command */
        /***/
        grabLancPrompt();
        serPutString(SER_SPARE, "102B\r");
        tmrClear(&timeLapseImageTimer);

        timeLapseImageState = TL_IMAGE_PHOTO_WAIT;
        debugPrint("TL_IMAGE_PHOTO_WAIT\r\n");
    }

    if ( timeLapseImageState == TL_IMAGE_PHOTO_WAIT )
    {
        /***/
        /* wait for photo write */
        /***/
        if ( tmrRead(&timeLapseImageTimer) < TL_PHOTO_WRITE_TIME )
            return;

        /* turn off lights here */
        _LATB12 = 0;
        Nop();
        _LATB13 = 0;
        Nop();

        timeLapseImageState = TL_IMAGE_LANC_OFF;
        debugPrint("TL_IMAGE_LANC_OFF\r\n");
    }

    if ( timeLapseImageState == TL_IMAGE_LANC_OFF )
    {
        /***/
        /* send power down LANC command */
        /***/
        grabLancPrompt();
        serPutString(SER_SPARE, "105E\r");
        tmrClear(&timeLapseImageTimer);

        timeLapseImageState = TL_IMAGE_POWER_DOWN;
        debugPrint("TL_IMAGE_POWER_DOWN\r\n");
    }

    if ( timeLapseImageState == TL_IMAGE_POWER_DOWN )
    {
        /***/
        /* wait for power down */
        /***/
        if ( tmrRead(&timeLapseImageTimer) < TL_CAM_POWER_DOWN )
            return;

        /***/
        /* switch off FET */
        /***/
        _LATF0 = 0;
        
        timeLapseImageState = TL_IMAGE_READY;
        tmrStop(&timeLapseImageTimer);
        tmrClear(&timeLapseImageTimer);

        debugPrint("TL_IMAGE_READY\r\n");
    }
}

void timeLapseVideoTask()
{
    /* if you're done with the video get out */
    if ( timeLapseVideoState == TL_VIDEO_READY )
        return;

    if ( timeLapseVideoState == TL_VIDEO_START )
    {
        /***/
        /* switch on FET */
        /***/
        _LATF0 = 1;
        tmrStart(&timeLapseVideoTimer);
        tmrClear(&timeLapseVideoTimer);
        timeLapseVideoState = TL_VIDEO_POWER_1_WAIT;
        debugPrint("TL_VIDEO_POWER_1_WAIT\r\n");
    }

    if ( timeLapseVideoState == TL_VIDEO_POWER_1_WAIT )
    {
        /***/
        /* wait for FET power */
        /***/
        if ( tmrRead(&timeLapseVideoTimer) < TL_CAM_POWER_UP_1 )
            return;

        /***/
        /* send power up LANC command */
        /***/
        grabLancPrompt();
        serPutString(SER_SPARE, "ATSP\r");
        tmrClear(&timeLapseVideoTimer);
        
        timeLapseVideoState = TL_VIDEO_POWER_2_WAIT;
        debugPrint("TL_VIDEO_POWER_2_WAIT\r\n");
    }

    if ( timeLapseVideoState == TL_VIDEO_POWER_2_WAIT )
    {
        /***/
        /* wait for power up */
        /***/
        if ( tmrRead(&timeLapseVideoTimer) < TL_CAM_POWER_UP_2 )
            return;

        timeLapseVideoState = TL_VIDEO_RECORD;
        debugPrint("TL_VIDEO_RECORD\r\n");
    }

    if ( timeLapseVideoState == TL_VIDEO_RECORD )
    {
        /* turn on lights here */
        _LATB12 = 1;
        Nop();
        _LATB13 = 1;
        Nop();

        /***/
        /* send vide record LANC command */
        /***/
        grabLancPrompt();
        serPutString(SER_SPARE, "103A\r");
        tmrClear(&timeLapseVideoTimer);

        timeLapseVideoState = TL_VIDEO_RECORD_WAIT;
        debugPrint("TL_VIDEO_RECORD_WAIT\r\n");
    }

    if ( timeLapseVideoState == TL_VIDEO_RECORD_WAIT )
    {
        /***/
        /* wait for video to record */
        /***/
        if ( tmrRead(&timeLapseVideoTimer) < (tskLenOfVideo * SEC_TO_MS) )
            return;

        /* turn off lights here */
        _LATB12 = 0;
        Nop();
        _LATB13 = 0;
        Nop();

        /***/
        /* send LANC video record stop command */
        /***/
        grabLancPrompt();
        serPutString(SER_SPARE, "1033\r");
        tmrClear(&timeLapseVideoTimer);

        timeLapseVideoState = TL_VIDEO_RECORD_STOP;
        debugPrint("TL_VIDEO_RECORD_STOP\r\n");
    }

    if ( timeLapseVideoState == TL_VIDEO_RECORD_STOP )
    {
        /***/
        /* wait for video to stop recording */
        /***/
        if ( tmrRead(&timeLapseVideoTimer) < TL_VIDEO_STOP_TIME )
            return;

        timeLapseVideoState = TL_VIDEO_LANC_OFF;
        debugPrint("TL_VIDEO_LANC_OFF\r\n");
    }

    if ( timeLapseVideoState == TL_VIDEO_LANC_OFF )
    {
        /***/
        /* send power down LANC command */
        /***/
        grabLancPrompt();
        serPutString(SER_SPARE, "105E\r");
        tmrClear(&timeLapseVideoTimer);

        timeLapseVideoState = TL_VIDEO_POWER_DOWN;
        debugPrint("TL_VIDEO_POWER_DOWN\r\n");
    }

    if ( timeLapseVideoState == TL_VIDEO_POWER_DOWN )
    {
        /***/
        /* wait for power down */
        /***/
        if ( tmrRead(&timeLapseVideoTimer) < TL_CAM_POWER_DOWN )
            return;

        /***/
        /* switch off FET */
        /***/
        _LATF0 = 0;
        
        timeLapseVideoState = TL_VIDEO_READY;
        tmrStop(&timeLapseVideoTimer);
        tmrClear(&timeLapseVideoTimer);

        debugPrint("TL_VIDEO_READY\r\n");
    }
}

void timeLapseBattMonTask()
{

    unsigned long batt_volt = 0;

    /* only enter the battery monitor routine if tskBattLevel > 0 */
    if ( tskBattLevel == 0 )
        return;

    /* don't enter the battery monitor routine after the low threshold */
    if ( timeLapseBattMonState == TL_B_MON_STOPPED )
        return;

    /* read battery voltage */
    batt_volt = tskBattVolt();

    /* initialize the battery monitor state on the first run through */
    if ( timeLapseBattMonState == TL_B_MON_INIT )
    {
        if ( batt_volt < tskBattLevel )
        {
            timeLapseBattMonState = TL_B_MON_LOW;
            tmrStart(&timeLapseBattMonTimer);
            tmrClear(&timeLapseBattMonTimer);
        }
        else
        {
            timeLapseBattMonState = TL_B_MON_NORMAL;
            tmrStop(&timeLapseBattMonTimer);
            tmrClear(&timeLapseBattMonTimer);
        }
    }

    /* if the battery mon state is low check for how long */
    if ( timeLapseBattMonState == TL_B_MON_LOW )
    {
        /* check the current battery level against the threshold */
        if ( batt_volt < tskBattLevel )
        {
            /* if the battery level has remained below the
            threshold for too long, then stop the scheduler */
            if ( tmrRead(&timeLapseBattMonTimer) > TL_LOW_BATT_TIME )
            {
                tmrStop(&timeLapseBattMonTimer);
                timeLapseSchedState = TL_SCHED_STOPPED;
                timeLapseBattMonState = TL_B_MON_STOPPED;
            }
        }
        else
        {
            timeLapseBattMonState = TL_B_MON_NORMAL;
        }

    }

    if ( timeLapseBattMonState == TL_B_MON_NORMAL )
    {
        /* check the current battery level against the threshold */
        if ( batt_volt < tskBattLevel )
        {
            timeLapseBattMonState = TL_B_MON_LOW;
            tmrStart(&timeLapseBattMonTimer);
            tmrClear(&timeLapseBattMonTimer);
        }
        else
        {
            tmrStop(&timeLapseBattMonTimer);
            tmrClear(&timeLapseBattMonTimer);
        }

    }

    return;
}

void debugPrint(char* dbg_msg)
{
    if (debugMode) 
        serPutString(SER_CONSOLE, dbg_msg);
}


