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; }