Dependencies:   nRF51822

Source/main.cpp

Committer:
sgetz7908
Date:
2020-09-18
Revision:
51:53fe9aff625a
Parent:
50:8dca54c1e3fd
Child:
52:23cc9f576e4a

File content as of revision 51:53fe9aff625a:

//**********************************************************************
//
// SmartCap2 Main
//
// SPG 2/22/2019
//
// Copyright (c) 2019 Polygenesis
//
//**********************************************************************
/// @file main.cpp

#include <events/mbed_events.h>
#include <mbed.h>
#include <ctype.h>
#include "main.h"
#include "hw.h"
#include "ble/BLE.h"
#include "ble/Gap.h"
#include "BLE_Stuff.h"
#include "ble/services/UARTService.h"
#include "infoService.h"
#include "log.h"
#include "nrf_soc.h"
#include "mem.h"
#include "CCITTcrc16.h"

void process_state(void);
void start_periodic_tick(uint32_t sec);

EventQueue eventQueue(/* event count */ 16 * EVENTS_EVENT_SIZE);


// define I/O
InterruptIn is_package_open(LIGHT_SENSE, PullNone); // will be pulled hi when package_is_open (only if package_open_sense_enable = 1)
DigitalOut package_open_sense_enable(LIGHT_SENSE_ENABLE, 0);

DigitalOut cap_sense_led(CAP_SENSE_LED, 0);
DigitalOut vdd_enable(VDD_ENABLE, 0);

DigitalOut led(LED, 0); // LED for debugging purposes

#if UART_DEBUGGING
    #define debug(STR) BLE_UART_xmit(STR); BLE_UART_xmit("\n");
#else
    #define debug(...) 
#endif

LowPowerTicker periodic_ticker; // this handle the RTC
LowPowerTicker capofffast_ticker; // this handle accellerated polling of the cap if CAP_THRESHOLD_OFF1 is passed
 
float tick_rate = FAST_TICK_SEC;

bool log_enable = true;     // turn off event logging when false (engineering function) – DEW

volatile bool package_open_detected = false;
volatile bool is_cap_off = false; // 0=cap on, 1=cap off
volatile bool last_cap_off = false;
volatile bool adaptive_active = false;

uint32_t cap_off_time;

volatile int16_t off_reading;
volatile int16_t on_reading;
volatile int16_t on_reading_filtered = 0;
volatile int16_t on_reading_peak = 0;

volatile uint16_t cap_threshold_on = CAP_THRESHOLD_ON_INITIAL;

typedef  enum {
    INIT,
    POST, 
    TEST_MODE, 
    SHIP_MODE_WAIT_DARK,
    SHIP_MODE_WAIT_LIGHT,
    SHIP_MODE_CHECK_CAP,
    SHIP_MODE_WAIT_CAP_OFF,
    IN_USE_SETUP,
    WAIT_CAP_OFF, 
    WAIT_CAP_ON,
    EOL_WAIT_CAP_OFF,
    EOL_WAIT_CAP_ON,
    EOL_WAIT_LIGHT,
    OTHER
} state_t;

state_t state = INIT;

volatile uint8_t veryfasttick_on = 0;

void fastcap_tick(void);

/// Light detected interrupt
void light_interrupt(void)
{ // dark to light transition
    package_open_detected = true;
    start_periodic_tick(FAST_TICK_SEC);
}
    
