Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: mbed BLE_API nRF51822
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
Generated on Sat Jul 30 2022 22:14:18 by
1.7.2