Nicholas Refalo / Mbed 2 deprecated BLE_Sensor

Dependencies:   mbed BLE_API nRF51822

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 /*  
00002  * Copyright (c) Eric Tsai 2017
00003  *
00004  *
00005  * Licensed under the Apache License, Version 2.0 (the "License");
00006  * you may not use this file except in compliance with the License.
00007  * You may obtain a copy of the License at
00008  *
00009  *     http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS,
00013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  *
00017  *
00018  * Credit: started with the basic BLE Temperature Beacon code from mbed Bluetooth Low Energy team
00019  * https://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_TemperatureBeacon/file/0a8bbb6dea16/main.cpp
00020  *
00021  * BLE sensor as Beacon advertisements.  Intended to function with specific BLE observer.
00022  * Tested on nRF51822 targets on mbed.
00023  * keywords:  todo, tochange
00024 */
00025 
00026 
00027 extern "C"
00028 {
00029    #include "nrf_ecb.h"  //required to call the ecb functions for encryption
00030 }
00031  
00032 #include "mbed.h"
00033 #include "toolchain.h"
00034 #include "ble/BLE.h"
00035 #include "TMP_nrf51/TMP_nrf51.h"
00036 
00037 
00038 /*******************************************************************************************
00039  * START tochange: items that may need customization depending on sensors, hardware, and desired behavior
00040 *******************************************************************************************/
00041 const uint16_t Periodic_Update_Seconds = 20; //number of seconds between periodic I/O status re-transmits 900s =15 min.
00042 #define MyDebugEnb 0  //enables serial output for debug, consumes ~1mA when idle
00043 uint8_t magnet_near=0;  //this I/O, specifically for reed switch sensor
00044 
00045 
00046 /* hardware interrupt pins, selected based on hardware
00047  *Syntax:  Pin "P0.4" on nRF51822 documentation is mbed "p4".
00048  * InterruptIn is pulled-up.  GND the pin to activate.
00049 */
00050 //InterruptIn button1(p0);    //nRF51822 P0.0
00051 //InterruptIn button2(p1);    //nRF51822 P0.1
00052 
00053 InterruptIn button1(p9);     //nRF51822 P0.0
00054 InterruptIn button2(p11);    //nRF51822 P0.1
00055 /******************************************************************************************
00056  * END tochange
00057 *******************************************************************************************/
00058 
00059 
00060 #if MyDebugEnb
00061 // if you see ~1mA consumption during sleep, that's because MyDebugEnb==1, it's enabled.
00062 Serial device(p9, p11);  //nRF51822 uart :  TX=p9.  RX=p11
00063 #endif
00064 
00065 static Ticker Tic_Stop_Adv;   //used to stop advertising after X seconds
00066 static Ticker Tic_Debounce; //debounce I/O
00067 static Ticker Tic_Periodic; //transmit sensor data on a periodic basis outside I/O events
00068 
00069 const uint16_t Periodicity = 1800;   //birthday periodicity used for spoof checking, must match gateway. Should be 1800 seconds for 30minutes
00070 static Timer Tmr_From_Birthday;  //holds number of seconds since birthday, for spoof detection
00071 static Ticker Tic_Birthday; //resets Tmr_From_Birthday every Periodicity seconds, for spoof detection
00072 
00073 
00074 static bool Flag_Update_IO = false;  //flag to indicate event is hardware interrupt
00075 static bool Flag_Periodic_Call = false;  //flag to indicate event is periodic callback
00076 static bool Flag_Detach_Adv_Tic = false;  //flag to stop advertising
00077 
00078 /* Optional: Device Name, add for human read-ability */
00079 const static char     DEVICE_NAME[] = "LOL";
00080 
00081 
00082 //Advertisement Data
00083 //note:  AdvData[] holds bytes [5] to byte [30] of entire advertising data.  The user content part after ADV flag and header
00084 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};  //26 Bytes manufacturer specific data
00085 char buffer[10]={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; //hold I/O reading json
00086 char bat_volt_char[6] = {0, 0, 0, 0, 0, 0}; //hold json for battery reading
00087 uint8_t Adv_First_Section[10];  //holds the first several bytes with a pattern indicating this sensor is "one of ours" 
00088 uint8_t mac_reverse[6] = {0x0,0x0,0x0,0x0,0x0,0x0};  //mac address for this module
00089 
00090 /*****  Advertisement structure is 31 Bytes  ****************
00091 
00092 https://docs.mbed.com/docs/ble-intros/en/latest/Advanced/CustomGAP/
00093 
00094 Full Advertisement:
00095 First 5 bytes are set by stack according to flag and header parameters.
00096 Last 26 bytes are user data
00097 -- tabbed --
00098 Byte 0  |   AD1 Length  |       0x02    |   AD1 is 2 bytes long
00099 Byte 1  |   AD1 Type    |       0x01    |   AD1 Data interpreted as flag
00100 Byte 2  |   AD1 Data 0  |       0x06    |   AD1 Data flag mean "00000110"
00101 Byte 3  |   AD2 Length  |       0x1B    |   AD2 is 27 bytes (0x1B) long (rest of this data)
00102 Byte 4  |   AD2 Type    |       0xFF    |   0xFF mean Manufacturer Specific Data
00103 Byte 5  |   AD2 Data 0  |   ADV_Data[0] |   "our device" flag, MAC[3]
00104 Byte 6  |   AD2 Data 1  |   ADV_Data[1] |   "out device" flag, MAC[2]
00105 Byte 7  |   AD2 Data 2  |   ADV_Data[2] |   "out device" flag, MAC[1]
00106 Byte 8  |   AD2 Data 3  |   ADV_Data[3] |   "out device" flag, MAC[0]
00107 Byte 9  |   AD2 Data 4  |   ADV_Data[4] |   battery voltage json MSB, ie 3 in 3.14
00108 Byte 10 |   AD2 Data 5  |   ADV_Data[5] |   battery voltage json
00109 Byte 11 |   AD2 Data 6  |   ADV_Data[6] |   battery voltage json
00110 Byte 12 |   AD2 Data 7  |   ADV_Data[7] |   battery voltage json LSB, ie 4 in 3.14
00111 Byte 13 |   AD2 Data 8  |   ADV_Data[8] |   reserved
00112 Byte 14 |   AD2 Data 9  |   ADV_Data[9] |   reserved
00113 Byte 15 |   AD2 Data 10 |   ADV_Data[10] Encrypted  |   spoof - clock high byte, range 0 to 1800 seconds
00114 Byte 16 |   AD2 Data 11 |   ADV_Data[11] Encrypted  |   spoof - clock low byte
00115 Byte 17 |   AD2 Data 12 |   ADV_Data[12] Encrypted  |   Xmit_Cnt - increments per transmit event, 0-255
00116 Byte 18 |   AD2 Data 13 |   ADV_Data[13] Encrypted  |   JSON[0]
00117 Byte 19 |   AD2 Data 14 |   ADV_Data[14] Encrypted  |   JSON[1]
00118 Byte 20 |   AD2 Data 15 |   ADV_Data[15] Encrypted  |   JSON[2]
00119 Byte 21 |   AD2 Data 16 |   ADV_Data[16] Encrypted  |   JSON[3]
00120 Byte 22 |   AD2 Data 17 |   ADV_Data[17] Encrypted  |   JSON[4]
00121 Byte 23 |   AD2 Data 18 |   ADV_Data[18] Encrypted  |   JSON[5]
00122 Byte 24 |   AD2 Data 19 |   ADV_Data[19] Encrypted  |   JSON[6]
00123 Byte 25 |   AD2 Data 20 |   ADV_Data[20] Encrypted  |   JSON[7]
00124 Byte 26 |   AD2 Data 21 |   ADV_Data[21] Encrypted  |   JSON[8]
00125 Byte 27 |   AD2 Data 22 |   ADV_Data[22] Encrypted  |   JSON[9]
00126 Byte 28 |   AD2 Data 23 |   ADV_Data[23] Encrypted  |   JSON[10]
00127 Byte 29 |   AD2 Data 24 |   ADV_Data[24] Encrypted  |   JSON[11]
00128 Byte 30 |   AD2 Data 25 |   ADV_Data[25] Encrypted  |   JSON[12]
00129 
00130 ***************************************************/
00131 
00132 
00133 static uint8_t key[16] = {0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4};
00134 //26 bytes adv data
00135 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 */
00136 //static uint8_t key_buf[16] = {0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4};
00137 static uint8_t key_buf[16] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x1, 0x2};
00138 static uint8_t src_buf[16] = {0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4};
00139 static uint8_t des_buf[16] = {0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4};
00140 
00141 uint8_t Xmit_Cnt = 1;
00142 
00143 
00144 
00145 /* **** NOT USED **** */
00146 //16byte UUID loading happens here
00147 //Look at <GapAdvertisingData.h> for rest of definition
00148 struct ApplicationData_t {
00149     //Byte 0:  AppID High Byte
00150     //Byte 1:  AppID Low Byte
00151     //Byte 2:  sensor High Word
00152     //Byte 3:
00153     //Byte 4:
00154     //Byte 5:  sensor Low Byte
00155     
00156     
00157     //app ID is 16 bit, (0xFEFE)
00158     uint16_t    applicationSpecificId; /* An ID used to identify temperature value in the manufacture specific AD data field */
00159     
00160     TMP_nrf51::TempSensorValue_t tmpSensorValue;        /* this is a float (32-bit), user data */
00161 } PACKED;
00162 
00163 
00164 
00165 void debounce_Callback(void)
00166 {
00167     Tic_Debounce.detach();
00168     Flag_Update_IO = true;  //start advertising
00169     /* Note that the buttonPressedCallback() executes in interrupt context, so it is safer to access
00170      * BLE device API from the main thread. */
00171 
00172 }
00173 
00174 //ISR for I/O interrupt
00175 void buttonPressedCallback(void)
00176 {
00177     Tic_Debounce.attach(debounce_Callback, 1); //ok to attach multiple times, recent one wins
00178 }
00179 
00180 //ISR for I/O interrupt
00181 void buttonReleasedCallback(void)
00182 {
00183     
00184     Tic_Debounce.attach(debounce_Callback, 1);  
00185 }
00186 
00187 
00188 void stop_adv_Callback(void)
00189 {
00190     //stops advertising after X seconds
00191     /* Note that the Callback() executes in interrupt context, so it is safer to do
00192      * heavy-weight sensor polling from the main thread (where we should be able to block safely, if needed). */
00193     Flag_Detach_Adv_Tic = true;
00194 
00195 }
00196 
00197 /* ****************************************
00198  * Decides what actions need to be performed on periodic basis
00199 *******************************************/
00200 void periodic_Callback(void)
00201 {
00202     Flag_Update_IO = true;
00203     Flag_Periodic_Call = true;
00204 }
00205 
00206 
00207 /* ****************************************
00208  * No RTC available, tickers only have a 35 minute range.
00209  * So periodicity for spoof avoidance is set to 30 minutes
00210 *******************************************/
00211 void clock_reset_Callback(void)
00212 {
00213 #if MyDebugEnb
00214     device.printf("===== reset timer =====");
00215     device.printf("\r\n");
00216 #endif
00217     Tmr_From_Birthday.reset();
00218 };
00219 
00220 
00221 void setupApplicationData(ApplicationData_t &appData)
00222 {
00223     // two byte ID:  0xFEFE
00224     static const uint16_t APP_SPECIFIC_ID_TEST = 0xFEFE;        //2 byte application ID
00225 
00226     appData.applicationSpecificId = APP_SPECIFIC_ID_TEST;
00227 }
00228 
00229 
00230 
00231 /**
00232  * This function is called when the ble initialization process has failled
00233  */
00234 void onBleInitError(BLE &ble, ble_error_t error)
00235 {
00236     /* Initialization error handling should go here */
00237 }
00238 
00239 
00240 
00241 /**
00242  * Callback triggered when the ble initialization process has finished
00243  */
00244 void bleInitComplete(BLE::InitializationCompleteCallbackContext *params)
00245 {
00246     BLE&        ble   = params->ble;
00247     ble_error_t error = params->error;
00248 
00249     if (error != BLE_ERROR_NONE) {
00250         /* In case of error, forward the error handling to onBleInitError */
00251         onBleInitError(ble, error);
00252         return;
00253     }
00254 
00255     /* Ensure that it is the default instance of BLE */
00256     if(ble.getInstanceID() != BLE::DEFAULT_INSTANCE) {
00257         return;
00258     }
00259     
00260     /* Set device name characteristic data */
00261     ble.gap().setDeviceName((const uint8_t *) DEVICE_NAME);
00262 
00263     /* Setup advertising payload */
00264     //set modes "no EDR", "discoverable" for beacon type advertisements
00265     ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
00266     
00267 
00268     //from GAP example
00269     /* Sacrifice 2B of 31B to AdvType overhead, rest goes to AdvData array you define */
00270     ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::MANUFACTURER_SPECIFIC_DATA, AdvData, sizeof(AdvData));
00271 
00272     /* Setup advertising parameters:  not connectable */
00273     ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED);
00274     ble.gap().setAdvertisingInterval(900);  //one advertisment every 300ms.  Self tickers, so you don't have to worry.
00275 
00276 }
00277 
00278 
00279 //not needed anymore
00280 void my_analogin_init(void)
00281 {
00282     
00283     NRF_ADC->CONFIG = (ADC_CONFIG_RES_10bit << ADC_CONFIG_RES_Pos) |
00284                       (ADC_CONFIG_INPSEL_SupplyOneThirdPrescaling << ADC_CONFIG_INPSEL_Pos) |
00285                       //(ADC_CONFIG_INPSEL_AnalogInputOneThirdPrescaling << ADC_CONFIG_INPSEL_Pos) |
00286                       (ADC_CONFIG_REFSEL_VBG << ADC_CONFIG_REFSEL_Pos) |
00287                       (ADC_CONFIG_PSEL_Disabled << ADC_CONFIG_PSEL_Pos) |
00288                       //(ADC_CONFIG_PSEL_AnalogInput4 << ADC_CONFIG_PSEL_Pos) |
00289                       (ADC_CONFIG_EXTREFSEL_None << ADC_CONFIG_EXTREFSEL_Pos);
00290     NRF_ADC->ENABLE = ADC_ENABLE_ENABLE_Enabled;
00291 }
00292 
00293 
00294 /* ****************************************
00295  * Read battery voltage using bandgap reference
00296  * shunt Vdd to ADC, thanks to Marcelo Salazar's notes here:
00297  * https://developer.mbed.org/users/MarceloSalazar/notebook/measuring-battery-voltage-with-nordic-nrf51x/
00298 *******************************************/
00299 uint16_t read_bat_volt(void)
00300 {
00301     //10 bit resolution, route Vdd as analog input, set ADC ref to VBG band gap
00302     //disable analog pin select "PSEL" because we're using Vdd as analog input
00303     //no external voltage reference
00304     NRF_ADC->CONFIG = (ADC_CONFIG_RES_10bit << ADC_CONFIG_RES_Pos) |
00305                       (ADC_CONFIG_INPSEL_SupplyOneThirdPrescaling << ADC_CONFIG_INPSEL_Pos) |
00306                       //(ADC_CONFIG_INPSEL_AnalogInputOneThirdPrescaling << ADC_CONFIG_INPSEL_Pos) |
00307                       (ADC_CONFIG_REFSEL_VBG << ADC_CONFIG_REFSEL_Pos) |
00308                       (ADC_CONFIG_PSEL_Disabled << ADC_CONFIG_PSEL_Pos) |
00309                       //(ADC_CONFIG_PSEL_AnalogInput4 << ADC_CONFIG_PSEL_Pos) |
00310                       (ADC_CONFIG_EXTREFSEL_None << ADC_CONFIG_EXTREFSEL_Pos);
00311 
00312     //NRF_ADC->CONFIG     &= ~ADC_CONFIG_PSEL_Msk;
00313     //NRF_ADC->CONFIG     |= ADC_CONFIG_PSEL_Disabled << ADC_CONFIG_PSEL_Pos;
00314     NRF_ADC->ENABLE = ADC_ENABLE_ENABLE_Enabled;
00315     NRF_ADC->TASKS_START = 1;
00316     
00317     
00318     //while loop doesn't actually loop until reading comlete, use a wait.
00319     while (((NRF_ADC->BUSY & ADC_BUSY_BUSY_Msk) >> ADC_BUSY_BUSY_Pos) == ADC_BUSY_BUSY_Busy) {};
00320     wait_ms(1);
00321 
00322     //save off RESULT before disabling.
00323     //uint16_t myresult = (uint16_t)NRF_ADC->RESULT;
00324     
00325     //disable ADC to lower bat consumption
00326     NRF_ADC->TASKS_STOP = 1;
00327     //NRF_ADC->ENABLE = ADC_ENABLE_ENABLE_Disabled;    //disable to shutdown ADC & lower bat consumption
00328     
00329     return (uint16_t)NRF_ADC->RESULT; // 10 bit
00330     //return myresult;
00331 }  //end read_bat_volt
00332 
00333 
00334 
00335 /* ****************************************
00336  * Read battery voltage using bandgap reference
00337  * shunt analog pin to ADC, from API here
00338  * https://developer.mbed.org/users/mbed_official/code/mbed-src/file/cb4253f91ada/targets/hal/TARGET_NORDIC/TARGET_NRF51822/analogin_api.c
00339 *******************************************/
00340 uint16_t read_ADC_pin(void)
00341 {
00342 
00343     //10 bit resolution, route PSEL pin as 1/3 input sel,
00344     //set ADC ref to VBG band gap
00345     //set AnalogInput4 as input pin (this is P0.03)
00346     //no external voltage reference
00347     NRF_ADC->CONFIG = (ADC_CONFIG_RES_10bit << ADC_CONFIG_RES_Pos) |
00348                       //(ADC_CONFIG_INPSEL_SupplyOneThirdPrescaling << ADC_CONFIG_INPSEL_Pos) |
00349                       (ADC_CONFIG_INPSEL_AnalogInputOneThirdPrescaling << ADC_CONFIG_INPSEL_Pos) |
00350                       (ADC_CONFIG_REFSEL_VBG << ADC_CONFIG_REFSEL_Pos) |
00351                        //ADC_CONFIG_PSEL_Disabled << ADC_CONFIG_PSEL_Pos) |
00352                       (ADC_CONFIG_PSEL_AnalogInput4 << ADC_CONFIG_PSEL_Pos) |
00353                       (ADC_CONFIG_EXTREFSEL_None << ADC_CONFIG_EXTREFSEL_Pos);
00354     //set pin select to AnalogInput4 = pin 7 = p0.03 = AIN4
00355     //NRF_ADC->CONFIG     &= ~ADC_CONFIG_PSEL_Msk;
00356     //NRF_ADC->CONFIG     |= ADC_CONFIG_PSEL_AnalogInput4 << ADC_CONFIG_PSEL_Pos;
00357     NRF_ADC->ENABLE = ADC_ENABLE_ENABLE_Enabled;
00358     NRF_ADC->TASKS_START = 1;
00359     
00360     
00361     //while loop doesn't actually loop until reading comlete, use a wait.
00362     while (((NRF_ADC->BUSY & ADC_BUSY_BUSY_Msk) >> ADC_BUSY_BUSY_Pos) == ADC_BUSY_BUSY_Busy) {};
00363     wait_ms(1);     //needed because busy while loop doesn't run.
00364 
00365     //save off RESULT before disabling.
00366     //uint16_t myresult = (uint16_t)NRF_ADC->RESULT;
00367     
00368     //disable ADC to lower bat consumption
00369     //NRF_ADC->TASKS_STOP = 1;
00370     //NRF_ADC->ENABLE = ADC_ENABLE_ENABLE_Disabled;    //disable to shutdown ADC & lower bat consumption
00371     
00372     return (uint16_t)NRF_ADC->RESULT; // 10 bit
00373     //return myresult;
00374 }  //end read_ADC_pin
00375 
00376 
00377 /* ****************************************
00378  * Pattern scheme indicating "one of ours"
00379  * generate first part of ADV data so that observer can recognize it as "one of ours".
00380  * use specific schema to decide how we're recognizing our sensor ADV
00381 *******************************************/
00382 void hash_first_section(uint8_t * dest, const uint8_t * mac_addr, const char * bat_volt_str)
00383 {
00384     dest[0] = mac_addr[3];
00385     dest[1] = mac_addr[2];
00386     dest[2] = mac_addr[1];
00387     dest[3] = mac_addr[0];
00388     dest[4] = bat_volt_str[0];
00389     dest[5] = bat_volt_str[1];
00390     dest[6] = bat_volt_str[2];
00391     dest[7] = bat_volt_str[3];
00392     dest[8] = 0x10;
00393     dest[9] = 0x11;
00394     #if MyDebugEnb
00395         
00396         device.printf("hash array: ");
00397         for (int i=0; i<10; i++)
00398         {
00399             device.printf("%x ", dest[i]);
00400         }
00401         device.printf("\r\n");
00402     #endif
00403 }
00404 
00405 
00406 /* ****************************************
00407  * 
00408  * Main Loop
00409  * 
00410 *******************************************/
00411 int main(void)
00412 {
00413 
00414     #if MyDebugEnb
00415         device.baud(9600);
00416         device.printf("started sensor node 36 ");
00417         device.printf("\r\n");
00418     #endif
00419 
00420     
00421     Tmr_From_Birthday.start();      //tracks # sec since birthday
00422 
00423 
00424     BLE &ble = BLE::Instance();
00425     ble.init(bleInitComplete);
00426     
00427     float bat_reading;  //hold battery voltage reading (Vbg/Vcc)
00428     
00429     my_analogin_init();//routes band-gap to analog input
00430 
00431     /* SpinWait for initialization to complete. This is necessary because the
00432      * BLE object is used in the main loop below. */
00433     while (ble.hasInitialized() == false) { /* spin loop */ }
00434     
00435     //every X seconds, sends period update, up to 1800 (30 minutes)
00436     Tic_Periodic.attach(periodic_Callback, Periodic_Update_Seconds);  //send updated I/O every x seconds
00437     Tic_Birthday.attach(clock_reset_Callback, Periodicity);  //clock algorithm periodicity
00438 
00439 
00440     ble.getAddress(0,mac_reverse);  //last byte of MAC (as shown on phone app) is at mac[0], not mac[6];
00441     #if MyDebugEnb
00442         device.printf("mac = ");
00443         for (int i=0; i<6; i++) //prints out MAC address in reverse order; opps.
00444         {
00445             device.printf("%x:", mac_reverse[i]);
00446         }
00447         device.printf("\r\n");
00448     #endif
00449     while (true) 
00450     {  //Main Loop
00451 
00452         uint16_t seconds_Old =(uint16_t)(Tmr_From_Birthday.read_ms()/1000); // 0-1800 seconds (30 minutes)
00453 
00454         #if MyDebugEnb
00455             device.printf("current time in seconds: %d \r\n", seconds_Old);
00456         #endif
00457 
00458         //set both pins to pull-up, so they're not floating when we read state
00459         button1.mode(PullUp);
00460         button2.mode(PullUp);
00461         
00462         //expect either button1 or button2 is grounded, b/c using SPDT reed switch
00463         //the "common" pin on the reed switch should be on GND
00464         uint8_t button1_state = button1.read();
00465         uint8_t button2_state = button2.read();
00466         
00467         
00468         //let's just update the pins on every wake.  Insurance against const drain.
00469         //if state == 0, pin is grounded.  Unset interrupt and float pin, set the other pin for ISR
00470         if ( (button1_state == 0) && (button2_state == 1) )
00471         {
00472             magnet_near = 1;
00473             //button1.disable_irq() //don't know if disables IRQ on port or pin
00474             button1.fall(NULL);     //disable interrupt
00475             button1.rise(NULL);     //disable interrupt
00476             button1.mode(PullNone); //float pin to save battery
00477             
00478             //button2.disable_irq() //don't know if disables IRQ on port or pin
00479             button2.fall(buttonReleasedCallback);     //enable interrupt
00480             button2.rise(buttonReleasedCallback);     //enable interrupt
00481             button2.mode(PullUp); //pull up on pin to get interrupt
00482             #if MyDebugEnb
00483             device.printf("=== button 1!  %d seconds=== \r\n", seconds_Old);
00484             #endif
00485         }  //end if button2
00486         else if ( (button1_state == 1) && (button2_state == 0) )       //assume other pin is open circuit
00487         {
00488             magnet_near = 0;
00489             //button1.disable_irq() //don't know if disables IRQ on port or pin
00490             button1.fall(buttonReleasedCallback);     //enable interrupt
00491             button1.rise(buttonReleasedCallback);     //enable interrupt
00492             button1.mode(PullUp); //pull up on pin to get interrupt
00493             
00494             //button2.disable_irq() //don't know if disables IRQ on port or pin
00495             button2.fall(NULL);     //disable interrupt
00496             button2.rise(NULL);     //disable interrupt
00497             button2.mode(PullNone); //float pin to save battery
00498             #if MyDebugEnb
00499             device.printf("=== button 2! === %d seconds\r\n", seconds_Old);
00500             #endif
00501         }  //end if button1
00502         else    //odd state, shouldn't happen, suck battery and pullup both pins
00503         {
00504             magnet_near = 2;
00505             //AdvData[4] = 0x33;
00506             //button1.disable_irq() //don't know if disables IRQ on port or pin
00507             button1.fall(buttonReleasedCallback);     //disable interrupt
00508             button1.rise(buttonReleasedCallback);     //disable interrupt
00509             button1.mode(PullUp); //float pin to save battery
00510             
00511             //button2.disable_irq() //don't know if disables IRQ on port or pin
00512             button2.fall(buttonReleasedCallback);     //disable interrupt
00513             button2.rise(buttonReleasedCallback);     //disable interrupt
00514             button2.mode(PullUp); //float pin to save battery
00515             #if MyDebugEnb
00516             device.printf("no buttons!! %d seconds\r\n", seconds_Old);
00517             #endif
00518         }  //end odd state
00519         
00520         
00521         if (Flag_Update_IO) {
00522             /* Do blocking calls or whatever hardware-specific action is
00523              * necessary to poll the sensor. */
00524 
00525             //call attach again on periodic update to reset ticker
00526             //next periodic updates happens Perioidc_Update_Seconds after I/O events
00527             Tic_Periodic.attach(periodic_Callback, Periodic_Update_Seconds);   
00528             Xmit_Cnt++; //increment transmit counter when updating I/O
00529             
00530             
00531             //read and convert battery voltage
00532             bat_reading = (float)read_bat_volt();    
00533             bat_reading = (bat_reading * 3.6) / 1024.0;
00534             #if MyDebugEnb
00535             device.printf("bat reading: %f \r\n", bat_reading);
00536             #endif
00537             //write battery voltage
00538             uint8_t total_chars;
00539             memset(&bat_volt_char[0], 0, sizeof(bat_volt_char));      //clear out buffer
00540             //convert battery voltage float value to string reprsentation to 2 decimal places, and save the size of string.
00541             total_chars = sprintf (bat_volt_char, "%.2f", bat_reading);
00542             
00543             
00544             //read and convert analog voltage.  Comment out this section if note needed, saves some battery
00545             NRF_ADC->TASKS_STOP = 1;
00546             float analogreading;
00547             analogreading = (float)read_ADC_pin();
00548             analogreading = (analogreading * 3.6) / 1024.0;
00549             #if MyDebugEnb
00550             device.printf("separate analog reading: %.02f \r\n", analogreading);
00551             #endif
00552             
00553             //disable ADC to save power
00554             NRF_ADC->TASKS_STOP = 1;
00555             NRF_ADC->ENABLE = ADC_ENABLE_ENABLE_Disabled;    //disable to shutdown ADC & lower bat consumption
00556 
00557 
00558             #if MyDebugEnb
00559             device.printf("char buff: %c%c%c%c \r\n", bat_volt_char[0], bat_volt_char[1], bat_volt_char[2], bat_volt_char[3]);
00560             device.printf("num chars: %d \r\n", total_chars);
00561             #endif
00562 
00563 
00564             //Generate "First Section" for ADV_Data so gateway will recognize our advertisement pattern
00565             hash_first_section(Adv_First_Section, mac_reverse, bat_volt_char);
00566 
00567 
00568             /* ****************************************
00569              * start writing out ADVData array
00570              * todo: this is easy to write but hard to read.  Maybe make it easy to read and hard to write?
00571              ******************************************/
00572             memset(&AdvData[0], 0, sizeof(AdvData));
00573             uint8_t JSON_loc=0; //AdvData[0]
00574 
00575             AdvData[0] = Adv_First_Section[0];          //"our device" flag, MAC[3]
00576             JSON_loc++; //JSON_loc == 1
00577             AdvData[1] = Adv_First_Section[1];          //"out device" flag, MAC[2]...
00578             JSON_loc++; //JSON_loc == 2
00579             AdvData[2] = Adv_First_Section[2];
00580             JSON_loc++; //JSON_loc == 3
00581             AdvData[3] = Adv_First_Section[3];
00582             JSON_loc++;  //JSON_loc == 4
00583             AdvData[4] = Adv_First_Section[4];
00584             JSON_loc++;  //JSON_loc == 5
00585             AdvData[5] = Adv_First_Section[5];
00586             JSON_loc++;  //JSON_loc == 6
00587             AdvData[6] = Adv_First_Section[6];
00588             JSON_loc++;
00589             AdvData[7] = Adv_First_Section[7];
00590             JSON_loc++;
00591             AdvData[8] = Adv_First_Section[8];
00592             JSON_loc++;
00593             AdvData[9] = Adv_First_Section[9];
00594             JSON_loc++;
00595 
00596             #if MyDebugEnb
00597                 device.printf("ADV first 10 array: ");
00598                 for (int i=0; i<10; i++)
00599                 {
00600                     device.printf("%x ", AdvData[i]);
00601                 }
00602                 device.printf("\r\n");
00603             #endif
00604 
00605 
00606             JSON_loc = 10;
00607             //Start of encrypted user data
00608             
00609             //[10] and [11] hold 2 bytes for how many seconds since birthday, little endian
00610             AdvData[10] = seconds_Old & 0xFF;
00611             JSON_loc++;
00612             AdvData[11] = (seconds_Old >> 8) & 0xFF;
00613             JSON_loc++;
00614             
00615             AdvData[12] = Xmit_Cnt;
00616             JSON_loc++;
00617             
00618             //start of jason data
00619             //"mag":
00620             JSON_loc = 13;
00621             AdvData[JSON_loc] = 0x22;       //ADV_Data[13] = "
00622             JSON_loc++; //14
00623             
00624             AdvData[JSON_loc] = 0x6d;       //ADV_Data[14] = m
00625             JSON_loc++; //15
00626             
00627             AdvData[JSON_loc] = 0x61;       //ADV_Data[15] = a
00628             JSON_loc++; //16
00629             
00630             AdvData[JSON_loc] = 0x67;       //ADV_Data[16] = g
00631             JSON_loc++; //17
00632             
00633             //for periodic calls, we want to add an extra mqtt level "p", using "/p"
00634             //to delineate between MQTT publishes from real world I/O interrupts vs timer interrupts
00635             if (Flag_Periodic_Call)
00636             {
00637                 AdvData[JSON_loc] = 0x2f;       // ADV_Data[17] = /
00638                 JSON_loc++;  //18
00639                 AdvData[JSON_loc] = 0x70;       // ADV_Data[18] =p
00640                 JSON_loc++;  //19
00641             }
00642             
00643             AdvData[JSON_loc] = 0x22;       //ADV_Data[17 or 19] = "   
00644             JSON_loc++; //20
00645 
00646             AdvData[JSON_loc] = 0x3a;       //ADV_Data[18 or 20] = :
00647             JSON_loc++; //21
00648             
00649             //convert magnet variable to string, for magnet sensor, this is easy
00650             //since we only have 1 or 0, but this also works for analog values
00651             memset(&buffer[0], 0, sizeof(buffer));      //clear out buffer
00652             total_chars = sprintf (buffer, "%d", magnet_near);    //returns total number of characters, which is 1 character.
00653             for (int i=0; i < total_chars; i++)
00654             {
00655                 AdvData[JSON_loc] = buffer[i];
00656                 JSON_loc++; //23
00657             } //JSON_loc left at location of next character
00658             
00659                         
00660             //AdvData[JSON_loc] = 0x0;    //since AdvData was cleared to start with, we don't need to null term
00661 
00662             ApplicationData_t appData;
00663             setupApplicationData(appData);
00664             
00665             /*********************
00666              * start encrypting last 16 bytes of ADV_Data
00667             *********************/
00668             for (int i=0; i<16; i++)
00669             {
00670                 src_buf[i] = AdvData[i+10]; //start of encrypted section is at AdvData[10]
00671             }
00672             nrf_ecb_init();
00673             nrf_ecb_set_key(key_buf);
00674             bool successful_ecb = nrf_ecb_crypt(des_buf, src_buf);
00675             #if MyDebugEnb
00676                 device.printf("success ecb = %d \r\n", successful_ecb);
00677                 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]);
00678                 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]);
00679             #endif
00680             for (int i=0; i<16; i++)  //replace last 16 bytes with encrypted 16 bytes
00681             {
00682                 AdvData[i+10] = des_buf[i];
00683             }
00684             
00685             //set payload for advertisement to our custom manufactured data.  First 5 bytes is BLE standard, last 26 bytes is our array
00686             //ble.gap().updateAdvertisingPayload(GapAdvertisingData::MANUFACTURER_SPECIFIC_DATA, (uint8_t *) &appData, sizeof(ApplicationData_t));
00687             ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::MANUFACTURER_SPECIFIC_DATA, AdvData, sizeof(AdvData));
00688             
00689             Flag_Update_IO = false;
00690             Flag_Periodic_Call = false;
00691             
00692             ble.gap().startAdvertising();
00693             Tic_Stop_Adv.attach(stop_adv_Callback, 3); /* trigger turn off advertisement after X seconds */
00694         
00695         }//end Flag_Update_IO
00696         
00697         
00698         if (Flag_Detach_Adv_Tic == true)    //ticker callback flag to stop advertising
00699         {
00700             ble.gap().stopAdvertising();    //may be safer to execute BLE operations in main
00701             Tic_Stop_Adv.detach();
00702             Flag_Detach_Adv_Tic = false;
00703         }
00704 
00705         
00706         ble.waitForEvent(); //sleeps until interrupt form ticker or I/O
00707     }//end forever while
00708 }//end main