/// Test to see if Cap is off or on
void test_cap(void)
{     
    vdd_enable = 1; // enable analog power
    //wait(0.00075);
    wait(CAP_LED_OFF_DELAY);  // increase wait time to allow full settling - FTD 11/21/2019
    off_reading = adc_read(ADC_CHAN_CAP_SENSE,CAP_SAMPLES);
    cap_sense_led = 1; // enable led
    wait(CAP_LED_ON_DELAY);
    //wait(0.001);  // increase wait time to allow full settling - FTD 11/21/2019
    on_reading = adc_read(ADC_CHAN_CAP_SENSE,CAP_SAMPLES);
    // turn everything off
    cap_sense_led = 0;
    vdd_enable = 0;
    
    // switch cap state using hysteresis
    if(last_cap_off)
    { // cap was off
        if(on_reading > off_reading + cap_threshold_on)
        { // cap is now on
            is_cap_off = 0;
            #if ENABLE_LED        
                led = 0;
            #endif  
        }
    }
    else
    { // cap was on
        uint16_t off_threshold; // there are two cap off thresholds, normal use and EOL, this provides a variable for the proper value - FTD 08212020
        // test for proper threshold needed for EOL - FTD 08212020
        if(NV_NOT_EOL) off_threshold = CAP_THRESHOLD_OFF2;
        else    off_threshold = CAP_THRESHOLD_OFF_EOL;
        
        if(on_reading < off_reading + off_threshold)
        { // cap is now off
            is_cap_off = 1;
            capofffast_ticker.detach();
            veryfasttick_on = 0;
            #if ENABLE_LED        
                led = 1;
            #endif 
            //BLE_UART_xmit("2");
        }
        else if(NV_NOT_EOL && on_reading < off_reading + CAP_THRESHOLD_OFF1) // check for cap moving toward off
        { // cap is almost off, start fast ticks (turns off after next regular periodic tick, or if full cap off is detected)
            if(veryfasttick_on==0)
            {
                capofffast_ticker.detach();
                capofffast_ticker.attach(&fastcap_tick ,VERY_FAST_TICK_SEC);
                veryfasttick_on = VERY_FAST_TICK_ON_TIME_SEC;
            }
            //BLE_UART_xmit("1");
        }
    

    }
    
    // if adapting enabled and cap is on then adapt
    if(adaptive_active && !is_cap_off)
    {
        // adaptive cap on threshold processing
        int16_t diff = on_reading - off_reading;
        on_reading_filtered = (256-CAP_THRESHOLD_ADAPT_RATE)*(int32_t)on_reading_filtered/256 + (CAP_THRESHOLD_ADAPT_RATE)*(int32_t)diff/256;
        
        if(on_reading_filtered > on_reading_peak)
        { // new higher diff value found
            on_reading_peak = on_reading_filtered;
            cap_threshold_on = CAP_THRESHOLD_PERCENT_OF_PEAK*on_reading_peak/256;             
        }
    }
    
    last_cap_off = is_cap_off;
}

void fastcap_tick(void)
{
    eventQueue.call(test_cap); // starts as non-interrupt task so we can use wait();
    //test_cap();
}

void periodic_tick_task(void)
{
    //BLE_UART_xmit("+");
    test_cap();
    process_state();
    if(veryfasttick_on)
    {
        veryfasttick_on--;
        if(veryfasttick_on==0) capofffast_ticker.detach();
    }
}

/// this interrupt is run every PERIODIC_TICK_SEC seconds
void periodic_tick()
{
    update_rtc(tick_rate); // keep rtc updated
    eventQueue.call(periodic_tick_task); // starts as non-interrupt task so we can use wait();
}

void start_periodic_tick(uint32_t sec)
{
    tick_rate = sec;
    periodic_ticker.detach();
    periodic_ticker.attach(&periodic_tick ,sec);
}

void stop_periodic_tick(void)
{
    periodic_ticker.detach();
}

/// call here to flash the LED n times.
// n=0 to flash forever
void flash_led(int n, float sec)
{
    if(n==0)
    { // flash forever
        while(1)
        {
            led = 1;
            wait(sec);
            led = 0;
            wait(sec);
        }
    }
    else
    { // flash n times
        while(n--)
        {
            led = 1;
            wait(sec);
            led = 0;
            wait(sec);
        }
    }
}


state_t last_state = OTHER;
int last_cap;

