/** * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA * * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form, except as embedded into a Nordic * Semiconductor ASA integrated circuit in a product or a software update for * such product, must reproduce the above copyright notice, this list of * conditions and the following disclaimer in the documentation and/or other * materials provided with the distribution. ù * * 3. Neither the name of Nordic Semiconductor ASA nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * 4. This software, with or without modification, must only be used with a * Nordic Semiconductor ASA integrated circuit. * * 5. Any software provided in binary form under this license must not be reverse * engineered, decompiled, modified and/or disassembled. * * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "nrf_drv_pwm.h" #include "bsp.h" #include "nrf_drv_clock.h" #include "nrf_drv_wdt.h" #include #include "nrf.h" #include "app_error.h" #include "ble_srv_common.h" #include "ble_advertising.h" #include "nrf_sdh_soc.h" #include "nrf_sdh_ble.h" #include "fds.h" #include "peer_manager.h" #include "bsp_btn_ble.h" #include "nrf_ble_qwr.h" #include "bleb_settings.h" #include "nrf_drv_saadc.h" #include "app_util_platform.h" #include #include #include #include "nordic_common.h" #include "nrf_sdh.h" #include "app_timer.h" //#include "bsp_btn_ble.h" #include "ble.h" #include "ble_hci.h" #include "ble_advdata.h" #include "ble_advertising.h" #include "ble_conn_params.h" #include "ble_db_discovery.h" #include "ble_lbs_c.h" #include "ble_conn_state.h" #include "nrf_ble_gatt.h" #include "nrf_pwr_mgmt.h" #include "nrf_log.h" #include "nrf_log_ctrl.h" #include "nrf_log_default_backends.h" #include "nrf_soc.h" #include "nrf_delay.h" #include "nrf_nvic.h" #include "nrf_drv_twi.h" #include "bme680.h" #include "bsec_integration.h" #include "bleb_service.h" #include "app_scheduler.h" #include "boards.h" #include "nfc_t4t_lib.h" #include "ndef_file_m.h" #include "nfc_ndef_msg.h" void advertising_start(bool erase_bonds); void scan_start(void); static ble_gap_addr_t ble_address; static uint8_t address[6]; //Address of this specific device, used to accept commands from host application static uint8_t g_address[6]={0,1,2,3,4,5}; //Generic address accepted by any BLE-B /* TWI instance ID. */ #if TWI0_ENABLED #define TWI_INSTANCE_ID 0 #endif /* Number of possible TWI addresses. */ #define TWI_ADDRESSES 127 /* TWI instance. */ static const nrf_drv_twi_t m_twi = NRF_DRV_TWI_INSTANCE(TWI_INSTANCE_ID); nrf_drv_wdt_channel_id m_channel_id; void wdt_event_handler(void) { //NOTE: The max amount of time we can spend in WDT interrupt is two cycles of 32768[Hz] clock - after that, reset occurs } APP_TIMER_DEF(m_analogs_timer_id); //Battery and analog channels measurement timer definition APP_TIMER_DEF(m_temp_meas_timer_id); //Temperature measurement timer definition APP_TIMER_DEF(m_adv_start_timer_id); //Advertising start timer definition APP_TIMER_DEF(m_scan_start_timer_id); //Scan start timer definition APP_TIMER_DEF(m_reset_req_check_timeout_id); //Reset request check timeout timer definition APP_TIMER_DEF(m_imalive_led_timer_id); //"I'm alive" LED blink timer definition APP_TIMER_DEF(m_cvc_notification_timer_id); //Custom Value Characteristic Notification timer definition APP_TIMER_DEF(m_wdt_feed_id); //Watchdog Feeding Timer APP_TIMER_DEF(m_delay_timer_id); //Delay timer definition APP_TIMER_DEF(m_delay_timer_fast_id); //Delay timer fast definition BLEB_SERVICE_DEF(m_bleb_service); uint32_t bleb_service_custom_value_01_update(bleb_service_t * p_bleb_service, int8_t * custom_values); uint32_t bleb_service_custom_value_02_update(bleb_service_t * p_bleb_service, int8_t * custom_values); uint32_t bleb_service_custom_value_A9_update(bleb_service_t * p_bleb_service, int8_t * custom_values); uint32_t bleb_service_custom_value_FF_update(bleb_service_t * p_bleb_service, int8_t * custom_values); //#define DEVICE_NAME "BLEB" //$ //---------------------------------------Additional defines from advertising fw----------------------------------------------------------------------- #define MANUFACTURER_NAME "BlebTechnology" /**< Manufacturer. Will be passed to Device Information Service. */ #define APP_ADV_INTERVAL 320 /**< The advertising interval (in units of 0.625 ms. This value corresponds to 1 s). */ #define APP_ADV_DURATION 12000 /**< The advertising duration (120 seconds) in units of 10 milliseconds. */ #define DEAD_BEEF 0xDEADBEEF /**< Value used as error code on stack dump, can be used to identify stack location on stack unwind. */ //---------------------------------------------------------------------------------------------------------------------------------------------------- #define APP_BLE_CONN_CFG_TAG 1 /**< A tag that refers to the BLE stack configuration we set with @ref sd_ble_cfg_set. Default tag is @ref APP_BLE_CONN_CFG_TAG. */ #define APP_BLE_OBSERVER_PRIO 3 /**< Application's BLE observer priority. You shouldn't need to modify this value. */ #define LEDBUTTON_BUTTON BSP_BUTTON_0 /**< Button that will write to the LED characteristic of the peer. */ #define BUTTON_DETECTION_DELAY APP_TIMER_TICKS(5) /**< Delay from a GPIOTE event until a button is reported as pushed (in number of timer ticks). */ #define INITIAL_SCAN_INTERVAL 0x0140 //0x00A0 /**< Determines scan interval in units of 0.625 millisecond. */ #define INITIAL_SCAN_WINDOW 0x00A0 /**< Determines scan window in units of 0.625 millisecond. */ #define SCAN_DURATION 0x0000 /**< Duration of the scanning in units of 10 milliseconds. If set to 0x0000, scanning will continue until it is explicitly disabled. */ #define SAADC_SAMPLES_IN_BUFFER 1 //------------------------------------------------------------Connection Definitions------------------------------------------------- #define MIN_CONN_INTERVAL MSEC_TO_UNITS(8, UNIT_1_25_MS) /**< Minimum acceptable connection interval (0.1 seconds). */ #define MAX_CONN_INTERVAL MSEC_TO_UNITS(200, UNIT_1_25_MS) /**< Maximum acceptable connection interval (0.2 second). */ #define SLAVE_LATENCY 0 /**< Slave latency. */ #define CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS) /**< Connection supervisory timeout (4 seconds). */ #define FIRST_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(5000) /**< Time from initiating event (connect or start of notification) to first time sd_ble_gap_conn_param_update is called (5 seconds). */ #define NEXT_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(30000) /**< Time between each call to sd_ble_gap_conn_param_update after the first call (30 seconds). */ #define MAX_CONN_PARAMS_UPDATE_COUNT 3 /**< Number of attempts before giving up the connection parameter negotiation. */ #define SEC_PARAM_BOND 1 /**< Perform bonding. */ #define SEC_PARAM_MITM 0 /**< Man In The Middle protection not required. */ #define SEC_PARAM_LESC 0 /**< LE Secure Connections not enabled. */ #define SEC_PARAM_KEYPRESS 0 /**< Keypress notifications not enabled. */ #define SEC_PARAM_IO_CAPABILITIES BLE_GAP_IO_CAPS_NONE /**< No I/O capabilities. */ #define SEC_PARAM_OOB 0 /**< Out Of Band data not available. */ #define SEC_PARAM_MIN_KEY_SIZE 7 /**< Minimum encryption key size. */ #define SEC_PARAM_MAX_KEY_SIZE 16 //---------------------------------------------------------------------------------------------------------------------------- //BLE_DB_DISCOVERY_ARRAY_DEF(m_db_disc, NRF_SDH_BLE_CENTRAL_LINK_COUNT); /**< Database discovery module instances. */ BLE_ADVERTISING_DEF(m_advertising); /**< Advertising module instance. */ NRF_BLE_GATT_DEF(m_gatt); /**< GATT module instance. */ NRF_BLE_QWR_DEF(m_qwr); /**< Context for the Queued Write module.*/ void set_addr(void){ static ble_gap_addr_t m_central_addr; m_central_addr.addr_type = BLE_GAP_ADDR_TYPE_RANDOM_STATIC; m_central_addr.addr[0] = 0xaa; m_central_addr.addr[1] = 0xbb; m_central_addr.addr[2] = 0xcc; m_central_addr.addr[3] = 0xdd; m_central_addr.addr[4] = 0xee; m_central_addr.addr[5] = 0xc6; // 2MSB must be set 11 sd_ble_gap_addr_set(&m_central_addr); } void saadc_init(); static uint16_t led_breath_index=0; static bool erase_bonds; static bool demo_mode=true; static bool bleb_is_connected=false; static void led_on(uint8_t led_num){ if (led_num==0) bsp_board_led_on(BSP_BOARD_LED_0); else if (led_num==1) bsp_board_led_on(BSP_BOARD_LED_1); else if (led_num==2) bsp_board_led_on(BSP_BOARD_LED_2); } static void leds_on(){ for (int i=0; i<3; i++){ led_on(i); } } static void led_off(uint8_t led_num){ if (led_num==0) bsp_board_led_off(BSP_BOARD_LED_0); else if (led_num==1) bsp_board_led_off(BSP_BOARD_LED_1); else if (led_num==2) bsp_board_led_off(BSP_BOARD_LED_2); } static void leds_off(){ for (int i=0; i<3; i++){ led_off(i); } } ble_advertising_t * ble_adv_instance_ptr_get(void) { return &m_advertising; } static void led_blink(uint32_t led_id, uint8_t blink_reps, uint8_t delays){ for (int i=0; i> 16); bme680_data[2]=(uint8_t)(bme680_pres >> 8); bme680_data[3]=(uint8_t)(bme680_pres); bme680_data[4]=(uint8_t)(bme680_hum); bme680_data[5]=(uint8_t)(bme680_iaq); bme680_data[6]=(uint8_t)(bme680_iaq_acc); } else{ for (int i=0; i<7; i++){ bme680_data[i]=0; } } } void output_ready(int64_t timestamp, float iaq, uint8_t iaq_accuracy, float temperature, float humidity, float pressure, float raw_temperature, float raw_humidity, float gas, bsec_library_return_t bsec_status) { bme680_temp=(int32_t)temperature; bme680_pres=(uint32_t)pressure; bme680_iaq=(uint8_t)((iaq)/(float)2); bme680_iaq_acc=iaq_accuracy; bme680_hum=(uint8_t)humidity; if (bme680_notification){ bme680_data_update(); bleb_service_custom_value_A9_update(&m_bleb_service, bme680_data); } calls++; } // Gets a register as input and send it to i2c device and reads back the reply and stores it in rx_buffer static int8_t bme680_twi_read(uint8_t slave_addr, uint8_t reg_addr, uint8_t * twi_rx_buffer, uint16_t len){ for (int i=0; i=current_us_32) us_32_H++; system_current_time=((int64_t)us_32_H << 32) | (int64_t)(current_us_32); microseconds=system_current_time; //global microseconds variable update us_32=current_us_32; // global variable for overflow detection return system_current_time; } //-----------------------------------------Static variables declarations from advertising fw------------------------------------------------ static ble_uuid_t m_adv_uuids[] = /**< Universally unique service identifiers. */ { {BLEB_SERVICE_UUID, BLE_UUID_TYPE_VENDOR_BEGIN} }; static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID; /**< Handle of the current connection. */ //------------------------------------------------------------------------------------------------------------------------------------------ static uint8_t button_state=0xAA; static uint8_t r_manufacturer_specific_address[]={0,0}; static uint8_t r_manufacturer_specific_data[25]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; //TBD: implement variable size array static int8_t rssi; static int8_t watched_rssi; static int8_t rssi_threshold=-99; static uint8_t m_scan_buffer_data[BLE_GAP_SCAN_BUFFER_MIN]; /**< buffer where advertising reports will be stored by the SoftDevice. */ static uint8_t btn_ack=0; static uint8_t saadc_active_channels=SAADC_SAMPLES_IN_BUFFER; static nrf_saadc_value_t m_buffer_pool[2][3]; static bool m_saadc_initialized=false; static uint8_t name[6]={66,76,69,45,66,0}; //Default name is BLE-B static uint32_t adv_interval=APP_ADV_INTERVAL; static uint32_t adv_duration=APP_ADV_DURATION; static uint8_t advertising_settings[3]={100, 242, 100}; // Interval: 100 (corresponds to 1 s), Duration: 242 (corresponds to 2 mins), Restart: 100 (corresponds to 1 s) static uint8_t scan_settings[3]={200, 100, 5}; // Interval: 200 (corresponds to 200 ms), Window: 100 (corresponds to 100 ms), Restart: 5 (corresponds to 500 ms) static uint16_t SCAN_INTERVAL=INITIAL_SCAN_INTERVAL; static uint16_t SCAN_WINDOW=INITIAL_SCAN_WINDOW; static int8_t power_tx; static uint8_t settings[9]; static bool timers_started=false; static uint32_t ANALOGS_INTERVAL=APP_TIMER_TICKS(60000); //Battery and analog channels measurement timer initial value in ms static uint8_t analogs_interval_in_s=60; static uint32_t TEMP_MEAS_INTERVAL=APP_TIMER_TICKS(5000); //Temperature measurement timer initial value in ms static uint8_t temperature_interval_in_s=5; static uint32_t ADV_START_INTERVAL=APP_TIMER_TICKS(250); //Advertising start timer initial value in ms static uint32_t SCAN_START_INTERVAL=APP_TIMER_TICKS(150); //Scan start timer initial value in ms static uint32_t RESET_REQ_CHECK_INTERVAL=APP_TIMER_TICKS(1000); //Reset request check timer initial value in ms static uint32_t IMALIVE_LED_INTERVAL=APP_TIMER_TICKS(3000); //"I'm alive" LED blink timer initial value in ms static uint8_t imalive_led_interval_in_s=30; static uint32_t CVC_NOTIFICATION_INTERVAL=APP_TIMER_TICKS(500); //Custom Value Characteristic Notification timer initial value in ms static uint32_t WDT_FEED_INTERVAL=APP_TIMER_TICKS(500); //Watchdog Timer Feed static uint32_t btn_count,btn_count_new; static uint8_t fw_revision=10; //First digit is major revision number, second digit is minor revision number /**@brief Pointer to the buffer where advertising reports will be stored by the SoftDevice. */ static ble_data_t m_scan_buffer = { m_scan_buffer_data, BLE_GAP_SCAN_BUFFER_MIN }; /**@brief Scan parameters requested for scanning and connection. */ static ble_gap_scan_params_t m_scan_params = // static ble_gap_scan_params_t const m_scan_params = { .active = 0, .interval = INITIAL_SCAN_INTERVAL, .window = INITIAL_SCAN_WINDOW, .timeout = SCAN_DURATION, .scan_phys = BLE_GAP_PHY_1MBPS, .filter_policy = BLE_GAP_SCAN_FP_ACCEPT_ALL, }; //---------------------------Additional functions from advertising fw------------------------------------------------------- static void tx_power_set(int8_t transmission_power_dBm){ int8_t tx_power_dBm=transmission_power_dBm; if (tx_power_dBm== -40 || \ tx_power_dBm== -20 || \ tx_power_dBm== -16 || \ tx_power_dBm== -12 || \ tx_power_dBm== -8 || \ tx_power_dBm== -4 || \ tx_power_dBm== 3 || \ tx_power_dBm== 4){ sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_ADV, m_advertising.adv_handle, tx_power_dBm); //BLE_GAP_ADV_SET_HANDLE_NOT_SET } else{ tx_power_dBm=0; sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_ADV, m_advertising.adv_handle, tx_power_dBm); } power_tx=tx_power_dBm; } /**@brief Function for the GAP initialization. * * @details This function sets up all the necessary GAP (Generic Access Profile) parameters of the * device including the device name, appearance, and the preferred connection parameters. */ static void gap_params_init(void){ //can be reduced, as only sets the name ret_code_t err_code; ble_gap_conn_params_t gap_conn_params; ble_gap_conn_sec_mode_t sec_mode; BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode); err_code=sd_ble_gap_device_name_set(&sec_mode, (const uint8_t *)DEVICE_NAME, strlen(DEVICE_NAME)); APP_ERROR_CHECK(err_code); //-------------------------Connection Parameters------------------------------- memset(&gap_conn_params, 0, sizeof(gap_conn_params)); gap_conn_params.min_conn_interval = MIN_CONN_INTERVAL; gap_conn_params.max_conn_interval = MAX_CONN_INTERVAL; gap_conn_params.slave_latency = SLAVE_LATENCY; gap_conn_params.conn_sup_timeout = CONN_SUP_TIMEOUT; err_code = sd_ble_gap_ppcp_set(&gap_conn_params); APP_ERROR_CHECK(err_code); //----------------------------------------------------------------------------- } /**@brief Function for initializing the GATT module. */ static void gatt_init(void) { ret_code_t err_code = nrf_ble_gatt_init(&m_gatt, NULL); APP_ERROR_CHECK(err_code); } /**@brief Function for handling Queued Write Module errors. * * @details A pointer to this function will be passed to each service which may need to inform the * application about an error. * * @param[in] nrf_error Error code containing information about what went wrong. */ static void nrf_qwr_error_handler(uint32_t nrf_error) { APP_ERROR_HANDLER(nrf_error); } /**@brief Function for handling the Connection Parameters Module. * * @details This function will be called for all events in the Connection Parameters Module which * are passed to the application. * @note All this function does is to disconnect. This could have been done by simply * setting the disconnect_on_fail config parameter, but instead we use the event * handler mechanism to demonstrate its use. * * @param[in] p_evt Event received from the Connection Parameters Module. */ static void on_conn_params_evt(ble_conn_params_evt_t * p_evt) { ret_code_t err_code; if (p_evt->evt_type == BLE_CONN_PARAMS_EVT_FAILED) { // err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_CONN_INTERVAL_UNACCEPTABLE); // APP_ERROR_CHECK(err_code); } } /**@brief Function for handling a Connection Parameters error. * * @param[in] nrf_error Error code containing information about what went wrong. */ static void conn_params_error_handler(uint32_t nrf_error) { APP_ERROR_HANDLER(nrf_error); } /**@brief Function for initializing the Connection Parameters module. */ static void conn_params_init(void) { ret_code_t err_code; ble_conn_params_init_t cp_init; memset(&cp_init, 0, sizeof(cp_init)); cp_init.p_conn_params = NULL; cp_init.first_conn_params_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY; cp_init.next_conn_params_update_delay = NEXT_CONN_PARAMS_UPDATE_DELAY; cp_init.max_conn_params_update_count = MAX_CONN_PARAMS_UPDATE_COUNT; cp_init.start_on_notify_cccd_handle = BLE_GATT_HANDLE_INVALID; cp_init.disconnect_on_fail = false; cp_init.evt_handler = on_conn_params_evt; cp_init.error_handler = conn_params_error_handler; err_code = ble_conn_params_init(&cp_init); APP_ERROR_CHECK(err_code); } /**@brief Function for putting the chip into sleep mode. * * @note This function will not return. */ static void sleep_mode_enter(void){ bsp_indication_set(BSP_INDICATE_IDLE); // Prepare wakeup buttons. bsp_btn_ble_sleep_mode_prepare(); // Go to system-off mode (this function will not return; wakeup will cause a reset). sd_power_system_off(); } /**@brief Function for handling advertising events. * * @details This function will be called for advertising events which are passed to the application. * * @param[in] ble_adv_evt Advertising event. */ static void on_adv_evt(ble_adv_evt_t ble_adv_evt) { ret_code_t err_code; switch (ble_adv_evt) { case BLE_ADV_EVT_FAST: //NRF_LOG_INFO("Fast advertising."); err_code = bsp_indication_set(BSP_INDICATE_SCANNING); APP_ERROR_CHECK(err_code); break; case BLE_ADV_EVT_IDLE: sleep_mode_enter(); break; default: break; } } ///**@brief Function for initializing the Advertising functionality. // */ static void advertising_init(void){ m_advertising.adv_mode_current = BLE_ADV_MODE_IDLE; //m_advertising.adv_modes_config = init.config; //m_advertising.evt_handler = init.evt_handler; //m_advertising.error_handler = init.error_handler; m_advertising.p_adv_data = &m_advertising.adv_data; tx_power_set(power_tx); // Function to set tx power level // Copy advertising data. if (!m_advertising.initialized) { m_advertising.adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET; } //Flags m_advertising.enc_advdata[0]=2; m_advertising.enc_advdata[1]=1; m_advertising.enc_advdata[2]=6; //Battery service m_advertising.enc_advdata[3]=4; m_advertising.enc_advdata[4]=22; m_advertising.enc_advdata[5]=15; m_advertising.enc_advdata[6]=24; m_advertising.enc_advdata[7]=100; //Manufacturer data m_advertising.enc_advdata[8]=14; m_advertising.enc_advdata[9]=0xFF; m_advertising.enc_advdata[10]=0x68; m_advertising.enc_advdata[11]=0x06; m_advertising.enc_advdata[12]=0; m_advertising.enc_advdata[13]=0; m_advertising.enc_advdata[14]=0; m_advertising.enc_advdata[15]=0; m_advertising.enc_advdata[16]=0; m_advertising.enc_advdata[17]=0; m_advertising.enc_advdata[18]=0; m_advertising.enc_advdata[19]=0; m_advertising.enc_advdata[20]=0; m_advertising.enc_advdata[21]=0; m_advertising.enc_advdata[22]=0; //Device Name m_advertising.enc_advdata[23]=7; m_advertising.enc_advdata[24]=9; m_advertising.enc_advdata[25]=name[0]; //B m_advertising.enc_advdata[26]=name[1]; //L m_advertising.enc_advdata[27]=name[2]; //E m_advertising.enc_advdata[28]=name[3]; //- m_advertising.enc_advdata[29]=name[4]; //B m_advertising.enc_advdata[30]=name[5]; m_advertising.adv_data.adv_data.p_data = m_advertising.enc_advdata; m_advertising.adv_data.adv_data.len = BLE_GAP_ADV_SET_DATA_SIZE_MAX; // Configure a initial advertising configuration. The advertising data and and advertising // parameters will be changed later when we call @ref ble_advertising_start, but must be set // to legal values here to define an advertising handle. m_advertising.adv_params.primary_phy = BLE_GAP_PHY_1MBPS; m_advertising.adv_params.duration = m_advertising.adv_modes_config.ble_adv_fast_timeout; m_advertising.adv_params.properties.type = BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED; m_advertising.adv_params.p_peer_addr = NULL; m_advertising.adv_params.filter_policy = BLE_GAP_ADV_FP_ANY; m_advertising.adv_params.interval = adv_interval; m_advertising.adv_modes_config.ble_adv_fast_enabled = true; sd_ble_gap_adv_set_configure(&m_advertising.adv_handle, &m_advertising.adv_data, &m_advertising.adv_params); ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG); } /**@brief Clear bond information from persistent storage. */ static void delete_bonds(void) { ret_code_t err_code; //NRF_LOG_INFO("Erase bonds!"); err_code = pm_peers_delete(); APP_ERROR_CHECK(err_code); } /**@brief Function for starting advertising. */ void advertising_start(bool erase_bonds){ if (erase_bonds == true) { delete_bonds(); // Advertising is started by PM_EVT_PEERS_DELETED_SUCEEDED event } else{ tx_power_set(power_tx); //Function to set tx power level //Battery service m_advertising.enc_advdata[3]=4; m_advertising.enc_advdata[4]=22; m_advertising.enc_advdata[5]=15; m_advertising.enc_advdata[6]=24; m_advertising.enc_advdata[7]=100; //Manufacturer data m_advertising.enc_advdata[8]=14; m_advertising.enc_advdata[9]=0xFF; m_advertising.enc_advdata[10]=0X68; m_advertising.enc_advdata[11]=0X06; m_advertising.enc_advdata[12]=0; //Device Name m_advertising.enc_advdata[23]=7; m_advertising.enc_advdata[24]=9; for (int i=0; i<6; i++){ //Refresh Device Name m_advertising.enc_advdata[25+i]=name[i]; } m_advertising.adv_modes_config.ble_adv_fast_interval=adv_interval; //advertising interval set from host application (in units of 0.625 ms) m_advertising.adv_modes_config.ble_adv_fast_timeout=adv_duration; m_advertising.adv_data.adv_data.p_data = m_advertising.enc_advdata; m_advertising.adv_data.adv_data.len = BLE_GAP_ADV_SET_DATA_SIZE_MAX; m_advertising.evt_handler=NULL; ret_code_t err_code=ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST); APP_ERROR_CHECK(err_code); } } /**@brief Function for handling events from the BSP module. * * @param[in] event Event generated when button is pressed. */ static void bsp_event_handler(bsp_event_t event) { ret_code_t err_code; switch (event) { case BSP_EVENT_SLEEP: sleep_mode_enter(); break; // BSP_EVENT_SLEEP case BSP_EVENT_DISCONNECT: break; // BSP_EVENT_DISCONNECT case BSP_EVENT_WHITELIST_OFF: if (m_conn_handle == BLE_CONN_HANDLE_INVALID) { err_code = ble_advertising_restart_without_whitelist(&m_advertising); if (err_code != NRF_ERROR_INVALID_STATE) { APP_ERROR_CHECK(err_code); } } break; // BSP_EVENT_KEY_0 default: break; } } /**@brief Function for initializing buttons and leds. * * @param[out] p_erase_bonds Will be true if the clear bonding button was pressed to wake the application up. */ static void buttons_leds_init(bool * p_erase_bonds) { ret_code_t err_code; bsp_event_t startup_event; err_code = bsp_init(BSP_INIT_LEDS | BSP_INIT_BUTTONS, bsp_event_handler); APP_ERROR_CHECK(err_code); err_code = bsp_btn_ble_init(NULL, &startup_event); APP_ERROR_CHECK(err_code); *p_erase_bonds = (startup_event == BSP_EVENT_CLEAR_BONDING_DATA); } /**@brief Function to handle asserts in the SoftDevice. * * @details This function will be called in case of an assert in the SoftDevice. * * @warning This handler is an example only and does not fit a final product. You need to analyze * how your product is supposed to react in case of Assert. * @warning On assert from the SoftDevice, the system can only recover on reset. * * @param[in] line_num Line number of the failing ASSERT call. * @param[in] p_file_name File name of the failing ASSERT call. */ void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name){ app_error_handler(DEAD_BEEF, line_num, p_file_name); } /**@brief Function for handling Peer Manager events. * * @param[in] p_evt Peer Manager event. */ static void pm_evt_handler(pm_evt_t const * p_evt) { ret_code_t err_code; switch (p_evt->evt_id) { case PM_EVT_BONDED_PEER_CONNECTED: { //NRF_LOG_INFO("Connected to a previously bonded device."); } break; case PM_EVT_CONN_SEC_SUCCEEDED: { // NRF_LOG_INFO("Connection secured: role: %d, conn_handle: 0x%x, procedure: %d.", // ble_conn_state_role(p_evt->conn_handle), // p_evt->conn_handle, // p_evt->params.conn_sec_succeeded.procedure); } break; case PM_EVT_CONN_SEC_FAILED: { /* Often, when securing fails, it shouldn't be restarted, for security reasons. * Other times, it can be restarted directly. * Sometimes it can be restarted, but only after changing some Security Parameters. * Sometimes, it cannot be restarted until the link is disconnected and reconnected. * Sometimes it is impossible, to secure the link, or the peer device does not support it. * How to handle this error is highly application dependent. */ } break; case PM_EVT_CONN_SEC_CONFIG_REQ: { // Reject pairing request from an already bonded peer. pm_conn_sec_config_t conn_sec_config = {.allow_repairing = false}; pm_conn_sec_config_reply(p_evt->conn_handle, &conn_sec_config); } break; case PM_EVT_STORAGE_FULL: { // Run garbage collection on the flash. err_code = fds_gc(); if (err_code == FDS_ERR_NO_SPACE_IN_QUEUES) { // Retry. } else { APP_ERROR_CHECK(err_code); } } break; case PM_EVT_PEERS_DELETE_SUCCEEDED: { advertising_start(false); } break; case PM_EVT_PEER_DATA_UPDATE_FAILED: { // Assert. APP_ERROR_CHECK(p_evt->params.peer_data_update_failed.error); } break; case PM_EVT_PEER_DELETE_FAILED: { // Assert. APP_ERROR_CHECK(p_evt->params.peer_delete_failed.error); } break; case PM_EVT_PEERS_DELETE_FAILED: { // Assert. APP_ERROR_CHECK(p_evt->params.peers_delete_failed_evt.error); } break; case PM_EVT_ERROR_UNEXPECTED: { // Assert. APP_ERROR_CHECK(p_evt->params.error_unexpected.error); } break; case PM_EVT_CONN_SEC_START: case PM_EVT_PEER_DATA_UPDATE_SUCCEEDED: case PM_EVT_PEER_DELETE_SUCCEEDED: case PM_EVT_LOCAL_DB_CACHE_APPLIED: case PM_EVT_LOCAL_DB_CACHE_APPLY_FAILED: // This can happen when the local DB has changed. case PM_EVT_SERVICE_CHANGED_IND_SENT: case PM_EVT_SERVICE_CHANGED_IND_CONFIRMED: default: break; } } /**@brief Function to start scanning. */ void scan_start(void){ ret_code_t ret = sd_ble_gap_scan_start(&m_scan_params, &m_scan_buffer); APP_ERROR_CHECK(ret); } static void button_event_handler(uint8_t pin_no, uint8_t button_action){ ret_code_t err_code; switch (button_action) { case 1:{ //Button press (0 is for BLE-B v1.2, 1 for DK and BLE-B v1.5) btn_count=app_timer_cnt_get(); } break; case 0:{ //Button release (1 is for BLE-B v1.2, 0 for DK and BLE-B v1.5) btn_count_new=app_timer_cnt_get(); if ((btn_count_new-btn_count)>2000){ if((btn_count_new-btn_count)<10000){ //Button held down for more than 2s, less than 10s: long-press button_state=0x02; //Long Press app_timer_stop(m_adv_start_timer_id); sd_ble_gap_adv_stop(m_advertising.adv_handle); advertising_start(erase_bonds); app_timer_start(m_adv_start_timer_id, ADV_START_INTERVAL, NULL); } } else{ if (btn_ack==0){ //NRF_LOG_INFO("Button set"); btn_ack=1; button_state=0x01; uint8_t tx_buff[1]={1}; } else if (btn_ack==1){ //NRF_LOG_INFO("Button reset"); btn_ack=0; button_state=0x00; uint8_t tx_buff[1]={0}; //nrf_drv_twi_tx(&m_twi, 0x15, tx_buff, 1, false); } else{ app_timer_stop(m_adv_start_timer_id); sd_ble_gap_adv_stop(m_advertising.adv_handle); advertising_start(erase_bonds); app_timer_start(m_adv_start_timer_id, ADV_START_INTERVAL, NULL); } } } break; } } /**@brief Function for initializing the button handler module. */ static void buttons_init(void) { ret_code_t err_code; //The array must be static because a pointer to it will be saved in the button handler module. static app_button_cfg_t buttons[] = { {LEDBUTTON_BUTTON, false, BUTTON_PULL, button_event_handler} }; err_code = app_button_init(buttons, ARRAY_SIZE(buttons), BUTTON_DETECTION_DELAY); APP_ERROR_CHECK(err_code); } /**@brief Function for handling database discovery events. * * @details This function is callback function to handle events from the database discovery module. * Depending on the UUIDs that are discovered, this function should forward the events * to their respective services. * * @param[in] p_event Pointer to the database discovery event. */ static void db_disc_handler(ble_db_discovery_evt_t * p_evt) { // NRF_LOG_DEBUG("call to ble_lbs_on_db_disc_evt for instance %d and link 0x%x!", // p_evt->conn_handle, // p_evt->conn_handle); // ble_lbs_on_db_disc_evt(&m_lbs_c[p_evt->conn_handle], p_evt); } /** @brief Database discovery initialization. */ static void db_discovery_init(void) { ret_code_t err_code = ble_db_discovery_init(db_disc_handler); APP_ERROR_CHECK(err_code); } /**@brief Function for initializing power management. */ static void power_management_init(void) { ret_code_t err_code; err_code = nrf_pwr_mgmt_init(); APP_ERROR_CHECK(err_code); } /**@brief Function for handling the idle state (main loop). * * @details Handle any pending log operation(s), then sleep until the next event occurs. */ static void idle_state_handle(void) { if (NRF_LOG_PROCESS() == false) { nrf_pwr_mgmt_run(); } } /** @brief Function for initializing the log module. */ static void log_init(void) { ret_code_t err_code = NRF_LOG_INIT(NULL); APP_ERROR_CHECK(err_code); NRF_LOG_DEFAULT_BACKENDS_INIT(); } static void adv_start_timeout_handler(void * p_context){ sd_ble_gap_adv_stop(m_advertising.adv_handle); advertising_start(erase_bonds); nrf_drv_wdt_channel_feed(m_channel_id); } static void reset_req_check_timeout_handler(void * p_context){ if (reset_req) sd_nvic_SystemReset(); } static void imalive_led_timeout_handler(void * p_context){ led_blink(1,1,50); app_timer_start(m_imalive_led_timer_id, IMALIVE_LED_INTERVAL, NULL); } /**@brief Function for handling the advertising report BLE event. * * @param[in] p_adv_report Advertising report from the SoftDevice. */ static void on_adv_report(ble_gap_evt_adv_report_t const * p_adv_report) { ret_code_t err_code; } /**@brief Function for handling BLE events. * * @param[in] p_ble_evt Bluetooth stack event. * @param[in] p_context Unused. */ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context) { ret_code_t err_code = NRF_SUCCESS; // For readability. ble_gap_evt_t const * p_gap_evt = &p_ble_evt->evt.gap_evt; switch (p_ble_evt->header.evt_id) { case BLE_GAP_EVT_DISCONNECTED: bleb_is_connected=false; app_timer_start(m_adv_start_timer_id, ADV_START_INTERVAL, NULL); break; case BLE_GAP_EVT_CONNECTED: nrf_drv_wdt_channel_feed(m_channel_id); app_timer_stop(m_adv_start_timer_id); app_timer_stop(m_imalive_led_timer_id); bleb_is_connected=true; m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle; err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr, m_conn_handle); APP_ERROR_CHECK(err_code); app_timer_start(m_imalive_led_timer_id, APP_TIMER_TICKS(10), NULL); break; case BLE_GAP_EVT_ADV_REPORT: on_adv_report(&p_gap_evt->params.adv_report); break; case BLE_GAP_EVT_PHY_UPDATE_REQUEST: { // NRF_LOG_DEBUG("PHY update request."); ble_gap_phys_t const phys = { .rx_phys = BLE_GAP_PHY_AUTO, .tx_phys = BLE_GAP_PHY_AUTO, }; err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys); APP_ERROR_CHECK(err_code); } break; case BLE_GATTC_EVT_TIMEOUT: // Disconnect on GATT Client timeout event. // NRF_LOG_DEBUG("GATT Client Timeout."); err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); APP_ERROR_CHECK(err_code); break; case BLE_GATTS_EVT_TIMEOUT: // Disconnect on GATT Server timeout event. // NRF_LOG_DEBUG("GATT Server Timeout."); err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); APP_ERROR_CHECK(err_code); break; default: // No implementation needed. break; } } /**@brief Function for the Peer Manager initialization. */ static void peer_manager_init(void) { ble_gap_sec_params_t sec_param; ret_code_t err_code; err_code = pm_init(); APP_ERROR_CHECK(err_code); memset(&sec_param, 0, sizeof(ble_gap_sec_params_t)); // Security parameters to be used for all security procedures. sec_param.bond = SEC_PARAM_BOND; sec_param.mitm = SEC_PARAM_MITM; sec_param.lesc = SEC_PARAM_LESC; sec_param.keypress = SEC_PARAM_KEYPRESS; sec_param.io_caps = SEC_PARAM_IO_CAPABILITIES; sec_param.oob = SEC_PARAM_OOB; sec_param.min_key_size = SEC_PARAM_MIN_KEY_SIZE; sec_param.max_key_size = SEC_PARAM_MAX_KEY_SIZE; sec_param.kdist_own.enc = 1; sec_param.kdist_own.id = 1; sec_param.kdist_peer.enc = 1; sec_param.kdist_peer.id = 1; err_code = pm_sec_params_set(&sec_param); APP_ERROR_CHECK(err_code); err_code = pm_register(pm_evt_handler); APP_ERROR_CHECK(err_code); } /**@brief Function for initializing the BLE stack. * * @details Initializes the SoftDevice and the BLE event interrupts. */ static void ble_stack_init(void){ ret_code_t err_code; err_code = nrf_sdh_enable_request(); APP_ERROR_CHECK(err_code); // Configure the BLE stack using the default settings. // Fetch the start address of the application RAM. uint32_t ram_start = 0; err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start); APP_ERROR_CHECK(err_code); // Enable BLE stack. err_code = nrf_sdh_ble_enable(&ram_start); APP_ERROR_CHECK(err_code); // Register a handler for BLE events. NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL); } static void scan_start_timeout_handler(void * p_context){ sd_ble_gap_scan_stop(); m_scan_params.interval=SCAN_INTERVAL; m_scan_params.window=SCAN_WINDOW; scan_start(); } /**@brief Function for handling the BLE-B Service events. * * @details This function will be called for all BLE-B Service events which are passed to * the application. * * @param[in] p_bleb_service BLE-B Service structure. * @param[in] p_evt Event received from the Custom Service. * */ static void on_bleb_service_evt(bleb_service_t * p_bleb_service, bleb_service_evt_t * p_evt){ ret_code_t err_code; // uint8_t led_id=0x02; //Green // uint8_t led_num=1; // uint8_t led_excluded1=0; // uint8_t led_excluded2=2; // // uint8_t led_dc=10; switch(p_evt->evt_type) { case BLEB_SERVICE_EVT_NOTIFICATION_ENABLED: app_timer_start(m_cvc_notification_timer_id, CVC_NOTIFICATION_INTERVAL, NULL); //Custom Value Characteristic Update Timer break; case BLEB_SERVICE_EVT_NOTIFICATION_DISABLED: app_timer_stop(m_cvc_notification_timer_id); break; case BLEB_SERVICE_EVT_NOTIFICATION_ENABLEDA9: //BME680 Notification Enabled bme680_notification=true; bme680_data_update(); bleb_service_custom_value_A9_update(&m_bleb_service, bme680_data); break; case BLEB_SERVICE_EVT_NOTIFICATION_DISABLEDA9: //BME680 Notification Disabled bme680_notification=false; break; case BLEB_SERVICE_EVT_READA9: bme680_data_update(); bleb_service_custom_value_A9_update(&m_bleb_service, bme680_data); break; case BLEB_SERVICE_EVT_CONNECTED: break; case BLEB_SERVICE_EVT_DISCONNECTED: break; case BLEB_SERVICE_EVT_WRITE: break; default: // No implementation needed. break; } } /**@brief Function for initializing services that will be used by the application. */ static void services_init() { ret_code_t err_code; nrf_ble_qwr_init_t qwr_init = {0}; // Initialize Queued Write Module. qwr_init.error_handler = nrf_qwr_error_handler; err_code = nrf_ble_qwr_init(&m_qwr, &qwr_init); APP_ERROR_CHECK(err_code); bleb_service_init_t bleb_serv_init; memset(&bleb_serv_init, 0, sizeof(bleb_serv_init)); // Initialize BLE-B Service init structure to zero BLE_GAP_CONN_SEC_MODE_SET_OPEN(&bleb_serv_init.custom_value_char_attr_md.read_perm); //Set the read/write permissions to the characteristic value attribute to "open" BLE_GAP_CONN_SEC_MODE_SET_OPEN(&bleb_serv_init.custom_value_char_attr_md.write_perm); //(i.e. the peer is allowed to read/write the value without encrypting the link first) bleb_serv_init.evt_handler = on_bleb_service_evt; //Set the BLE-B Service event handler err_code=bleb_service_init(&m_bleb_service, &bleb_serv_init); APP_ERROR_CHECK(err_code); } static void ble_inits(){ ble_stack_init(); gap_params_init(); gatt_init(); advertising_init(); conn_params_init(); peer_manager_init(); db_discovery_init(); sd_ble_gap_addr_get(&ble_address); //Save the address of this specific device for (int i=0; i<6; i++){ address[i]=ble_address.addr[5-i]; } } /* Timestamp variables */ static int64_t time_stamp = 0; static int64_t time_stamp_interval_ms = 0; /* Allocate enough memory for up to BSEC_MAX_PHYSICAL_SENSOR physical inputs*/ static bsec_input_t bsec_inputs[BSEC_MAX_PHYSICAL_SENSOR]; /* Number of inputs to BSEC */ static uint8_t num_bsec_inputs = 0; /* BSEC sensor settings struct */ static bsec_bme_settings_t sensor_settings; static bsec_library_return_t bsec_status = BSEC_OK; /* Global sensor APIs data structure */ static struct bme680_dev bme680_g; /*! * @brief Save library state to non-volatile memory * * @param[in] state_buffer buffer holding the state to be stored * @param[in] length length of the state string to be stored * * @return none */ void state_save(const uint8_t * state_buffer, uint32_t length) { //Nothing for the moment } static void wdt_feed_timeout_handler(void * p_context){ nrf_drv_wdt_channel_feed(m_channel_id); } /** @brief Function for initializing the timer. */ static void timers_init(void) { ret_code_t err_code = app_timer_init(); APP_ERROR_CHECK(err_code); err_code = app_timer_create(&m_adv_start_timer_id, APP_TIMER_MODE_REPEATED, adv_start_timeout_handler); APP_ERROR_CHECK(err_code); err_code = app_timer_create(&m_scan_start_timer_id, APP_TIMER_MODE_REPEATED, scan_start_timeout_handler); APP_ERROR_CHECK(err_code); err_code = app_timer_create(&m_reset_req_check_timeout_id, APP_TIMER_MODE_SINGLE_SHOT, reset_req_check_timeout_handler); APP_ERROR_CHECK(err_code); err_code = app_timer_create(&m_imalive_led_timer_id, APP_TIMER_MODE_SINGLE_SHOT, imalive_led_timeout_handler); APP_ERROR_CHECK(err_code); err_code = app_timer_create(&m_wdt_feed_id, APP_TIMER_MODE_REPEATED, wdt_feed_timeout_handler); APP_ERROR_CHECK(err_code); } static void timers_start(void){ timers_started=true; ret_code_t err_code = app_timer_start(m_adv_start_timer_id, ADV_START_INTERVAL, NULL); APP_ERROR_CHECK(err_code); err_code = app_timer_start(m_imalive_led_timer_id, APP_TIMER_TICKS(500), NULL); APP_ERROR_CHECK(err_code); err_code = app_timer_start(m_wdt_feed_id, WDT_FEED_INTERVAL, NULL); APP_ERROR_CHECK(err_code); } static void leds_buttons_init(void){ bsp_init(BSP_INIT_LEDS, NULL); buttons_init(); app_button_enable(); } int main(void){ timers_init(); leds_buttons_init(); power_management_init(); ble_inits(); twi_init(); services_init(); //Configure WDT. nrf_drv_wdt_config_t config = NRF_DRV_WDT_DEAFULT_CONFIG; nrf_drv_wdt_init(&config, wdt_event_handler); nrf_drv_wdt_channel_alloc(&m_channel_id); nrf_drv_wdt_enable(); // Notify power on led_blink(1,3,50); nrf_drv_wdt_channel_feed(m_channel_id); timers_start(); advertising_start(erase_bonds); bme680_enabled=true; for (;;){ if (bme680_enabled && !bme680_configured){ bsec_iot_init(bme680_op_mode, (float)(-bme680_temperature_offset), bme680_twi_write, bme680_twi_read, nrf_delay_ms); bsec_iot_loop(nrf_delay_ms, get_timestamp_us, output_ready, state_save, 10000, bme680_hot_plate_temperature, bme680_hot_plate_heating_time); bme680_configured=true; } idle_state_handle(); } }