#include #include #include #include "nordic_common.h" #include "nrf.h" #include "app_error.h" #include "ble.h" #include "ble_hci.h" #include "ble_srv_common.h" #include "ble_advdata.h" #include "ble_advertising.h" #include "ble_bas.h" #include "ble_hrs.h" #include "ble_dis.h" #ifdef BLE_DFU_APP_SUPPORT #include "ble_dfu.h" #include "dfu_app_handler.h" #endif // BLE_DFU_APP_SUPPORT #include "ble_conn_params.h" #include "boards.h" #include "sensorsim.h" #include "softdevice_handler.h" #include "app_timer.h" #include "device_manager.h" #include "pstorage.h" #include "app_trace.h" #include "bsp.h" #include "nrf_delay.h" #include "bsp_btn_ble.h" #include "ble_hts.h" #include "app_uart.h" #include "ble_nus.h" #include "nrf_gpio.h" #include "nrf_drv_config.h" #include "nrf_drv_clock.h" #include #include "app_util_platform.h" #include "nrf_drv_ppi.h" #include "nrf_drv_timer.h" #include "nrf_drv_saadc.h" #include "nrf_drv_gpiote.h" #include "hr.h" #define IS_SRVC_CHANGED_CHARACT_PRESENT 0 /**< Include or not the service_changed characteristic. if not enabled, the server's database cannot be changed for the lifetime of the device*/ #define CENTRAL_LINK_COUNT 0 /**< Number of central links used by the application. When changing this number remember to adjust the RAM settings*/ #define PERIPHERAL_LINK_COUNT 1 /**< Number of peripheral links used by the application. When changing this number remember to adjust the RAM settings*/ #define DEVICE_NAME "device" /**< Name of device. Will be included in the advertising data. */ #define MANUFACTURER_NAME "NordicSemiconductor" /**< Manufacturer. Will be passed to Device Information Service. */ #define MODEL_NUM "NS-HTS-EXAMPLE" /**< Model number. Will be passed to Device Information Service. */ #define MANUFACTURER_ID 0x1122334455 /**< Manufacturer ID, part of System ID. Will be passed to Device Information Service. */ #define ORG_UNIQUE_ID 0x667788 /**< Organizational Unique ID, part of System ID. Will be passed to Device Information Service. */ #define NUS_SERVICE_UUID_TYPE BLE_UUID_TYPE_VENDOR_BEGIN #define APP_ADV_INTERVAL 300 //40 /**< The advertising interval (in units of 0.625 ms. This value corresponds to 25 ms). */ #define APP_ADV_TIMEOUT_IN_SECONDS 180 /**< The advertising timeout in units of seconds. */ #define APP_TIMER_PRESCALER 0 /**< Value of the RTC1 PRESCALER register. */ #define APP_TIMER_OP_QUEUE_SIZE 16 /**< Size of timer operation queues. */ #define BATTERY_LEVEL_MEAS_INTERVAL APP_TIMER_TICKS(60000, APP_TIMER_PRESCALER) /**< Battery level measurement interval (ticks). */ #define BATTERY_LEVEL_INCREMENT 1 /**< Increment between each simulated battery level measurement. */ #define HEART_RATE_MEAS_INTERVAL APP_TIMER_TICKS(1000, APP_TIMER_PRESCALER) /**< Heart rate measurement interval (ticks). */ #define MIN_HEART_RATE 140 /**< Minimum heart rate as returned by the simulated measurement function. */ #define MAX_HEART_RATE 300 /**< Maximum heart rate as returned by the simulated measurement function. */ #define HEART_RATE_INCREMENT 10 /**< Value by which the heart rate is incremented/decremented for each call to the simulated measurement function. */ #define RR_INTERVAL_INTERVAL APP_TIMER_TICKS(300, APP_TIMER_PRESCALER) /**< RR interval interval (ticks). */ #define MIN_RR_INTERVAL 100 /**< Minimum RR interval as returned by the simulated measurement function. */ #define MAX_RR_INTERVAL 500 /**< Maximum RR interval as returned by the simulated measurement function. */ #define RR_INTERVAL_INCREMENT 1 /**< Value by which the RR interval is incremented/decremented for each call to the simulated measurement function. */ #define SENSOR_CONTACT_DETECTED_INTERVAL APP_TIMER_TICKS(5000, APP_TIMER_PRESCALER) /**< Sensor Contact Detected toggle interval (ticks). */ #define MIN_CONN_INTERVAL MSEC_TO_UNITS(7.5, UNIT_1_25_MS) /**< Minimum acceptable connection interval (0.4 seconds). */ #define MAX_CONN_INTERVAL MSEC_TO_UNITS(7.5, UNIT_1_25_MS) /**< Maximum acceptable connection interval (0.65 second). */ #define SLAVE_LATENCY 0 /**< Slave latency. */ #define CONN_SUP_TIMEOUT MSEC_TO_UNITS(32000, UNIT_10_MS) /**< Connection supervisory timeout (4 seconds). */ #define FIRST_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(5000, APP_TIMER_PRESCALER) /**< 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, APP_TIMER_PRESCALER)/**< Time between each call to sd_ble_gap_conn_param_update after the first call (30 seconds). */ #define MAX_CONN_PARAMS_UPDATE_COUNT 5 /**< 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 /**< Maximum encryption key size. */ #define DEAD_BEEF 0xDEADBEEF /**< Value used as error code on stack dump, can be used to identify stack location on stack unwind. */ #ifdef BLE_DFU_APP_SUPPORT #define DFU_REV_MAJOR 0x00 /** DFU Major revision number to be exposed. */ #define DFU_REV_MINOR 0x01 /** DFU Minor revision number to be exposed. */ #define DFU_REVISION ((DFU_REV_MAJOR << 8) | DFU_REV_MINOR) /** DFU Revision number to be exposed. Combined of major and minor versions. */ #define APP_SERVICE_HANDLE_START 0x000C /**< Handle of first application specific service when when service changed characteristic is present. */ #define BLE_HANDLE_MAX 0xFFFF /**< Max handle value in BLE. */ #define UART_TX_BUF_SIZE 1024 /**< UART TX buffer size. */ #define UART_RX_BUF_SIZE 1 /**< UART RX buffer size. */ STATIC_ASSERT(IS_SRVC_CHANGED_CHARACT_PRESENT); /** When having DFU Service support in application the Service Changed Characteristic should always be present. */ #endif // BLE_DFU_APP_SUPPORT uint32_t err_code; static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID; /**< Handle of the current connection. */ static ble_bas_t m_bas; /**< Structure used to identify the battery service. */ static ble_hrs_t m_hrs; /**< Structure used to identify the heart rate service. */ static ble_nus_t m_nus; //static bool m_rr_interval_enabled = true; /**< Flag for enabling and disabling the registration of new RR interval measurements (the purpose of disabling this is just to test sending HRM without RR interval data. */ static sensorsim_cfg_t m_battery_sim_cfg; /**< Battery Level sensor simulator configuration. */ static sensorsim_state_t m_battery_sim_state; /**< Battery Level sensor simulator state. */ static sensorsim_cfg_t m_heart_rate_sim_cfg; /**< Heart Rate sensor simulator configuration. */ static sensorsim_state_t m_heart_rate_sim_state; /**< Heart Rate sensor simulator state. */ static sensorsim_cfg_t m_rr_interval_sim_cfg; /**< RR Interval sensor simulator configuration. */ static sensorsim_state_t m_rr_interval_sim_state; /**< RR Interval sensor simulator state. */ static dm_application_instance_t m_app_handle; /**< Application identifier allocated by device manager */ static ble_uuid_t m_adv_uuids[] = {{BLE_UUID_HEART_RATE_SERVICE, BLE_UUID_TYPE_BLE}, {BLE_UUID_HEALTH_THERMOMETER_SERVICE, BLE_UUID_TYPE_BLE}, //{BLE_UUID_NUS_SERVICE, NUS_SERVICE_UUID_TYPE}, {BLE_UUID_BATTERY_SERVICE, BLE_UUID_TYPE_BLE}, {BLE_UUID_DEVICE_INFORMATION_SERVICE, BLE_UUID_TYPE_BLE}}; /**< Universally unique service identifiers. */ APP_TIMER_DEF(m_battery_timer_id); /**< Battery timer. */ APP_TIMER_DEF(m_heart_rate_timer_id); /**< Heart rate measurement timer. */ //APP_TIMER_DEF(m_rr_interval_timer_id); /**< RR interval timer. */ /**< RR interval timer. */ APP_TIMER_DEF(m_sensor_contact_timer_id); /**< Sensor contact detected timer. */ //======================================================SAADC and ECG and Battery========================================= #define SAMPLES_IN_BUFFER 24 #define SAADC_BURST_MODE 1 //Set to 1 to enable BURST mode, otherwise set to 0. static const nrf_drv_timer_t m_timer = NRF_DRV_TIMER_INSTANCE(2); static nrf_saadc_value_t m_buffer_pool[2][SAMPLES_IN_BUFFER]; static nrf_ppi_channel_t m_ppi_channel; uint8_t count=0; static uint8_t battery_level; static uint16_t tempconst = 0; static uint32_t seq_num=3; static bool LA_LDO = false;//RA_LDO=false; static bool mode_2_flag = false,data_ready = false; float ecg[1008]={0}; void timer_handler(nrf_timer_event_t event_type, void* p_context) { } void saadc_sampling_event_init(void) { ret_code_t err_code; err_code = nrf_drv_ppi_init(); APP_ERROR_CHECK(err_code); err_code = nrf_drv_timer_init(&m_timer, NULL, timer_handler); APP_ERROR_CHECK(err_code); /* setup m_timer for compare event every 400ms */ uint32_t ticks = nrf_drv_timer_us_to_ticks(&m_timer, 2000); nrf_drv_timer_extended_compare(&m_timer, NRF_TIMER_CC_CHANNEL0, ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, false); nrf_drv_timer_enable(&m_timer); uint32_t timer_compare_event_addr = nrf_drv_timer_compare_event_address_get(&m_timer, NRF_TIMER_CC_CHANNEL0); uint32_t saadc_sample_event_addr = nrf_drv_saadc_sample_task_get(); /* setup ppi channel so that timer compare event is triggering sample task in SAADC */ err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel); APP_ERROR_CHECK(err_code); err_code = nrf_drv_ppi_channel_assign(m_ppi_channel, timer_compare_event_addr, saadc_sample_event_addr); APP_ERROR_CHECK(err_code); } void send_to_app(uint8_t *buff) { uint32_t err_code; if(seq_num<=84) { buff[0] = seq_num; //seq_num++; } else { seq_num = 3; buff[0] = seq_num; } err_code = ble_nus_string_send(&m_nus, buff, 20); if (err_code != NRF_ERROR_INVALID_STATE) { APP_ERROR_CHECK(err_code); } } int16_t ADC_values[12] = {0},ADC_values1[12] = {0},Battery_values[12] = {0},Battery_values1[12] = {0}; nrf_saadc_value_t *data_buffer, *data_buffer1; void mode2_function(nrf_drv_saadc_evt_t const * p_event) { } void packet_form(nrf_drv_saadc_evt_t const * p_event) { } void saadc_callback(nrf_drv_saadc_evt_t const * p_event) { if (p_event->type == NRF_DRV_SAADC_EVT_DONE) { ret_code_t err_code; err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, SAMPLES_IN_BUFFER); APP_ERROR_CHECK(err_code); if(mode_2_flag!=true) { packet_form(p_event); } if (mode_2_flag==true) mode2_function(p_event); } } void saadc_init(void) { ret_code_t err_code; //###########################################ANI0 nrf_saadc_channel_config_t channel_0_config; channel_0_config.resistor_p = NRF_SAADC_RESISTOR_DISABLED; channel_0_config.resistor_n = NRF_SAADC_RESISTOR_DISABLED; channel_0_config.gain = NRF_SAADC_GAIN1_6; channel_0_config.reference = NRF_SAADC_REFERENCE_VDD4; channel_0_config.acq_time = NRF_SAADC_ACQTIME_10US; channel_0_config.mode = NRF_SAADC_MODE_SINGLE_ENDED; channel_0_config.pin_p = NRF_SAADC_INPUT_AIN0; channel_0_config.pin_n = NRF_SAADC_INPUT_DISABLED; //#############################################AIN1 nrf_saadc_channel_config_t channel_1_config; channel_1_config.resistor_p = NRF_SAADC_RESISTOR_DISABLED; channel_1_config.resistor_n = NRF_SAADC_RESISTOR_DISABLED; channel_1_config.gain = NRF_SAADC_GAIN1_6; channel_1_config.reference = NRF_SAADC_REFERENCE_VDD4; channel_1_config.acq_time = NRF_SAADC_ACQTIME_10US; channel_1_config.mode = NRF_SAADC_MODE_SINGLE_ENDED; channel_1_config.pin_p = NRF_SAADC_INPUT_AIN1; channel_1_config.pin_n = NRF_SAADC_INPUT_DISABLED; nrf_drv_saadc_config_t saadc_config; //Configure SAADC saadc_config.resolution = NRF_SAADC_RESOLUTION_12BIT; //Set SAADC resolution to 12-bit. This will make the SAADC output values from 0 (when input voltage is 0V) to 2^12=2048 (when input voltage is 3.6V for channel gain setting of 1/6). saadc_config.oversample = NRF_SAADC_OVERSAMPLE_DISABLED; //Set oversample to 4x. This will make the SAADC output a single averaged value when the SAMPLE task is triggered 4 times. saadc_config.interrupt_priority = APP_IRQ_PRIORITY_LOW; //Set SAADC interrupt to low priority. err_code = nrf_drv_saadc_init(&saadc_config, saadc_callback); APP_ERROR_CHECK(err_code); err_code = nrf_drv_saadc_channel_init(0, &channel_0_config); APP_ERROR_CHECK(err_code); err_code = nrf_drv_saadc_channel_init(1, &channel_1_config); APP_ERROR_CHECK(err_code); err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0],SAMPLES_IN_BUFFER); APP_ERROR_CHECK(err_code); err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[1],SAMPLES_IN_BUFFER); APP_ERROR_CHECK(err_code); } //=====================================================SAADC and ECG and Battery END====================================== ////===================================================GPIO configuration==================================================== // #ifdef Lo_pos #define LA_disconnect Lo_pos //#define RA_disconnect Lo_neg #endif #ifndef LA_disconnect #error "Please indicate input pin" #endif // // // void in_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action) { LA_LDO = true; } //void in_pin_handler1(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action) //{ // RA_LDO = true; //} // static void gpio_init(void) { ret_code_t err_code; err_code = nrf_drv_gpiote_init(); APP_ERROR_CHECK(err_code); nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_LOTOHI(true); in_config.pull = NRF_GPIO_PIN_PULLUP; err_code = nrf_drv_gpiote_in_init(LA_disconnect, &in_config, in_pin_handler); APP_ERROR_CHECK(err_code); nrf_drv_gpiote_in_event_enable(LA_disconnect, true); // err_code = nrf_drv_gpiote_in_init(RA_disconnect, &in_config, in_pin_handler1); // APP_ERROR_CHECK(err_code); // // nrf_drv_gpiote_in_event_enable(RA_disconnect, true); } //======================================================================================================================== //=========================================UART====================================================== //#define ENABLE_LOOPBACK_TEST /**< if defined, then this example will be a loopback test, which means that TX should be connected to RX to get data loopback. */ #define MAX_TEST_DATA_BYTES (3U) /**< max number of test bytes to be used for tx and rx. */ #define UART_TX_BUF_SIZE 256 /**< UART TX buffer size. */ #define UART_RX_BUF_SIZE 1 /**< UART RX buffer size. */ void saadc_sampling_event_enable(void) { ret_code_t err_code = nrf_drv_ppi_channel_enable(m_ppi_channel); APP_ERROR_CHECK(err_code); } void saadc_sampling_event_disable(void) { ret_code_t err_code = nrf_drv_ppi_channel_disable(m_ppi_channel); APP_ERROR_CHECK(err_code); } static void nus_data_handler(ble_nus_t * p_nus, uint8_t * p_data, uint16_t length) { if (p_data[0] == 1) { saadc_sampling_event_enable(); mode_2_flag = false; } if (p_data[0] == 2) { mode_2_flag = true; } } //=========================================UART====================================================== //#define ENABLE_LOOPBACK_TEST /**< if defined, then this example will be a loopback test, which means that TX should be connected to RX to get data loopback. */ #define MAX_TEST_DATA_BYTES (3U) /**< max number of test bytes to be used for tx and rx. */ #define UART_TX_BUF_SIZE 256 /**< UART TX buffer size. */ #define UART_RX_BUF_SIZE 1 /**< UART RX buffer size. */ /**@snippet [Handling the data received over BLE] */ void uart_event_handle(app_uart_evt_t * p_event) { //static uint8_t data_array[BLE_NUS_MAX_DATA_LEN]; //static uint8_t index = 0; //uint32_t err_code; switch (p_event->evt_type) { case APP_UART_COMMUNICATION_ERROR: APP_ERROR_HANDLER(p_event->data.error_communication); break; case APP_UART_FIFO_ERROR: APP_ERROR_HANDLER(p_event->data.error_code); break; default: break; } } /**@snippet [Handling the data received over UART] */ void uart_int(void) { const app_uart_comm_params_t comm_params = { RX_PIN_NUMBER, TX_PIN_NUMBER, RTS_PIN_NUMBER, CTS_PIN_NUMBER, APP_UART_FLOW_CONTROL_ENABLED, false, UART_BAUDRATE_BAUDRATE_Baud115200 }; APP_UART_FIFO_INIT(&comm_params, UART_RX_BUF_SIZE, UART_TX_BUF_SIZE, uart_event_handle, APP_IRQ_PRIORITY_LOW, err_code); APP_ERROR_CHECK(err_code); } //============================================UART END=================================================== #ifdef BLE_DFU_APP_SUPPORT static ble_dfu_t m_dfus; /**< Structure used to identify the DFU service. */ #endif // BLE_DFU_APP_SUPPORT /**@brief Callback function for 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] 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 performing battery measurement and updating the Battery Level characteristic * in Battery Service. */ static void battery_level_update(void) { err_code = ble_bas_battery_level_update(&m_bas, battery_level); if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE) && (err_code != BLE_ERROR_NO_TX_PACKETS) && (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING) ) { APP_ERROR_HANDLER(err_code); } } /**@brief Function for handling the Battery measurement timer timeout. * * @details This function will be called each time the battery level measurement timer expires. * * @param[in] p_context Pointer used for passing some arbitrary information (context) from the * app_start_timer() call to the timeout handler. */ static void battery_level_meas_timeout_handler(void * p_context) { UNUSED_PARAMETER(p_context); battery_level_update(); } //==================================================HRS START===================================== extern uint16_t heartRate; extern uint16_t RPeak[10]; extern uint8_t beat; extern uint16_t fs; uint8_t HR[8] = {0} ; static void heart_rate_meas_timeout_handler(void * p_context) { uint16_t r_peaks[10]={0},rr_interval; UNUSED_PARAMETER(p_context); err_code = ble_hrs_heart_rate_measurement_send(&m_hrs, heartRate); if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE) && (err_code != BLE_ERROR_NO_TX_PACKETS) && (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING) ) { APP_ERROR_HANDLER(err_code); } memcpy(r_peaks,RPeak,(beat*2)); for(uint8_t i=0;i<(beat-1);i++) { rr_interval = (r_peaks[i+1] - r_peaks[i]); HR[i] = (uint8_t)((60*fs)/(rr_interval)); } ble_hrs_rr_interval_add(&m_hrs, HR); } /**@brief Function for handling the Sensor Contact Detected timer timeout. * * @details This function will be called each time the Sensor Contact Detected timer expires. * * @param[in] p_context Pointer used for passing some arbitrary information (context) from the * app_start_timer() call to the timeout handler. */ static void sensor_contact_detected_timeout_handler(void * p_context) { static bool sensor_contact_detected = false; UNUSED_PARAMETER(p_context); sensor_contact_detected = !sensor_contact_detected; ble_hrs_sensor_contact_detected_update(&m_hrs, sensor_contact_detected); } //==================================================HRS END===================================== /**@brief Function for the Timer initialization. * * @details Initializes the timer module. This creates and starts application timers. */ static void timers_init(void) { // Initialize timer module. APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_OP_QUEUE_SIZE, false); // Create timers. err_code = app_timer_create(&m_battery_timer_id, APP_TIMER_MODE_REPEATED, battery_level_meas_timeout_handler); APP_ERROR_CHECK(err_code); err_code = app_timer_create(&m_heart_rate_timer_id, APP_TIMER_MODE_REPEATED, heart_rate_meas_timeout_handler); APP_ERROR_CHECK(err_code); err_code = app_timer_create(&m_sensor_contact_timer_id, APP_TIMER_MODE_REPEATED, sensor_contact_detected_timeout_handler); APP_ERROR_CHECK(err_code); } /**@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) { 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); err_code = sd_ble_gap_appearance_set(BLE_APPEARANCE_HEART_RATE_SENSOR_HEART_RATE_BELT); APP_ERROR_CHECK(err_code); 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); } #ifdef BLE_DFU_APP_SUPPORT /**@brief Function for stopping advertising. */ static void advertising_stop(void) { err_code = sd_ble_gap_adv_stop(); APP_ERROR_CHECK(err_code); err_code = bsp_indication_set(BSP_INDICATE_IDLE); APP_ERROR_CHECK(err_code); } /**@brief Function for loading application-specific context after establishing a secure connection. * * @details This function will load the application context and check if the ATT table is marked as * changed. If the ATT table is marked as changed, a Service Changed Indication * is sent to the peer if the Service Changed CCCD is set to indicate. * * @param[in] p_handle The Device Manager handle that identifies the connection for which the context * should be loaded. */ static void app_context_load(dm_handle_t const * p_handle) { static uint32_t context_data; dm_application_context_t context; context.len = sizeof(context_data); context.p_data = (uint8_t *)&context_data; err_code = dm_application_context_get(p_handle, &context); if (err_code == NRF_SUCCESS) { // Send Service Changed Indication if ATT table has changed. if ((context_data & (DFU_APP_ATT_TABLE_CHANGED << DFU_APP_ATT_TABLE_POS)) != 0) { err_code = sd_ble_gatts_service_changed(m_conn_handle, APP_SERVICE_HANDLE_START, BLE_HANDLE_MAX); if ((err_code != NRF_SUCCESS) && (err_code != BLE_ERROR_INVALID_CONN_HANDLE) && (err_code != NRF_ERROR_INVALID_STATE) && (err_code != BLE_ERROR_NO_TX_PACKETS) && (err_code != NRF_ERROR_BUSY) && (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)) { APP_ERROR_HANDLER(err_code); } } err_code = dm_application_context_delete(p_handle); APP_ERROR_CHECK(err_code); } else if (err_code == DM_NO_APP_CONTEXT) { // No context available. Ignore. } else { APP_ERROR_HANDLER(err_code); } } /** @snippet [DFU BLE Reset prepare] */ /**@brief Function for preparing for system reset. * * @details This function implements @ref dfu_app_reset_prepare_t. It will be called by * @ref dfu_app_handler.c before entering the bootloader/DFU. * This allows the current running application to shut down gracefully. */ static void reset_prepare(void) { if (m_conn_handle != BLE_CONN_HANDLE_INVALID) { // Disconnect from peer. err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); APP_ERROR_CHECK(err_code); err_code = bsp_indication_set(BSP_INDICATE_IDLE); APP_ERROR_CHECK(err_code); } else { // If not connected, the device will be advertising. Hence stop the advertising. advertising_stop(); } err_code = ble_conn_params_stop(); APP_ERROR_CHECK(err_code); nrf_delay_ms(500); } /** @snippet [DFU BLE Reset prepare] */ #endif // BLE_DFU_APP_SUPPORT /**@brief Function for initializing services that will be used by the application. * * @details Initialize the Heart Rate, Battery and Device Information services. */ static void services_init(void) { ble_hrs_init_t hrs_init; ble_bas_init_t bas_init; ble_dis_init_t dis_init; ble_nus_init_t nus_init; uint8_t body_sensor_location; // Initialize Heart Rate Service. body_sensor_location = BLE_HRS_BODY_SENSOR_LOCATION_FINGER; memset(&hrs_init, 0, sizeof(hrs_init)); hrs_init.evt_handler = NULL; hrs_init.is_sensor_contact_supported = true; hrs_init.p_body_sensor_location = &body_sensor_location; // Here the sec level for the Heart Rate Service can be changed/increased. BLE_GAP_CONN_SEC_MODE_SET_OPEN(&hrs_init.hrs_hrm_attr_md.cccd_write_perm); BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&hrs_init.hrs_hrm_attr_md.read_perm); BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&hrs_init.hrs_hrm_attr_md.write_perm); BLE_GAP_CONN_SEC_MODE_SET_OPEN(&hrs_init.hrs_bsl_attr_md.read_perm); BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&hrs_init.hrs_bsl_attr_md.write_perm); err_code = ble_hrs_init(&m_hrs, &hrs_init); APP_ERROR_CHECK(err_code); // Initialize UART Service. memset(&nus_init, 0, sizeof(nus_init)); nus_init.data_handler = nus_data_handler; err_code = ble_nus_init(&m_nus, &nus_init); APP_ERROR_CHECK(err_code); // Initialize Battery Service. memset(&bas_init, 0, sizeof(bas_init)); // Here the sec level for the Battery Service can be changed/increased. BLE_GAP_CONN_SEC_MODE_SET_OPEN(&bas_init.battery_level_char_attr_md.cccd_write_perm); BLE_GAP_CONN_SEC_MODE_SET_OPEN(&bas_init.battery_level_char_attr_md.read_perm); BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&bas_init.battery_level_char_attr_md.write_perm); BLE_GAP_CONN_SEC_MODE_SET_OPEN(&bas_init.battery_level_report_read_perm); bas_init.evt_handler = NULL; bas_init.support_notification = true; bas_init.p_report_ref = NULL; bas_init.initial_batt_level = 100; err_code = ble_bas_init(&m_bas, &bas_init); APP_ERROR_CHECK(err_code); // Initialize Device Information Service. memset(&dis_init, 0, sizeof(dis_init)); ble_srv_ascii_to_utf8(&dis_init.manufact_name_str, (char *)MANUFACTURER_NAME); BLE_GAP_CONN_SEC_MODE_SET_OPEN(&dis_init.dis_attr_md.read_perm); BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&dis_init.dis_attr_md.write_perm); err_code = ble_dis_init(&dis_init); APP_ERROR_CHECK(err_code); #ifdef BLE_DFU_APP_SUPPORT /** @snippet [DFU BLE Service initialization] */ ble_dfu_init_t dfus_init; // Initialize the Device Firmware Update Service. memset(&dfus_init, 0, sizeof(dfus_init)); dfus_init.evt_handler = dfu_app_on_dfu_evt; dfus_init.error_handler = NULL; dfus_init.evt_handler = dfu_app_on_dfu_evt; dfus_init.revision = DFU_REVISION; err_code = ble_dfu_init(&m_dfus, &dfus_init); APP_ERROR_CHECK(err_code); dfu_app_reset_prepare_set(reset_prepare); dfu_app_dm_appl_instance_set(m_app_handle); /** @snippet [DFU BLE Service initialization] */ #endif // BLE_DFU_APP_SUPPORT } /**@brief Function for initializing the sensor simulators. */ static void sensor_simulator_init(void) { sensorsim_init(&m_battery_sim_state, &m_battery_sim_cfg); m_heart_rate_sim_cfg.min = MIN_HEART_RATE; m_heart_rate_sim_cfg.max = MAX_HEART_RATE; m_heart_rate_sim_cfg.incr = HEART_RATE_INCREMENT; m_heart_rate_sim_cfg.start_at_max = false; sensorsim_init(&m_heart_rate_sim_state, &m_heart_rate_sim_cfg); m_rr_interval_sim_cfg.min = MIN_RR_INTERVAL; m_rr_interval_sim_cfg.max = MAX_RR_INTERVAL; m_rr_interval_sim_cfg.incr = RR_INTERVAL_INCREMENT; m_rr_interval_sim_cfg.start_at_max = false; sensorsim_init(&m_rr_interval_sim_state, &m_rr_interval_sim_cfg); } /**@brief Function for starting application timers. */ static void application_timers_start(void) { // Start application timers. err_code = app_timer_start(m_battery_timer_id, BATTERY_LEVEL_MEAS_INTERVAL, NULL); APP_ERROR_CHECK(err_code); err_code = app_timer_start(m_heart_rate_timer_id, HEART_RATE_MEAS_INTERVAL, NULL); APP_ERROR_CHECK(err_code); err_code = app_timer_start(m_sensor_contact_timer_id, SENSOR_CONTACT_DETECTED_INTERVAL, NULL); APP_ERROR_CHECK(err_code); } /**@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) { 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) { 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 = m_hrs.hrm_handles.cccd_handle; 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) { err_code = bsp_indication_set(BSP_INDICATE_IDLE); APP_ERROR_CHECK(err_code); // Prepare wakeup buttons. err_code = bsp_btn_ble_sleep_mode_prepare(); APP_ERROR_CHECK(err_code); // Go to system-off mode (this function will not return; wakeup will cause a reset). err_code = sd_power_system_off(); APP_ERROR_CHECK(err_code); } /**@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) { switch (ble_adv_evt) { case BLE_ADV_EVT_FAST: err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING); APP_ERROR_CHECK(err_code); break; case BLE_ADV_EVT_IDLE: sleep_mode_enter(); break; default: break; } } /**@brief Function for handling the Application's BLE Stack events. * * @param[in] p_ble_evt Bluetooth stack event. */ static void on_ble_evt(ble_evt_t * p_ble_evt) { //uint8_t *dat; switch (p_ble_evt->header.evt_id) { case BLE_GAP_EVT_CONNECTED: err_code = bsp_indication_set(BSP_INDICATE_CONNECTED); APP_ERROR_CHECK(err_code); m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle; break; case BLE_GAP_EVT_DISCONNECTED: m_conn_handle = BLE_CONN_HANDLE_INVALID; saadc_sampling_event_disable(); break; case BLE_GATTS_EVT_TIMEOUT: if (p_ble_evt->evt.gatts_evt.params.timeout.src == BLE_GATT_TIMEOUT_SRC_PROTOCOL) { err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); APP_ERROR_CHECK(err_code); } break; case BLE_GAP_EVT_SEC_PARAMS_REQUEST: // Pairing not supported err_code = sd_ble_gap_sec_params_reply(m_conn_handle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL); APP_ERROR_CHECK(err_code); break; case BLE_GATTS_EVT_SYS_ATTR_MISSING: // No system attributes have been stored. err_code = sd_ble_gatts_sys_attr_set(m_conn_handle, NULL, 0, 0); APP_ERROR_CHECK(err_code); break; // case BLE_GATTS_EVT_WRITE: // dat = p_ble_evt->evt.gatts_evt.params.write.data; // if(dat[0]==1) // { // saadc_sampling_event_enable(); // } // else // { // saadc_sampling_event_disable(); // } // break; default: // No implementation needed. break; } } /**@brief Function for dispatching a BLE stack event to all modules with a BLE stack event handler. * * @details This function is called from the BLE Stack event interrupt handler after a BLE stack * event has been received. * * @param[in] p_ble_evt Bluetooth stack event. */ static void ble_evt_dispatch(ble_evt_t * p_ble_evt) { dm_ble_evt_handler(p_ble_evt); ble_hrs_on_ble_evt(&m_hrs, p_ble_evt); ble_nus_on_ble_evt(&m_nus, p_ble_evt); ble_bas_on_ble_evt(&m_bas, p_ble_evt); ble_conn_params_on_ble_evt(p_ble_evt); bsp_btn_ble_on_ble_evt(p_ble_evt); #ifdef BLE_DFU_APP_SUPPORT /** @snippet [Propagating BLE Stack events to DFU Service] */ ble_dfu_on_ble_evt(&m_dfus, p_ble_evt); /** @snippet [Propagating BLE Stack events to DFU Service] */ #endif // BLE_DFU_APP_SUPPORT on_ble_evt(p_ble_evt); ble_advertising_on_ble_evt(p_ble_evt); } /**@brief Function for dispatching a system event to interested modules. * * @details This function is called from the System event interrupt handler after a system * event has been received. * * @param[in] sys_evt System stack event. */ static void sys_evt_dispatch(uint32_t sys_evt) { pstorage_sys_event_handler(sys_evt); ble_advertising_on_sys_evt(sys_evt); } /**@brief Function for initializing the BLE stack. * * @details Initializes the SoftDevice and the BLE event interrupt. */ static void ble_stack_init(void) { nrf_clock_lf_cfg_t clock_lf_cfg = NRF_CLOCK_LFCLKSRC; // Initialize the SoftDevice handler module. SOFTDEVICE_HANDLER_INIT(&clock_lf_cfg, NULL); ble_enable_params_t ble_enable_params; err_code = softdevice_enable_get_default_config(CENTRAL_LINK_COUNT, PERIPHERAL_LINK_COUNT, &ble_enable_params); APP_ERROR_CHECK(err_code); #ifdef BLE_DFU_APP_SUPPORT ble_enable_params.gatts_enable_params.service_changed = 1; #endif // BLE_DFU_APP_SUPPORT //Check the ram settings against the used number of links CHECK_RAM_START_ADDR(CENTRAL_LINK_COUNT,PERIPHERAL_LINK_COUNT); // Enable BLE stack. err_code = softdevice_enable(&ble_enable_params); APP_ERROR_CHECK(err_code); // Register with the SoftDevice handler module for BLE events. err_code = softdevice_ble_evt_handler_set(ble_evt_dispatch); APP_ERROR_CHECK(err_code); // Register with the SoftDevice handler module for BLE events. err_code = softdevice_sys_evt_handler_set(sys_evt_dispatch); APP_ERROR_CHECK(err_code); } /**@brief Function for handling events from the BSP module. * * @param[in] event Event generated by button press. */ void bsp_event_handler(bsp_event_t event) { switch (event) { case BSP_EVENT_SLEEP: sleep_mode_enter(); break; case BSP_EVENT_DISCONNECT: err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); if (err_code != NRF_ERROR_INVALID_STATE) { APP_ERROR_CHECK(err_code); } break; case BSP_EVENT_WHITELIST_OFF: err_code = ble_advertising_restart_without_whitelist(); if (err_code != NRF_ERROR_INVALID_STATE) { APP_ERROR_CHECK(err_code); } break; default: break; } } /**@brief Function for handling the Device Manager events. * * @param[in] p_evt Data associated to the device manager event. */ static uint32_t device_manager_evt_handler(dm_handle_t const * p_handle, dm_event_t const * p_event, ret_code_t event_result) { APP_ERROR_CHECK(event_result); switch(p_event->event_id) { case DM_EVT_LINK_SECURED: // Send a single temperature measurement if indication is enabled. // NOTE: For this to work, make sure ble_hts_on_ble_evt() is called before // dm_ble_evt_handler() in ble_evt_dispatch(). /* err_code = ble_hts_is_indication_enabled(&m_hts, &is_indication_enabled); APP_ERROR_CHECK(err_code); if (is_indication_enabled) { temperature_measurement_send(); } break;*/ default: break; } #ifdef BLE_DFU_APP_SUPPORT if (p_event->event_id == DM_EVT_LINK_SECURED) { app_context_load(p_handle); } #endif // BLE_DFU_APP_SUPPORT return NRF_SUCCESS; } /**@brief Function for the Device Manager initialization. * * @param[in] erase_bonds Indicates whether bonding information should be cleared from * persistent storage during initialization of the Device Manager. */ static void device_manager_init(bool erase_bonds) { dm_init_param_t init_param = {.clear_persistent_data = erase_bonds}; dm_application_param_t register_param; // Initialize persistent storage module. err_code = pstorage_init(); APP_ERROR_CHECK(err_code); err_code = dm_init(&init_param); APP_ERROR_CHECK(err_code); memset(®ister_param.sec_param, 0, sizeof(ble_gap_sec_params_t)); register_param.sec_param.bond = SEC_PARAM_BOND; register_param.sec_param.mitm = SEC_PARAM_MITM; register_param.sec_param.lesc = SEC_PARAM_LESC; register_param.sec_param.keypress = SEC_PARAM_KEYPRESS; register_param.sec_param.io_caps = SEC_PARAM_IO_CAPABILITIES; register_param.sec_param.oob = SEC_PARAM_OOB; register_param.sec_param.min_key_size = SEC_PARAM_MIN_KEY_SIZE; register_param.sec_param.max_key_size = SEC_PARAM_MAX_KEY_SIZE; register_param.evt_handler = device_manager_evt_handler; register_param.service_type = DM_PROTOCOL_CNTXT_GATT_SRVR_ID; err_code = dm_register(&m_app_handle, ®ister_param); APP_ERROR_CHECK(err_code); } /**@brief Function for initializing the Advertising functionality. */ static void advertising_init(void) { ble_advdata_t advdata; // Build advertising data struct to pass into @ref ble_advertising_init. memset(&advdata, 0, sizeof(advdata)); advdata.name_type = BLE_ADVDATA_FULL_NAME; advdata.include_appearance = true; advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE; advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]); advdata.uuids_complete.p_uuids = m_adv_uuids; ble_adv_modes_config_t options = {0}; options.ble_adv_fast_enabled = BLE_ADV_FAST_ENABLED; options.ble_adv_fast_interval = APP_ADV_INTERVAL; options.ble_adv_fast_timeout = APP_ADV_TIMEOUT_IN_SECONDS; err_code = ble_advertising_init(&advdata, NULL, &options, on_adv_evt, NULL); APP_ERROR_CHECK(err_code); } /**@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) { bsp_event_t startup_event; err_code = bsp_init(BSP_INIT_LED | BSP_INIT_BUTTONS, APP_TIMER_TICKS(100, APP_TIMER_PRESCALER), 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 for the Power manager. */ static void power_manage(void) { err_code = sd_app_evt_wait(); APP_ERROR_CHECK(err_code); } /**@brief Function for application main entry. */ int main(void) { bool erase_bonds; // Initialize. gpio_init(); app_trace_init(); timers_init(); buttons_leds_init(&erase_bonds); ble_stack_init(); device_manager_init(erase_bonds); gap_params_init(); services_init(); advertising_init(); sensor_simulator_init(); conn_params_init(); // Start execution. application_timers_start(); err_code = ble_advertising_start(BLE_ADV_MODE_FAST); APP_ERROR_CHECK(err_code); saadc_sampling_event_init(); saadc_init(); // Enter main loop. for (;;) { if (data_ready == true) { data_ready = false; //if 996 samples done then call hr_algorithm hr_algorithm(); } power_manage(); } }