/// Main state machine
/// Called whenever a sensor changes
void process_state(void)
{
    do
    {
        #if UART_DEBUGGING==1    
            if(last_state != state)
            {
                debug(uli2a(state));
                debug("\n");    
            }
        #endif
        last_state = state;
        
        switch(state)
        {
            case INIT:
                log_add(EVENT_POWER, 0, 0, 0); // log event 
                
                start_periodic_tick(FAST_TICK_SEC);
                #if SKIP_SHIP_MODE
                    // package_open_sense_enable = 1;
                    state = IN_USE_SETUP;
                #else
                    state = POST;
                #endif
                break;
                
            case POST:
                // check CRC ?
                
                // Check Misc.
                
                //if(error) flash_led(0,0.1); // flash forever to indicate error

                if(NV_TESTING_REQUIRED) 
                {
                    flash_led(1, 1.0);
                    package_open_sense_enable = 1;
                    start_periodic_tick(FAST_TICK_SEC);
                    last_cap = 99;
                    set_radio(true, 0); // advertise forever until stopped
                    state = TEST_MODE;
                }
                else 
                {
                    state = IN_USE_SETUP;                    
                }
               
                break;
                
            case TEST_MODE:
                test_cap();
                led = is_cap_off;
                if(last_cap != is_cap_off)
                {
                    last_cap = is_cap_off;
                    BLE_UART_xmit("*Cap ");
                    if(last_cap) BLE_UART_xmit("Off"); else BLE_UART_xmit("On");
                    BLE_UART_xmit("\n");
                }
                if(!NV_TESTING_REQUIRED && is_package_open)
                { // testing passed
                    set_radio(false); // already done when NV_TESTING_REQUIRED was cleared.
                    led = 0;
                    state = SHIP_MODE_WAIT_DARK;
                }
                break;
                
            case SHIP_MODE_WAIT_DARK: // Wait for light sensor to see darkness
                //flash_led(1,0.1);
                if(!is_package_open)
                { // its dark
                    state = SHIP_MODE_WAIT_LIGHT;
                }
            break;           
            
            case SHIP_MODE_WAIT_LIGHT: 
                // set up and enable the Light Sense Interrupt
                // go to lowest power state
                
                // before going to sleep, adapt the cap_on threshold (iff the cap is on)
                {
                    adaptive_active = true;
                    int i = 20; // multiple calls aallow it to adapt slowly to eliminate potencial noise.
                    while(i--) 
                    {
                        test_cap();
                    }
                    adaptive_active = false;
                }
                
                // now go to sleep
                debug("Going SHIP MODE\n");
                is_package_open.disable_irq();
                stop_periodic_tick();
                led = 0;
                package_open_detected = false;
                //is_package_open.mode(PullDown);
                is_package_open.rise(&light_interrupt);
                is_package_open.enable_irq();
                state = SHIP_MODE_CHECK_CAP;

            break;
            
            case SHIP_MODE_CHECK_CAP:
                
                if(package_open_detected)
                {
                    debug("Awake\n");
                    //flash_led(3,0.25);
                    test_cap();
                    if(is_cap_off)
                    {
                        state = SHIP_MODE_WAIT_DARK;
                    }
                    else
                    {
                        state = SHIP_MODE_WAIT_CAP_OFF;
                    }
                }
                break;
                
            case SHIP_MODE_WAIT_CAP_OFF:
                if(!is_package_open) 
                {
                    state = SHIP_MODE_WAIT_LIGHT;
                }
                else
                { 
                    test_cap();
                    if(is_cap_off)
                    {
                        package_open_sense_enable = 0;
                        set_rtc(0);
                        log_add(EVENT_WAKE_FROM_SHIP, 0, 0, 0);
                        state = IN_USE_SETUP;
                    }
                }
                break;
                
            case IN_USE_SETUP:
                flash_led(3, .25);
                start_periodic_tick(PERIODIC_TICK_SEC);
                debug("In Use\n"); 
                if(NV_NOT_IN_USE) nv_clear(NV_NOT_IN_USE_ADDR);            
                state = WAIT_CAP_OFF;
            break;
            
            case WAIT_CAP_OFF: // cap is on, waiting for cap to be removed
                if(read_clock()>((uint32_t)EOL_TIMEOUT_DAYS*24*60*60))
                { // EOL detected due to maximum time
                    if(NV_NOT_EOL) nv_clear(NV_NOT_EOL_ADDR);
                }
                
                if(!NV_NOT_EOL)
                { // EOL flagged
                    if(log_code_count(EVENT_EOL)==0) log_add(EVENT_EOL, 0, 0, 0); // log event
                    start_periodic_tick(EOL_TICK_SEC);  // just tick less often
                    state = EOL_WAIT_CAP_OFF;
                }
                else if(is_cap_off)
                { // cap just taken off   
                    // save cap off time
                    cap_off_time = read_clock();
                    debug("Cap Off \n");            
                    state = WAIT_CAP_ON;
                }
    
            break;
            
            case WAIT_CAP_ON: // cap currently off, waiting for cap to be put on
                if(!is_cap_off)
                { // cap just put on  
                    // log time cap was off
                    uint32_t cap_off_dur = read_clock()-cap_off_time;
                    if(cap_off_dur>65535) cap_off_dur = 65535;
                    log_add(EVENT_CAP_ON, cap_off_dur & 0xff, (cap_off_dur >> 8)&0xff, 0); // log event
                    
                    if(log_code_count(EVENT_CAP_ON)>= EOL_MAX_USES) 
                    { // EOL detected due to maximum uses
                        if(NV_NOT_EOL) nv_clear(NV_NOT_EOL_ADDR); 
                    }
                    
                    set_radio(true);
                    state = WAIT_CAP_OFF;
                }
                break;
            
            case EOL_WAIT_CAP_OFF: // Cap is on
                if(is_cap_off)
                { // cap just taken off   
                    debug("EOL Cap Off \n");            
                    state = EOL_WAIT_CAP_ON;
                }
                //else if(!is_package_open)
                //{ // its dark
                //    start_periodic_tick(EOL_TICK_SEC);  // just tick less often
                //    state = EOL_WAIT_LIGHT;
                //}
                break;
                
            case EOL_WAIT_CAP_ON: // Cap is off
                if(!is_cap_off)
                { // cap just put on  
                    debug("EOL Cap On \n");  
                    set_radio(true);
                    state = EOL_WAIT_CAP_OFF;
                }
                break;
/*                
            case EOL_WAIT_LIGHT: // EOL and its dark, save power
                // set up and enable the Light Sense Interrupt
                // go to lowest power state
                
                debug("Going to EOL dark mode\n");
                led = 0;
                if(is_package_open) 
                {
                    start_periodic_tick(PERIODIC_TICK_SEC);
                    state = EOL_WAIT_CAP_OFF;
                }
                break;
*/            
            default: // illegal state
                state = INIT;
            break;
        }
    } while(state != last_state);
}

