BLE ADV Gateway, converts advertisement to proper JSON serial output
Dependencies: BLE_API mbed mbedtls nRF51822
Diff: main.cpp
- Revision:
- 8:46c5e0bfab05
- Parent:
- 7:0a8bbb6dea16
- Child:
- 9:1ea51b2048a8
diff -r 0a8bbb6dea16 -r 46c5e0bfab05 main.cpp --- a/main.cpp Tue Jan 12 10:12:43 2016 +0000 +++ b/main.cpp Mon Jul 10 03:57:10 2017 +0000 @@ -1,51 +1,192 @@ -/* mbed Microcontroller Library - * Copyright (c) 2006-2015 ARM Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. +/* + + Eric Tsai + todo: hash mac to create unqiue identifier for "my" sensors. + + */ +//required to call the ecb functions +extern "C" +{ + #include "nrf_ecb.h" +} + #include "mbed.h" #include "toolchain.h" #include "ble/BLE.h" #include "TMP_nrf51/TMP_nrf51.h" -static Ticker ticker; -static TMP_nrf51 tempSensor; -static bool triggerTempValueUpdate = false; -static DigitalOut alivenessLED(LED1, 1); +//comment out when done with debug uart, else eats batteries +#define MyDebugEnb 1 + +//Pin "P0.4" on nRF51822 = mbed "p4". +//InterruptIn is pulled-up. GND the pin to activate. + +// waveshare board ****** +//InterruptIn button1(p10); +//InterruptIn button2(p11); + +// purple board ****** +InterruptIn button1(p23); +InterruptIn button2(p24); + + +//Serial device(p9, p11); // tx, rx, purple board and Rigado +#if MyDebugEnb +// if you see ~1mA consumption during sleep, that's because uart is enabled. +Serial device(p9, p11); //nRF51822 uart : TX=p9. RX=p11 +#endif + +//InterruptIn button(p4); //Pin P0.4 on = mbed "p3"; + + +static Ticker tic_adv; //stop adv +static Ticker tic_debounce; //debounce I/O +static Ticker tic_periodic; //regular updates +//static TMP_nrf51 tempSensor; +static bool flag_update_io = false; +static bool flag_periodic_call = false; +static bool flag_detach_adv_tic = false; +static bool flag_set_debounce_tic = false; //not used + +//static DigitalOut alivenessLED(LED1, 1); +float mySensor = 2.0f; + + +/* Optional: Device Name, add for human read-ability */ +const static char DEVICE_NAME[] = "CUU"; +/* You have up to 26 bytes of advertising data to use. */ +/* +Advertisement + +DECvolt:3.11111111,mag:1 +AdvData[0-2] = Look for DEC on observer +AdvData[3] = beginning of data +*/ +//full with nullls +static uint8_t AdvData[] = {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0}; /* Example of hex data */ +char buffer[10]={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; //battery analog reading +uint8_t magnet_near=0; + + + +static uint8_t key[16] = {0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4}; +//26 bytes adv data +static uint8_t encrypted[26] = {0x0,0x0,0x0,0x1,0x1,0x1,0x2,0x2,0x2,0x3,0x3,0x3,0x4,0x4,0x4,0x5,0x5,0x5,0x6,0x6,0x6,0x7,0x7,0x7,0x8,0x8}; /* Example of hex data */ +//static uint8_t key_buf[16] = {0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4}; +static uint8_t key_buf[16] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x1, 0x2}; +static uint8_t src_buf[16] = {0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4}; +static uint8_t des_buf[16] = {0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4}; + +//const static uint8_t AdvData[] = {"ChangeThisData"}; /* Example of character data */ + + + +/* **** NOT USED **** */ +//16byte UUID loading happens here +//Look at <GapAdvertisingData.h> for rest of definition struct ApplicationData_t { - uint16_t applicationSpecificId; /* An ID used to identify temperature value in the manufacture specific AD data field */ - TMP_nrf51::TempSensorValue_t tmpSensorValue; /* User defined application data */ + //Byte 0: AppID High Byte + //Byte 1: AppID Low Byte + //Byte 2: sensor High Word + //Byte 3: + //Byte 4: + //Byte 5: sensor Low Byte + + + //app ID is 16 bit, (0xFEFE) + uint16_t applicationSpecificId; /* An ID used to identify temperature value in the manufacture specific AD data field */ + + //float = 32-bit. + //tsai: change this to uint32_t!!! + TMP_nrf51::TempSensorValue_t tmpSensorValue; /* this is a float (32-bit), user data */ } PACKED; -void periodicCallback(void) + + +void debounce_Callback(void) +{ + tic_debounce.detach(); + flag_set_debounce_tic = false; //not used + flag_update_io = true; //start advertising + /* Note that the buttonPressedCallback() executes in interrupt context, so it is safer to access + * BLE device API from the main thread. */ + //buttonState = PRESSED; +} + +//----- button rising --- +void buttonPressedCallback(void) { - alivenessLED = !alivenessLED; /* Do blinky on LED1 while we're waiting for BLE events. */ + + + //flag_update_io = true; + + tic_debounce.attach(debounce_Callback, 1); //ok to attach multiple times, first one wins + + + //buttonState = PRESSED; + + /* + if (flag_set_debounce_tic == false) + { + flag_set_debounce_tic = true; + + } + */ +} - /* Note that the periodicCallback() executes in interrupt context, so it is safer to do +//----- button falling --- +void buttonReleasedCallback(void) +{ + + + //flag_update_io = true; + + tic_debounce.attach(debounce_Callback, 1); + + + +} + + +void stop_adv_Callback(void) +{ + //stops advertising after X seconds + /* Note that the Callback() executes in interrupt context, so it is safer to do * heavy-weight sensor polling from the main thread (where we should be able to block safely, if needed). */ - triggerTempValueUpdate = true; + + //tic_adv.detach(); + + flag_detach_adv_tic = true; + //ble.gap().stopAdvertising(); + + +} + + + + + +void periodic_Callback(void) +{ + flag_update_io = true; + flag_periodic_call = true; } void setupApplicationData(ApplicationData_t &appData) { - static const uint16_t APP_SPECIFIC_ID_TEST = 0xFEFE; + // two byte ID: 0xFEFE + static const uint16_t APP_SPECIFIC_ID_TEST = 0xFEFE; //2 byte application ID appData.applicationSpecificId = APP_SPECIFIC_ID_TEST; - appData.tmpSensorValue = tempSensor.get(); + //appData.tmpSensorValue = tempSensor.get(); + appData.tmpSensorValue = mySensor; } + + /** * This function is called when the ble initialization process has failled */ @@ -72,43 +213,416 @@ if(ble.getInstanceID() != BLE::DEFAULT_INSTANCE) { return; } + + /* Set device name characteristic data */ + ble.gap().setDeviceName((const uint8_t *) DEVICE_NAME); /* Setup advertising payload */ + //set modes "no EDR", "discoverable" for beacon type advertisements ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); - ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_THERMOMETER); - ApplicationData_t appData; - setupApplicationData(appData); - ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::MANUFACTURER_SPECIFIC_DATA, (uint8_t *)&appData, sizeof(ApplicationData_t)); + + //set device profile in ADV/GATT + //ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_THERMOMETER); + + //set advertising data (ADV data) + //ApplicationData_t appData; + //setupApplicationData(appData); + //in /BLE_API/ble/GapAdvertisingData.h: sets payload (uuid) + //ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::MANUFACTURER_SPECIFIC_DATA, (uint8_t *)&appData, sizeof(ApplicationData_t)); + + //from GAP example + /* Sacrifice 2B of 31B to AdvType overhead, rest goes to AdvData array you define */ + ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::MANUFACTURER_SPECIFIC_DATA, AdvData, sizeof(AdvData)); + + /* Setup advertising parameters: not connectable */ + ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED); + ble.gap().setAdvertisingInterval(300); //one advertisment every 300ms. Self tickers, so you don't have to worry. + + + + //don't start advertising on init. Only advertise on pin interrupt. + //ble.gap().startAdvertising(); +} + - /* Setup advertising parameters */ - ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED); - ble.gap().setAdvertisingInterval(500); +//https://developer.mbed.org/users/MarceloSalazar/notebook/measuring-battery-voltage-with-nordic-nrf51x/ +void my_analogin_init(void) +{ + NRF_ADC->ENABLE = ADC_ENABLE_ENABLE_Enabled; + NRF_ADC->CONFIG = (ADC_CONFIG_RES_10bit << ADC_CONFIG_RES_Pos) | + (ADC_CONFIG_INPSEL_SupplyOneThirdPrescaling << ADC_CONFIG_INPSEL_Pos) | + (ADC_CONFIG_REFSEL_VBG << ADC_CONFIG_REFSEL_Pos) | + (ADC_CONFIG_PSEL_Disabled << ADC_CONFIG_PSEL_Pos) | + (ADC_CONFIG_EXTREFSEL_None << ADC_CONFIG_EXTREFSEL_Pos); +} - ble.gap().startAdvertising(); +uint16_t my_analogin_read_u16(void) +{ + NRF_ADC->CONFIG &= ~ADC_CONFIG_PSEL_Msk; + NRF_ADC->CONFIG |= ADC_CONFIG_PSEL_Disabled << ADC_CONFIG_PSEL_Pos; + NRF_ADC->TASKS_START = 1; + + + //while loop doesn't actually loop until reading comlete, use a wait. + while (((NRF_ADC->BUSY & ADC_BUSY_BUSY_Msk) >> ADC_BUSY_BUSY_Pos) == ADC_BUSY_BUSY_Busy) {}; + wait_ms(2); + + //save off RESULT before disabling. + //uint16_t myresult = (uint16_t)NRF_ADC->RESULT; + + //disable ADC to lower bat consumption + //NRF_ADC->TASKS_STOP = 1; + //NRF_ADC->ENABLE = ADC_ENABLE_ENABLE_Disabled; //disable to shutdown ADC & lower bat consumption + + return (uint16_t)NRF_ADC->RESULT; // 10 bit + //return myresult; } int main(void) { - ticker.attach(periodicCallback, 2); /* trigger sensor polling every 2 seconds */ + +#if MyDebugEnb + device.baud(9600); + device.printf("started sensor node 36 "); + device.printf("\r\n"); +#endif + + + + Timer myTimer; //timed advertising + myTimer.start(); + + + AdvData[0] = 0x44; //D + AdvData[1] = 0x45; //E + AdvData[2] = 0x43; //C + AdvData[3] = 0x22; //" + AdvData[4] = 0x76; //V volt + AdvData[5] = 0x6f; //o + AdvData[6] = 0x22; //" + AdvData[7] = 0x3a; //: + AdvData[8] = 0x24; //3 # + AdvData[9] = 0x24; //. # + AdvData[10] = 0x24; //1 # + AdvData[11] = 0x24; //1 # + AdvData[12] = 0x2c; //, + AdvData[13] = 0x22; //" mag + AdvData[14] = 0x6d; //a + AdvData[15] = 0x22; //" + AdvData[16] = 0x3a; //: + AdvData[17] = 0x24; //0 or 1, 30 or 31 + + button1.fall(buttonPressedCallback); + button1.rise(buttonReleasedCallback); + button1.mode(PullNone); + button1.fall(NULL); + button1.rise(NULL); BLE &ble = BLE::Instance(); ble.init(bleInitComplete); + + //debug uart + //device.baud(115200); + float bat_reading; //hold battery voltage reading (Vbg/Vcc) + + my_analogin_init();//routes band-gap to analog input /* SpinWait for initialization to complete. This is necessary because the * BLE object is used in the main loop below. */ - while (ble.hasInitialized() == false) { /* spin loop */ } + while (ble.hasInitialized() == false) { /* spin loop */ } + + //every X seconds, sends period update, up to 1800 (30 minutes) + tic_periodic.attach(periodic_Callback, 10); while (true) { - if (triggerTempValueUpdate) { + + uint16_t mySeconds =(uint16_t)(myTimer.read_ms()/1000); //problem: mySeconds is only 2 byte + //reading the ADV value, only goes up to 0-255; + //reading the uart: current time in seconds: -1782, goes negative. + //need to be able to count 1800 seconds since that's the length of timer. + +#if MyDebugEnb + device.printf("current time in seconds: %d \r\n", mySeconds); +#endif + //**** set which pin should be interrupt, set pullups *** + + //set both pins to pull-up, so they're not floating when we read state + button1.mode(PullUp); + button2.mode(PullUp); + + //wait_ms(300); //contact settle + + + //AdvData[12] is automatically CR? why? + + //0x33 0x2E 0x33 0x32 0x13 + // 3 . 3 2 CR + + //expect either button1 or button2 is grounded, b/c using SPDT reed switch + //the "common" pin on the reed switch should be on GND + uint8_t button1_state = button1.read(); + uint8_t button2_state = button2.read(); + + + //let's just update the pins on every wake. Insurance against const drain. + //if state == 0, pin is grounded. Unset interrupt and float pin + //set the other input + if ( (button1_state == 0) && (button2_state == 1) ) + { + magnet_near = 1; + //AdvData[4] = 0x11; //dont' set ADV data directly. Using json now, need spacing + //button1.disable_irq() //don't know if disables IRQ on port or pin + button1.fall(NULL); //disable interrupt + button1.rise(NULL); //disable interrupt + button1.mode(PullNone); //float pin to save battery + + //button2.disable_irq() //don't know if disables IRQ on port or pin + button2.fall(buttonReleasedCallback); //disable interrupt + button2.rise(buttonReleasedCallback); //disable interrupt + button2.mode(PullUp); //float pin to save battery + } + else if ( (button1_state == 1) && (button2_state == 0) ) //assume other pin is open circuit + { + magnet_near = 0; + //AdvData[4] = 0x22; //dont' set ADV data directly. Using json now, need spacing + //button1.disable_irq() //don't know if disables IRQ on port or pin + button1.fall(buttonReleasedCallback); //disable interrupt + button1.rise(buttonReleasedCallback); //disable interrupt + button1.mode(PullUp); //float pin to save battery + + //button2.disable_irq() //don't know if disables IRQ on port or pin + button2.fall(NULL); //disable interrupt + button2.rise(NULL); //disable interrupt + button2.mode(PullNone); //float pin to save battery + } + else //odd state, shouldn't happen, suck battery and pullup both pins + { + magnet_near = 2; + //AdvData[4] = 0x33; + //button1.disable_irq() //don't know if disables IRQ on port or pin + button1.fall(buttonReleasedCallback); //disable interrupt + button1.rise(buttonReleasedCallback); //disable interrupt + button1.mode(PullUp); //float pin to save battery + + //button2.disable_irq() //don't know if disables IRQ on port or pin + button2.fall(buttonReleasedCallback); //disable interrupt + button2.rise(buttonReleasedCallback); //disable interrupt + button2.mode(PullUp); //float pin to save battery + } + + + if (flag_update_io) { /* Do blocking calls or whatever hardware-specific action is * necessary to poll the sensor. */ + + //analog reading consumes 940uA if not disabled + bat_reading = (float)my_analogin_read_u16(); + bat_reading = (bat_reading * 3.6) / 1024.0; +#if MyDebugEnb + device.printf("bat reading: %f \r\n", bat_reading); +#endif + + //memset(&buffer[0], 0, sizeof(buffer)); //clear out buffer + //sprintf (buffer, "%f.2", bat_reading); //don't know what i'm doing + //sprintf (buffer, "%.2f", bat_reading); + //AdvData[8] = buffer[0]; //"3"=0x33 + //AdvData[9] = buffer[1]; //"."=0x2E + //AdvData[10] = buffer[2];//"3"=0x33 + //AdvData[11] = buffer[3];//"2"=0x32 + + //disable ADC + NRF_ADC->TASKS_STOP = 1; + NRF_ADC->ENABLE = ADC_ENABLE_ENABLE_Disabled; //disable to shutdown ADC & lower bat consumption + + //*********************************** + //form JSON string in ADV_DATA + //1) volts starts at AdvData[8] + + + memset(&AdvData[0], 0, sizeof(AdvData)); + uint8_t JSON_loc=0; //DEC"vo": <--- + + AdvData[JSON_loc] = 0x44; //D + JSON_loc++; + AdvData[JSON_loc] = 0x45; //E + JSON_loc++; + AdvData[JSON_loc] = 0x43; //C + JSON_loc++; + AdvData[JSON_loc] = mySeconds & 0xFF; //reserved for timer + JSON_loc++; + AdvData[JSON_loc] = (mySeconds >> 8) & 0xFF;; //reserved for timer + JSON_loc++; + AdvData[JSON_loc] = 0x22; //" + JSON_loc++; + AdvData[JSON_loc] = 0x76; //V volt + JSON_loc++; + AdvData[JSON_loc] = 0x6f; //o + JSON_loc++; + AdvData[JSON_loc] = 0x6C; //l + JSON_loc++; + AdvData[JSON_loc] = 0x74; //t + JSON_loc++; + AdvData[JSON_loc] = 0x22; //" + JSON_loc++; + AdvData[JSON_loc] = 0x3a; //: + JSON_loc++; + + + + //write battery voltage + uint8_t total_chars; + memset(&buffer[0], 0, sizeof(buffer)); //clear out buffer + total_chars = sprintf (buffer, "%.2f", bat_reading); //returns total number of characters + +#if MyDebugEnb + device.printf("char buff: %c%c%c%c \r\n", buffer[0], buffer[1], buffer[2], buffer[3]); + device.printf("num chars: %d \r\n", total_chars); +#endif + + for (int i=0; i < total_chars; i++) + { + AdvData[JSON_loc] = buffer[i]; + JSON_loc++; +#if MyDebugEnb + device.printf("JSON_loc: %d buf[]:%c \r\n", JSON_loc, buffer[i]); +#endif + } //JSON_loc left at location of next character + + //DEC"vo":3.11," + AdvData[JSON_loc] = 0x2c; //, + JSON_loc++; + + AdvData[JSON_loc] = 0x22; //" start mag + JSON_loc++; + + AdvData[JSON_loc] = 0x6d; //m + JSON_loc++; + + AdvData[JSON_loc] = 0x61; //a + JSON_loc++; + + AdvData[JSON_loc] = 0x67; //g + JSON_loc++; + + if (flag_periodic_call) + { + //AdvData[JSON_loc] = 0x2f; // "/" + //JSON_loc++; + AdvData[JSON_loc] = 0x2f; // "/" + JSON_loc++; + AdvData[JSON_loc] = 0x70; // "p" + JSON_loc++; + }//end if period call + + AdvData[JSON_loc] = 0x22; //" + JSON_loc++; + + AdvData[JSON_loc] = 0x3a; //: + JSON_loc++; + + //prep magnet location (1 or 0) for char[] + memset(&buffer[0], 0, sizeof(buffer)); //clear out buffer + //magnet_near is an integer + total_chars = sprintf (buffer, "%d", magnet_near); //returns total number of characters + for (int i=0; i < total_chars; i++) + { + AdvData[JSON_loc] = buffer[i]; + JSON_loc++; + } //JSON_loc left at location of next character + + //MUST null terminate for JSON to read correctly, else get intermittent JSON parse errors at gateway + //happens when string is shorter than last string, get trash left overs + //not really needed after clearning AdvData at start. + + + //AdvData[JSON_loc] = 0x0; //null terminate here + ApplicationData_t appData; setupApplicationData(appData); - ble.gap().updateAdvertisingPayload(GapAdvertisingData::MANUFACTURER_SPECIFIC_DATA, (uint8_t *) &appData, sizeof(ApplicationData_t)); + + + for (int i=0; i<16; i++) + { + src_buf[i] = AdvData[i+3]; + } - triggerTempValueUpdate = false; + //nrf_ecb_set_key(key_buf); + nrf_ecb_init(); + nrf_ecb_set_key(key_buf); + bool successful_ecb = nrf_ecb_crypt(des_buf, src_buf); +#if MyDebugEnb + device.printf("success ecb = %d \r\n", successful_ecb); + device.printf("src_buf: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x \r\n", src_buf[0], src_buf[1], src_buf[2], src_buf[3], src_buf[4], src_buf[5], src_buf[6], src_buf[7], src_buf[8], src_buf[9], src_buf[10], src_buf[11], src_buf[12], src_buf[13], src_buf[14], src_buf[15]); + device.printf("des_buf: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x \r\n", des_buf[0], des_buf[1], des_buf[2], des_buf[3], des_buf[4], des_buf[5], des_buf[6], des_buf[7], des_buf[8], des_buf[9], des_buf[10], des_buf[11], des_buf[12], des_buf[13], des_buf[14], des_buf[15]); +#endif + for (int i=0; i<16; i++) + { + AdvData[i+3] = des_buf[i]; + } + + //ble.gap().updateAdvertisingPayload(GapAdvertisingData::MANUFACTURER_SPECIFIC_DATA, (uint8_t *) &appData, sizeof(ApplicationData_t)); + ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::MANUFACTURER_SPECIFIC_DATA, AdvData, sizeof(AdvData)); + + flag_update_io = false; + flag_periodic_call = false; + + //GAP::AddressType_t *myType; + //GAP::Address_t myAddress + //ble_error_t getAddress(Gap::AddressType_t *typeP, Gap::Address_t address) + //ble.gap().getAddress(myType, myAddress); + //ble.gap().getAddress(Gap::AddressType_t *typeP, Gap::Address_t address); + uint8_t mac[6] = {0x0,0x0,0x0,0x0,0x0,0x0}; + ble.getAddress(0,mac); //NOTE: last byte of MAC (as shown on phone app) is at mac[0], not mac[6]; +#if MyDebugEnb + device.printf("mac = "); + for (int i=0; i<6; i++) + { + device.printf("%x:", mac[i]); + } + device.printf("\r\n"); +#endif + ble.gap().startAdvertising(); + tic_adv.attach(stop_adv_Callback, 2); /* trigger turn off advertisement after X seconds */ + + /* + Timer myTimer; //timed advertising + myTimer.start(); + uint32_t duration = myTimer.read_ms(); + + //do this as a ticker instead of keeping processor on + while (duration < 15000) //advertise for 1000 ms + { + duration = myTimer.read_ms(); //read miliseconds + } + myTimer.stop(); + ble.gap().stopAdvertising(); + */ + + }//end flag_update_io + + + /* + if (flag_set_debounce_tic == true) + { + tic_debounce.attach(); + //flag_set_debounce_tic = false; + } + */ + + //if (trigger_Detach_ADV_Tick == false) + //{ + //} + if (flag_detach_adv_tic == true) //Stop Advertising + { + ble.gap().stopAdvertising(); //may be safer to execute BLE operations in main + tic_adv.detach(); + flag_detach_adv_tic = false; + } + //device.printf("Input Voltage: %f\n\r",bat_reading); + + ble.waitForEvent(); //sleeps until interrupt + - ble.waitForEvent(); - } -} + }//end forever while +}//end main