void dataWasRead(void)
{
    flash_led(1, 0.04);
}
    
/// process commands sent to the SmartCap over Bluetooth UART
void process_cmd(char * cmd)
{
    switch(tolower(cmd[0])) {
        case 'r': // Get records
            log_show();
            //batt_voltage = read_battery_voltage();
            //updateBattValue(batt_voltage); 
            break;

        case 's': // status
            switch(tolower(cmd[1])) {
                
                case 'd':  // disable event logging with cap sensor toggle - DEW
                    log_enable = false;
                    BLE_UART_xmit("Log DISABLED\n");
                    break;
                    
                case 'e':  // (default) enable event logging with cap sensor toggle - DEW
                    log_enable = true;
                    BLE_UART_xmit("Log ENABLED\n");
                    break;
                    
                case 'f':  // show current state of logging enable function - DEW
                    if(log_enable) BLE_UART_xmit("Log ENABLED\n");
                    else BLE_UART_xmit("Log DISABLED\n");
                    break;
                    
                case 0: // old, check battery voltage
                case 'b': // new, check battery voltage
                    batt_voltage = read_battery_voltage();
                    BLE_UART_xmit("Bat=");
                    BLE_UART_xmit(batt_voltage);
                    BLE_UART_xmit("\n");
                    break;
                    
                //case 'x': // checksum
                //    BLE_UART_xmit("sx=");
                //    BLE_UART_xmit(0);
                //    BLE_UART_xmit("\n");
                //    break;
                              
                case 'c': // cap sensor analog readings
                    BLE_UART_xmit("sc=");
                    test_cap();
                    BLE_UART_xmit(on_reading);
                    BLE_UART_xmit(",");
                    BLE_UART_xmit(off_reading);
                    BLE_UART_xmit("\n");
                    break;
                   
                case 'm': // CRC of Softdevice and App (Not including NV storage for logs and flags)
                    {
                        uint16_t crc = crc16((const unsigned char*)CRC_START_ADDR, (uint32_t)(CRC_END_ADDR - CRC_START_ADDR + 1) );
                        BLE_UART_xmit("sm=");
                        BLE_UART_xmit(char2hex(crc, 4));
                        BLE_UART_xmit("\n");
                    }
                    break;
                    
                case 's': // sensors as binary, with bits '000eutcp', e=(0=EOL), u=(0=in use), t=(1=TESTING REQUIRED), c=(1 is cap off), p=(1 is light sensed)
                    {
                        //int val = 0;
                        BLE_UART_xmit("*ss=000");
                        if(NV_NOT_EOL) BLE_UART_xmit("1"); else BLE_UART_xmit("0");
                        if(NV_NOT_IN_USE) BLE_UART_xmit("1"); else BLE_UART_xmit("0");
                        if(NV_TESTING_REQUIRED) BLE_UART_xmit("1"); else BLE_UART_xmit("0");
                        int save = package_open_sense_enable;
                        package_open_sense_enable = 1;
                        test_cap();
                        if(is_cap_off) BLE_UART_xmit("1"); else BLE_UART_xmit("0");
                        wait(LIGHT_SENSE_PWRON_DELAY);
                        if(is_package_open) BLE_UART_xmit("1"); else BLE_UART_xmit("0");
                        package_open_sense_enable = save;
                        BLE_UART_xmit("\n");
                    }
                    break;
                case 'l': // read light sensor analog reading via ADC.  Circuit enabled before doing reading.
                    {
                        BLE_UART_xmit("sl=");
                        int save2 = package_open_sense_enable;
                        package_open_sense_enable = 1;
                        wait(LIGHT_SENSE_PWRON_DELAY);
                        BLE_UART_xmit(adc_read(ADC_CHAN_LIGHT_SENSE,2));
                        package_open_sense_enable = save2;
                        BLE_UART_xmit("\n");
                    }
                    break;
                case 'q':
                    BLE_UART_xmit("sq=");
                    BLE_UART_xmit(on_reading_filtered);
                    BLE_UART_xmit(", ");
                    BLE_UART_xmit(on_reading_peak);
                    BLE_UART_xmit(", ");
                    BLE_UART_xmit(cap_threshold_on);
                    BLE_UART_xmit("\n");
                    break;
            }
            break;
        case 'p': // test passed
            if(tolower(cmd[1])=='z')
            {
                log_add(EVENT_TEST_PASS, 0, 0, 0);
                if(NV_TESTING_REQUIRED) nv_clear(NV_TESTING_REQUIRED_ADDR);
            }

            break;
            
        case 't': // get time
            BLE_UART_xmit(read_clock());
            BLE_UART_xmit("\n");
            break;

        case 'c': // set time
            {
                int i = 1;
                uint32_t num = 0;
    
                while(cmd[i]>='0' && cmd[i]<='9') {
                    num = num*10 + cmd[i++]-'0';
                }
    
                if(i>1) {
                    set_time_offset(num);
                    BLE_UART_xmit("*Time Set\n");
                }
            }
            break;
                
        case 'v': // version
            BLE_UART_xmit("v=");
            BLE_UART_xmit(FW_VERSION);
            BLE_UART_xmit("\n");
            break;
                 
        default:
            BLE_UART_xmit("S=Status\n");
            break;
    }
    cmd[0] = 0;
}

//*****************************************************************************

int main(void)
{                                                 
    // blink LED to indicate power applied
    //flash_led(1,1.0);

    if(Init_BLE_Stuff()) // init and start advertising    
    {
        flash_led(0, 0.05); // indicate a ble init error
    }
    
    eventQueue.call(process_state);

    eventQueue.dispatch_forever(); // run all tasks in the event queue (non-interrupt routines)
    return 0;
}